2016-08-14 00:31:14 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* amutils.c
|
|
|
|
* SQL-level APIs related to index access methods.
|
|
|
|
*
|
2021-01-02 19:06:25 +01:00
|
|
|
* Copyright (c) 2016-2021, PostgreSQL Global Development Group
|
2016-08-14 00:31:14 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/backend/utils/adt/amutils.c
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2019-12-27 00:09:00 +01:00
|
|
|
#include "access/amapi.h"
|
2016-08-14 00:31:14 +02:00
|
|
|
#include "access/htup_details.h"
|
|
|
|
#include "catalog/pg_class.h"
|
|
|
|
#include "catalog/pg_index.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* Convert string property name to enum, for efficiency */
|
|
|
|
struct am_propname
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
IndexAMProperty prop;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct am_propname am_propnames[] =
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"asc", AMPROP_ASC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"desc", AMPROP_DESC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"nulls_first", AMPROP_NULLS_FIRST
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"nulls_last", AMPROP_NULLS_LAST
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"orderable", AMPROP_ORDERABLE
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"distance_orderable", AMPROP_DISTANCE_ORDERABLE
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"returnable", AMPROP_RETURNABLE
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"search_array", AMPROP_SEARCH_ARRAY
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"search_nulls", AMPROP_SEARCH_NULLS
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"clusterable", AMPROP_CLUSTERABLE
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"index_scan", AMPROP_INDEX_SCAN
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"bitmap_scan", AMPROP_BITMAP_SCAN
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"backward_scan", AMPROP_BACKWARD_SCAN
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"can_order", AMPROP_CAN_ORDER
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"can_unique", AMPROP_CAN_UNIQUE
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"can_multi_col", AMPROP_CAN_MULTI_COL
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"can_exclude", AMPROP_CAN_EXCLUDE
|
2018-04-08 07:02:05 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"can_include", AMPROP_CAN_INCLUDE
|
|
|
|
},
|
2016-08-14 00:31:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static IndexAMProperty
|
|
|
|
lookup_prop_name(const char *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < lengthof(am_propnames); i++)
|
|
|
|
{
|
|
|
|
if (pg_strcasecmp(am_propnames[i].name, name) == 0)
|
|
|
|
return am_propnames[i].prop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We do not throw an error, so that AMs can define their own properties */
|
|
|
|
return AMPROP_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Common code for properties that are just bit tests of indoptions.
|
|
|
|
*
|
2018-04-08 07:02:05 +02:00
|
|
|
* tuple: the pg_index heaptuple
|
|
|
|
* attno: identify the index column to test the indoptions of.
|
2016-08-14 00:31:14 +02:00
|
|
|
* guard: if false, a boolean false result is forced (saves code in caller).
|
|
|
|
* iopt_mask: mask for interesting indoption bit.
|
|
|
|
* iopt_expect: value for a "true" result (should be 0 or iopt_mask).
|
|
|
|
*
|
|
|
|
* Returns false to indicate a NULL result (for "unknown/inapplicable"),
|
|
|
|
* otherwise sets *res to the boolean value to return.
|
|
|
|
*/
|
|
|
|
static bool
|
2018-04-08 07:02:05 +02:00
|
|
|
test_indoption(HeapTuple tuple, int attno, bool guard,
|
2016-08-14 00:31:14 +02:00
|
|
|
int16 iopt_mask, int16 iopt_expect,
|
|
|
|
bool *res)
|
|
|
|
{
|
|
|
|
Datum datum;
|
|
|
|
bool isnull;
|
|
|
|
int2vector *indoption;
|
|
|
|
int16 indoption_val;
|
|
|
|
|
|
|
|
if (!guard)
|
|
|
|
{
|
|
|
|
*res = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
datum = SysCacheGetAttr(INDEXRELID, tuple,
|
|
|
|
Anum_pg_index_indoption, &isnull);
|
|
|
|
Assert(!isnull);
|
|
|
|
|
|
|
|
indoption = ((int2vector *) DatumGetPointer(datum));
|
|
|
|
indoption_val = indoption->values[attno - 1];
|
|
|
|
|
|
|
|
*res = (indoption_val & iopt_mask) == iopt_expect;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test property of an index AM, index, or index column.
|
|
|
|
*
|
|
|
|
* This is common code for different SQL-level funcs, so the amoid and
|
|
|
|
* index_oid parameters are mutually exclusive; we look up the amoid from the
|
|
|
|
* index_oid if needed, or if no index oid is given, we're looking at AM-wide
|
|
|
|
* properties.
|
|
|
|
*/
|
|
|
|
static Datum
|
|
|
|
indexam_property(FunctionCallInfo fcinfo,
|
|
|
|
const char *propname,
|
|
|
|
Oid amoid, Oid index_oid, int attno)
|
|
|
|
{
|
|
|
|
bool res = false;
|
|
|
|
bool isnull = false;
|
|
|
|
int natts = 0;
|
|
|
|
IndexAMProperty prop;
|
|
|
|
IndexAmRoutine *routine;
|
|
|
|
|
|
|
|
/* Try to convert property name to enum (no error if not known) */
|
|
|
|
prop = lookup_prop_name(propname);
|
|
|
|
|
|
|
|
/* If we have an index OID, look up the AM, and get # of columns too */
|
|
|
|
if (OidIsValid(index_oid))
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_class rd_rel;
|
|
|
|
|
|
|
|
Assert(!OidIsValid(amoid));
|
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(index_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (rd_rel->relkind != RELKIND_INDEX &&
|
|
|
|
rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
|
2016-08-14 00:31:14 +02:00
|
|
|
{
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
amoid = rd_rel->relam;
|
|
|
|
natts = rd_rel->relnatts;
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-04-26 20:47:16 +02:00
|
|
|
* At this point, either index_oid == InvalidOid or it's a valid index
|
|
|
|
* OID. Also, after this test and the one below, either attno == 0 for
|
2018-04-08 07:02:05 +02:00
|
|
|
* index-wide or AM-wide tests, or it's a valid column number in a valid
|
|
|
|
* index.
|
2016-08-14 00:31:14 +02:00
|
|
|
*/
|
|
|
|
if (attno < 0 || attno > natts)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get AM information. If we don't have a valid AM OID, return NULL.
|
|
|
|
*/
|
|
|
|
routine = GetIndexAmRoutineByAmId(amoid, true);
|
|
|
|
if (routine == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there's an AM property routine, give it a chance to override the
|
|
|
|
* generic logic. Proceed if it returns false.
|
|
|
|
*/
|
|
|
|
if (routine->amproperty &&
|
|
|
|
routine->amproperty(index_oid, attno, prop, propname,
|
|
|
|
&res, &isnull))
|
|
|
|
{
|
|
|
|
if (isnull)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_BOOL(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attno > 0)
|
|
|
|
{
|
2018-04-08 07:02:05 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_index rd_index;
|
|
|
|
bool iskey = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle column-level properties. Many of these need the pg_index row
|
|
|
|
* (which we also need to use to check for nonkey atts) so we fetch
|
|
|
|
* that first.
|
|
|
|
*/
|
|
|
|
tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
rd_index = (Form_pg_index) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
Assert(index_oid == rd_index->indexrelid);
|
|
|
|
Assert(attno > 0 && attno <= rd_index->indnatts);
|
|
|
|
|
|
|
|
isnull = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If amcaninclude, we might be looking at an attno for a nonkey
|
|
|
|
* column, for which we (generically) assume that most properties are
|
|
|
|
* null.
|
|
|
|
*/
|
|
|
|
if (routine->amcaninclude
|
|
|
|
&& attno > rd_index->indnkeyatts)
|
|
|
|
iskey = false;
|
|
|
|
|
2016-08-14 00:31:14 +02:00
|
|
|
switch (prop)
|
|
|
|
{
|
|
|
|
case AMPROP_ASC:
|
2018-04-08 07:02:05 +02:00
|
|
|
if (iskey &&
|
|
|
|
test_indoption(tuple, attno, routine->amcanorder,
|
2016-08-14 00:31:14 +02:00
|
|
|
INDOPTION_DESC, 0, &res))
|
2018-04-08 07:02:05 +02:00
|
|
|
isnull = false;
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_DESC:
|
2018-04-08 07:02:05 +02:00
|
|
|
if (iskey &&
|
|
|
|
test_indoption(tuple, attno, routine->amcanorder,
|
2016-08-14 00:31:14 +02:00
|
|
|
INDOPTION_DESC, INDOPTION_DESC, &res))
|
2018-04-08 07:02:05 +02:00
|
|
|
isnull = false;
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_NULLS_FIRST:
|
2018-04-08 07:02:05 +02:00
|
|
|
if (iskey &&
|
|
|
|
test_indoption(tuple, attno, routine->amcanorder,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
INDOPTION_NULLS_FIRST, INDOPTION_NULLS_FIRST, &res))
|
2018-04-08 07:02:05 +02:00
|
|
|
isnull = false;
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_NULLS_LAST:
|
2018-04-08 07:02:05 +02:00
|
|
|
if (iskey &&
|
|
|
|
test_indoption(tuple, attno, routine->amcanorder,
|
2016-08-14 00:31:14 +02:00
|
|
|
INDOPTION_NULLS_FIRST, 0, &res))
|
2018-04-08 07:02:05 +02:00
|
|
|
isnull = false;
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_ORDERABLE:
|
2018-04-26 20:47:16 +02:00
|
|
|
|
2018-04-08 07:02:05 +02:00
|
|
|
/*
|
|
|
|
* generic assumption is that nonkey columns are not orderable
|
|
|
|
*/
|
|
|
|
res = iskey ? routine->amcanorder : false;
|
|
|
|
isnull = false;
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_DISTANCE_ORDERABLE:
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The conditions for whether a column is distance-orderable
|
|
|
|
* are really up to the AM (at time of writing, only GiST
|
2018-04-08 07:02:05 +02:00
|
|
|
* supports it at all). The planner has its own idea based on
|
2016-08-14 00:31:14 +02:00
|
|
|
* whether it finds an operator with amoppurpose 'o', but
|
|
|
|
* getting there from just the index column type seems like a
|
2018-04-08 07:02:05 +02:00
|
|
|
* lot of work. So instead we expect the AM to handle this in
|
|
|
|
* its amproperty routine. The generic result is to return
|
2018-04-26 20:47:16 +02:00
|
|
|
* false if the AM says it never supports this, or if this is
|
|
|
|
* a nonkey column, and null otherwise (meaning we don't
|
|
|
|
* know).
|
2016-08-14 00:31:14 +02:00
|
|
|
*/
|
2018-04-08 07:02:05 +02:00
|
|
|
if (!iskey || !routine->amcanorderbyop)
|
|
|
|
{
|
|
|
|
res = false;
|
|
|
|
isnull = false;
|
|
|
|
}
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_RETURNABLE:
|
|
|
|
|
2018-04-08 07:02:05 +02:00
|
|
|
/* note that we ignore iskey for this property */
|
|
|
|
|
|
|
|
isnull = false;
|
|
|
|
res = false;
|
|
|
|
|
|
|
|
if (routine->amcanreturn)
|
2016-08-14 00:31:14 +02:00
|
|
|
{
|
2018-04-08 07:02:05 +02:00
|
|
|
/*
|
|
|
|
* If possible, the AM should handle this test in its
|
2018-04-26 20:47:16 +02:00
|
|
|
* amproperty function without opening the rel. But this
|
|
|
|
* is the generic fallback if it does not.
|
2018-04-08 07:02:05 +02:00
|
|
|
*/
|
2016-08-14 00:31:14 +02:00
|
|
|
Relation indexrel = index_open(index_oid, AccessShareLock);
|
|
|
|
|
|
|
|
res = index_can_return(indexrel, attno);
|
|
|
|
index_close(indexrel, AccessShareLock);
|
|
|
|
}
|
2018-04-08 07:02:05 +02:00
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_SEARCH_ARRAY:
|
2018-04-08 07:02:05 +02:00
|
|
|
if (iskey)
|
|
|
|
{
|
|
|
|
res = routine->amsearcharray;
|
|
|
|
isnull = false;
|
|
|
|
}
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
case AMPROP_SEARCH_NULLS:
|
2018-04-08 07:02:05 +02:00
|
|
|
if (iskey)
|
|
|
|
{
|
|
|
|
res = routine->amsearchnulls;
|
|
|
|
isnull = false;
|
|
|
|
}
|
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
|
|
|
|
default:
|
2018-04-08 07:02:05 +02:00
|
|
|
break;
|
2016-08-14 00:31:14 +02:00
|
|
|
}
|
2018-04-08 07:02:05 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
if (!isnull)
|
|
|
|
PG_RETURN_BOOL(res);
|
|
|
|
PG_RETURN_NULL();
|
2016-08-14 00:31:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (OidIsValid(index_oid))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Handle index-level properties. Currently, these only depend on the
|
|
|
|
* AM, but that might not be true forever, so we make users name an
|
|
|
|
* index not just an AM.
|
|
|
|
*/
|
|
|
|
switch (prop)
|
|
|
|
{
|
|
|
|
case AMPROP_CLUSTERABLE:
|
|
|
|
PG_RETURN_BOOL(routine->amclusterable);
|
|
|
|
|
|
|
|
case AMPROP_INDEX_SCAN:
|
|
|
|
PG_RETURN_BOOL(routine->amgettuple ? true : false);
|
|
|
|
|
|
|
|
case AMPROP_BITMAP_SCAN:
|
|
|
|
PG_RETURN_BOOL(routine->amgetbitmap ? true : false);
|
|
|
|
|
|
|
|
case AMPROP_BACKWARD_SCAN:
|
|
|
|
PG_RETURN_BOOL(routine->amcanbackward);
|
|
|
|
|
|
|
|
default:
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle AM-level properties (those that control what you can say in
|
|
|
|
* CREATE INDEX).
|
|
|
|
*/
|
|
|
|
switch (prop)
|
|
|
|
{
|
|
|
|
case AMPROP_CAN_ORDER:
|
|
|
|
PG_RETURN_BOOL(routine->amcanorder);
|
|
|
|
|
|
|
|
case AMPROP_CAN_UNIQUE:
|
|
|
|
PG_RETURN_BOOL(routine->amcanunique);
|
|
|
|
|
|
|
|
case AMPROP_CAN_MULTI_COL:
|
|
|
|
PG_RETURN_BOOL(routine->amcanmulticol);
|
|
|
|
|
|
|
|
case AMPROP_CAN_EXCLUDE:
|
|
|
|
PG_RETURN_BOOL(routine->amgettuple ? true : false);
|
|
|
|
|
2018-04-08 07:02:05 +02:00
|
|
|
case AMPROP_CAN_INCLUDE:
|
|
|
|
PG_RETURN_BOOL(routine->amcaninclude);
|
|
|
|
|
2016-08-14 00:31:14 +02:00
|
|
|
default:
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test property of an AM specified by AM OID
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
pg_indexam_has_property(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid amoid = PG_GETARG_OID(0);
|
|
|
|
char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
|
|
|
|
|
|
|
|
return indexam_property(fcinfo, propname, amoid, InvalidOid, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test property of an index specified by index OID
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
pg_index_has_property(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid relid = PG_GETARG_OID(0);
|
|
|
|
char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
|
|
|
|
|
|
|
|
return indexam_property(fcinfo, propname, InvalidOid, relid, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test property of an index column specified by index OID and column number
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
pg_index_column_has_property(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid relid = PG_GETARG_OID(0);
|
|
|
|
int32 attno = PG_GETARG_INT32(1);
|
|
|
|
char *propname = text_to_cstring(PG_GETARG_TEXT_PP(2));
|
|
|
|
|
|
|
|
/* Reject attno 0 immediately, so that attno > 0 identifies this case */
|
|
|
|
if (attno <= 0)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
return indexam_property(fcinfo, propname, InvalidOid, relid, attno);
|
|
|
|
}
|
Report progress of CREATE INDEX operations
This uses the progress reporting infrastructure added by c16dc1aca5e0,
adding support for CREATE INDEX and CREATE INDEX CONCURRENTLY.
There are two pieces to this: one is index-AM-agnostic, and the other is
AM-specific. The latter is fairly elaborate for btrees, including
reportage for parallel index builds and the separate phases that btree
index creation uses; other index AMs, which are much simpler in their
building procedures, have simplistic reporting only, but that seems
sufficient, at least for non-concurrent builds.
The index-AM-agnostic part is fairly complete, providing insight into
the CONCURRENTLY wait phases as well as block-based progress during the
index validation table scan. (The index validation index scan requires
patching each AM, which has not been included here.)
Reviewers: Rahila Syed, Pavan Deolasee, Tatsuro Yamada
Discussion: https://postgr.es/m/20181220220022.mg63bhk26zdpvmcj@alvherre.pgsql
2019-04-02 20:18:08 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the name of the given phase, as used for progress reporting by the
|
|
|
|
* given AM.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
pg_indexam_progress_phasename(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid amoid = PG_GETARG_OID(0);
|
|
|
|
int32 phasenum = PG_GETARG_INT32(1);
|
|
|
|
IndexAmRoutine *routine;
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
routine = GetIndexAmRoutineByAmId(amoid, true);
|
|
|
|
if (routine == NULL || !routine->ambuildphasename)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
name = routine->ambuildphasename(phasenum);
|
|
|
|
if (!name)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
PG_RETURN_TEXT_P(CStringGetTextDatum(name));
|
|
|
|
}
|