code for Global-Partition-Index feature

Signed-off-by: xiliu <xiliu_h@163.com>
This commit is contained in:
xiliu 2020-08-25 15:10:14 +08:00
parent 339cd59f26
commit c040d78287
157 changed files with 12502 additions and 939 deletions

View File

@ -90,7 +90,7 @@ static remoteConn* getConnectionByName(const char* name);
static HTAB* createConnHash(void);
static void createNewConnection(const char* name, remoteConn* rconn);
static void deleteConnection(const char* name);
static char** get_pkey_attnames(Relation rel, int16* numatts);
static char** get_pkey_attnames(Relation rel, int16* indnkeyatts);
static char** get_text_array_contents(ArrayType* array, int* numitems);
static char* get_sql_insert(Relation rel, int* pkattnums, int pknumatts, char** src_pkattvals, char** tgt_pkattvals);
static char* get_sql_delete(Relation rel, int* pkattnums, int pknumatts, char** tgt_pkattvals);
@ -1310,7 +1310,7 @@ Datum dblink_exec(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(dblink_get_pkey);
Datum dblink_get_pkey(PG_FUNCTION_ARGS)
{
int16 numatts;
int16 indnkeyatts;
char** results;
FuncCallContext* funcctx = NULL;
int32 call_cntr;
@ -1335,7 +1335,7 @@ Datum dblink_get_pkey(PG_FUNCTION_ARGS)
rel = get_rel_from_relname(PG_GETARG_TEXT_P(0), AccessShareLock, ACL_SELECT);
/* get the array of attnums */
results = get_pkey_attnames(rel, &numatts);
results = get_pkey_attnames(rel, &indnkeyatts);
relation_close(rel, AccessShareLock);
@ -1353,8 +1353,8 @@ Datum dblink_get_pkey(PG_FUNCTION_ARGS)
attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta = attinmeta;
if ((results != NULL) && (numatts > 0)) {
funcctx->max_calls = numatts;
if ((results != NULL) && (indnkeyatts > 0)) {
funcctx->max_calls = indnkeyatts;
/* got results, keep track of them */
funcctx->user_fctx = results;
@ -1758,9 +1758,9 @@ Datum dblink_get_notify(PG_FUNCTION_ARGS)
* get_pkey_attnames
*
* Get the primary key attnames for the given relation.
* Return NULL, and set numatts = 0, if no primary key exists.
* Return NULL, and set indnkeyatts = 0, if no primary key exists.
*/
static char** get_pkey_attnames(Relation rel, int16* numatts)
static char** get_pkey_attnames(Relation rel, int16* indnkeyatts)
{
Relation indexRelation;
ScanKeyData skey;
@ -1770,8 +1770,8 @@ static char** get_pkey_attnames(Relation rel, int16* numatts)
char** result = NULL;
TupleDesc tupdesc;
/* initialize numatts to 0 in case no primary key exists */
*numatts = 0;
/* initialize indnkeyatts to 0 in case no primary key exists */
*indnkeyatts = 0;
tupdesc = rel->rd_att;
@ -1786,12 +1786,13 @@ static char** get_pkey_attnames(Relation rel, int16* numatts)
/* we're only interested if it is the primary key */
if (index->indisprimary) {
*numatts = index->indnatts;
if (*numatts > 0) {
result = (char**)palloc(*numatts * sizeof(char*));
*indnkeyatts = index->indnkeyatts;
if (*indnkeyatts > 0) {
result = (char**)palloc(*indnkeyatts * sizeof(char*));
for (i = 0; i < *numatts; i++)
for (i = 0; i < *indnkeyatts; i++) {
result[i] = SPI_fname(tupdesc, index->indkey.values[i]);
}
}
break;
}

View File

@ -136,9 +136,9 @@ Datum triggered_change_notification(PG_FUNCTION_ARGS)
index = (Form_pg_index)GETSTRUCT(indexTuple);
/* we're only interested if it is the primary key and valid */
if (index->indisprimary && IndexIsValid(index)) {
int numatts = index->indnatts;
int indnkeyatts = index->indnkeyatts;
if (numatts > 0) {
if (indnkeyatts > 0) {
int i;
foundPK = true;
@ -147,7 +147,7 @@ Datum triggered_change_notification(PG_FUNCTION_ARGS)
appendStringInfoCharMacro(payload, ',');
appendStringInfoCharMacro(payload, operation);
for (i = 0; i < numatts; i++) {
for (i = 0; i < indnkeyatts; i++) {
int colno = index->indkey.values[i];
appendStringInfoCharMacro(payload, ',');

View File

@ -1408,7 +1408,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
} else {
appendPQExpBuffer(&buf, "\n NULL AS attcollation");
}
if (tableinfo.relkind == 'i') {
if (tableinfo.relkind == 'i' || tableinfo.relkind == 'I') {
appendPQExpBuffer(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
} else {
appendPQExpBuffer(&buf, ",\n NULL AS indexdef");
@ -1437,7 +1437,8 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
}
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_attribute a");
appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
appendPQExpBuffer(&buf,
"\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped AND a.attname <> 'tableoid'", oid);
appendPQExpBuffer(&buf, "\nORDER BY a.attnum;");
res = PSQLexec(buf.data, false);
@ -1467,6 +1468,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""), schemaname, relationname);
break;
case 'i':
case 'I':
if (tableinfo.relpersistence == 'u')
printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""), schemaname, relationname);
else
@ -1506,7 +1508,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
if (tableinfo.relkind == 'S')
headers[cols++] = gettext_noop("Value");
if (tableinfo.relkind == 'i')
if (tableinfo.relkind == 'i' || tableinfo.relkind == 'I')
headers[cols++] = gettext_noop("Definition");
if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
@ -1585,7 +1587,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
printTableAddCell(&cont, seq_values[i], false, false);
/* Expression for index column */
if (tableinfo.relkind == 'i')
if (tableinfo.relkind == 'i' || tableinfo.relkind == 'I')
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
/* FDW options for foreign table column, only for 9.2 or later */
@ -1620,7 +1622,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
}
/* Make footers */
if (tableinfo.relkind == 'i') {
if (tableinfo.relkind == 'i' || tableinfo.relkind == 'I') {
/* Footer information about an index */
PGresult* result = NULL;
@ -3028,6 +3030,7 @@ bool listTables(const char* tabtypes, const char* pattern, bool verbose, bool sh
" WHEN 'v' THEN '%s'"
" WHEN 'm' THEN '%s'"
" WHEN 'i' THEN '%s'"
" WHEN 'I' THEN '%s'"
" WHEN 'S' THEN '%s'"
" WHEN 's' THEN '%s'"
" WHEN 'f' THEN '%s'"
@ -3039,6 +3042,7 @@ bool listTables(const char* tabtypes, const char* pattern, bool verbose, bool sh
gettext_noop("view"),
gettext_noop("materialized view"),
gettext_noop("index"),
gettext_noop("index"),
gettext_noop("sequence"),
gettext_noop("special"),
gettext_noop("foreign table"),
@ -3086,7 +3090,7 @@ bool listTables(const char* tabtypes, const char* pattern, bool verbose, bool sh
if (showMatViews)
appendPQExpBuffer(&buf, "'m',");
if (showIndexes)
appendPQExpBuffer(&buf, "'i',");
appendPQExpBuffer(&buf, "'i','I',");
if (showSeq)
appendPQExpBuffer(&buf, "'S',");
if (showSystem || NULL != pattern)

View File

@ -1700,7 +1700,7 @@ static void ExecGrant_Relation(InternalGrant* istmt)
pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
/* Not sensible to grant on an index */
if (pg_class_tuple->relkind == RELKIND_INDEX)
if (pg_class_tuple->relkind == RELKIND_INDEX || pg_class_tuple->relkind == RELKIND_GLOBAL_INDEX)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", NameStr(pg_class_tuple->relname))));

View File

@ -487,6 +487,7 @@ bool CreateCUDescTable(Relation rel, Datum reloptions, bool isPartition)
IndexInfo* indexInfo = makeNode(IndexInfo);
indexInfo->ii_NumIndexAttrs = 2;
indexInfo->ii_NumIndexKeyAttrs = 2;
indexInfo->ii_KeyAttrNumbers[0] = 1;
indexInfo->ii_KeyAttrNumbers[1] = 2;
indexInfo->ii_Expressions = NIL;
@ -512,8 +513,7 @@ bool CreateCUDescTable(Relation rel, Datum reloptions, bool isPartition)
coloptions[1] = 0;
IndexCreateExtraArgs extra;
extra.existingPSortOid = InvalidOid;
extra.isPartitionedIndex = false;
SetIndexCreateExtraArgs(&extra, InvalidOid, false, false);
if (u_sess->proc_cxt.IsBinaryUpgrade) {
u_sess->upg_cxt.binary_upgrade_next_index_pg_class_oid = bupgrade_get_next_cudesc_index_oid();

View File

@ -1180,7 +1180,7 @@ static void doDeletion(const ObjectAddress* object, int flags)
bool isTmpSequence = false;
bool isTmpTable = false;
if (relKind == RELKIND_INDEX) {
if (relKind == RELKIND_INDEX || relKind == RELKIND_GLOBAL_INDEX) {
bool concurrent = (((uint32)flags & PERFORM_DELETION_CONCURRENTLY) == PERFORM_DELETION_CONCURRENTLY);
Assert(object->objectSubId == 0);
@ -3023,6 +3023,7 @@ static void getRelationDescription(StringInfo buffer, Oid relid)
appendStringInfo(buffer, _("table %s"), relname);
break;
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
appendStringInfo(buffer, _("index %s"), relname);
break;
case RELKIND_SEQUENCE:

View File

@ -182,6 +182,7 @@ void createDfsDescTable(Relation rel, Datum relOptions)
*/
IndexInfo* indexInfo = makeNode(IndexInfo);
indexInfo->ii_NumIndexAttrs = DfsDescIndexMaxAttrNum;
indexInfo->ii_NumIndexKeyAttrs = DfsDescIndexMaxAttrNum;
indexInfo->ii_KeyAttrNumbers[0] = Anum_pg_dfsdesc_duid;
indexInfo->ii_Expressions = NIL;
indexInfo->ii_ExpressionsState = NIL;
@ -201,8 +202,7 @@ void createDfsDescTable(Relation rel, Datum relOptions)
colOptions[0] = 0;
IndexCreateExtraArgs extra;
extra.existingPSortOid = InvalidOid;
extra.isPartitionedIndex = false;
SetIndexCreateExtraArgs(&extra, InvalidOid, false, false);
if (u_sess->proc_cxt.IsBinaryUpgrade) {
u_sess->upg_cxt.binary_upgrade_next_index_pg_class_oid = bupgrade_get_next_cudesc_index_oid();

View File

@ -453,6 +453,9 @@ Relation heap_create(const char* relname, Oid relnamespace, Oid reltablespace, O
*/
reltablespace = InvalidOid;
break;
case RELKIND_GLOBAL_INDEX:
create_storage = true;
break;
default:
if (!partitioned_relation) {
create_storage = true;
@ -1030,6 +1033,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, O
case RELKIND_MATVIEW:
case RELKIND_INDEX:
case RELKIND_TOASTVALUE:
case RELKIND_GLOBAL_INDEX:
/* The relation is real, but as yet empty */
new_rel_reltup->relpages = 0;
new_rel_reltup->reltuples = 0;
@ -2966,7 +2970,8 @@ static void StoreRelCheck(
is_validated,
RelationGetRelid(rel), /* relation */
attNos, /* attrs in the constraint */
keycount, /* # attrs in the constraint */
keycount, /* # key attrs in the constraint */
keycount, /* # total attrs in the constraint */
InvalidOid, /* not a domain constraint */
InvalidOid, /* no associated index */
InvalidOid, /* Foreign key fields */
@ -3674,7 +3679,7 @@ static void RelationTruncateIndexes(Relation heapRelation, LOCKMODE lockmode)
/* Initialize the index and rebuild */
/* Note: we do not need to re-establish pkey setting */
index_build(heapRelation, NULL, currentIndex, NULL, indexInfo, false, true, false);
index_build(heapRelation, NULL, currentIndex, NULL, indexInfo, false, true, INDEX_CREATE_NONE_PARTITION);
/* We're done with this index */
index_close(currentIndex, NoLock);
@ -3910,7 +3915,7 @@ void heap_truncate_one_rel(Relation rel)
}
p = partitionOpen(rel, indexPart->pd_part->indextblid, NoLock);
index_build(rel, p, currentIndex, indexPart, indexInfo, false, true, true);
index_build(rel, p, currentIndex, indexPart, indexInfo, false, true, INDEX_CREATE_LOCAL_PARTITION);
partitionClose(rel, p, NoLock);
partitionClose(currentIndex, indexPart, NoLock);
@ -5231,7 +5236,6 @@ static void addNewPartitionTupleForValuePartitionedTable(Relation pg_partition_r
values[Anum_pg_partition_relpages - 1] = Float8GetDatum(0);
values[Anum_pg_partition_reltuples - 1] = Float8GetDatum(0);
values[Anum_pg_partition_relallvisible - 1] = UInt32GetDatum(0);
;
values[Anum_pg_partition_reltoastrelid - 1] = ObjectIdGetDatum(InvalidOid);
values[Anum_pg_partition_reltoastidxid - 1] = ObjectIdGetDatum(InvalidOid);
values[Anum_pg_partition_indextblid - 1] = ObjectIdGetDatum(InvalidOid);
@ -5302,6 +5306,7 @@ static void addNewPartitionTupleForTable(Relation pg_partition_rel, const char*
RangePartitionDefState* lastPartition = NULL;
Relation relation = NULL;
Partition new_partition = NULL;
Datum newOptions;
Oid new_partition_rfoid = InvalidOid;
@ -5354,6 +5359,9 @@ static void addNewPartitionTupleForTable(Relation pg_partition_rel, const char*
new_partition->pd_part->relcudescidx = InvalidOid;
new_partition->pd_part->indisusable = true;
/* Update reloptions with wait_clean_gpi=n */
newOptions = SetWaitCleanGpiRelOptions(reloptions, false);
/*step 2: insert into pg_partition tuple*/
addNewPartitionTuple(pg_partition_rel, /* RelationData pointer for pg_partition */
new_partition, /* Local PartitionData pointer for new partition */
@ -5362,7 +5370,7 @@ static void addNewPartitionTupleForTable(Relation pg_partition_rel, const char*
interval, /* interval partitioned table's interval*/
(Datum)0, /* partitioned table's boundary value is empty in pg_partition */
transition_point, /* interval's partitioned table's transition point*/
reloptions);
newOptions);
relation = relation_open(reloid, NoLock);
partitionClose(relation, new_partition, NoLock);
relation_close(relation, NoLock);
@ -5505,9 +5513,11 @@ void heap_truncate_one_part(Relation rel, Oid partOid)
parentIndId = (((Form_pg_partition)GETSTRUCT(partIndexTuple)))->parentid;
partIndId = HeapTupleGetOid(partIndexTuple);
parentIndex = index_open(parentIndId, AccessShareLock);
indexPart = partitionOpen(parentIndex, partIndId, AccessExclusiveLock);
reindex_partIndex(rel, p, parentIndex, indexPart);
partitionClose(parentIndex, indexPart, NoLock);
if (!RelationIsGlobalIndex(parentIndex)) {
indexPart = partitionOpen(parentIndex, partIndId, AccessExclusiveLock);
reindex_partIndex(rel, p, parentIndex, indexPart);
partitionClose(parentIndex, indexPart, NoLock);
}
index_close(parentIndex, NoLock);
}
}
@ -5874,6 +5884,7 @@ List* AddRelClusterConstraints(Relation rel, List* clusterKeys)
RelationGetRelid(rel), /* relation */
attNums, /* attrs in the constraint */
colNum, /* # attrs in the constraint */
colNum, /* # attrs in the constraint */
InvalidOid, /* not a domain constraint */
InvalidOid, /* no associated index */
InvalidOid, /* Foreign key fields */

View File

@ -96,7 +96,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo* indexInfo,
static void IndexCheckExclusion(Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo);
static void IndexCheckExclusionForBucket(Relation heapRelation, Partition heapPartition, Relation indexRelation,
Partition indexPartition, IndexInfo* indexInfo);
static bool validate_index_callback(ItemPointer itemptr, void* opaque);
static bool validate_index_callback(ItemPointer itemptr, void* opaque, Oid partOid = InvalidOid);
static void validate_index_heapscan(
Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo, Snapshot snapshot, v_i_state* state);
static bool ReindexIsCurrentlyProcessingIndex(Oid indexOid);
@ -205,7 +205,7 @@ void index_check_primary_key(Relation heapRel, IndexInfo* indexInfo, bool is_alt
* null, otherwise attempt to ALTER TABLE .. SET NOT NULL
*/
cmds = NIL;
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) {
for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++) {
AttrNumber attnum = indexInfo->ii_KeyAttrNumbers[i];
HeapTuple atttuple;
Form_pg_attribute attform;
@ -259,6 +259,7 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo* inde
Oid accessMethodObjectId, Oid* collationObjectId, Oid* classObjectId)
{
int numatts = indexInfo->ii_NumIndexAttrs;
int numkeyatts = indexInfo->ii_NumIndexKeyAttrs;
ListCell* colnames_item = list_head(indexColNames);
ListCell* indexpr_item = list_head(indexInfo->ii_Expressions);
HeapTuple amtuple;
@ -338,7 +339,7 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo* inde
to->atthasdef = false;
to->attislocal = true;
to->attinhcount = 0;
to->attcollation = collationObjectId[i];
to->attcollation = (i < numkeyatts) ? collationObjectId[i] : InvalidOid;
} else {
/* Expressional index */
Node* indexkey = NULL;
@ -374,7 +375,7 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo* inde
to->attcacheoff = -1;
to->atttypmod = -1;
to->attislocal = true;
to->attcollation = collationObjectId[i];
to->attcollation = (i < numkeyatts) ? collationObjectId[i] : InvalidOid;
ReleaseSysCache(tuple);
@ -407,18 +408,40 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo* inde
/*
* Check the opclass and index AM to see if either provides a keytype
* (overriding the attribute type). Opclass takes precedence.
* (overriding the attribute type). Opclass (if exists) takes
* precedence.
*/
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(classObjectId[i]));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for opclass %u", classObjectId[i])));
opclassTup = (Form_pg_opclass)GETSTRUCT(tuple);
if (OidIsValid(opclassTup->opckeytype))
keyType = opclassTup->opckeytype;
else
keyType = amform->amkeytype;
ReleaseSysCache(tuple);
keyType = amform->amkeytype;
/*
* Code below is concerned to the opclasses which are not used with
* the included columns.
*/
if (i < indexInfo->ii_NumIndexKeyAttrs) {
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(classObjectId[i]));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for opclass %u", classObjectId[i])));
}
opclassTup = (Form_pg_opclass)GETSTRUCT(tuple);
if (OidIsValid(opclassTup->opckeytype))
keyType = opclassTup->opckeytype;
/*
* If keytype is specified as ANYELEMENT, and opcintype is
* ANYARRAY, then the attribute type must be an array (else it'd
* not have matched this opclass); use its element type.
*/
if (keyType == ANYELEMENTOID && opclassTup->opcintype == ANYARRAYOID) {
keyType = get_base_element_type(to->atttypid);
if (!OidIsValid(keyType)) {
ereport(ERROR, (errmsg("could not get element type of array type %u", to->atttypid)));
}
}
ReleaseSysCache(tuple);
}
if (OidIsValid(keyType) && keyType != to->atttypid) {
/* index value and heap value have different types */
@ -527,9 +550,9 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo* indexInfo,
indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs);
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
indkey->values[i] = indexInfo->ii_KeyAttrNumbers[i];
indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexAttrs);
indclass = buildoidvector(classOids, indexInfo->ii_NumIndexAttrs);
indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexAttrs);
indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexKeyAttrs);
indclass = buildoidvector(classOids, indexInfo->ii_NumIndexKeyAttrs);
indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexKeyAttrs);
/*
* Convert the index expressions (if any) to a text datum
@ -570,6 +593,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo* indexInfo,
values[Anum_pg_index_indexrelid - 1] = ObjectIdGetDatum(indexoid);
values[Anum_pg_index_indrelid - 1] = ObjectIdGetDatum(heapoid);
values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs);
values[Anum_pg_index_indnkeyatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexKeyAttrs);
values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique);
values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary);
values[Anum_pg_index_indisexclusion - 1] = BoolGetDatum(isexclusion);
@ -718,6 +742,11 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
if (indexInfo->ii_NumIndexAttrs < 1)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("must index at least one column")));
if (indexInfo->ii_NumIndexKeyAttrs > INDEX_MAX_KEYS) {
ereport(
ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("must index at most %u column", INDEX_MAX_KEYS)));
}
if (!allow_system_table_mods && IsSystemRelation(heapRelation) && IsNormalProcessingMode())
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -829,6 +858,10 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
}
}
char relKind = RELKIND_INDEX;
if (extra->isGlobalPartitionedIndex) {
relKind = RELKIND_GLOBAL_INDEX;
}
/*
* create the index relation's relcache entry and physical disk file. (If
* we fail further down, it's the smgr's responsibility to remove the disk
@ -841,9 +874,9 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
relFileNode,
RELATION_CREATE_BUCKET(heapRelation) ? heapRelation->rd_bucketoid : InvalidOid,
indexTupDesc,
RELKIND_INDEX,
relKind,
relpersistence,
extra ? extra->isPartitionedIndex : false,
extra->isPartitionedIndex != extra->isGlobalPartitionedIndex ? true : false,
false,
shared_relation,
mapped_relation,
@ -894,7 +927,7 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
* store index's pg_class entry
*/
InsertPgClassTuple(
pg_class, indexRelation, RelationGetRelid(indexRelation), (Datum)0, reloptions, RELKIND_INDEX, NULL);
pg_class, indexRelation, RelationGetRelid(indexRelation), (Datum)0, reloptions, relKind, NULL);
/* done with pg_class */
heap_close(pg_class, RowExclusiveLock);
@ -1021,7 +1054,7 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
/* Store dependency on collations */
/* The default collation is pinned, so don't bother recording it */
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) {
for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++) {
if (OidIsValid(collationObjectId[i]) && collationObjectId[i] != DEFAULT_COLLATION_OID) {
referenced.classId = CollationRelationId;
referenced.objectId = collationObjectId[i];
@ -1032,7 +1065,7 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
}
/* Store dependency on operator classes */
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) {
for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++) {
referenced.classId = OperatorClassRelationId;
referenced.objectId = classObjectId[i];
referenced.objectSubId = 0;
@ -1085,6 +1118,7 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
else
Assert(indexRelation->rd_indexcxt != NULL);
indexRelation->rd_index->indnkeyatts = (int2)indexInfo->ii_NumIndexKeyAttrs;
/*
* If this is bootstrap (initdb) time, then we don't actually fill in the
* index yet. We'll be creating more indexes and classes later, so we
@ -1109,9 +1143,9 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index
-1.0);
/* Make the above update visible */
CommandCounterIncrement();
} else if (extra && !extra->isPartitionedIndex) /* we don't build a partitioned index */
{
index_build(heapRelation, NULL, indexRelation, NULL, indexInfo, isprimary, false, false);
} else if (extra && (!extra->isPartitionedIndex || extra->isGlobalPartitionedIndex)) {
/* support regular index or GLOBAL partition index */
index_build(heapRelation, NULL, indexRelation, NULL, indexInfo, isprimary, false, PARTITION_TYPE(extra));
}
/* Recode the index create time. */
@ -1221,7 +1255,14 @@ Oid partition_index_create(const char* partIndexName, /* the name of partition i
/* build the index */
if (!skipBuild) {
index_build(partitionedTable, partition, parentIndex, partitionIndex, indexInfo, false, false, true);
index_build(partitionedTable,
partition,
parentIndex,
partitionIndex,
indexInfo,
false,
false,
INDEX_CREATE_LOCAL_PARTITION);
}
partitionClose(parentIndex, partitionIndex, NoLock);
@ -1295,6 +1336,7 @@ void index_constraint_create(Relation heapRelation, Oid indexRelationId, IndexIn
true,
RelationGetRelid(heapRelation),
indexInfo->ii_KeyAttrNumbers,
indexInfo->ii_NumIndexKeyAttrs,
indexInfo->ii_NumIndexAttrs,
InvalidOid, /* no domain */
indexRelationId, /* index OID */
@ -1788,17 +1830,10 @@ void index_drop(Oid indexId, bool concurrent)
*/
IndexInfo* BuildIndexInfo(Relation index)
{
IndexInfo* ii = makeNode(IndexInfo);
IndexInfo* ii;
Form_pg_index indexStruct = index->rd_index;
int i;
int numKeys;
/* check the number of keys, and copy attr numbers into the IndexInfo */
numKeys = indexStruct->indnatts;
if (numKeys < 1 || numKeys > INDEX_MAX_KEYS)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("invalid indnatts %d for index %u", numKeys, RelationGetRelid(index))));
int numAtts;
ii = makeIndexInfo(indexStruct->indnatts,
RelationGetIndexExpressions(index),
@ -1807,8 +1842,19 @@ IndexInfo* BuildIndexInfo(Relation index)
IndexIsReady(indexStruct),
false);
/* check the number of keys, and copy attr numbers into the IndexInfo */
numAtts = indexStruct->indnatts;
if (numAtts < 1 || numAtts > INDEX_MAX_KEYS)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("invalid indnatts %d for index %u", numAtts, RelationGetRelid(index))));
ii->ii_NumIndexAttrs = numAtts;
ii->ii_NumIndexKeyAttrs = indexStruct->indnkeyatts;
Assert(ii->ii_NumIndexKeyAttrs != 0);
Assert(ii->ii_NumIndexKeyAttrs <= ii->ii_NumIndexAttrs);
/* fill in attribute numbers */
for (i = 0; i < numKeys; i++) {
for (i = 0; i < numAtts; i++) {
ii->ii_KeyAttrNumbers[i] = indexStruct->indkey.values[i];
}
/* fetch exclusion constraint info if any */
@ -2110,7 +2156,7 @@ void index_update_stats(
BlockNumber relpages = RelationGetNumberOfBlocks(rel);
BlockNumber relallvisible;
if (rd_rel->relkind != RELKIND_INDEX)
if (rd_rel->relkind != RELKIND_INDEX && rd_rel->relkind != RELKIND_GLOBAL_INDEX)
relallvisible = visibilitymap_count(rel, NULL);
else /* don't bother for indexes */
relallvisible = 0;
@ -2314,47 +2360,79 @@ static void partition_index_update_stats(
relation_close(parent, NoLock);
}
static void index_build_storage(Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo,
RegProcedure procedure, double* indextuples, double* heaptuples)
static IndexBuildResult* index_build_storage(Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo,
RegProcedure procedure)
{
IndexBuildResult* stats = NULL;
procedure = indexRelation->rd_am->ambuild;
Assert(RegProcedureIsValid(procedure));
stats = (IndexBuildResult*)DatumGetPointer(OidFunctionCall3(
IndexBuildResult* stats = (IndexBuildResult*)DatumGetPointer(OidFunctionCall3(
procedure, PointerGetDatum(heapRelation), PointerGetDatum(indexRelation), PointerGetDatum(indexInfo)));
Assert(PointerIsValid(stats));
*indextuples = stats->index_tuples;
*heaptuples = stats->heap_tuples;
if (RELPERSISTENCE_UNLOGGED == heapRelation->rd_rel->relpersistence) {
index_build_init_fork(heapRelation, indexRelation);
}
return stats;
}
static void index_build_storage_for_bucket(Relation heapRelation, Relation indexRelation, Partition heapPartition,
Partition indexPartition, IndexInfo* indexInfo, RegProcedure procedure, double* indextuples, double* heaptuples)
static IndexBuildResult* index_build_storage_for_bucket(Relation heapRelation, Relation indexRelation,
Partition heapPartition, Partition indexPartition, IndexInfo* indexInfo, RegProcedure procedure)
{
Relation heapBucketRel = NULL;
Relation indexBucketRel = NULL;
oidvector* bucketlist = searchHashBucketByOid(indexRelation->rd_bucketoid);
for (int i = 0; i < bucketlist->dim1; i++) {
double heaptup;
double idxtup;
IndexBuildResult* stats = (IndexBuildResult*)palloc0(sizeof(IndexBuildResult));
for (int i = 0; i < bucketlist->dim1; i++) {
heapBucketRel = bucketGetRelation(heapRelation, heapPartition, bucketlist->values[i]);
indexBucketRel = bucketGetRelation(indexRelation, indexPartition, bucketlist->values[i]);
index_build_storage(heapBucketRel, indexBucketRel, indexInfo, procedure, &idxtup, &heaptup);
IndexBuildResult* results = index_build_storage(heapBucketRel, indexBucketRel, indexInfo, procedure);
bucketCloseRelation(heapBucketRel);
bucketCloseRelation(indexBucketRel);
*indextuples += idxtup;
*heaptuples += heaptup;
stats->index_tuples += results->index_tuples;
stats->heap_tuples += results->heap_tuples;
pfree(results);
}
return stats;
}
void UpdateStatsForGlobalIndex(
Relation heapRelation, Relation indexRelation, IndexBuildResult* stats, bool isprimary, Oid cudesc_idx_oid)
{
ListCell* partitioncell = NULL;
Oid partitionid;
Partition partition = NULL;
List* partitionidlist = NIL;
partitionidlist = relationGetPartitionOidList(heapRelation);
int partitionidx = 0;
Assert(PointerIsValid(stats->global_index_tuples));
foreach (partitioncell, partitionidlist) {
partitionid = lfirst_oid(partitioncell);
partition = partitionOpen(heapRelation, partitionid, ShareLock);
partition_index_update_stats(partition,
true,
isprimary,
(heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ? RelationGetRelid(indexRelation) : InvalidOid,
cudesc_idx_oid,
stats->global_index_tuples[partitionidx]);
partitionClose(heapRelation, partition, NoLock);
partitionidx++;
}
index_update_stats(heapRelation,
true,
isprimary,
(heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ? RelationGetRelid(indexRelation) : InvalidOid,
InvalidOid,
-1);
index_update_stats(indexRelation, false, false, InvalidOid, InvalidOid, stats->index_tuples);
}
/*
@ -2377,11 +2455,11 @@ static void index_build_storage_for_bucket(Relation heapRelation, Relation index
* The caller opened 'em, and the caller should close 'em.
*/
void index_build(Relation heapRelation, Partition heapPartition, Relation indexRelation, Partition indexPartition,
IndexInfo* indexInfo, bool isprimary, bool isreindex, bool isPartition)
IndexInfo* indexInfo, bool isprimary, bool isreindex, IndexCreatePartitionType partitionType)
{
RegProcedure procedure;
double indextuples = 0;
double heaptuples = 0;
double indextuples;
double heaptuples;
Oid save_userid;
int save_sec_context;
int save_nestlevel;
@ -2398,17 +2476,21 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
Assert(RelationIsValid(indexRelation));
Assert(PointerIsValid(indexRelation->rd_am));
if (!isPartition) {
if (partitionType == INDEX_CREATE_NONE_PARTITION) {
Assert(!PointerIsValid(heapPartition));
Assert(!PointerIsValid(indexPartition));
Assert(!RELATION_IS_PARTITIONED(heapRelation));
} else if (partitionType == INDEX_CREATE_GLOBAL_PARTITION) {
Assert(!PointerIsValid(heapPartition));
Assert(!PointerIsValid(indexPartition));
Assert(RELATION_IS_PARTITIONED(heapRelation));
} else {
Assert(PointerIsValid(heapPartition));
Assert(PointerIsValid(indexPartition));
Assert(RELATION_IS_PARTITIONED(heapRelation));
}
if (isPartition) {
if (partitionType == INDEX_CREATE_LOCAL_PARTITION) {
heapPartRel = partitionGetRelation(heapRelation, heapPartition);
indexPartRel = partitionGetRelation(indexRelation, indexPartition);
targetHeapRelation = heapPartRel;
@ -2458,20 +2540,22 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
/*
* Call the access method's build procedure
*/
hasbucket = (!isPartition && RELATION_CREATE_BUCKET(heapRelation)) ||
(isPartition && RELATION_OWN_BUCKETKEY(heapRelation));
if (hasbucket) {
index_build_storage_for_bucket(heapRelation,
hasbucket = (partitionType == INDEX_CREATE_NONE_PARTITION && RELATION_CREATE_BUCKET(heapRelation)) ||
(partitionType != INDEX_CREATE_NONE_PARTITION && RELATION_OWN_BUCKETKEY(heapRelation));
IndexBuildResult* stats = NULL;
if (hasbucket == true) {
stats = index_build_storage_for_bucket(heapRelation,
indexRelation,
heapPartition,
indexPartition,
indexInfo,
procedure,
&indextuples,
&heaptuples);
procedure);
} else {
index_build_storage(targetHeapRelation, targetIndexRelation, indexInfo, procedure, &indextuples, &heaptuples);
stats = index_build_storage(targetHeapRelation, targetIndexRelation, indexInfo, procedure);
}
indextuples = stats->index_tuples;
heaptuples = stats->heap_tuples;
/*
* If we found any potentially broken HOT chains, mark the index as not
@ -2496,7 +2580,8 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
* about any concurrent readers of the tuple; no other transaction can see
* it yet.
*/
if (indexInfo->ii_BrokenHotChain && !isreindex && !isPartition && !indexInfo->ii_Concurrent) {
if (indexInfo->ii_BrokenHotChain && !isreindex &&
partitionType != INDEX_CREATE_LOCAL_PARTITION && !indexInfo->ii_Concurrent) {
Oid indexId = RelationGetRelid(indexRelation);
Relation pg_index;
HeapTuple indexTuple;
@ -2533,7 +2618,7 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
/*
* Update heap and index pg_class rows
*/
if (!isPartition) {
if (partitionType == INDEX_CREATE_NONE_PARTITION) {
index_update_stats(heapRelation,
true,
isprimary,
@ -2542,7 +2627,7 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
heaptuples);
index_update_stats(indexRelation, false, false, InvalidOid, InvalidOid, indextuples);
} else {
} else if (partitionType == INDEX_CREATE_LOCAL_PARTITION) {
/*
* if the build partition index, the heapRelation is faked from Parent RelationData and PartitionData,
* so we reopen the Partition , it seems weird.
@ -2555,7 +2640,12 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
heaptuples);
partition_index_update_stats(indexPartition, false, false, InvalidOid, cudesc_idx_oid, indextuples);
} else if (partitionType == INDEX_CREATE_GLOBAL_PARTITION) {
UpdateStatsForGlobalIndex(heapRelation, indexRelation, stats, isprimary, cudesc_idx_oid);
pfree(stats->global_index_tuples);
}
pfree(stats);
/* Make the updated catalog row versions visible */
CommandCounterIncrement();
@ -2578,7 +2668,7 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
/* Restore userid and security context */
SetUserIdAndSecContext(save_userid, save_sec_context);
if (isPartition) {
if (partitionType == INDEX_CREATE_LOCAL_PARTITION) {
releaseDummyRelation(&heapPartRel);
releaseDummyRelation(&indexPartRel);
}
@ -2970,6 +3060,34 @@ double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexIn
return reltuples;
}
double* GlobalIndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo,
IndexBuildCallback callback, void* callbackState)
{
ListCell* partitionCell = NULL;
Oid partitionId;
Partition partition = NULL;
List* partitionIdList = NIL;
Relation heapPartRel = NULL;
partitionIdList = relationGetPartitionOidList(heapRelation);
double relTuples;
int partitionIdx = 0;
int partNum = partitionIdList->length;
double* globalIndexTuples = (double*)palloc0(partNum * sizeof(double));
foreach(partitionCell, partitionIdList) {
partitionId = lfirst_oid(partitionCell);
partition = partitionOpen(heapRelation, partitionId, ShareLock);
heapPartRel = partitionGetRelation(heapRelation, partition);
relTuples = IndexBuildHeapScan(heapPartRel, indexRelation, indexInfo, true, callback, callbackState);
globalIndexTuples[partitionIdx] = relTuples;
releaseDummyRelation(&heapPartRel);
partitionClose(heapRelation, partition, NoLock);
partitionIdx++;
}
return globalIndexTuples;
}
double IndexBuildVectorBatchScan(Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo,
VectorBatch* vecScanBatch, Snapshot snapshot, IndexBuildVecBatchScanCallback callback, void* callback_state,
void* transferFuncs)
@ -3310,7 +3428,7 @@ void validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
/*
* validate_index_callback - bulkdelete callback to collect the index TIDs
*/
static bool validate_index_callback(ItemPointer itemptr, void* opaque)
static bool validate_index_callback(ItemPointer itemptr, void* opaque, Oid partOid)
{
v_i_state* state = (v_i_state*)opaque;
@ -3658,7 +3776,7 @@ void reindex_indexpart_internal(Relation heapRelation, Relation iRel, IndexInfo*
PartitionSetNewRelfilenode(iRel, indexpart, InvalidTransactionId);
index_build(heapRelation, heapPart, iRel, indexpart, indexInfo, false, true, true);
index_build(heapRelation, heapPart, iRel, indexpart, indexInfo, false, true, INDEX_CREATE_LOCAL_PARTITION);
/*the whole partitioned index has brokenUndoChain if any one partition has brokenUndoChain */
partitionClose(iRel, indexpart, NoLock);
@ -3668,6 +3786,29 @@ void reindex_indexpart_internal(Relation heapRelation, Relation iRel, IndexInfo*
// step 2: reset indisusable state of index partition
ATExecSetIndexUsableState(PartitionRelationId, indexPartId, true);
}
/*
* ReindexGlobalIndexInternal - This routine is used to recreate a single global index
*/
void ReindexGlobalIndexInternal(Relation heapRelation, Relation iRel, IndexInfo* indexInfo)
{
List* partitionList = NULL;
/* We'll open any partition of relation by partition OID and lock it */
partitionList = relationGetPartitionList(heapRelation, ShareLock);
/* We'll build a new physical relation for the index */
RelationSetNewRelfilenode(iRel, InvalidTransactionId);
/* Initialize the index and rebuild */
/* Note: we do not need to re-establish pkey setting */
index_build(heapRelation, NULL, iRel, NULL, indexInfo, false, true, INDEX_CREATE_GLOBAL_PARTITION);
releasePartitionList(heapRelation, &partitionList, NoLock);
// call the internal function, update pg_index system table
ATExecSetIndexUsableState(IndexRelationId, iRel->rd_id, true);
}
/*
* reindex_index - This routine is used to recreate a single index
*/
@ -3785,7 +3926,7 @@ void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks,
/* Initialize the index and rebuild */
/* Note: we do not need to re-establish pkey setting */
index_build(heapRelation, NULL, iRel, NULL, indexInfo, false, true, false);
index_build(heapRelation, NULL, iRel, NULL, indexInfo, false, true, INDEX_CREATE_NONE_PARTITION);
// call the internal function, update pg_index system table
ATExecSetIndexUsableState(IndexRelationId, iRel->rd_id, true);
@ -3793,6 +3934,8 @@ void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks,
{
if (OidIsValid(indexPartId)) {
reindex_indexpart_internal(heapRelation, iRel, indexInfo, indexPartId);
} else if (RelationIsGlobalIndex(iRel)) {
ReindexGlobalIndexInternal(heapRelation, iRel, indexInfo);
} else {
List* indexPartOidList = NULL;
ListCell* partCell = NULL;
@ -3930,7 +4073,7 @@ void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks,
* when relevant). Note that a CommandCounterIncrement will occur after each
* index rebuild.
*/
bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo, bool dbWide)
bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo, bool dbWide, IndexKind indexKind)
{
Relation rel;
Oid toast_relid;
@ -3955,7 +4098,14 @@ bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo,
* relcache to get this with a sequential scan if ignoring system
* indexes.)
*/
indexIds = RelationGetIndexList(rel);
if (indexKind == ALL_KIND) {
indexIds = RelationGetIndexList(rel);
} else if (indexKind == GLOBAL_INDEX) {
indexIds = RelationGetSpecificKindIndexList(rel, true);
} else {
indexIds = RelationGetSpecificKindIndexList(rel, false);
}
/*
* reindex_index will attempt to update the pg_class rows for the relation
@ -4216,7 +4366,7 @@ void reindex_partIndex(Relation heapRel, Partition heapPart, Relation indexRel,
// build the part index
indexInfo = BuildIndexInfo(indexRel);
index_build(heapRel, heapPart, indexRel, indexPart, indexInfo, false, true, true);
index_build(heapRel, heapPart, indexRel, indexPart, indexInfo, false, true, INDEX_CREATE_LOCAL_PARTITION);
}
/*
@ -4274,7 +4424,7 @@ bool reindexPartition(Oid relid, Oid partOid, int flags, int reindexType)
* relcache to get this with a sequential scan if ignoring system
* indexes.)
*/
indexIds = RelationGetIndexList(rel);
indexIds = RelationGetSpecificKindIndexList(rel, false);
/*
* reindex_index will attempt to update the pg_class rows for the relation
@ -4317,6 +4467,10 @@ bool reindexPartition(Oid relid, Oid partOid, int flags, int reindexType)
foreach (indexId, indexIds) {
Oid indexOid = lfirst_oid(indexId);
Relation indexRel = index_open(indexOid, AccessShareLock);
if (RelationIsGlobalIndex(indexRel)) {
index_close(indexRel, AccessShareLock);
continue;
}
if ((((uint32)reindexType) & REINDEX_ALL_INDEX) ||
((((uint32)reindexType) & REINDEX_BTREE_INDEX) && (indexRel->rd_rel->relam == BTREE_AM_OID)) ||
@ -4486,7 +4640,7 @@ static void reindexPartIndex(Oid indexId, Oid partOid, bool skip_constraint_chec
CheckPartitionNotInUse(indexpart, "REINDEX INDEX index_partition");
PartitionSetNewRelfilenode(iRel, indexpart, InvalidTransactionId);
index_build(heapRelation, heapPart, iRel, indexpart, indexInfo, false, true, true);
index_build(heapRelation, heapPart, iRel, indexpart, indexInfo, false, true, INDEX_CREATE_LOCAL_PARTITION);
/*
* The whole partitioned index has brokenUndoChain if any one
@ -4732,6 +4886,7 @@ Oid psort_create(const char* indexRelationName, Relation indexRelation, Oid tabl
psortRelationId, /* relation */
attrNums, /* attrs in the constraint */
natts, /* # attrs in the constraint */
natts, /* # attrs in the constraint */
InvalidOid, /* not a domain constraint */
InvalidOid, /* no associated index */
InvalidOid, /* Foreign key fields */
@ -4953,3 +5108,16 @@ static Oid bupgrade_get_next_psort_array_pg_type_oid()
return old_psort_array_pg_type_oid;
}
/*
* Parameter isPartitionedIndex indicates whether the index is a partition index.
* Parameter isGlobalPartitionedIndex indicates whether the index is a global partition index.
*
* Notes: isGlobalPartitionedIndex as means isPartitionedIndex is true.
*/
void SetIndexCreateExtraArgs(IndexCreateExtraArgs* extra, Oid psortOid, bool isPartition, bool isGlobal)
{
extra->existingPSortOid = psortOid;
extra->isPartitionedIndex = isPartition;
extra->isGlobalPartitionedIndex = isGlobal;
}

View File

@ -116,6 +116,7 @@ void CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
Assert(indexInfo->ii_Predicate == NIL);
Assert(indexInfo->ii_ExclusionOps == NULL);
Assert(relationDescs[i]->rd_index->indimmediate);
Assert(indexInfo->ii_NumIndexKeyAttrs != 0);
/*
* FormIndexDatum fills in its values and isnull parameters with the

View File

@ -508,7 +508,8 @@ static ObjectAddress get_relation_by_qualified_name(
switch (objtype) {
case OBJECT_INDEX:
if (relation->rd_rel->relkind != RELKIND_INDEX)
if (relation->rd_rel->relkind != RELKIND_INDEX &&
relation->rd_rel->relkind != RELKIND_GLOBAL_INDEX)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not an index", RelationGetRelationName(relation))));

View File

@ -43,10 +43,11 @@
* from the constraint to the things it depends on.
*/
Oid CreateConstraintEntry(const char* constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable,
bool isDeferred, bool isValidated, Oid relId, const int16* constraintKey, int constraintNKeys, Oid domainId,
Oid indexRelId, Oid foreignRelId, const int16* foreignKey, const Oid* pfEqOp, const Oid* ppEqOp, const Oid* ffEqOp,
int foreignNKeys, char foreignUpdateType, char foreignDeleteType, char foreignMatchType, const Oid* exclOp,
Node* conExpr, const char* conBin, const char* conSrc, bool conIsLocal, int conInhCount, bool conNoInherit,
bool isDeferred, bool isValidated, Oid relId, const int16* constraintKey, int constraintNKeys,
int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16* foreignKey,
const Oid* pfEqOp, const Oid* ppEqOp, const Oid* ffEqOp, int foreignNKeys, char foreignUpdateType,
char foreignDeleteType, char foreignMatchType, const Oid* exclOp, Node* conExpr, const char* conBin,
const char* conSrc, bool conIsLocal, int conInhCount, bool conNoInherit,
InformationalConstraint* inforConstraint) /* @hdfs informatinal constaint */
{
Relation conDesc = NULL;
@ -55,6 +56,7 @@ Oid CreateConstraintEntry(const char* constraintName, Oid constraintNamespace, c
bool nulls[Natts_pg_constraint];
Datum values[Natts_pg_constraint];
ArrayType* conkeyArray = NULL;
ArrayType* conincludingArray = NULL;
ArrayType* confkeyArray = NULL;
ArrayType* conpfeqopArray = NULL;
ArrayType* conppeqopArray = NULL;
@ -79,8 +81,22 @@ Oid CreateConstraintEntry(const char* constraintName, Oid constraintNamespace, c
for (i = 0; i < constraintNKeys; i++)
conkey[i] = Int16GetDatum(constraintKey[i]);
conkeyArray = construct_array(conkey, constraintNKeys, INT2OID, 2, true, 's');
} else
} else {
conkeyArray = NULL;
}
if (constraintNTotalKeys > constraintNKeys) {
Datum* conincluding;
int j = 0;
int constraintNIncludedKeys = constraintNTotalKeys - constraintNKeys;
conincluding = (Datum*)palloc(constraintNIncludedKeys * sizeof(Datum));
for (i = constraintNKeys; i < constraintNTotalKeys; i++)
conincluding[j++] = Int16GetDatum(constraintKey[i]);
conincludingArray = construct_array(conincluding, constraintNIncludedKeys, INT2OID, 2, true, 's');
} else {
conincludingArray = NULL;
}
if (foreignNKeys > 0) {
Datum* fkdatums = NULL;
@ -151,6 +167,11 @@ Oid CreateConstraintEntry(const char* constraintName, Oid constraintNamespace, c
else
nulls[Anum_pg_constraint_conkey - 1] = true;
if (conincludingArray)
values[Anum_pg_constraint_conincluding - 1] = PointerGetDatum(conincludingArray);
else
nulls[Anum_pg_constraint_conincluding - 1] = true;
if (confkeyArray != NULL)
values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
else
@ -214,8 +235,8 @@ Oid CreateConstraintEntry(const char* constraintName, Oid constraintNamespace, c
relobject.classId = RelationRelationId;
relobject.objectId = relId;
if (constraintNKeys > 0) {
for (i = 0; i < constraintNKeys; i++) {
if (constraintNTotalKeys > 0) {
for (i = 0; i < constraintNTotalKeys; i++) {
relobject.objectSubId = constraintKey[i];
recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);

View File

@ -600,6 +600,7 @@ Oid get_constraint_index(Oid constraintId)
ScanKeyData key[3];
SysScanDesc scan = NULL;
HeapTuple tup = NULL;
char relkind;
/* Search the dependency table for the dependent index */
depRel = heap_open(DependRelationId, AccessShareLock);
@ -619,10 +620,12 @@ Oid get_constraint_index(Oid constraintId)
* must be what we are looking for. (The relkind test is just
* paranoia; there shouldn't be any such dependencies otherwise.)
*/
if (deprec->classid == RelationRelationId && deprec->objsubid == 0 && deprec->deptype == DEPENDENCY_INTERNAL &&
get_rel_relkind(deprec->objid) == RELKIND_INDEX) {
indexId = deprec->objid;
break;
if (deprec->classid == RelationRelationId && deprec->objsubid == 0 && deprec->deptype == DEPENDENCY_INTERNAL) {
relkind = get_rel_relkind(deprec->objid);
if (relkind == RELKIND_INDEX || relkind == RELKIND_GLOBAL_INDEX) {
indexId = deprec->objid;
break;
}
}
}

View File

@ -242,6 +242,7 @@ PgObjectType GetPgObjectTypePgClass(char relkind)
objectType = OBJECT_TYPE_SEQUENCE;
break;
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
objectType = OBJECT_TYPE_INDEX;
break;
default:

View File

@ -1245,7 +1245,8 @@ void init_gtt_storage(CmdType operation, ResultRelInfo* resultRelInfo)
IndexInfo* info = resultRelInfo->ri_IndexRelationInfo[i];
Assert(index->rd_index->indisvalid);
Assert(index->rd_index->indisready);
index_build(relation, NULL, index, NULL, info, index->rd_index->indisprimary, false, false);
index_build(
relation, NULL, index, NULL, info, index->rd_index->indisprimary, false, INDEX_CREATE_NONE_PARTITION);
}
toastrelid = relation->rd_rel->reltoastrelid;
@ -1265,8 +1266,14 @@ void init_gtt_storage(CmdType operation, ResultRelInfo* resultRelInfo)
currentIndex = index_open(indexId, RowExclusiveLock);
indexInfo = BuildDummyIndexInfo(currentIndex);
index_build(
toastrel, NULL, currentIndex, NULL, indexInfo, currentIndex->rd_index->indisprimary, false, false);
index_build(toastrel,
NULL,
currentIndex,
NULL,
indexInfo,
currentIndex->rd_index->indisprimary,
false,
INDEX_CREATE_NONE_PARTITION);
index_close(currentIndex, NoLock);
}

View File

@ -279,6 +279,7 @@ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Da
*/
indexInfo = makeNode(IndexInfo);
indexInfo->ii_NumIndexAttrs = 2;
indexInfo->ii_NumIndexKeyAttrs = indexInfo->ii_NumIndexAttrs;
indexInfo->ii_KeyAttrNumbers[0] = 1;
indexInfo->ii_KeyAttrNumbers[1] = 2;
indexInfo->ii_Expressions = NIL;
@ -304,8 +305,7 @@ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Da
coloptions[1] = 0;
IndexCreateExtraArgs extra;
extra.existingPSortOid = InvalidOid;
extra.isPartitionedIndex = false;
SetIndexCreateExtraArgs(&extra, InvalidOid, false, false);
index_create(toast_rel,
toast_idxname,

View File

@ -3690,6 +3690,7 @@ static Constraint* _copyConstraint(const Constraint* from)
COPY_NODE_FIELD(raw_expr);
COPY_STRING_FIELD(cooked_expr);
COPY_NODE_FIELD(keys);
COPY_NODE_FIELD(including);
COPY_NODE_FIELD(exclusions);
COPY_NODE_FIELD(options);
COPY_STRING_FIELD(indexname);
@ -4446,6 +4447,7 @@ static IndexStmt* _copyIndexStmt(const IndexStmt* from)
COPY_STRING_FIELD(accessMethod);
COPY_STRING_FIELD(tableSpace);
COPY_NODE_FIELD(indexParams);
COPY_NODE_FIELD(indexIncludingParams);
COPY_NODE_FIELD(options);
COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(excludeOpNames);
@ -4454,6 +4456,7 @@ static IndexStmt* _copyIndexStmt(const IndexStmt* from)
COPY_SCALAR_FIELD(oldNode);
COPY_NODE_FIELD(partClause);
COPY_SCALAR_FIELD(isPartitioned);
COPY_SCALAR_FIELD(isGlobal);
COPY_SCALAR_FIELD(unique);
COPY_SCALAR_FIELD(primary);
COPY_SCALAR_FIELD(isconstraint);

View File

@ -1259,6 +1259,7 @@ static bool _equalIndexStmt(const IndexStmt* a, const IndexStmt* b)
COMPARE_STRING_FIELD(accessMethod);
COMPARE_STRING_FIELD(tableSpace);
COMPARE_NODE_FIELD(indexParams);
COMPARE_NODE_FIELD(indexIncludingParams);
COMPARE_NODE_FIELD(options);
COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(excludeOpNames);
@ -2238,6 +2239,7 @@ static bool _equalConstraint(const Constraint* a, const Constraint* b)
COMPARE_NODE_FIELD(raw_expr);
COMPARE_STRING_FIELD(cooked_expr);
COMPARE_NODE_FIELD(keys);
COMPARE_NODE_FIELD(including);
COMPARE_NODE_FIELD(exclusions);
COMPARE_NODE_FIELD(options);
COMPARE_STRING_FIELD(indexname);

View File

@ -3284,6 +3284,7 @@ static void _outIndexStmt(StringInfo str, IndexStmt* node)
WRITE_STRING_FIELD(accessMethod);
WRITE_STRING_FIELD(tableSpace);
WRITE_NODE_FIELD(indexParams);
WRITE_NODE_FIELD(indexIncludingParams);
WRITE_NODE_FIELD(options);
WRITE_NODE_FIELD(whereClause);
WRITE_NODE_FIELD(excludeOpNames);
@ -4281,6 +4282,7 @@ static void _outConstraint(StringInfo str, Constraint* node)
case CONSTR_PRIMARY:
appendStringInfo(str, "PRIMARY_KEY");
WRITE_NODE_FIELD(keys);
WRITE_NODE_FIELD(including);
WRITE_NODE_FIELD(options);
WRITE_STRING_FIELD(indexname);
WRITE_STRING_FIELD(indexspace);
@ -4290,6 +4292,7 @@ static void _outConstraint(StringInfo str, Constraint* node)
case CONSTR_UNIQUE:
appendStringInfo(str, "UNIQUE");
WRITE_NODE_FIELD(keys);
WRITE_NODE_FIELD(including);
WRITE_NODE_FIELD(options);
WRITE_STRING_FIELD(indexname);
WRITE_STRING_FIELD(indexspace);
@ -4299,6 +4302,7 @@ static void _outConstraint(StringInfo str, Constraint* node)
case CONSTR_EXCLUSION:
appendStringInfo(str, "EXCLUSION");
WRITE_NODE_FIELD(exclusions);
WRITE_NODE_FIELD(including);
WRITE_NODE_FIELD(options);
WRITE_STRING_FIELD(indexname);
WRITE_STRING_FIELD(indexspace);

View File

@ -80,7 +80,23 @@
#define WORDS_PER_PAGE ((MAX_TUPLES_PER_PAGE - 1) / BITS_PER_BITMAPWORD + 1)
/* number of active words for a lossy chunk: */
#define WORDS_PER_CHUNK ((PAGES_PER_CHUNK - 1) / BITS_PER_BITMAPWORD + 1)
/* compare two entry node. For regular table, partitionOid is set to Invalid */
#define IS_ENTRY_NODE_MATCH(tarNode, matchNode) \
(tarNode.blockNo == matchNode.blockNo && tarNode.partitionOid == matchNode.partitionOid)
#define IS_CHUNK_BEFORE_PAGE(chunkNode, pageNode) \
(chunkNode.partitionOid < pageNode.partitionOid \
? true \
: (chunkNode.partitionOid > pageNode.partitionOid \
? false \
: (chunkNode.blockNo < pageNode.blockNo ? true : false)))
/*
* Used as key of hash table for PagetableEntry.
*/
typedef struct PagetableEntryNode_s {
BlockNumber blockNo; /* page number (hashtable key) */
Oid partitionOid; /* used for GLOBAL partition index to indicate partition table */
} PagetableEntryNode;
/*
* The hashtable entries are represented by this data structure. For
* an exact page, blockno is the page number and bit k of the bitmap
@ -96,12 +112,11 @@
* must be checked for each (ie, these are candidate matches).
*/
typedef struct PagetableEntry {
BlockNumber blockno; /* page number (hashtable key) */
PagetableEntryNode entryNode;
bool ischunk; /* T = lossy storage, F = exact */
bool recheck; /* should the tuples be rechecked? */
bitmapword words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)];
} PagetableEntry;
/*
* dynahash.c is optimized for relatively large, long-lived hash tables.
* This is not ideal for TIDBitMap, particularly when we are using a bitmap
@ -132,6 +147,7 @@ struct TIDBitmap {
int npages; /* number of exact entries in pagetable */
int nchunks; /* number of lossy entries in pagetable */
bool iterating; /* tbm_begin_iterate called? */
bool isGlobalPart; /* represent global partition index tbm */
PagetableEntry entry1; /* used when status == TBM_ONE_PAGE */
/* these are valid when iterating is true: */
PagetableEntry** spages; /* sorted exact-page list, or NULL */
@ -155,10 +171,10 @@ struct TBMIterator {
/* Local function prototypes */
static void tbm_union_page(TIDBitmap* a, const PagetableEntry* bpage);
static bool tbm_intersect_page(TIDBitmap* a, PagetableEntry* apage, const TIDBitmap* b);
static const PagetableEntry* tbm_find_pageentry(const TIDBitmap* tbm, BlockNumber pageno);
static PagetableEntry* tbm_get_pageentry(TIDBitmap* tbm, BlockNumber pageno);
static bool tbm_page_is_lossy(const TIDBitmap* tbm, BlockNumber pageno);
static void tbm_mark_page_lossy(TIDBitmap* tbm, BlockNumber pageno);
static const PagetableEntry* tbm_find_pageentry(const TIDBitmap* tbm, PagetableEntryNode pageNode);
static PagetableEntry* tbm_get_pageentry(TIDBitmap* tbm, PagetableEntryNode pageNode);
static bool tbm_page_is_lossy(const TIDBitmap* tbm, PagetableEntryNode pageNode);
static void tbm_mark_page_lossy(TIDBitmap* tbm, PagetableEntryNode pageNode);
static void tbm_lossify(TIDBitmap* tbm);
static int tbm_comparator(const void* left, const void* right);
@ -179,7 +195,7 @@ TIDBitmap* tbm_create(long maxbytes)
tbm->mcxt = CurrentMemoryContext;
tbm->status = TBM_EMPTY;
tbm->isGlobalPart = false;
/*
* Estimate number of hashtable entries we can have within maxbytes. This
* estimates the hash overhead at MAXALIGN(sizeof(HASHELEMENT)) plus a
@ -211,7 +227,7 @@ static void tbm_create_pagetable(TIDBitmap* tbm)
/* Create the hashtable proper */
rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
securec_check(rc, "", "");
hash_ctl.keysize = sizeof(BlockNumber);
hash_ctl.keysize = sizeof(PagetableEntryNode);
hash_ctl.entrysize = sizeof(PagetableEntry);
hash_ctl.hash = tag_hash;
hash_ctl.hcxt = tbm->mcxt;
@ -225,7 +241,7 @@ static void tbm_create_pagetable(TIDBitmap* tbm)
PagetableEntry* page = NULL;
bool found = false;
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&tbm->entry1.blockno, HASH_ENTER, &found);
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&tbm->entry1.entryNode, HASH_ENTER, &found);
Assert(!found);
errno_t rc = memcpy_s(page, sizeof(PagetableEntry), &tbm->entry1, sizeof(PagetableEntry));
securec_check(rc, "\0", "\0");
@ -257,7 +273,7 @@ void tbm_free(TIDBitmap* tbm)
* If recheck is true, then the recheck flag will be set in the
* TBMIterateResult when any of these tuples are reported out.
*/
void tbm_add_tuples(TIDBitmap* tbm, const ItemPointer tids, int ntids, bool recheck)
void tbm_add_tuples(TIDBitmap* tbm, const ItemPointer tids, int ntids, bool recheck, Oid partitionOid)
{
int i;
@ -266,6 +282,7 @@ void tbm_add_tuples(TIDBitmap* tbm, const ItemPointer tids, int ntids, bool rech
BlockNumber blk = ItemPointerGetBlockNumber(tids + i);
OffsetNumber off = ItemPointerGetOffsetNumber(tids + i);
PagetableEntry* page = NULL;
PagetableEntryNode pageNode = {blk, partitionOid};
int wordnum, bitnum;
/* safety check to ensure we don't overrun bit array bounds */
@ -276,11 +293,11 @@ void tbm_add_tuples(TIDBitmap* tbm, const ItemPointer tids, int ntids, bool rech
errmsg("tuple offset out of range: %u", off)));
}
if (tbm_page_is_lossy(tbm, blk)) {
if (tbm_page_is_lossy(tbm, pageNode)) {
continue; /* whole page is already marked */
}
page = tbm_get_pageentry(tbm, blk);
page = tbm_get_pageentry(tbm, pageNode);
if (page->ischunk) {
/* The page is a lossy chunk header, set bit for itself */
@ -307,8 +324,9 @@ void tbm_add_tuples(TIDBitmap* tbm, const ItemPointer tids, int ntids, bool rech
*/
void tbm_add_page(TIDBitmap* tbm, BlockNumber pageno)
{
PagetableEntryNode pnode = {pageno, InvalidOid};
/* Enter the page in the bitmap, or mark it lossy if already present */
tbm_mark_page_lossy(tbm, pageno);
tbm_mark_page_lossy(tbm, pnode);
/* If we went over the memory limit, lossify some more pages */
if (tbm->nentries > tbm->maxentries) {
tbm_lossify(tbm);
@ -356,21 +374,22 @@ static void tbm_union_page(TIDBitmap* a, const PagetableEntry* bpage)
if (w != 0) {
BlockNumber pg;
pg = bpage->blockno + (wordnum * BITS_PER_BITMAPWORD);
pg = bpage->entryNode.blockNo + (wordnum * BITS_PER_BITMAPWORD);
while (w != 0) {
if (w & 1) {
tbm_mark_page_lossy(a, pg);
PagetableEntryNode unionNode = {pg, bpage->entryNode.partitionOid};
tbm_mark_page_lossy(a, unionNode);
}
pg++;
w >>= 1;
}
}
}
} else if (tbm_page_is_lossy(a, bpage->blockno)) {
} else if (tbm_page_is_lossy(a, bpage->entryNode)) {
/* page is already lossy in a, nothing to do */
return;
} else {
apage = tbm_get_pageentry(a, bpage->blockno);
apage = tbm_get_pageentry(a, bpage->entryNode);
if (apage->ischunk) {
/* The page is a lossy chunk header, set bit for itself */
apage->words[0] |= ((bitmapword)1 << 0);
@ -425,7 +444,7 @@ void tbm_intersect(TIDBitmap* a, const TIDBitmap* b)
a->npages--;
}
a->nentries--;
if (hash_search(a->pagetable, (void*)&apage->blockno, HASH_REMOVE, NULL) == NULL) {
if (hash_search(a->pagetable, (void*)&apage->entryNode, HASH_REMOVE, NULL) == NULL) {
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED), errmodule(MOD_EXECUTOR), errmsg("hash table corrupted")));
}
@ -456,11 +475,12 @@ static bool tbm_intersect_page(TIDBitmap* a, PagetableEntry* apage, const TIDBit
BlockNumber pg;
int bitnum;
pg = apage->blockno + (wordnum * BITS_PER_BITMAPWORD);
pg = apage->entryNode.blockNo + (wordnum * BITS_PER_BITMAPWORD);
bitnum = 0;
while (w != 0) {
if (w & 1) {
if (!tbm_page_is_lossy(b, pg) && tbm_find_pageentry(b, pg) == NULL) {
PagetableEntryNode pNode = {pg, apage->entryNode.partitionOid};
if (!tbm_page_is_lossy(b, pNode) && tbm_find_pageentry(b, pNode) == NULL) {
/* Page is not in b at all, lose lossy bit */
neww &= ~((bitmapword)1 << (unsigned int)bitnum);
}
@ -476,7 +496,7 @@ static bool tbm_intersect_page(TIDBitmap* a, PagetableEntry* apage, const TIDBit
}
}
return candelete;
} else if (tbm_page_is_lossy(b, apage->blockno)) {
} else if (tbm_page_is_lossy(b, apage->entryNode)) {
/*
* Some of the tuples in 'a' might not satisfy the quals for 'b', but
* because the page 'b' is lossy, we don't know which ones. Therefore
@ -488,7 +508,7 @@ static bool tbm_intersect_page(TIDBitmap* a, PagetableEntry* apage, const TIDBit
} else {
bool candelete = true;
bpage = tbm_find_pageentry(b, apage->blockno);
bpage = tbm_find_pageentry(b, apage->entryNode);
if (bpage != NULL) {
/* Both pages are exact, merge at the bit level */
Assert(!bpage->ischunk);
@ -638,12 +658,14 @@ TBMIterateResult* tbm_iterate(TBMIterator* iterator)
*/
if (iterator->schunkptr < tbm->nchunks) {
PagetableEntry* chunk = tbm->schunks[iterator->schunkptr];
BlockNumber chunk_blockno;
chunk_blockno = chunk->blockno + iterator->schunkbit;
if (iterator->spageptr >= tbm->npages || chunk_blockno < tbm->spages[iterator->spageptr]->blockno) {
PagetableEntryNode pnode;
pnode.blockNo = chunk->entryNode.blockNo + iterator->schunkbit;
pnode.partitionOid = chunk->entryNode.partitionOid;
if (iterator->spageptr >= tbm->npages ||
IS_CHUNK_BEFORE_PAGE(pnode, tbm->spages[iterator->spageptr]->entryNode)) {
/* Return a lossy page indicator from the chunk */
output->blockno = chunk_blockno;
output->blockno = pnode.blockNo;
output->partitionOid = pnode.partitionOid;
output->ntuples = -1;
output->recheck = true;
iterator->schunkbit++;
@ -680,7 +702,8 @@ TBMIterateResult* tbm_iterate(TBMIterator* iterator)
}
}
}
output->blockno = page->blockno;
output->blockno = page->entryNode.blockNo;
output->partitionOid = page->entryNode.partitionOid;
output->ntuples = ntuples;
output->recheck = page->recheck;
iterator->spageptr++;
@ -708,7 +731,7 @@ void tbm_end_iterate(TBMIterator* iterator)
*
* Returns NULL if there is no non-lossy entry for the pageno.
*/
static const PagetableEntry* tbm_find_pageentry(const TIDBitmap* tbm, BlockNumber pageno)
static const PagetableEntry* tbm_find_pageentry(const TIDBitmap* tbm, PagetableEntryNode pageNode)
{
const PagetableEntry* page = NULL;
@ -718,14 +741,14 @@ static const PagetableEntry* tbm_find_pageentry(const TIDBitmap* tbm, BlockNumbe
if (tbm->status == TBM_ONE_PAGE) {
page = &tbm->entry1;
if (page->blockno != pageno) {
if (!IS_ENTRY_NODE_MATCH(page->entryNode, pageNode)) {
return NULL;
}
Assert(!page->ischunk);
return page;
}
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&pageno, HASH_FIND, NULL);
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&pageNode, HASH_FIND, NULL);
if (page == NULL) {
return NULL;
}
@ -743,7 +766,7 @@ static const PagetableEntry* tbm_find_pageentry(const TIDBitmap* tbm, BlockNumbe
* This may cause the table to exceed the desired memory size. It is
* up to the caller to call tbm_lossify() at the next safe point if so.
*/
static PagetableEntry* tbm_get_pageentry(TIDBitmap* tbm, BlockNumber pageno)
static PagetableEntry* tbm_get_pageentry(TIDBitmap* tbm, PagetableEntryNode pageNode)
{
PagetableEntry* page = NULL;
bool found = false;
@ -757,7 +780,7 @@ static PagetableEntry* tbm_get_pageentry(TIDBitmap* tbm, BlockNumber pageno)
} else {
if (tbm->status == TBM_ONE_PAGE) {
page = &tbm->entry1;
if (page->blockno == pageno) {
if (IS_ENTRY_NODE_MATCH(page->entryNode, pageNode)) {
return page;
}
/* Time to switch from one page to a hashtable */
@ -765,14 +788,15 @@ static PagetableEntry* tbm_get_pageentry(TIDBitmap* tbm, BlockNumber pageno)
}
/* Look up or create an entry */
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&pageno, HASH_ENTER, &found);
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&pageNode, HASH_ENTER, &found);
}
/* Initialize it if not present before */
if (!found) {
rc = memset_s(page, sizeof(PagetableEntry), 0, sizeof(PagetableEntry));
securec_check(rc, "", "");
page->blockno = pageno;
page->entryNode.blockNo = pageNode.blockNo;
page->entryNode.partitionOid = pageNode.partitionOid;
/* must count it too */
tbm->nentries++;
tbm->npages++;
@ -784,10 +808,10 @@ static PagetableEntry* tbm_get_pageentry(TIDBitmap* tbm, BlockNumber pageno)
/*
* tbm_page_is_lossy - is the page marked as lossily stored?
*/
static bool tbm_page_is_lossy(const TIDBitmap* tbm, BlockNumber pageno)
static bool tbm_page_is_lossy(const TIDBitmap* tbm, PagetableEntryNode pageNode)
{
PagetableEntry* page = NULL;
BlockNumber chunk_pageno;
BlockNumber chunkPageNo;
int bitno;
/* we can skip the lookup if there are no lossy chunks */
@ -796,9 +820,10 @@ static bool tbm_page_is_lossy(const TIDBitmap* tbm, BlockNumber pageno)
}
Assert(tbm->status == TBM_HASH);
bitno = pageno % PAGES_PER_CHUNK;
chunk_pageno = pageno - bitno;
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&chunk_pageno, HASH_FIND, NULL);
bitno = pageNode.blockNo % PAGES_PER_CHUNK;
chunkPageNo = pageNode.blockNo - bitno;
PagetableEntryNode chunkNode = {chunkPageNo, pageNode.partitionOid};
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&chunkNode, HASH_FIND, NULL);
if (page != NULL && page->ischunk) {
int wordnum = WORDNUM(bitno);
int bitnum = BITNUM(bitno);
@ -816,11 +841,11 @@ static bool tbm_page_is_lossy(const TIDBitmap* tbm, BlockNumber pageno)
* This may cause the table to exceed the desired memory size. It is
* up to the caller to call tbm_lossify() at the next safe point if so.
*/
static void tbm_mark_page_lossy(TIDBitmap* tbm, BlockNumber pageno)
static void tbm_mark_page_lossy(TIDBitmap* tbm, PagetableEntryNode pageNode)
{
PagetableEntry* page = NULL;
bool found = false;
BlockNumber chunk_pageno;
BlockNumber chunkPageNo;
int bitno;
int wordnum;
int bitnum;
@ -831,15 +856,15 @@ static void tbm_mark_page_lossy(TIDBitmap* tbm, BlockNumber pageno)
tbm_create_pagetable(tbm);
}
bitno = pageno % PAGES_PER_CHUNK;
chunk_pageno = pageno - bitno;
bitno = pageNode.blockNo % PAGES_PER_CHUNK;
chunkPageNo = pageNode.blockNo - bitno;
PagetableEntryNode chunkNode = {chunkPageNo, pageNode.partitionOid};
/*
* Remove any extant non-lossy entry for the page. If the page is its own
* chunk header, however, we skip this and handle the case below.
*/
if (bitno != 0) {
if (hash_search(tbm->pagetable, (void*)&pageno, HASH_REMOVE, NULL) != NULL) {
if (hash_search(tbm->pagetable, (void*)&pageNode, HASH_REMOVE, NULL) != NULL) {
/* It was present, so adjust counts */
tbm->nentries--;
tbm->npages--; /* assume it must have been non-lossy */
@ -847,13 +872,13 @@ static void tbm_mark_page_lossy(TIDBitmap* tbm, BlockNumber pageno)
}
/* Look up or create entry for chunk-header page */
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&chunk_pageno, HASH_ENTER, &found);
page = (PagetableEntry*)hash_search(tbm->pagetable, (void*)&chunkNode, HASH_ENTER, &found);
/* Initialize it if not present before */
if (!found) {
rc = memset_s(page, sizeof(PagetableEntry), 0, sizeof(PagetableEntry));
securec_check(rc, "", "");
page->blockno = chunk_pageno;
page->entryNode = chunkNode;
page->ischunk = true;
/* must count it too */
tbm->nentries++;
@ -862,7 +887,7 @@ static void tbm_mark_page_lossy(TIDBitmap* tbm, BlockNumber pageno)
/* chunk header page was formerly non-lossy, make it lossy */
rc = memset_s(page, sizeof(PagetableEntry), 0, sizeof(PagetableEntry));
securec_check(rc, "", "");
page->blockno = chunk_pageno;
page->entryNode = chunkNode;
page->ischunk = true;
/* we assume it had some tuple bit(s) set, so mark it lossy */
page->words[0] = ((bitmapword)1 << 0);
@ -906,12 +931,12 @@ static void tbm_lossify(TIDBitmap* tbm)
* If the page would become a chunk header, we won't save anything by
* converting it to lossy, so skip it.
*/
if ((page->blockno % PAGES_PER_CHUNK) == 0) {
if ((page->entryNode.blockNo % PAGES_PER_CHUNK) == 0) {
continue;
}
/* This does the dirty work ... */
tbm_mark_page_lossy(tbm, page->blockno);
tbm_mark_page_lossy(tbm, page->entryNode);
if (tbm->nentries <= tbm->maxentries / 2) {
/* we have done enough */
@ -946,13 +971,27 @@ static void tbm_lossify(TIDBitmap* tbm)
*/
static int tbm_comparator(const void* left, const void* right)
{
BlockNumber l = (*((PagetableEntry* const*)left))->blockno;
BlockNumber r = (*((PagetableEntry* const*)right))->blockno;
PagetableEntryNode l = (*((PagetableEntry* const*)left))->entryNode;
PagetableEntryNode r = (*((PagetableEntry* const*)right))->entryNode;
if (l < r) {
if (l.partitionOid < r.partitionOid) {
return -1;
} else if (l > r) {
} else if (l.partitionOid > r.partitionOid) {
return 1;
} else if (l.blockNo < r.blockNo) {
return -1;
} else if (l.blockNo > r.blockNo) {
return 1;
}
return 0;
}
bool tbm_is_global(const TIDBitmap* tbm)
{
return tbm->isGlobalPart;
}
void tbm_set_global(TIDBitmap* tbm, bool isGlobal)
{
tbm->isGlobalPart = isGlobal;
}

View File

@ -373,6 +373,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
aggr_args old_aggr_definition old_aggr_list
oper_argtypes RuleActionList RuleActionMulti
opt_column_list columnList opt_name_list opt_analyze_column_define opt_multi_name_list
opt_include opt_c_include index_including_params
sort_clause opt_sort_clause sortby_list index_params
name_list from_clause from_list opt_array_bounds
qualified_name_list any_name any_name_list
@ -651,7 +652,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
HANDLER HAVING HDFSDIRECTORY HEADER_P HOLD HOUR_P
IDENTIFIED IDENTITY_P IF_P IGNORE_EXTRA_DATA ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P
IDENTIFIED IDENTITY_P IF_P IGNORE_EXTRA_DATA ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDE
INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIAL_P INITIALLY INITRANS INLINE_P INMEMORY
INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERNAL
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
@ -4746,20 +4747,21 @@ ConstraintElem:
n->initially_valid = !n->skip_validation;
$$ = (Node *)n;
}
| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
| UNIQUE '(' columnList ')' opt_c_include opt_definition OptConsTableSpace
ConstraintAttributeSpec InformationalConstraintElem
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_UNIQUE;
n->location = @1;
n->keys = $3;
n->options = $5;
n->including = $5;
n->options = $6;
n->indexname = NULL;
n->indexspace = $6;
processCASbits($7, @7, "UNIQUE",
n->indexspace = $7;
processCASbits($8, @8, "UNIQUE",
&n->deferrable, &n->initdeferred, NULL,
NULL, yyscanner);
n->inforConstraint = (InformationalConstraint *) $8; /* informational constraint info */
n->inforConstraint = (InformationalConstraint *) $9; /* informational constraint info */
$$ = (Node *)n;
}
| UNIQUE ExistingIndex ConstraintAttributeSpec InformationalConstraintElem
@ -4768,6 +4770,7 @@ ConstraintElem:
n->contype = CONSTR_UNIQUE;
n->location = @1;
n->keys = NIL;
n->including = NIL;
n->options = NIL;
n->indexname = $2;
n->indexspace = NULL;
@ -4777,20 +4780,21 @@ ConstraintElem:
n->inforConstraint = (InformationalConstraint *) $4; /* informational constraint info */
$$ = (Node *)n;
}
| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
| PRIMARY KEY '(' columnList ')' opt_c_include opt_definition OptConsTableSpace
ConstraintAttributeSpec InformationalConstraintElem
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_PRIMARY;
n->location = @1;
n->keys = $4;
n->options = $6;
n->including = $6;
n->options = $7;
n->indexname = NULL;
n->indexspace = $7;
processCASbits($8, @8, "PRIMARY KEY",
n->indexspace = $8;
processCASbits($9, @9, "PRIMARY KEY",
&n->deferrable, &n->initdeferred, NULL,
NULL, yyscanner);
n->inforConstraint = (InformationalConstraint *) $9; /* informational constraint info */
n->inforConstraint = (InformationalConstraint *) $10; /* informational constraint info */
$$ = (Node *)n;
}
| PRIMARY KEY ExistingIndex ConstraintAttributeSpec InformationalConstraintElem
@ -4799,6 +4803,7 @@ ConstraintElem:
n->contype = CONSTR_PRIMARY;
n->location = @1;
n->keys = NIL;
n->including = NIL;
n->options = NIL;
n->indexname = $3;
n->indexspace = NULL;
@ -4809,7 +4814,7 @@ ConstraintElem:
$$ = (Node *)n;
}
| EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
opt_definition OptConsTableSpace ExclusionWhereClause
opt_c_include opt_definition OptConsTableSpace ExclusionWhereClause
ConstraintAttributeSpec
{
ereport(ERROR,
@ -4820,11 +4825,12 @@ ConstraintElem:
n->location = @1;
n->access_method = $2;
n->exclusions = $4;
n->options = $6;
n->including = $6;
n->options = $7;
n->indexname = NULL;
n->indexspace = $7;
n->where_clause = $8;
processCASbits($9, @9, "EXCLUDE",
n->indexspace = $8;
n->where_clause = $9;
processCASbits($10, @10, "EXCLUDE",
&n->deferrable, &n->initdeferred, NULL,
NULL, yyscanner);
$$ = (Node *)n;
@ -4886,6 +4892,10 @@ columnElem: ColId
}
;
opt_c_include: INCLUDE '(' columnList ')' { $$ = $3; }
| /* EMPTY */ { $$ = NIL; }
;
key_match: MATCH FULL
{
$$ = FKCONSTR_MATCH_FULL;
@ -8842,7 +8852,7 @@ defacl_privilege_target:
IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_index_name
ON qualified_name access_method_clause '(' index_params ')'
opt_reloptions OptPartitionElement where_clause
opt_include opt_reloptions OptPartitionElement where_clause
{
IndexStmt *n = makeNode(IndexStmt);
n->unique = $2;
@ -8852,15 +8862,17 @@ IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_index_name
n->relation = $7;
n->accessMethod = $8;
n->indexParams = $10;
n->options = $12;
n->tableSpace = $13;
n->whereClause = $14;
n->indexIncludingParams = $12;
n->options = $13;
n->tableSpace = $14;
n->whereClause = $15;
n->excludeOpNames = NIL;
n->idxcomment = NULL;
n->indexOid = InvalidOid;
n->oldNode = InvalidOid;
n->partClause = NULL;
n->isPartitioned = false;
n->isGlobal = false;
n->primary = false;
n->isconstraint = false;
n->deferrable = false;
@ -8884,6 +8896,36 @@ IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_index_name
n->options = $14;
n->tableSpace = $15;
n->isPartitioned = true;
n->isGlobal = false;
n->excludeOpNames = NIL;
n->idxcomment = NULL;
n->indexOid = InvalidOid;
n->oldNode = InvalidOid;
n->primary = false;
n->isconstraint = false;
n->deferrable = false;
n->initdeferred = false;
$$ = (Node *)n;
}
| CREATE opt_unique INDEX opt_concurrently opt_index_name
ON qualified_name access_method_clause '(' index_params ')'
GLOBAL opt_reloptions OptTableSpace
{
IndexStmt *n = makeNode(IndexStmt);
n->unique = $2;
n->concurrent = $4;
n->schemaname = $5->schemaname;
n->idxname = $5->relname;
n->relation = $7;
n->accessMethod = $8;
n->indexParams = $10;
n->partClause = NULL;
n->options = $13;
n->tableSpace = $14;
n->isPartitioned = true;
n->isGlobal = true;
n->excludeOpNames = NIL;
n->idxcomment = NULL;
n->indexOid = InvalidOid;
@ -8984,6 +9026,15 @@ index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
}
;
opt_include: INCLUDE '(' index_including_params ')' { $$ = $3; }
| /* EMPTY */ { $$ = NIL; }
;
index_including_params: index_elem { $$ = list_make1($1); }
| index_including_params ',' index_elem { $$ = lappend($1, $3); }
;
opt_collate: COLLATE any_name { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
@ -17989,6 +18040,7 @@ unreserved_keyword:
| IMMEDIATE
| IMMUTABLE
| IMPLICIT_P
| INCLUDE
| INCLUDING
| INCREMENT
| INDEX

View File

@ -798,7 +798,7 @@ List* checkInsertTargets(ParseState* pstate, List* cols, List** attrnos)
}
Form_pg_attribute* attr = pstate->p_target_relation->rd_att->attrs;
int numcol = pstate->p_target_relation->rd_rel->relnatts;
int numcol = RelationGetNumberOfAttributes(pstate->p_target_relation);
int i;
for (i = 0; i < numcol; i++) {

View File

@ -2149,9 +2149,10 @@ static IndexStmt* generateClonedIndexStmt(
}
/* Build the list of IndexElem */
index->indexParams = NIL;
index->indexIncludingParams = NIL;
indexprItem = list_head(indexprs);
for (keyno = 0; keyno < idxrec->indnatts; keyno++) {
for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++) {
IndexElem* iparam = NULL;
AttrNumber attnum = idxrec->indkey.values[keyno];
uint16 opt = (uint16)source_idx->rd_indoption[keyno];
@ -2240,6 +2241,12 @@ static IndexStmt* generateClonedIndexStmt(
}
}
/* Handle included columns separately */
if (idxrec->indnkeyatts != idxrec->indnatts) {
/* Only global-partition-index would satisfy this condition in the current code */
index->isGlobal = true;
}
/* Copy reloptions if any */
datum = SysCacheGetAttr(RELOID, htIdxrel, Anum_pg_class_reloptions, &isnull);
if (!isnull)
@ -2473,6 +2480,7 @@ static void transformIndexConstraints(CreateStmtContext* cxt)
IndexStmt* priorindex = (IndexStmt*)lfirst(k);
if (equal(index->indexParams, priorindex->indexParams) &&
equal(index->indexIncludingParams, priorindex->indexIncludingParams) &&
equal(index->whereClause, priorindex->whereClause) &&
equal(index->excludeOpNames, priorindex->excludeOpNames) &&
strcmp(index->accessMethod, priorindex->accessMethod) == 0 &&
@ -2630,6 +2638,7 @@ static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtCon
index->tableSpace = constraint->indexspace;
index->whereClause = constraint->where_clause;
index->indexParams = NIL;
index->indexIncludingParams = NIL;
index->excludeOpNames = NIL;
index->idxcomment = NULL;
index->indexOid = InvalidOid;
@ -2724,22 +2733,26 @@ static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtCon
RELATION_HAS_BUCKET(heapRel));
attname = pstrdup(NameStr(attform->attname));
/*
* Insist on default opclass and sort options. While the index
* would still work as a constraint with non-default settings, it
* might not provide exactly the same uniqueness semantics as
* you'd get from a normally-created constraint; and there's also
* the dump/reload problem mentioned above.
*/
defopclass = GetDefaultOpClass(attform->atttypid, indexRel->rd_rel->relam);
if (indclass->values[i] != defopclass || indexRel->rd_indoption[i] != 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("index \"%s\" does not have default sorting behavior", indexName),
errdetail("Cannot create a primary key or unique constraint using such an index."),
parser_errposition(cxt->pstate, constraint->location)));
if (i < indexForm->indnkeyatts) {
/*
* Insist on default opclass and sort options. While the
* index would still work as a constraint with non-default
* settings, it might not provide exactly the same uniqueness
* semantics as you'd get from a normally-created constraint;
* and there's also the dump/reload problem mentioned above.
*/
defopclass = GetDefaultOpClass(attform->atttypid, indexRel->rd_rel->relam);
if (indclass->values[i] != defopclass || indexRel->rd_indoption[i] != 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("index \"%s\" does not have default sorting behavior", indexName),
errdetail("Cannot create a primary key or unique constraint using such an index."),
parser_errposition(cxt->pstate, constraint->location)));
constraint->keys = lappend(constraint->keys, makeString(attname));
constraint->keys = lappend(constraint->keys, makeString(attname));
} else {
constraint->including = lappend(constraint->including, makeString(attname));
}
}
/* Close the index relation but keep the lock */
@ -2768,80 +2781,200 @@ static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtCon
index->indexParams = lappend(index->indexParams, elem);
index->excludeOpNames = lappend(index->excludeOpNames, opname);
}
} else {
return index;
/*
* For UNIQUE and PRIMARY KEY, we just have a list of column names.
*
* Make sure referenced keys exist. If we are making a PRIMARY KEY index,
* also make sure they are NOT NULL, if possible. (Although we could leave
* it to DefineIndex to mark the columns NOT NULL, it's more efficient to
* get it right the first time.)
*/
foreach (lc, constraint->keys) {
char* key = strVal(lfirst(lc));
bool found = false;
ColumnDef* column = NULL;
ListCell* columns = NULL;
IndexElem* iparam = NULL;
foreach (columns, cxt->columns) {
column = (ColumnDef*)lfirst(columns);
AssertEreport(IsA(column, ColumnDef), MOD_OPT, "");
if (strcmp(column->colname, key) == 0) {
found = true;
break;
}
}
if (found) {
/* found column in the new table; force it to be NOT NULL */
if (constraint->contype == CONSTR_PRIMARY && !constraint->inforConstraint->nonforced)
column->is_not_null = TRUE;
} else if (SystemAttributeByName(key, cxt->hasoids) != NULL) {
/*
* column will be a system column in the new table, so accept it.
* System columns can't ever be null, so no need to worry about
* PRIMARY/NOT NULL constraint.
*/
found = true;
} else if (cxt->inhRelations != NIL) {
/* try inherited tables */
ListCell* inher = NULL;
foreach (inher, cxt->inhRelations) {
RangeVar* inh = (RangeVar*)lfirst(inher);
Relation rel;
int count;
AssertEreport(IsA(inh, RangeVar), MOD_OPT, "");
rel = heap_openrv(inh, AccessShareLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("inherited relation \"%s\" is not a table", inh->relname)));
for (count = 0; count < rel->rd_att->natts; count++) {
Form_pg_attribute inhattr = rel->rd_att->attrs[count];
char* inhname = NameStr(inhattr->attname);
if (inhattr->attisdropped)
continue;
if (strcmp(key, inhname) == 0) {
found = true;
/*
* We currently have no easy way to force an inherited
* column to be NOT NULL at creation, if its parent
* wasn't so already. We leave it to DefineIndex to
* fix things up in this case.
*/
break;
}
}
heap_close(rel, NoLock);
if (found)
break;
}
}
/*
* In the ALTER TABLE case, don't complain about index keys not
* created in the command; they may well exist already. DefineIndex
* will complain about them if not, and will also take care of marking
* them NOT NULL.
*/
if (!found && !cxt->isalter)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" named in key does not exist", key),
parser_errposition(cxt->pstate, constraint->location)));
/* Check for PRIMARY KEY(foo, foo) */
foreach (columns, index->indexParams) {
iparam = (IndexElem*)lfirst(columns);
if (iparam->name && strcmp(key, iparam->name) == 0) {
if (index->primary)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in primary key constraint", key),
parser_errposition(cxt->pstate, constraint->location)));
else
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in unique constraint", key),
parser_errposition(cxt->pstate, constraint->location)));
}
}
#ifdef PGXC
/*
* Set fallback distribution column.
* If not set, set it to first column in index.
* If primary key, we prefer that over a unique constraint.
*/
if (index->indexParams == NIL && (index->primary || cxt->fallback_dist_col == NULL)) {
if (cxt->fallback_dist_col != NULL) {
list_free_deep(cxt->fallback_dist_col);
cxt->fallback_dist_col = NULL;
}
cxt->fallback_dist_col = lappend(cxt->fallback_dist_col, makeString(pstrdup(key)));
}
#endif
/* OK, add it to the index definition */
iparam = makeNode(IndexElem);
iparam->name = pstrdup(key);
iparam->expr = NULL;
iparam->indexcolname = NULL;
iparam->collation = NIL;
iparam->opclass = NIL;
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
index->indexParams = lappend(index->indexParams, iparam);
}
}
/*
* For UNIQUE and PRIMARY KEY, we just have a list of column names.
*
* Make sure referenced keys exist. If we are making a PRIMARY KEY index,
* also make sure they are NOT NULL, if possible. (Although we could leave
* it to DefineIndex to mark the columns NOT NULL, it's more efficient to
* get it right the first time.)
*/
foreach (lc, constraint->keys) {
/* Add included columns to index definition */
foreach (lc, constraint->including) {
char* key = strVal(lfirst(lc));
bool found = false;
ColumnDef* column = NULL;
ListCell* columns = NULL;
IndexElem* iparam = NULL;
ListCell* columns;
IndexElem* iparam;
foreach (columns, cxt->columns) {
column = (ColumnDef*)lfirst(columns);
AssertEreport(IsA(column, ColumnDef), MOD_OPT, "");
column = lfirst_node(ColumnDef, columns);
if (strcmp(column->colname, key) == 0) {
found = true;
break;
}
}
if (found) {
/* found column in the new table; force it to be NOT NULL */
if (constraint->contype == CONSTR_PRIMARY && !constraint->inforConstraint->nonforced)
column->is_not_null = TRUE;
} else if (SystemAttributeByName(key, cxt->hasoids) != NULL) {
/*
* column will be a system column in the new table, so accept it.
* System columns can't ever be null, so no need to worry about
* PRIMARY/NOT NULL constraint.
*/
found = true;
} else if (cxt->inhRelations != NIL) {
/* try inherited tables */
ListCell* inher = NULL;
foreach (inher, cxt->inhRelations) {
RangeVar* inh = (RangeVar*)lfirst(inher);
Relation rel;
int count;
if (!found) {
if (SystemAttributeByName(key, cxt->hasoids) != NULL) {
/*
* column will be a system column in the new table, so accept
* it. System columns can't ever be null, so no need to worry
* about PRIMARY/NOT NULL constraint.
*/
found = true;
} else if (cxt->inhRelations) {
/* try inherited tables */
ListCell* inher;
AssertEreport(IsA(inh, RangeVar), MOD_OPT, "");
rel = heap_openrv(inh, AccessShareLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("inherited relation \"%s\" is not a table", inh->relname)));
for (count = 0; count < rel->rd_att->natts; count++) {
Form_pg_attribute inhattr = rel->rd_att->attrs[count];
char* inhname = NameStr(inhattr->attname);
foreach (inher, cxt->inhRelations) {
RangeVar* inh = lfirst_node(RangeVar, inher);
Relation rel;
int count;
if (inhattr->attisdropped)
continue;
if (strcmp(key, inhname) == 0) {
found = true;
rel = heap_openrv(inh, AccessShareLock);
/* check user requested inheritance from valid relkind */
if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
rel->rd_rel->relkind != PARTTYPE_PARTITIONED_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("inherited relation \"%s\" is not a table or foreign table", inh->relname)));
for (count = 0; count < rel->rd_att->natts; count++) {
Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, count);
char* inhname = NameStr(inhattr->attname);
/*
* We currently have no easy way to force an inherited
* column to be NOT NULL at creation, if its parent
* wasn't so already. We leave it to DefineIndex to
* fix things up in this case.
*/
break;
if (inhattr->attisdropped)
continue;
if (strcmp(key, inhname) == 0) {
found = true;
/*
* We currently have no easy way to force an
* inherited column to be NOT NULL at creation, if
* its parent wasn't so already. We leave it to
* DefineIndex to fix things up in this case.
*/
break;
}
}
heap_close(rel, NoLock);
if (found)
break;
}
heap_close(rel, NoLock);
if (found)
break;
}
}
@ -2857,38 +2990,6 @@ static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtCon
errmsg("column \"%s\" named in key does not exist", key),
parser_errposition(cxt->pstate, constraint->location)));
/* Check for PRIMARY KEY(foo, foo) */
foreach (columns, index->indexParams) {
iparam = (IndexElem*)lfirst(columns);
if (iparam->name && strcmp(key, iparam->name) == 0) {
if (index->primary)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in primary key constraint", key),
parser_errposition(cxt->pstate, constraint->location)));
else
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in unique constraint", key),
parser_errposition(cxt->pstate, constraint->location)));
}
}
#ifdef PGXC
/*
* Set fallback distribution column.
* If not set, set it to first column in index.
* If primary key, we prefer that over a unique constraint.
*/
if (index->indexParams == NIL && (index->primary || cxt->fallback_dist_col == NULL)) {
if (cxt->fallback_dist_col != NULL) {
list_free_deep(cxt->fallback_dist_col);
cxt->fallback_dist_col = NULL;
}
cxt->fallback_dist_col = lappend(cxt->fallback_dist_col, makeString(pstrdup(key)));
}
#endif
/* OK, add it to the index definition */
iparam = makeNode(IndexElem);
iparam->name = pstrdup(key);
@ -2896,9 +2997,7 @@ static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtCon
iparam->indexcolname = NULL;
iparam->collation = NIL;
iparam->opclass = NIL;
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
index->indexParams = lappend(index->indexParams, iparam);
index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
}
return index;
@ -3062,9 +3161,23 @@ IndexStmt* transformIndexStmt(Oid relid, IndexStmt* stmt, const char* queryStrin
/* row store using btree index by default */
stmt->accessMethod = DEFAULT_INDEX_TYPE;
} else {
if (stmt->isGlobal) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Global partition index does not support column store.")));
}
/* column store using psort index by default */
stmt->accessMethod = DEFAULT_CSTORE_INDEX_TYPE;
}
} else if (stmt->isGlobal) {
/* Global partition index only support btree index */
if (pg_strcasecmp(stmt->accessMethod, DEFAULT_INDEX_TYPE) != 0) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Global partition index only support btree.")));
}
if (isColStore) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Global partition index does not support column store.")));
}
} else {
bool isDfsStore = RelationIsDfsStore(rel);
const bool isPsortMothed = (0 == pg_strcasecmp(stmt->accessMethod, DEFAULT_CSTORE_INDEX_TYPE));

View File

@ -1667,6 +1667,7 @@ Datum pg_relation_filenode(PG_FUNCTION_ARGS)
case RELKIND_RELATION:
case RELKIND_MATVIEW:
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
case RELKIND_SEQUENCE:
case RELKIND_TOASTVALUE:
/* okay, these have storage */
@ -1786,6 +1787,7 @@ Datum pg_relation_filepath(PG_FUNCTION_ARGS)
case RELKIND_RELATION:
case RELKIND_MATVIEW:
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
case RELKIND_SEQUENCE:
case RELKIND_TOASTVALUE:
/* okay, these have storage */
@ -2057,7 +2059,7 @@ static bool IsIndexRelationbyOid(Oid rel_oid)
Relation rel;
bool result = false;
rel = relation_open(rel_oid, AccessShareLock);
result = (rel->rd_rel->relkind == RELKIND_INDEX);
result = RelationIsIndex(rel);
relation_close(rel, AccessShareLock);
return result;
}
@ -2171,7 +2173,7 @@ static int64 pgxc_exec_sizefunc(Oid rel_oid, char* funcname, char* extra_arg)
if (!is_part_toast) {
Oid tab_rel_oid = InvalidOid;
if (rel->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(rel)) {
tab_rel_oid = rel->rd_index->indrelid;
} else {
tab_rel_oid = RelationGetRelid(rel);

View File

@ -2323,6 +2323,12 @@ static char* pg_get_indexdef_worker(
Oid keycoltype;
Oid keycolcollation;
/*
* Ignore non-key attributes if told to.
*/
if (keyno >= idxrec->indnkeyatts)
break;
if (!colno) {
appendStringInfoString(&buf, sep);
}
@ -2364,11 +2370,16 @@ static char* pg_get_indexdef_worker(
if (!attrs_only && (!colno || colno == keyno + 1)) {
Oid indcoll;
if (keyno >= idxrec->indnkeyatts) {
continue;
}
/* Add collation, if not default for column */
indcoll = indcollation->values[keyno];
if (OidIsValid(indcoll) && indcoll != keycolcollation) {
appendStringInfo(&buf, " COLLATE %s", generate_collation_name((indcoll)));
}
/* Add the operator class name, if not default */
get_opclass_name(indclass->values[keyno], keycoltype, &buf);
@ -2398,7 +2409,8 @@ static char* pg_get_indexdef_worker(
if (!attrs_only) {
appendStringInfoChar(&buf, ')');
if (idxrelrec->parttype == PARTTYPE_PARTITIONED_RELATION) {
if (idxrelrec->parttype == PARTTYPE_PARTITIONED_RELATION &&
idxrelrec->relkind != RELKIND_GLOBAL_INDEX) {
pg_get_indexdef_partitions(indexrelid, idxrec, show_tbl_spc, &buf);
}
@ -2653,6 +2665,17 @@ static char* pg_get_constraintdef_worker(Oid constraint_id, bool full_command, i
appendStringInfo(&buf, ")");
/* Fetch and build including column list */
isnull = true;
val = SysCacheGetAttr(CONSTROID, tup, Anum_pg_constraint_conincluding, &isnull);
if (!isnull) {
appendStringInfoString(&buf, " INCLUDE (");
decompile_column_index_array(val, con_form->conrelid, &buf);
appendStringInfoChar(&buf, ')');
}
indexId = get_constraint_index(constraint_id);
/* XXX why do we only print these bits if fullCommand? */
if (full_command && OidIsValid(indexId)) {

View File

@ -4734,7 +4734,7 @@ void examine_variable(PlannerInfo* root, Node* node, int var_relid, VariableStat
* Found a match ... is it a unique index? Tests here
* should match has_unique_index().
*/
if (index->unique && index->ncolumns == 1 && (index->indpred == NIL || index->predOK))
if (index->unique && index->nkeycolumns == 1 && (index->indpred == NIL || index->predOK))
var_data->isunique = true;
/*
@ -6815,7 +6815,7 @@ Datum btcostestimate(PG_FUNCTION_ARGS)
* clauselist_selectivity calculations. However, a ScalarArrayOp or
* NullTest invalidates that theory, even though it sets eq_qual_here.
*/
if (index->unique && index_col == index->ncolumns - 1 && eq_qual_here && !found_saop && !found_is_null_op)
if (index->unique && index_col == index->nkeycolumns - 1 && eq_qual_here && !found_saop && !found_is_null_op)
num_index_tuples = 1.0;
else {
List* selectivity_quals = NIL;
@ -6950,7 +6950,7 @@ Datum btcostestimate(PG_FUNCTION_ARGS)
var_correlation = -var_correlation;
}
if (index->ncolumns > 1) {
if (index->nkeycolumns > 1) {
*index_correlation = var_correlation * 0.75;
} else {
*index_correlation = var_correlation;
@ -7198,6 +7198,7 @@ static bool gincost_pattern(IndexOptInfo* index, int indexcol, Oid clause_op, Da
int32 search_mode = GIN_SEARCH_MODE_DEFAULT;
int32 i;
Assert(indexcol < index->nkeycolumns);
/*
* Get the operator's strategy number and declared input data types within
* the index opfamily. (We don't need the latter, but we use

View File

@ -73,6 +73,8 @@
#include "utils/partitionmap_gs.h"
#include "catalog/pg_partition.h"
#include "postmaster/autovacuum.h"
#include "nodes/makefuncs.h"
/*
* part 1:macro definitions, global virables, and typedefs
*/
@ -118,7 +120,7 @@ typedef struct partidcacheent {
*
*non-export function prototypes
*/
static HeapTuple ScanPgPartition(Oid targetPartId, bool indexOK);
static HeapTuple ScanPgPartition(Oid targetPartId, bool indexOK, Snapshot snapshot);
static Partition AllocatePartitionDesc(Form_pg_partition relp);
static Partition PartitionBuildDesc(Oid targetPartId, bool insertIt);
static void PartitionInitPhysicalAddr(Partition partition);
@ -129,7 +131,7 @@ static void PartitionReloadIndexInfo(Partition part);
static void PartitionParseRelOptions(Partition partition, HeapTuple tuple);
static HeapTuple ScanPgPartition(Oid targetPartId, bool indexOK)
static HeapTuple ScanPgPartition(Oid targetPartId, bool indexOK, Snapshot snapshot)
{
HeapTuple pg_partition_tuple;
Relation pg_partition_desc;
@ -163,7 +165,7 @@ static HeapTuple ScanPgPartition(Oid targetPartId, bool indexOK)
pg_partition_scan = systable_beginscan(pg_partition_desc,
PartitionOidIndexId,
indexOK && u_sess->relcache_cxt.criticalRelcachesBuilt,
SnapshotNow,
snapshot,
1,
key);
@ -234,7 +236,7 @@ static Partition PartitionBuildDesc(Oid targetPartId, bool insertIt)
/*
* find the tuple in pg_class corresponding to the given relation id
*/
pg_partition_tuple = ScanPgPartition(targetPartId, true);
pg_partition_tuple = ScanPgPartition(targetPartId, true, SnapshotNow);
/*
* if no such tuple exists, return NULL
*/
@ -370,7 +372,7 @@ Partition PartitionIdGetPartition(Oid partitionId)
char* PartitionOidGetName(Oid partOid)
{
HeapTuple tuple = ScanPgPartition(partOid, true);
HeapTuple tuple = ScanPgPartition(partOid, true, SnapshotNow);
if (!HeapTupleIsValid(tuple)) {
return NULL;
}
@ -386,7 +388,7 @@ char* PartitionOidGetName(Oid partOid)
Oid PartitionOidGetTablespace(Oid partOid)
{
HeapTuple tuple = ScanPgPartition(partOid, true);
HeapTuple tuple = ScanPgPartition(partOid, true, SnapshotNow);
if (!HeapTupleIsValid(tuple)) {
return InvalidOid;
}
@ -1191,8 +1193,9 @@ Relation partitionGetRelation(Relation rel, Partition part)
if (REALTION_BUCKETKEY_INITED(rel))
relation->rd_bucketkey = rel->rd_bucketkey;
else
relation->rd_bucketkey = NULL;
relation->rd_bucketkey = NULL;
relation->rd_att = rel->rd_att;
relation->rd_partHeapOid = part->pd_part->indextblid;
relation->rd_index = rel->rd_index;
relation->rd_indextuple = rel->rd_indextuple;
relation->rd_am = rel->rd_am;
@ -1318,7 +1321,7 @@ static void PartitionReloadIndexInfo(Partition part)
*/
Assert(part->pd_smgr == NULL);
pg_partition_tuple = ScanPgPartition(PartitionGetPartid(part), true);
pg_partition_tuple = ScanPgPartition(PartitionGetPartid(part), true, SnapshotNow);
if (!HeapTupleIsValid(pg_partition_tuple)) {
ereport(ERROR,
(errcode(ERRCODE_NO_DATA),
@ -1393,7 +1396,6 @@ void PartitionSetNewRelfilenode(Relation parent, Partition part, TransactionId f
partform = (Form_pg_partition)GETSTRUCT(tuple);
// CStore Relation must deal with cudesc relation, delta relation
//
if (RelationIsColStore(parent)) {
// step 1: CUDesc relation must set new relfilenode
// step 2: CUDesc index must be set new relfilenode
@ -1518,3 +1520,504 @@ static void PartitionParseRelOptions(Partition partition, HeapTuple tuple)
return;
}
/* Check one partition whether it is normal use, and save in Bitmapset liveParts */
static bool PartitionStatusIsLive(Oid partOid, Bitmapset** liveParts)
{
HeapTuple partTuple = NULL;
if (bms_is_member(partOid, *liveParts)) {
return true;
}
/* Get partition information from syscache */
partTuple = SearchSysCache1WithLogLevel(PARTRELID, ObjectIdGetDatum(partOid), LOG);
if (HeapTupleIsValid(partTuple)) {
ReleaseSysCache(partTuple);
*liveParts = bms_add_member(*liveParts, partOid);
return true;
}
return false;
}
/* Check one invisible partition whether enable clean, and save in Bitmapset enableCleanParts */
static bool InvisblePartEnableClean(HeapTuple partTuple, TupleDesc tupleDesc)
{
Datum partOptions;
bool isNull = false;
partOptions = fastgetattr(partTuple, Anum_pg_partition_reloptions, tupleDesc, &isNull);
if (isNull || !PartitionInvisibleMetadataKeep(partOptions)) {
return true;
}
return false;
}
/* Just for lazy vacuum check one partition's status */
static PartStatus PartTupleStatusForVacuum(HeapTuple partTuple, Buffer buffer, TransactionId oldestXmin)
{
PartStatus partStatus = PART_METADATA_NOEXIST;
/*
* We could possibly get away with not locking the buffer here,
* since caller should hold ShareLock on the relation, but let's
* be conservative about it. (This remark is still correct even
* with HOT-pruning: our pin on the buffer prevents pruning.)
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
switch (HeapTupleSatisfiesVacuum(partTuple, oldestXmin, buffer)) {
case HEAPTUPLE_INSERT_IN_PROGRESS:
case HEAPTUPLE_DELETE_IN_PROGRESS:
partStatus = PART_METADATA_CREATING;
break;
case HEAPTUPLE_LIVE:
partStatus = PART_METADATA_LIVE;
break;
case HEAPTUPLE_DEAD:
case HEAPTUPLE_RECENTLY_DEAD:
partStatus = PART_METADATA_INVISIBLE;
break;
default:
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("unexpected HeapTupleSatisfiesVacuum result")));
partStatus = PART_METADATA_NOEXIST; /* keep compiler quiet */
break;
}
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
return partStatus;
}
/*
* Check current partition status use HeapTupleSatisfiesVacuum
*
* Notes: return PART_METADATA_CEATING scenario occurs only in the process of automatically creating
* partitions when the interval partition insert statement is executed, Other partition
* change scenarios have AccessExclusiveLock locks, which are not executed concurrently
* with the vacuum process
*/
static PartStatus PartitionStatusForVacuum(Oid partOid)
{
Relation pgPartition = NULL;
SysScanDesc scan = NULL;
ScanKeyData key[1];
HeapTuple partTuple = NULL;
TransactionId oldestXmin;
PartStatus partStatus = PART_METADATA_NOEXIST;
pgPartition = heap_open(PartitionRelationId, RowExclusiveLock);
oldestXmin = u_sess->utils_cxt.RecentGlobalXmin;
ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(partOid));
scan = systable_beginscan(pgPartition, InvalidOid, false, SnapshotAny, 1, key);
while (HeapTupleIsValid(partTuple = systable_getnext(scan))) {
partStatus = PartTupleStatusForVacuum(partTuple, scan->scan->rs_cbuf, oldestXmin);
/* The status of a partition is creating or live, the partition status is the latest */
if (partStatus == PART_METADATA_CREATING || partStatus == PART_METADATA_LIVE) {
break;
}
}
systable_endscan(scan);
heap_close(pgPartition, NoLock);
return partStatus;
}
/*
* This function is used by global partition index to determine whether
* the partition corresponding to the partoid in index tuple should be ignored,
* The scenarios are as follows:
* a partition is created in a transaction and data is inserted into the partition,
* However, the transaction is aborted. Alternatively,
* a partition is created in a transaction, data is inserted into the partition,
* and the partition is deleted. The transaction is committed.
*
* Notes: In this case, lazy_vacuum of pg_partition must meet the following requirements:
* Before clearing a dead tuple, ensure that global partition index (if any) does not contain
* any indextuple containing partoid of the dead tuple.
*/
PartStatus PartitionGetMetadataStatus(Oid partOid, bool vacuumFlag)
{
HeapTuple partTuple;
/* Get partition information from syscache */
partTuple = SearchSysCache1WithLogLevel(PARTRELID, ObjectIdGetDatum(partOid), LOG);
if (HeapTupleIsValid(partTuple)) {
ReleaseSysCache(partTuple);
return PART_METADATA_LIVE;
}
/* When vacuum is performed, must checks whether the partition is being created */
if (vacuumFlag) {
return PartitionStatusForVacuum(partOid);
}
/*
* Find the tuple in pg_partition corresponding to the given partition oid
*
* Notes: use SnapshotAny to ensure that the tuple of pg_partition
* in the invisible state is obtained.
*/
partTuple = ScanPgPartition(partOid, false, SnapshotAny);
/* If get tuple exists, return status invisible */
if (HeapTupleIsValid(partTuple)) {
pfree_ext(partTuple);
return PART_METADATA_INVISIBLE;
}
return PART_METADATA_NOEXIST;
}
/* Set reloptions walt_clean_gpi, Just for pg_partition's tuple */
Datum SetWaitCleanGpiRelOptions(Datum oldOptions, bool enable)
{
Datum newOptions;
List* defList = NIL;
DefElem* def = NULL;
Value* defArg = enable ? makeString(OptEnabledWaitCleanGpi) : makeString(OptDisabledWaitCleanGpi);
def = makeDefElem(pstrdup("wait_clean_gpi"), (Node*)defArg);
defList = lappend(defList, def);
newOptions = transformRelOptions(oldOptions, defList, NULL, NULL, false, false);
pfree_ext(def->defname);
list_free_ext(defList);
return newOptions;
}
/* Update pg_partition's tuple attribute reloptions wait_clean_gpi */
static void UpdateWaitCleanGpiRelOptions(Relation pgPartition, HeapTuple partTuple, bool enable, bool inplace)
{
HeapTuple newTuple;
Datum partOptions;
Datum newOptions;
Datum replVal[Natts_pg_partition];
bool replNull[Natts_pg_partition];
bool replRepl[Natts_pg_partition];
errno_t rc;
bool isNull = false;
partOptions = fastgetattr(partTuple, Anum_pg_partition_reloptions, RelationGetDescr(pgPartition), &isNull);
/* If the caller use replacement to update reloptions, but the effect is the same as not set, just return */
if (inplace && enable == PartitionInvisibleMetadataKeep(partOptions)) {
return;
}
newOptions = SetWaitCleanGpiRelOptions(isNull ? (Datum)0 : partOptions, enable);
rc = memset_s(replVal, sizeof(replVal), 0, sizeof(replVal));
securec_check(rc, "\0", "\0");
rc = memset_s(replNull, sizeof(replNull), false, sizeof(replNull));
securec_check(rc, "\0", "\0");
rc = memset_s(replRepl, sizeof(replRepl), false, sizeof(replRepl));
securec_check(rc, "\0", "\0");
if (PointerIsValid(newOptions)) {
replVal[Anum_pg_partition_reloptions - 1] = newOptions;
replNull[Anum_pg_partition_reloptions - 1] = false;
} else {
replNull[Anum_pg_partition_reloptions - 1] = true;
}
replRepl[Anum_pg_partition_reloptions - 1] = true;
newTuple = heap_modify_tuple(partTuple, RelationGetDescr(pgPartition), replVal, replNull, replRepl);
if (inplace) {
heap_inplace_update(pgPartition, newTuple);
} else {
simple_heap_update(pgPartition, &newTuple->t_self, newTuple);
CatalogUpdateIndexes(pgPartition, newTuple);
}
ereport(LOG, (errmsg("partition %u set reloptions wait_clean_gpi=n success", HeapTupleGetOid(partTuple))));
heap_freetuple_ext(newTuple);
}
/* Set one partitioned relation's reloptions wait_clean_gpi */
void PartitionedSetWaitCleanGpi(const char* parentName, Oid parentPartOid, bool enable, bool inplace)
{
HeapTuple partTuple;
Relation pgPartition;
pgPartition = heap_open(PartitionRelationId, RowExclusiveLock);
partTuple = SearchSysCache3(PARTPARTOID,
PointerGetDatum(parentName),
CharGetDatum(PART_OBJ_TYPE_PARTED_TABLE),
ObjectIdGetDatum(parentPartOid));
if (!HeapTupleIsValid(partTuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for partition %u", parentPartOid)));
}
UpdateWaitCleanGpiRelOptions(pgPartition, partTuple, enable, inplace);
ReleaseSysCache(partTuple);
heap_close(pgPartition, NoLock);
/* Make changes visible */
CommandCounterIncrement();
ereport(LOG, (errmsg("partition relation %s set reloptions wait_clean_gpi success", parentName)));
}
/* Set one partition's reloptions wait_clean_gpi */
void PartitionSetWaitCleanGpi(Oid partOid, bool enable, bool inplace)
{
Relation pgPartition;
HeapTuple partTuple;
pgPartition = heap_open(PartitionRelationId, RowExclusiveLock);
partTuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partOid));
if (!HeapTupleIsValid(partTuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for partition %u", partOid)));
}
UpdateWaitCleanGpiRelOptions(pgPartition, partTuple, enable, inplace);
ReleaseSysCache(partTuple);
heap_close(pgPartition, NoLock);
/* Make changes visible */
CommandCounterIncrement();
ereport(LOG, (errmsg("partition %u set reloptions wait_clean_gpi success", partOid)));
}
/*
* Check one partition's invisible metadata tuple whether still keep
*
* Notes: if wait_clean_gpi=y is contained in reloptions, determine to keep
*/
bool PartitionInvisibleMetadataKeep(Datum datumRelOptions)
{
bool ret = false;
bytea* options = NULL;
char* waitCleanGpi;
if (!PointerIsValid(datumRelOptions)) {
return false;
}
options = heap_reloptions(RELKIND_RELATION, datumRelOptions, true);
if (options != NULL) {
waitCleanGpi = (char*)StdRdOptionsGetStringData(options, wait_clean_gpi, OptDisabledWaitCleanGpi);
if (pg_strcasecmp(OptEnabledWaitCleanGpi, waitCleanGpi) == 0) {
ret = true;
}
pfree_ext(options);
}
return ret;
}
/*
* In pg_partition, search all tuples (visible and invisible) containing wait_clean_gpi=y
* in reloptios of one partitioed relation and set wait_clean_gpi=n
*
* Notes: This function is called only when a partition table is lazy vacuumed,
* and cannot be executed in parallel with PartitionSetWaitCleanGpi, Currently,
* the AccessShareLock lock of ADD_PARTITION_ACTION is used to ensure that no concurrent
* operations are performed.
*/
void PartitionedSetEnabledClean(Oid parentOid)
{
Relation pgPartition = NULL;
SysScanDesc scan = NULL;
ScanKeyData key[2];
HeapTuple tuple = NULL;
pgPartition = heap_open(PartitionRelationId, RowExclusiveLock);
ScanKeyInit(
&key[0], Anum_pg_partition_parttype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(PART_OBJ_TYPE_PARTED_TABLE));
ScanKeyInit(&key[1], Anum_pg_partition_parentid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentOid));
scan = systable_beginscan(pgPartition, InvalidOid, false, SnapshotAny, 2, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
UpdateWaitCleanGpiRelOptions(pgPartition, tuple, false, true);
}
systable_endscan(scan);
heap_close(pgPartition, NoLock);
ereport(LOG, (errmsg("partitioned %u set reloptions wait_clean_gpi=n success", parentOid)));
}
/*
* In pg_partition, search all tuples (visible and invisible) containing wait_clean_gpi=y
* in reloptios of one partition's all partitions and set wait_clean_gpi=n
*
* input cleanedParts means a collection of partoids that have been cleaned of all remaining invalid partitions
* input invisibleParts means the collection of partoids for invalid partitions that have been deleted
* input updatePartitioned means need check whether update partitioned's reloptions
*
* Notes: This function is called only when a partition table is lazy vacuumed,
* and cannot be executed in parallel with PartitionSetWaitCleanGpi, if updatePartitioned
*/
void PartitionSetEnabledClean(
Oid parentOid, const Bitmapset* cleanedParts, const Bitmapset* invisibleParts, bool updatePartitioned)
{
Relation pgPartition = NULL;
TupleDesc partTupdesc = NULL;
SysScanDesc scan = NULL;
ScanKeyData key[2];
HeapTuple tuple = NULL;
Oid partOid;
Bitmapset* liveParts = NULL;
bool needSetOpts = false;
bool needSetPartitioned = updatePartitioned;
pgPartition = heap_open(PartitionRelationId, RowExclusiveLock);
partTupdesc = RelationGetDescr(pgPartition);
ScanKeyInit(&key[0],
Anum_pg_partition_parttype,
BTEqualStrategyNumber,
F_CHAREQ,
CharGetDatum(PART_OBJ_TYPE_TABLE_PARTITION));
ScanKeyInit(&key[1], Anum_pg_partition_parentid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentOid));
scan = systable_beginscan(pgPartition, InvalidOid, false, SnapshotAny, 2, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
needSetOpts = false;
partOid = HeapTupleGetOid(tuple);
if (bms_is_member(partOid, cleanedParts)) {
needSetOpts = true;
} else if (bms_is_member(partOid, invisibleParts)) {
needSetOpts = true;
} else if (PartitionStatusIsLive(partOid, &liveParts)) {
needSetOpts = true;
} else if (updatePartitioned && InvisblePartEnableClean(tuple, partTupdesc)) {
continue;
} else {
needSetPartitioned = false;
}
if (needSetOpts) {
UpdateWaitCleanGpiRelOptions(pgPartition, tuple, false, true);
}
}
systable_endscan(scan);
heap_close(pgPartition, NoLock);
bms_free(liveParts);
if (needSetPartitioned) {
PartitionedSetEnabledClean(parentOid);
}
}
/*
* In pg_partition, search all tuples containing wait_clean_gpi=y
* in reloptios of one relation's all partitions (visible and invisible)
* in a partition and set wait_clean_gpi=n
*
* Notes: This function is called only when a partitioned table is vacuum full,
* and cannot be executed in parallel with PartitionSetWaitCleanGpi.
*/
void PartitionSetAllEnabledClean(Oid parentOid)
{
Relation pgPartition = NULL;
SysScanDesc scan = NULL;
ScanKeyData key[1];
HeapTuple tuple = NULL;
pgPartition = heap_open(PartitionRelationId, RowExclusiveLock);
ScanKeyInit(&key[0], Anum_pg_partition_parentid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentOid));
scan = systable_beginscan(pgPartition, InvalidOid, false, SnapshotAny, 1, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
UpdateWaitCleanGpiRelOptions(pgPartition, tuple, false, true);
}
systable_endscan(scan);
heap_close(pgPartition, NoLock);
ereport(LOG, (errmsg("relation %u set all partition's reloptions wait_clean_gpi=n success", parentOid)));
}
/*
* Get all invisible partition from pg_partition
*
* Notes: Before calling the function, you must ensure that a lock with parentOid
* is already held (to prevent parallelism with any ALTER table partition process)
* and AccessShareLock for ADD_PARTITION_ACTION (to prevent parallelism with the
* process of automatically creating partitions in any interval partition)
*/
void PartitionGetAllInvisibleParts(Oid parentOid, Bitmapset** invisibleParts)
{
Relation pgPartition = NULL;
SysScanDesc scan = NULL;
ScanKeyData key[2];
HeapTuple tuple = NULL;
Bitmapset* liveParts = NULL;
Oid partOid;
pgPartition = heap_open(PartitionRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_partition_parttype,
BTEqualStrategyNumber,
F_CHAREQ,
CharGetDatum(PART_OBJ_TYPE_TABLE_PARTITION));
ScanKeyInit(&key[1], Anum_pg_partition_parentid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentOid));
scan = systable_beginscan(pgPartition, InvalidOid, false, SnapshotAny, 2, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
partOid = HeapTupleGetOid(tuple);
if (bms_is_member(partOid, *invisibleParts)) {
continue;
} else if (PartitionStatusIsLive(partOid, &liveParts)) {
continue;
} else {
*invisibleParts = bms_add_member(*invisibleParts, partOid);
}
}
systable_endscan(scan);
heap_close(pgPartition, NoLock);
bms_free(liveParts);
}
/*
* Check whether contain a tuple in pg_partition, which includes
* wait_clean_gpi=y in the reloPTIONS of the tuple
*
* Notes: this function is called only when vacuum full pg_partition
*/
bool PartitionMetadataDisabledClean(Relation pgPartition)
{
bool result = false;
TupleDesc partTupdesc = NULL;
SysScanDesc scan = NULL;
HeapTuple tuple = NULL;
ScanKeyData key[1];
Form_pg_partition partform;
char* relName = NULL;
if (RelationGetRelid(pgPartition) != PartitionRelationId) {
return result;
}
ScanKeyInit(
&key[0], Anum_pg_partition_parttype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(PART_OBJ_TYPE_PARTED_TABLE));
partTupdesc = RelationGetDescr(pgPartition);
scan = systable_beginscan(pgPartition, PartitionParentOidIndexId, true, SnapshotNow, 1, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
bool isNull = false;
Datum partOptions = fastgetattr(tuple, Anum_pg_partition_reloptions, partTupdesc, &isNull);
if (isNull) {
continue;
}
if (PartitionInvisibleMetadataKeep(partOptions)) {
partform = (Form_pg_partition)GETSTRUCT(tuple);
relName = (char*)palloc0(NAMEDATALEN);
error_t rc = strncpy_s(relName, NAMEDATALEN, partform->relname.data, NAMEDATALEN - 1);
securec_check_ss(rc, "\0", "\0");
result = true;
break;
}
}
systable_endscan(scan);
if (result) {
ereport(WARNING,
(errmsg("system table pg_partition contain relation %s have reloptions wait_clean_gpi=y,"
"must run the vacuum (full) %s first",
relName,
relName)));
}
return result;
}

View File

@ -1054,6 +1054,7 @@ static void relation_parse_rel_options(Relation relation, HeapTuple tuple)
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
break;
@ -1066,9 +1067,8 @@ static void relation_parse_rel_options(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
options = extractRelOptions(tuple,
get_pg_class_descriptor(),
relation->rd_rel->relkind == RELKIND_INDEX ? relation->rd_am->amoptions : InvalidOid);
options = extractRelOptions(
tuple, get_pg_class_descriptor(), RelationIsIndex(relation) ? relation->rd_am->amoptions : InvalidOid);
/*
* Copy parsed data into u_sess->cache_mem_cxt. To guard against the
* possibility of leaks in the reloptions code, we want to do the actual
@ -1142,7 +1142,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
/*
* add attribute data to relation->rd_att
*/
need = relation->rd_rel->relnatts;
need = RelationGetNumberOfAttributes(relation);
/* alter table instantly or load catalog init default during backend startup */
Assert(relation->rd_att->initdefvals == NULL || onlyLoadInitDefVal);
@ -1157,7 +1157,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
Form_pg_attribute attp;
attp = (Form_pg_attribute)GETSTRUCT(pg_attribute_tuple);
if (attp->attnum <= 0 || attp->attnum > relation->rd_rel->relnatts)
if (attp->attnum <= 0 || attp->attnum > RelationGetNumberOfAttributes(relation))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid attribute number %d for %s", attp->attnum, RelationGetRelationName(relation))));
@ -1195,7 +1195,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
if (attp->atthasdef && !onlyLoadInitDefVal) {
if (attrdef == NULL)
attrdef = (AttrDefault*)MemoryContextAllocZero(
u_sess->cache_mem_cxt, relation->rd_rel->relnatts * sizeof(AttrDefault));
u_sess->cache_mem_cxt, RelationGetNumberOfAttributes(relation) * sizeof(AttrDefault));
attrdef[ndef].adnum = attp->attnum;
attrdef[ndef].adbin = NULL;
ndef++;
@ -1214,7 +1214,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
if (need != 0) {
/* find all missed attributes, and print them */
StringInfo missing_attnums = makeStringInfo();
for (int i = 0; i < relation->rd_rel->relnatts; i++) {
for (int i = 0; i < RelationGetNumberOfAttributes(relation); i++) {
if (relation->rd_att->attrs[i]->attnum == 0) {
appendStringInfo(missing_attnums, "%d ", (i + 1));
}
@ -1235,7 +1235,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
if (initdvals != NULL && !has_init_def_val)
pfree_ext(initdvals);
else if (initdvals != NULL && relation->rd_att->initdefvals != NULL) {
for (int i = 0; i < relation->rd_rel->relnatts; ++i) {
for (int i = 0; i < RelationGetNumberOfAttributes(relation); ++i) {
if (initdvals[i].datum != NULL)
pfree_ext(initdvals[i].datum);
}
@ -1258,7 +1258,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
{
int i;
for (i = 0; i < relation->rd_rel->relnatts; i++)
for (i = 0; i < RelationGetNumberOfAttributes(relation); i++)
Assert(relation->rd_att->attrs[i]->attcacheoff == -1);
}
#endif
@ -1268,7 +1268,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
* attribute: it must be zero. This eliminates the need for special cases
* for attnum=1 that used to exist in fastgetattr() and index_getattr().
*/
if (relation->rd_rel->relnatts > 0)
if (RelationGetNumberOfAttributes(relation) > 0)
relation->rd_att->attrs[0]->attcacheoff = 0;
/*
@ -1278,7 +1278,7 @@ static void relation_build_tuple_desc(Relation relation, bool onlyLoadInitDefVal
relation->rd_att->constr = constr;
if (ndef > 0) { /* DEFAULTs */
if (ndef < relation->rd_rel->relnatts) {
if (ndef < RelationGetNumberOfAttributes(relation)) {
constr->defval = (AttrDefault*)repalloc(attrdef, ndef * sizeof(AttrDefault));
} else {
constr->defval = attrdef;
@ -2020,7 +2020,8 @@ void RelationInitIndexAccessInfo(Relation relation)
int2vector* indoption = NULL;
MemoryContext indexcxt;
MemoryContext oldcontext;
int natts;
int indnatts;
int indnkeyatts;
uint16 amsupport;
errno_t rc;
@ -2040,6 +2041,9 @@ void RelationInitIndexAccessInfo(Relation relation)
(void)MemoryContextSwitchTo(oldcontext);
ReleaseSysCache(tuple);
/* Just Use for partitionGetRelation */
relation->rd_partHeapOid = InvalidOid;
/*
* Make a copy of the pg_am entry for the index's access method
*/
@ -2054,13 +2058,19 @@ void RelationInitIndexAccessInfo(Relation relation)
ReleaseSysCache(tuple);
relation->rd_am = aform;
natts = relation->rd_rel->relnatts;
if (natts != relation->rd_index->indnatts)
indnatts = RelationGetNumberOfAttributes(relation);
if (indnatts != IndexRelationGetNumberOfAttributes(relation))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("relnatts disagrees with indnatts for index %u", RelationGetRelid(relation))));
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(relation);
amsupport = aform->amsupport;
if (indnkeyatts > INDEX_MAX_KEYS) {
ereport(
ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("must index at most %u column", INDEX_MAX_KEYS)));
}
/*
* Make the private context to hold index access info. The reason we need
* a context, and not just a couple of pallocs, is so that we won't leak
@ -2077,15 +2087,16 @@ void RelationInitIndexAccessInfo(Relation relation)
relation->rd_indexcxt = indexcxt;
/*
* Allocate arrays to hold data
* Allocate arrays to hold data. Opclasses are not used for included
* columns, so allocate them for indnkeyatts only.
*/
relation->rd_aminfo = (RelationAmInfo*)MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
relation->rd_opfamily = (Oid*)MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
relation->rd_opcintype = (Oid*)MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
relation->rd_opfamily = (Oid*)MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
relation->rd_opcintype = (Oid*)MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
if (amsupport > 0) {
int nsupport = natts * amsupport;
int nsupport = indnatts * amsupport;
relation->rd_support = (RegProcedure*)MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
relation->rd_supportinfo = (FmgrInfo*)MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
@ -2094,9 +2105,9 @@ void RelationInitIndexAccessInfo(Relation relation)
relation->rd_supportinfo = NULL;
}
relation->rd_indcollation = (Oid*)MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
relation->rd_indcollation = (Oid*)MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
relation->rd_indoption = (int16*)MemoryContextAllocZero(indexcxt, natts * sizeof(int16));
relation->rd_indoption = (int16*)MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(int16));
/*
* indcollation cannot be referenced directly through the C struct,
@ -2106,7 +2117,7 @@ void RelationInitIndexAccessInfo(Relation relation)
indcoll_datum = fastgetattr(relation->rd_indextuple, Anum_pg_index_indcollation, get_pg_index_descriptor(), &isnull);
Assert(!isnull);
indcoll = (oidvector*)DatumGetPointer(indcoll_datum);
rc = memcpy_s(relation->rd_indcollation, natts * sizeof(Oid), indcoll->values, natts * sizeof(Oid));
rc = memcpy_s(relation->rd_indcollation, indnkeyatts * sizeof(Oid), indcoll->values, indnkeyatts * sizeof(Oid));
securec_check(rc, "\0", "\0");
/*
@ -2124,7 +2135,7 @@ void RelationInitIndexAccessInfo(Relation relation)
* as zeroes, and are filled on-the-fly when used)
*/
index_support_initialize(
indclass, relation->rd_support, relation->rd_opfamily, relation->rd_opcintype, amsupport, natts);
indclass, relation->rd_support, relation->rd_opfamily, relation->rd_opcintype, amsupport, indnkeyatts);
/*
* Similarly extract indoption and copy it to the cache entry
@ -2132,7 +2143,7 @@ void RelationInitIndexAccessInfo(Relation relation)
indoption_datum = fastgetattr(relation->rd_indextuple, Anum_pg_index_indoption, get_pg_index_descriptor(), &isnull);
Assert(!isnull);
indoption = (int2vector*)DatumGetPointer(indoption_datum);
rc = memcpy_s(relation->rd_indoption, natts * sizeof(int16), indoption->values, natts * sizeof(int16));
rc = memcpy_s(relation->rd_indoption, indnkeyatts * sizeof(int16), indoption->values, indnkeyatts * sizeof(int16));
securec_check(rc, "\0", "\0");
/*
@ -2552,7 +2563,7 @@ Relation RelationIdGetRelation(Oid relationId)
* and we don't want to use the full-blown procedure because it's
* a headache for indexes that reload itself depends on.
*/
if (rd->rd_rel->relkind == RELKIND_INDEX)
if (RelationIsIndex(rd))
relation_reload_index_info(rd);
else
relation_clear_relation(rd, true);
@ -2677,7 +2688,7 @@ static void relation_reload_index_info(Relation relation)
Form_pg_class relp;
/* Should be called only for invalidated indexes */
Assert(relation->rd_rel->relkind == RELKIND_INDEX && !relation->rd_isvalid);
Assert(RelationIsIndex(relation) && !relation->rd_isvalid);
/* Should be closed at smgr level */
Assert(relation->rd_smgr == NULL);
@ -2970,7 +2981,7 @@ static void relation_clear_relation(Relation relation, bool rebuild)
if (relation->rd_isnailed) {
relation_init_physical_addr(relation);
if (relation->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(relation)) {
relation->rd_isvalid = false; /* needs to be revalidated */
if (relation->rd_refcnt > 1)
relation_reload_index_info(relation);
@ -2985,7 +2996,7 @@ static void relation_clear_relation(Relation relation, bool rebuild)
* re-read the pg_class row to handle possible physical relocation of the
* index, and we check for pg_index updates too.
*/
if (relation->rd_rel->relkind == RELKIND_INDEX && relation->rd_refcnt > 0 && relation->rd_indexcxt != NULL) {
if (RelationIsIndex(relation) && relation->rd_refcnt > 0 && relation->rd_indexcxt != NULL) {
relation->rd_isvalid = false; /* needs to be revalidated */
relation_reload_index_info(relation);
return;
@ -3823,7 +3834,7 @@ void DescTableSetNewRelfilenode(Oid relid, TransactionId freezeXid, bool partiti
/* Note: we do not need to re-establish pkey setting */
/* Fetch info needed for index_build */
IndexInfo* index_info = BuildIndexInfo(current_index);
index_build(cudesc_rel, NULL, current_index, NULL, index_info, false, true, false);
index_build(cudesc_rel, NULL, current_index, NULL, index_info, false, true, INDEX_CREATE_NONE_PARTITION);
index_close(current_index, NoLock);
}
@ -3861,7 +3872,7 @@ void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, bool
errno_t rc = EOK;
bool modifyPgClass = !RELATION_IS_GLOBAL_TEMP(relation);
/* Indexes, sequences must have Invalid frozenxid; other rels must not */
Assert(((relation->rd_rel->relkind == RELKIND_INDEX || relation->rd_rel->relkind == RELKIND_SEQUENCE)
Assert(((RelationIsIndex(relation) || relation->rd_rel->relkind == RELKIND_SEQUENCE)
? freezeXid == InvalidTransactionId : TransactionIdIsNormal(freezeXid)) ||
relation->rd_rel->relkind == RELKIND_RELATION);
@ -4649,6 +4660,37 @@ void SaveCopyList(Relation relation, List* result, int oidIndex)
(void)MemoryContextSwitchTo(oldcxt);
}
/*
* RelationGetSpecificKindIndexList -- get a list of OIDs of global indexes on this relation or not
* if isGlobal is true get list of global indexes ;if isGlobal is false get list of index without global
*/
List* RelationGetSpecificKindIndexList(Relation relation, bool isGlobal)
{
ListCell* indList = NULL;
List* result = NULL;
/* Ask the relcache to produce a list of the indexes of the rel */
foreach (indList, RelationGetIndexList(relation)) {
Oid indexId = lfirst_oid(indList);
Relation currentIndex;
/* Open the index relation; use exclusive lock, just to be sure */
currentIndex = index_open(indexId, AccessShareLock);
if (isGlobal) {
if (RelationIsGlobalIndex(currentIndex)) {
result = insert_ordered_oid(result, indexId);
}
} else {
if (!RelationIsGlobalIndex(currentIndex)) {
result = insert_ordered_oid(result, indexId);
}
}
index_close(currentIndex, AccessShareLock);
}
return result;
}
/*
* RelationGetIndexList -- get a list of OIDs of indexes on this relation
*
@ -5379,9 +5421,17 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) {
int attrnum = indexInfo->ii_KeyAttrNumbers[i];
/*
* Since we have covering indexes with non-key columns, we must
* handle them accurately here. non-key columns must be added into
* indexattrs, since they are in index, and HOT-update shouldn't
* miss them. Obviously, non-key columns couldn't be referenced by
* foreign key or identity key. Hence we do not include them into
* uindexattrs, pkindexattrs and idindexattrs bitmaps.
*/
if (attrnum != 0) {
indexattrs = bms_add_member(indexattrs, attrnum - FirstLowInvalidHeapAttributeNumber);
if (isIDKey)
if (isIDKey && i < indexInfo->ii_NumIndexKeyAttrs)
idindexattrs = bms_add_member(idindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber);
}
}
@ -5434,7 +5484,7 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
*/
void RelationGetExclusionInfo(Relation indexRelation, Oid** operators, Oid** procs, uint16** strategies)
{
int ncols = indexRelation->rd_rel->relnatts;
int indnkeyatts;
Oid* ops = NULL;
Oid* funcs = NULL;
uint16* strats = NULL;
@ -5446,16 +5496,18 @@ void RelationGetExclusionInfo(Relation indexRelation, Oid** operators, Oid** pro
MemoryContext oldcxt;
int i;
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(indexRelation);
/* Allocate result space in caller context */
*operators = ops = (Oid*)palloc(sizeof(Oid) * ncols);
*procs = funcs = (Oid*)palloc(sizeof(Oid) * ncols);
*strategies = strats = (uint16*)palloc(sizeof(uint16) * ncols);
*operators = ops = (Oid*)palloc(sizeof(Oid) * indnkeyatts);
*procs = funcs = (Oid*)palloc(sizeof(Oid) * indnkeyatts);
*strategies = strats = (uint16*)palloc(sizeof(uint16) * indnkeyatts);
/* Quick exit if we have the data cached already */
if (indexRelation->rd_exclstrats != NULL) {
MemCpy(ops, indexRelation->rd_exclops, sizeof(Oid) * ncols);
MemCpy(funcs, indexRelation->rd_exclprocs, sizeof(Oid) * ncols);
MemCpy(strats, indexRelation->rd_exclstrats, sizeof(uint16) * ncols);
MemCpy(ops, indexRelation->rd_exclops, sizeof(Oid) * indnkeyatts);
MemCpy(funcs, indexRelation->rd_exclprocs, sizeof(Oid) * indnkeyatts);
MemCpy(strats, indexRelation->rd_exclstrats, sizeof(uint16) * indnkeyatts);
return;
}
@ -5502,11 +5554,11 @@ void RelationGetExclusionInfo(Relation indexRelation, Oid** operators, Oid** pro
arr = DatumGetArrayTypeP(val); /* ensure not toasted */
nelem = ARR_DIMS(arr)[0];
if (ARR_NDIM(arr) != 1 || nelem != ncols || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID)
if (ARR_NDIM(arr) != 1 || nelem != indnkeyatts || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID)
ereport(
ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("conexclop is not a 1-D Oid array")));
int rc = memcpy_s(ops, sizeof(Oid) * ncols, ARR_DATA_PTR(arr), sizeof(Oid) * ncols);
int rc = memcpy_s(ops, sizeof(Oid) * indnkeyatts, ARR_DATA_PTR(arr), sizeof(Oid) * indnkeyatts);
securec_check(rc, "\0", "\0");
}
@ -5519,7 +5571,7 @@ void RelationGetExclusionInfo(Relation indexRelation, Oid** operators, Oid** pro
errmsg("exclusion constraint record missing for rel %s", RelationGetRelationName(indexRelation))));
/* We need the func OIDs and strategy numbers too */
for (i = 0; i < ncols; i++) {
for (i = 0; i < indnkeyatts; i++) {
funcs[i] = get_opcode(ops[i]);
strats[i] = get_op_opfamily_strategy(ops[i], indexRelation->rd_opfamily[i]);
/* shouldn't fail, since it was checked at index creation */
@ -5533,12 +5585,12 @@ void RelationGetExclusionInfo(Relation indexRelation, Oid** operators, Oid** pro
/* Save a copy of the results in the relcache entry. */
oldcxt = MemoryContextSwitchTo(indexRelation->rd_indexcxt);
indexRelation->rd_exclops = (Oid*)palloc(sizeof(Oid) * ncols);
indexRelation->rd_exclprocs = (Oid*)palloc(sizeof(Oid) * ncols);
indexRelation->rd_exclstrats = (uint16*)palloc(sizeof(uint16) * ncols);
MemCpy(indexRelation->rd_exclops, ops, sizeof(Oid) * ncols);
MemCpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * ncols);
MemCpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * ncols);
indexRelation->rd_exclops = (Oid*)palloc(sizeof(Oid) * indnkeyatts);
indexRelation->rd_exclprocs = (Oid*)palloc(sizeof(Oid) * indnkeyatts);
indexRelation->rd_exclstrats = (uint16*)palloc(sizeof(uint16) * indnkeyatts);
MemCpy(indexRelation->rd_exclops, ops, sizeof(Oid) * indnkeyatts);
MemCpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * indnkeyatts);
MemCpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * indnkeyatts);
(void)MemoryContextSwitchTo(oldcxt);
}
@ -5748,7 +5800,7 @@ static bool load_relcache_init_file(bool shared)
}
/* If it's an index, there's more to do */
if (rel->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(rel)) {
Form_pg_am am;
MemoryContext indexcxt;
Oid* opfamily = NULL;
@ -6099,7 +6151,7 @@ static void write_relcache_init_file(bool shared)
write_item(rel->rd_options, (rel->rd_options ? VARSIZE(rel->rd_options) : 0), fp);
/* If it's an index, there's more to do */
if (rel->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(rel)) {
Form_pg_am am = rel->rd_am;
/* write the pg_index tuple */

View File

@ -812,14 +812,14 @@ Tuplesortstate* tuplesort_begin_cluster(
if (u_sess->attr.attr_common.trace_sort) {
elog(LOG,
"begin tuple sort: nkeys = %d, workMem = %d, randomAccess = %c, maxMem = %d",
RelationGetNumberOfAttributes(indexRel),
IndexRelationGetNumberOfKeyAttributes(indexRel),
workMem,
randomAccess ? 't' : 'f',
maxMem);
}
#endif
state->nKeys = RelationGetNumberOfAttributes(indexRel);
state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);
TRACE_POSTGRESQL_SORT_START(CLUSTER_SORT,
false, /* no unique check */
@ -881,7 +881,7 @@ Tuplesortstate* tuplesort_begin_index_btree(
}
#endif
state->nKeys = RelationGetNumberOfAttributes(indexRel);
state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);
TRACE_POSTGRESQL_SORT_START(INDEX_SORT, enforceUnique, state->nKeys, workMem, randomAccess);
@ -3615,6 +3615,20 @@ static int comparetup_index_btree(const SortTuple* a, const SortTuple* b, Tuples
return (pos1 < pos2) ? -1 : 1;
}
if (RelationIsGlobalIndex(state->indexRel)) {
bool isnull1 = false;
bool isnull2 = false;
AttrNumber partitionOidAttr = IndexRelationGetNumberOfAttributes(state->indexRel);
Oid partOid1 = DatumGetUInt32(index_getattr(tuple1, partitionOidAttr, tupDes, &isnull1));
Assert(!isnull1);
Oid partOid2 = DatumGetUInt32(index_getattr(tuple2, partitionOidAttr, tupDes, &isnull2));
Assert(!isnull2);
if (partOid1 != partOid2) {
return (partOid1 < partOid2) ? -1 : 1;
}
}
return 0;
}

View File

@ -1211,7 +1211,7 @@ HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, B
return HEAPTUPLE_DELETE_IN_PROGRESS;
} else if (xidstatus == XID_COMMITTED) {
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleGetRawXmin(htup));
} else {
} else {
/*
* Not in Progress, Not Committed, so either Aborted or crashed
*/

View File

@ -314,6 +314,7 @@ Boot_DeclareIndexStmt:
stmt->accessMethod = $8;
stmt->tableSpace = NULL;
stmt->indexParams = $10;
stmt->indexIncludingParams = NIL;
stmt->options = NIL;
stmt->whereClause = NULL;
stmt->excludeOpNames = NIL;
@ -354,6 +355,7 @@ Boot_DeclareUniqueIndexStmt:
stmt->accessMethod = $9;
stmt->tableSpace = NULL;
stmt->indexParams = $11;
stmt->indexIncludingParams = NIL;
stmt->options = NIL;
stmt->whereClause = NULL;
stmt->excludeOpNames = NIL;

View File

@ -497,7 +497,7 @@ void boot_openrel(char* relname)
ereport(DEBUG4, (errmsg("open relation %s, attrsize %d", relname, (int)ATTRIBUTE_FIXED_PART_SIZE)));
t_thrd.bootstrap_cxt.boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock);
t_thrd.bootstrap_cxt.numattr = t_thrd.bootstrap_cxt.boot_reldesc->rd_rel->relnatts;
t_thrd.bootstrap_cxt.numattr = RelationGetNumberOfAttributes(t_thrd.bootstrap_cxt.boot_reldesc);
for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) {
if (t_thrd.bootstrap_cxt.attrtypes[i] == NULL)
t_thrd.bootstrap_cxt.attrtypes[i] = AllocateAttribute();
@ -977,7 +977,8 @@ void build_indices(void)
/* need not bother with locks during bootstrap */
heap = heap_open(t_thrd.bootstrap_cxt.ILHead->il_heap, NoLock);
ind = index_open(t_thrd.bootstrap_cxt.ILHead->il_ind, NoLock);
index_build(heap, NULL, ind, NULL, t_thrd.bootstrap_cxt.ILHead->il_info, false, false, false);
index_build(
heap, NULL, ind, NULL, t_thrd.bootstrap_cxt.ILHead->il_info, false, false, INDEX_CREATE_NONE_PARTITION);
index_close(ind, NoLock);
heap_close(heap, NoLock);

View File

@ -540,10 +540,12 @@ of scanning the relation and the resulting ordering of the tuples.
Sequential scan Paths have NIL pathkeys, indicating no known ordering.
Index scans have Path.pathkeys that represent the chosen index's ordering,
if any. A single-key index would create a single-PathKey list, while a
multi-column index generates a list with one element per index column.
(Actually, since an index can be scanned either forward or backward, there
are two possible sort orders and two possible PathKey lists it can
generate.)
multi-column index generates a list with one element per key index column.
Non-key columns specified in the INCLUDE clause of covering indexes don't
have corresponding PathKeys in the list, because the have no influence on
index ordering. (Actually, since an index can be scanned either forward or
backward, there are two possible sort orders and two possible PathKey lists
it can generate.)
Note that a bitmap scan has NIL pathkeys since we can say nothing about
the overall order of its result. Also, an indexscan on an unordered type

View File

@ -6339,7 +6339,7 @@ static void update_pages_and_tuples_pgclass(Relation onerel, VacuumStmt* vacstmt
if (RelationIsColStore(onerel)) {
nblocks = estimate_psort_index_blocks(Irel[ind]->rd_att, totalindexrows);
} else if (RelationIsPartitioned(onerel)) {
} else if (RelationIsPartitioned(onerel) && !RelationIsGlobalIndex(Irel[ind])) {
ListCell* partCell = NULL;
Partition part = NULL;
Oid indexOid = InvalidOid;

100
src/gausskernel/optimizer/commands/cluster.cpp Normal file → Executable file
View File

@ -1508,15 +1508,14 @@ static double copy_heap_data_internal(Relation OldHeap, Relation OldIndex, Relat
TupleDesc oldTupDesc;
TupleDesc newTupDesc;
Relation heapRelation = NULL;
int natts;
Datum* values = NULL;
bool* isnull = NULL;
IndexScanDesc indexScan;
HeapScanDesc heapScan;
bool use_wal = XLogIsNeeded() && RelationNeedsWAL(NewHeap);
;
bool is_system_catalog = IsSystemRelation(OldHeap);
;
RewriteState rwstate;
Tuplesortstate* tuplesort = NULL;
double num_tuples = 0;
@ -1561,10 +1560,18 @@ static double copy_heap_data_internal(Relation OldHeap, Relation OldIndex, Relat
* Prepare to scan the OldHeap. To ensure we see recently-dead tuples
* that still need to be copied, we scan with SnapshotAny and use
* HeapTupleSatisfiesVacuum for the visibility test.
* If index is global index, we will use indexScan to copy tuples.
*/
if (OldIndex != NULL && !use_sort) {
heapScan = NULL;
indexScan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, 0);
if (RelationIsGlobalIndex(OldIndex)) {
/* Open the parent heap relation. */
Oid heapId = IndexGetRelation(RelationGetRelid(OldIndex), false);
heapRelation = heap_open(heapId, NoLock);
indexScan = index_beginscan(heapRelation, OldIndex, SnapshotAny, 0, 0);
} else {
indexScan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, 0);
}
index_rescan(indexScan, NULL, 0, NULL, 0);
} else {
heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey)NULL);
@ -1625,6 +1632,10 @@ static double copy_heap_data_internal(Relation OldHeap, Relation OldIndex, Relat
if (tuple == NULL)
break;
if (RelationGetRelid(OldHeap) != tuple->t_tableOid) {
continue;
}
/* Since we used no scan keys, should never need to recheck */
if (indexScan->xs_recheck)
ereport(ERROR,
@ -1742,6 +1753,12 @@ static double copy_heap_data_internal(Relation OldHeap, Relation OldIndex, Relat
if (indexScan != NULL)
index_endscan(indexScan);
if (RelationIsValid(heapRelation)) {
Assert(RelationIsGlobalIndex(OldIndex));
heap_close(heapRelation, NoLock);
}
if (heapScan != NULL)
heap_endscan(heapScan);
@ -2006,16 +2023,20 @@ static void copyPartitionHeapData(Relation newHeap, Relation oldHeap, Oid indexO
if (OidIsValid(indexOid)) {
Oid partIndexOid = InvalidOid;
partTabIndexRel = index_open(indexOid, NoLock);
partIndexOid = getPartitionIndexOid(indexOid, RelationGetRelid(oldHeap));
partIndexRel = partitionOpen(partTabIndexRel, partIndexOid, ExclusiveLock);
if (!partIndexRel->pd_part->indisusable) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("can not cluster partition %s using %s bacause of unusable local index",
getPartitionName(oldHeap->rd_id, false),
get_rel_name(indexOid))));
if (RelationIsGlobalIndex(partTabIndexRel)) {
oldIndex = partTabIndexRel;
} else {
partIndexOid = getPartitionIndexOid(indexOid, RelationGetRelid(oldHeap));
partIndexRel = partitionOpen(partTabIndexRel, partIndexOid, ExclusiveLock);
if (!partIndexRel->pd_part->indisusable) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("can not cluster partition %s using %s bacause of unusable local index",
getPartitionName(oldHeap->rd_id, false),
get_rel_name(indexOid))));
}
oldIndex = partitionGetRelation(partTabIndexRel, partIndexRel);
}
oldIndex = partitionGetRelation(partTabIndexRel, partIndexRel);
} else {
oldIndex = NULL;
}
@ -2098,6 +2119,11 @@ static void copyPartitionHeapData(Relation newHeap, Relation oldHeap, Oid indexO
if (ptrDeleteTupleNum != NULL)
*ptrDeleteTupleNum = tups_vacuumed;
if (RelationIsValid(partTabIndexRel) && RelationIsGlobalIndex(partTabIndexRel)) {
index_close(partTabIndexRel, NoLock);
return;
}
if (oldIndex != NULL) {
releaseDummyRelation(&oldIndex);
partitionClose(partTabIndexRel, partIndexRel, NoLock);
@ -2262,7 +2288,7 @@ static void swap_relation_files(
* set rel1's frozen Xid
*/
nctup = NULL;
if (relform1->relkind != RELKIND_INDEX) {
if (relform1->relkind != RELKIND_INDEX && relform1->relkind != RELKIND_GLOBAL_INDEX) {
Datum values[Natts_pg_class];
bool nulls[Natts_pg_class];
bool replaces[Natts_pg_class];
@ -3096,6 +3122,54 @@ static void reform_and_rewrite_tuple(HeapTuple tuple, TupleDesc oldTupDesc, Tupl
}
}
/*
* GpiVacuumFullMainPartiton
*
* Clean up global partition index finally for the vacuum full, just reindex all gpi.
*/
void GpiVacuumFullMainPartiton(Oid parentOid)
{
Relation parentHeap = NULL;
bool result = false;
/* Check for user-requested abort. */
CHECK_FOR_INTERRUPTS();
// to promote the concurrency of vacuum full on partitions in mppdb version,
// degrade lockmode from AccessExclusiveLock to AccessShareLock.
t_thrd.storage_cxt.EnlargeDeadlockTimeout = true;
parentHeap = try_relation_open(parentOid, AccessExclusiveLock);
/* If the table has gone away, we can skip processing it */
if (!parentHeap)
return;
/*
* Don't process temp tables of other backends ... their local buffer
* manager is not going to cope.
*/
if (RELATION_IS_OTHER_TEMP(parentHeap)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot vacuum temporary tables of other sessions")));
}
/*
* Also check for active uses of the relation in the current transaction,
* including open scans and pending AFTER trigger events.
*/
CheckTableNotInUse(parentHeap, "VACUUM");
/* Rebuild index of partitioned table */
int reindexFlags = REINDEX_REL_SUPPRESS_INDEX_USE;
result = reindex_relation(parentOid, reindexFlags, REINDEX_ALL_INDEX, NULL, false, GLOBAL_INDEX);
heap_close(parentHeap, NoLock);
if (result) {
/* Update this partition's system catalog tuple in pg_partiton to make it can be cleaned up */
PartitionSetAllEnabledClean(RelationGetRelid(parentHeap));
}
}
/*
* vacuumFullPart
*

View File

@ -90,6 +90,10 @@ static void buildConstraintNameForInfoCnstrnt(
const IndexStmt* stmt, Relation rel, char** indexRelationName, Oid namespaceId, const List* indexColNames);
static Oid buildInformationalConstraint(
IndexStmt* stmt, Oid indexRelationId, const char* indexRelationName, Relation rel, IndexInfo* indexInfo, Oid namespaceId);
static bool CheckGlobalIndexCompatible(Oid relOid, bool isGlobal, const IndexInfo* indexInfo, Oid methodOid);
static bool CheckIndexMethodConsistency(HeapTuple indexTuple, Relation indexRelation, Oid currMethodOid);
static bool CheckSimpleAttrsConsistency(Form_pg_index indexTuple, const int16* currAttrsArray, int currKeyNum);
static int AttrComparator(const void* a, const void* b);
/*
* CheckIndexCompatible
@ -115,7 +119,9 @@ static Oid buildInformationalConstraint(
* indexes. We ackowledge this when all operator classes, collations and
* exclusion operators match. Though we could further permit intra-opfamily
* changes for btree and hash indexes, that adds subtle complexity with no
* concrete benefit for core types.
* concrete benefit for core types. Note, that INCLUDE columns aren't
* checked by this function, for them it's enough that table rewrite is
* skipped.
* When a comparison or exclusion operator has a polymorphic input type, the
* actual input types must also match. This defends against the possibility
@ -180,9 +186,13 @@ bool CheckIndexCompatible(Oid oldId, char* accessMethodName, List* attributeList
* the new index, so we can test whether it's compatible with the existing
* one. Note that ComputeIndexAttrs might fail here, but that's OK:
* DefineIndex would have called this function with the same arguments
* later on, and it would have failed then anyway.
* later on, and it would have failed then anyway. Our attributeList
* contains only key attributes, thus we're filling ii_NumIndexAttrs and
* ii_NumIndexKeyAttrs with same value.
*/
indexInfo = makeNode(IndexInfo);
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
indexInfo->ii_NumIndexKeyAttrs = numberOfAttributes;
indexInfo->ii_Expressions = NIL;
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_PredicateState = NIL;
@ -222,7 +232,7 @@ bool CheckIndexCompatible(Oid oldId, char* accessMethodName, List* attributeList
}
/* Any change in operator class or collation breaks compatibility. */
old_natts = indexForm->indnatts;
old_natts = indexForm->indnkeyatts;
Assert(old_natts == numberOfAttributes);
d = SysCacheGetAttr(INDEXRELID, tuple, Anum_pg_index_indcollation, &isnull);
@ -310,6 +320,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
Oid relfilenode = InvalidOid;
bool dfsTablespace = false;
List* indexColNames = NIL;
List* allIndexParams = NIL;
List *filenodeList = NIL;
Relation rel;
Relation indexRelation;
@ -321,6 +332,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
int16* coloptions = NULL;
IndexInfo* indexInfo = NULL;
int numberOfAttributes = 0;
int numberOfKeyAttributes;
VirtualTransactionId* old_lockholders = NULL;
VirtualTransactionId* old_snapshots = NULL;
int n_old_snapshots = 0;
@ -350,16 +362,6 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
concurrent = false;
}
/*
* count attributes in index
*/
numberOfAttributes = list_length(stmt->indexParams);
if (numberOfAttributes <= 0)
ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("must specify at least one column")));
if (numberOfAttributes > INDEX_MAX_KEYS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("cannot use more than %d columns in an index", INDEX_MAX_KEYS)));
/*
* Open heap relation, acquire a suitable lock on it, remember its OID
*
@ -388,6 +390,12 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
}
}
/* default partition index is set to Global index */
if (RELATION_IS_PARTITIONED(rel) && !stmt->isPartitioned) {
stmt->isPartitioned = true;
stmt->isGlobal = true;
}
/*
* normal table does not support local partitioned index
*/
@ -404,9 +412,18 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Partition table does not support to set deferrable.")));
} else if (stmt->isGlobal && stmt->whereClause != NULL) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Global partition index does not support WHERE clause.")));
}
}
if (list_length(stmt->indexIncludingParams) > 0) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("create index does not support have include parameter")));
}
/*
* partitioned index not is not support concurrent index
*/
@ -415,10 +432,47 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot create concurrent partitioned indexes ")));
}
/* partitioned table only support local partitioned index */
if (RELATION_IS_PARTITIONED(rel) && !stmt->isPartitioned) {
ereport(
ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("partitioned table does not support global index")));
if (stmt->isGlobal) {
IndexElem* iparam = makeNode(IndexElem);
iparam->name = pstrdup("tableoid");
iparam->expr = NULL;
iparam->indexcolname = NULL;
iparam->collation = NIL;
iparam->opclass = NIL;
stmt->indexIncludingParams = lappend(stmt->indexIncludingParams, iparam);
}
if (list_intersection(stmt->indexParams, stmt->indexIncludingParams) != NIL) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("included columns must not intersect with key columns")));
}
/*
* count key attributes in index
*/
numberOfKeyAttributes = list_length(stmt->indexParams);
/*
* Calculate the new list of index columns including both key columns and
* INCLUDE columns. Later we can determine which of these are key columns,
* and which are just part of the INCLUDE list by checking the list
* position. A list item in a position less than ii_NumIndexKeyAttrs is
* part of the key columns, and anything equal to and over is part of the
* INCLUDE columns.
*/
allIndexParams = list_concat(list_copy(stmt->indexParams), list_copy(stmt->indexIncludingParams));
/*
* count attributes in index
*/
numberOfAttributes = list_length(allIndexParams);
if (numberOfAttributes <= 0) {
ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("must specify at least one column")));
}
if (numberOfAttributes > INDEX_MAX_KEYS) {
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("cannot use more than %d columns in an index", INDEX_MAX_KEYS)));
}
indexRelationName = stmt->idxname;
@ -466,7 +520,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
}
/* Check permissions except when using database's default */
if (stmt->isPartitioned) {
if (stmt->isPartitioned && !stmt->isGlobal) { // LOCAL partition index check
ListCell* cell = NULL;
partitionTableList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_PARTITION, relationId);
@ -513,7 +567,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
}
/*
* partitioned index need check every index partition tablespace
* partitioned index need check every index partition tablespace
*/
if (!stmt->isPartitioned && OidIsValid(tablespaceId) && tablespaceId != u_sess->proc_cxt.MyDatabaseTableSpace) {
AclResult aclresult;
@ -527,27 +581,30 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
AclResult aclresult;
ListCell* tspcell = NULL;
foreach (tspcell, partitiontspList) {
tablespaceOid = lfirst_oid(tspcell);
if (OidIsValid(tablespaceOid) && tablespaceOid != u_sess->proc_cxt.MyDatabaseTableSpace) {
aclresult = pg_tablespace_aclcheck(tablespaceOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK) {
aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tablespaceOid));
if (!stmt->isGlobal) { // LOCAL partition index check
foreach (tspcell, partitiontspList) {
tablespaceOid = lfirst_oid(tspcell);
if (OidIsValid(tablespaceOid) && tablespaceOid != u_sess->proc_cxt.MyDatabaseTableSpace) {
aclresult = pg_tablespace_aclcheck(tablespaceOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK) {
aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tablespaceOid));
}
}
/* In all cases disallow placing user relations in pg_global */
if (tablespaceOid == GLOBALTABLESPACE_OID) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("only shared relations can be placed in pg_global tablespace")));
}
}
/* In all cases disallow placing user relations in pg_global */
if (tablespaceOid == GLOBALTABLESPACE_OID) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("only shared relations can be placed in pg_global tablespace")));
}
}
/*
* check unique , if it is a unique/exclusion index,
* index column must include the partition key
* index column must include the partition key.
* For global partition index, we cancel this check.
*/
if (stmt->unique) {
if (stmt->unique && !stmt->isGlobal) {
int2vector* partKey = ((RangePartitionMap*)rel->partMap)->partitionKey;
int j = 0;
@ -586,7 +643,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
/*
* Choose the index column names.
*/
indexColNames = ChooseIndexColumnNames(stmt->indexParams);
indexColNames = ChooseIndexColumnNames(allIndexParams);
/*
* Select name for index if caller didn't specify
@ -630,6 +687,13 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support unique indexes", accessMethodName)));
if (list_length(stmt->indexIncludingParams) > 0 && !accessMethodForm->amcaninclude) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support for global partition index", accessMethodName)));
}
if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -663,6 +727,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
*/
indexInfo = makeNode(IndexInfo);
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
indexInfo->ii_NumIndexKeyAttrs = numberOfKeyAttributes;
indexInfo->ii_Expressions = NIL; /* for now */
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = make_ands_implicit((Expr*)stmt->whereClause);
@ -686,7 +751,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
collationObjectId,
classObjectId,
coloptions,
stmt->indexParams,
allIndexParams,
stmt->excludeOpNames,
relationId,
accessMethodName,
@ -699,6 +764,16 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Partitioned table does not support EXCLUDE index")));
}
if (stmt->isGlobal && PointerIsValid(indexInfo->ii_Expressions)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Global partition index does not support EXPRESSION index")));
}
if (!CheckGlobalIndexCompatible(relationId, stmt->isGlobal, indexInfo, accessMethodId)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Global and local partition index should not be on same column")));
}
}
#ifdef PGXC
@ -793,8 +868,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
}
IndexCreateExtraArgs extra;
extra.existingPSortOid = stmt->oldPSortOid;
extra.isPartitionedIndex = stmt->isPartitioned;
SetIndexCreateExtraArgs(&extra, stmt->oldPSortOid, stmt->isPartitioned, stmt->isGlobal);
if (stmt->internal_flag) {
if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) {
@ -879,8 +953,8 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
if (stmt->idxcomment != NULL)
CreateComments(indexRelationId, RelationRelationId, 0, stmt->idxcomment);
/* create the index partition */
if (stmt->isPartitioned) {
/* create the LOCAL index partition */
if (stmt->isPartitioned && !stmt->isGlobal) {
Relation partitionedIndex = index_open(indexRelationId, AccessExclusiveLock);
if (rel->partMap->type == PART_TYPE_RANGE || rel->partMap->type == PART_TYPE_INTERVAL) {
@ -1108,7 +1182,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al
indexInfo->ii_BrokenHotChain = false;
/* Now build the index */
index_build(rel, NULL, indexRelation, NULL, indexInfo, stmt->primary, false, false);
index_build(rel, NULL, indexRelation, NULL, indexInfo, stmt->primary, false, INDEX_CREATE_NONE_PARTITION);
/* Close both the relations, but keep the locks */
heap_close(rel, NoLock);
@ -1397,15 +1471,14 @@ static void ComputeIndexAttrs(IndexInfo* indexInfo, Oid* typeOidP, Oid* collatio
ListCell* nextExclOp = NULL;
ListCell* lc = NULL;
int attn;
int nkeycols = indexInfo->ii_NumIndexKeyAttrs;
/* Allocate space for exclusion operator info, if needed */
if (exclusionOpNames != NULL) {
int ncols = list_length(attList);
Assert(list_length(exclusionOpNames) == ncols);
indexInfo->ii_ExclusionOps = (Oid*)palloc(sizeof(Oid) * ncols);
indexInfo->ii_ExclusionProcs = (Oid*)palloc(sizeof(Oid) * ncols);
indexInfo->ii_ExclusionStrats = (uint16*)palloc(sizeof(uint16) * ncols);
Assert(list_length(exclusionOpNames) == nkeycols);
indexInfo->ii_ExclusionOps = (Oid*)palloc(sizeof(Oid) * nkeycols);
indexInfo->ii_ExclusionProcs = (Oid*)palloc(sizeof(Oid) * nkeycols);
indexInfo->ii_ExclusionStrats = (uint16*)palloc(sizeof(uint16) * nkeycols);
nextExclOp = list_head(exclusionOpNames);
} else
nextExclOp = NULL;
@ -1449,6 +1522,11 @@ static void ComputeIndexAttrs(IndexInfo* indexInfo, Oid* typeOidP, Oid* collatio
Node* expr = attribute->expr;
Assert(expr != NULL);
if (attn >= nkeycols) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("expressions are not supported in included columns")));
}
atttype = exprType(expr);
attcollation = exprCollation(expr);
@ -1496,6 +1574,37 @@ static void ComputeIndexAttrs(IndexInfo* indexInfo, Oid* typeOidP, Oid* collatio
typeOidP[attn] = atttype;
/*
* Included columns have no collation, no opclass and no ordering options.
*/
if (attn >= nkeycols) {
if (attribute->collation) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support a collation")));
}
if (attribute->opclass) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support an operator class")));
}
if (attribute->ordering != SORTBY_DEFAULT) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support ASC/DESC options")));
}
if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support NULLS FIRST/LAST options")));
}
classOidP[attn] = InvalidOid;
colOptionP[attn] = 0;
collationOidP[attn] = InvalidOid;
attn++;
continue;
}
/*
* Apply collation override if any
*/
@ -2200,7 +2309,7 @@ void PartitionNameCallbackForIndexPartition(Oid partitionedRelationOid, const ch
if (!relkind) {
return;
}
if (relkind != RELKIND_INDEX)
if (relkind != RELKIND_INDEX && relkind != RELKIND_GLOBAL_INDEX)
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", partitionName)));
if (0 != memcmp(partitionName, getPartitionName(partId, false), strlen(partitionName)))
ereport(ERROR,
@ -2267,9 +2376,14 @@ static void RangeVarCallbackForReindexIndex(
if (!relkind) {
return;
}
if (relkind != RELKIND_INDEX)
if (relkind != RELKIND_INDEX && relkind != RELKIND_GLOBAL_INDEX)
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", relation->relname)));
if (target_is_partition && relkind == RELKIND_GLOBAL_INDEX) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot reindex global index with partition name")));
}
/* Check permissions */
if (!pg_class_ownercheck(relId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname);
@ -2600,6 +2714,12 @@ void addIndexForPartition(Relation partitionedRelation, Oid partOid)
indexElemList = NIL;
indexRelOid = lfirst_oid(cell);
indexRel = relation_open(indexRelOid, AccessShareLock);
/* Ignore global partition index */
if (RelationIsGlobalIndex(indexRel)) {
relation_close(indexRel, AccessShareLock);
continue;
}
indexInfo = BuildIndexInfo(indexRel);
indexTuple = SearchSysCacheCopy1(INDEXRELID, ObjectIdGetDatum(indexRel->rd_id));
@ -2887,6 +3007,7 @@ static Oid buildInformationalConstraint(
true,
RelationGetRelid(rel),
indexInfo->ii_KeyAttrNumbers,
indexInfo->ii_NumIndexKeyAttrs,
indexInfo->ii_NumIndexAttrs,
InvalidOid, /* no domain */
indexRelationId, /* InvalidOid */
@ -2911,3 +3032,111 @@ static Oid buildInformationalConstraint(
heap_close(rel, NoLock);
return InvalidOid;
}
/*
* Index constraint: Local partition index could not be on same column with global partition index
* This function check all exist index on table of 'relOid', compare index attr column wiht new index of 'indexInfo',
* return true indicate new index is compatible with all existing index, otherwise, return false.
*/
static bool CheckGlobalIndexCompatible(Oid relOid, bool isGlobal, const IndexInfo* indexInfo, Oid currMethodOid)
{
ScanKeyData skey[1];
SysScanDesc sysScan;
HeapTuple tarTuple;
Relation indexRelation;
bool ret = true;
errno_t rc;
bool isNull;
char currIdxKind = isGlobal ? RELKIND_GLOBAL_INDEX : RELKIND_INDEX;
int currSize = sizeof(int16) * indexInfo->ii_NumIndexKeyAttrs;
int currKeyNum = indexInfo->ii_NumIndexKeyAttrs;
ScanKeyInit(&skey[0], Anum_pg_index_indrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relOid));
indexRelation = heap_open(IndexRelationId, AccessShareLock);
sysScan = systable_beginscan(indexRelation, IndexIndrelidIndexId, true, SnapshotNow, 1, skey);
int16* currAttrsArray = (int16*)palloc0(currSize);
rc = memcpy_s(currAttrsArray, currSize, indexInfo->ii_KeyAttrNumbers, currSize);
securec_check(rc, "\0", "\0");
qsort(currAttrsArray, currKeyNum, sizeof(int16), AttrComparator);
while (HeapTupleIsValid(tarTuple = systable_getnext(sysScan))) {
Form_pg_index indexTuple = (Form_pg_index)GETSTRUCT(tarTuple);
char tarIdxKind = get_rel_relkind(indexTuple->indexrelid);
/* only check index of different type(local and global) */
if (currIdxKind != tarIdxKind) {
if (!CheckIndexMethodConsistency(tarTuple, indexRelation, currMethodOid)) {
ret = false;
break;
}
/*
* check expressions: GPI is not support expression, thus, if there is expression on LPI
* we assume it as compatible and check next index;
*/
heap_getattr(tarTuple, Anum_pg_index_indexprs, RelationGetDescr(indexRelation), &isNull);
if ((indexInfo->ii_Expressions != NIL) != (!isNull)) {
continue;
}
if (!CheckSimpleAttrsConsistency(indexTuple, currAttrsArray, currKeyNum)) {
ret = false;
break;
}
}
}
systable_endscan(sysScan);
heap_close(indexRelation, AccessShareLock);
pfree(currAttrsArray);
return ret;
}
/*
* check consistency of two index, we use first attrs opclass as key to search index method
*/
static bool CheckIndexMethodConsistency(HeapTuple indexTuple, Relation indexRelation, Oid currMethodOid)
{
bool isNull = false;
bool ret = true;
oidvector* opClass =
(oidvector*)heap_getattr(indexTuple, Anum_pg_index_indclass, RelationGetDescr(indexRelation), &isNull);
Assert(!isNull);
Oid opClassOid = opClass->values[0];
HeapTuple opClassTuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassOid));
if (!HeapTupleIsValid(opClassTuple)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("Operator class does not exist for index compatible check.")));
}
Oid tarMethodOid = ((Form_pg_opclass)GETSTRUCT(opClassTuple))->opcmethod;
if (tarMethodOid != currMethodOid) {
ret = false;
}
ReleaseSysCache(opClassTuple);
return ret;
}
/*
* check column consistency: if run here, then compare two indexes' simple index col,
* if index col array is totally same, it means not compatible situation.
*/
static bool CheckSimpleAttrsConsistency(Form_pg_index indexTuple, const int16* currAttrsArray, int currKeyNum)
{
int tarKeyNum = indexTuple->indnkeyatts;
bool ret = true;
int i;
if (tarKeyNum == currKeyNum) {
qsort(indexTuple->indkey.values, currKeyNum, sizeof(int16), AttrComparator);
for (i = 0; i < currKeyNum; i++) {
if (indexTuple->indkey.values[i] != currAttrsArray[i]) {
break;
}
}
if (i == currKeyNum) { // attrs of two index is totally same, which indicates not compatible.
ret = false;
}
}
return ret;
}
static int AttrComparator(const void* a, const void* b)
{
return *(int16*)a - *(int16*)b;
}

View File

@ -300,6 +300,12 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
gettext_noop("index \"%s\" does not exist, skipping"),
gettext_noop("\"%s\" is not an index"),
gettext_noop("Use DROP INDEX to remove an index.")},
{RELKIND_GLOBAL_INDEX,
ERRCODE_UNDEFINED_OBJECT,
gettext_noop("global partition index \"%s\" does not exist"),
gettext_noop("global partition index \"%s\" does not exist, skipping"),
gettext_noop("\"%s\" is not an global partition index"),
gettext_noop("Use DROP INDEX to remove an global partition index.")},
{RELKIND_COMPOSITE_TYPE,
ERRCODE_UNDEFINED_OBJECT,
gettext_noop("type \"%s\" does not exist"),
@ -546,6 +552,7 @@ static void ATExecAddPartition(Relation rel, AddPartitionState* partState);
static void ATExecDropPartition(Relation rel, AlterTableCmd* cmd);
static void ATExecUnusableIndexPartition(Relation rel, const char* partition_name);
static void ATExecUnusableIndex(Relation rel);
static void ATUnusableGlobalIndex(Relation rel);
static void ATExecUnusableAllIndexOnPartition(Relation rel, const char* partition_name);
static void ATExecModifyRowMovement(Relation rel, bool rowMovement);
static void ATExecTruncatePartition(Relation rel, AlterTableCmd* cmd);
@ -2790,9 +2797,16 @@ static void RangeVarCallbackForDropRelation(
return; /* concurrently dropped, so nothing to do */
classform = (Form_pg_class)GETSTRUCT(tuple);
if ((classform->relkind != relkind) && !(u_sess->attr.attr_common.IsInplaceUpgrade && relkind == RELKIND_RELATION &&
classform->relkind == RELKIND_TOASTVALUE))
char expected_relkind = relkind;
if (classform->relkind == RELKIND_GLOBAL_INDEX) {
expected_relkind = RELKIND_GLOBAL_INDEX;
}
if ((classform->relkind != expected_relkind) &&
!(u_sess->attr.attr_common.IsInplaceUpgrade &&
expected_relkind == RELKIND_RELATION &&
classform->relkind == RELKIND_TOASTVALUE)) {
DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
}
/* Allow DROP to either table owner or schema owner */
if (!pg_class_ownercheck(relOid, GetUserId()) && !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
@ -2813,7 +2827,7 @@ static void RangeVarCallbackForDropRelation(
* we do it the other way around. No error if we don't find a pg_index
* entry, though --- the relation may have been dropped.
*/
if (relkind == RELKIND_INDEX && relOid != oldRelOid) {
if ((relkind == RELKIND_INDEX || relkind == RELKIND_GLOBAL_INDEX) && relOid != oldRelOid) {
state->heapOid = IndexGetRelation(relOid, true);
if (OidIsValid(state->heapOid))
LockRelationOid(state->heapOid, heap_lockmode);
@ -4194,14 +4208,14 @@ static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing
* change names that are hardcoded into the system, hence the following
* restriction.
*/
if (relkind != RELKIND_RELATION && relkind != RELKIND_VIEW && relkind != RELKIND_MATVIEW &&
relkind != RELKIND_COMPOSITE_TYPE &&
relkind != RELKIND_INDEX && relkind != RELKIND_FOREIGN_TABLE)
if (relkind != RELKIND_RELATION && relkind != RELKIND_VIEW && relkind != RELKIND_MATVIEW &&
relkind != RELKIND_COMPOSITE_TYPE && relkind != RELKIND_INDEX &&
relkind != RELKIND_FOREIGN_TABLE && relkind != RELKIND_GLOBAL_INDEX) {
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, view, composite type, index, or foreign table",
NameStr(classform->relname))));
}
/*
* permissions checking. only the owner of a class can change its schema.
*/
@ -4641,7 +4655,7 @@ void RenameRelationInternal(Oid myrelid, const char* newrelname)
/*
* Also rename the associated constraint, if any.
*/
if (targetrelation->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(targetrelation)) {
Oid constraintId = get_index_constraint(myrelid);
if (OidIsValid(constraintId))
RenameConstraintById(constraintId, newrelname);
@ -4976,7 +4990,7 @@ void CheckTableNotInUse(Relation rel, const char* stmt)
stmt,
RelationGetRelationName(rel))));
if (rel->rd_rel->relkind != RELKIND_INDEX && AfterTriggerPendingOnRel(RelationGetRelid(rel)))
if (!RelationIsIndex(rel) && AfterTriggerPendingOnRel(RelationGetRelid(rel)))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
/* translator: first %s is a SQL command, eg ALTER TABLE */
@ -6663,7 +6677,7 @@ static void ATRewriteTables(List** wqueue, LOCKMODE lockmode)
RelationIsCUFormat(temprel) ? IDX_COL_TBL : (RelationIsPAXFormat(temprel) ? IDX_DFS_TBL : IDX_ROW_TBL);
idxPartitionedOrNot = RELATION_IS_PARTITIONED(temprel) ? IDX_PARTITIONED_TBL : IDX_ORDINARY_TBL;
heap_close(temprel, NoLock);
} else if (tab->relkind == RELKIND_INDEX) {
} else if (tab->relkind == RELKIND_INDEX || tab->relkind == RELKIND_GLOBAL_INDEX) {
Relation temprel = index_open(tab->relid, NoLock);
rel_format_idx = IDX_ROW_TBL; /* row relation */
idxPartitionedOrNot = RelationIsPartitioned(temprel) ? IDX_PARTITIONED_TBL : IDX_ORDINARY_TBL;
@ -7180,6 +7194,7 @@ static void ATSimplePermissions(Relation rel, int allowed_targets)
actual_target = ATT_MATVIEW;
break;
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
actual_target = ATT_INDEX;
break;
case RELKIND_COMPOSITE_TYPE:
@ -8149,7 +8164,7 @@ static void ATExecDropNotNull(Relation rel, const char* colName, LOCKMODE lockmo
* Loop over each attribute in the primary key and see if it
* matches the to-be-altered attribute
*/
for (i = 0; i < indexStruct->indnatts; i++) {
for (i = 0; i < indexStruct->indnkeyatts; i++) {
if (indexStruct->indkey.values[i] == attnum)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
@ -8290,11 +8305,11 @@ static void ATPrepSetStatistics(Relation rel, const char* colName, Node* newValu
* allowSystemTableMods to be turned on.
*/
if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_MATVIEW &&
rel->rd_rel->relkind != RELKIND_INDEX &&
rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
!RelationIsIndex(rel) && rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
RelationGetRelationName(rel))));
}
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
@ -9545,6 +9560,7 @@ static void ATAddForeignKeyConstraint(AlteredTableInfo* tab, Relation rel, Const
RelationGetRelid(rel),
fkattnum,
numfks,
numfks,
InvalidOid, /* not a domain constraint */
indexOid,
RelationGetRelid(pkrel),
@ -9876,7 +9892,7 @@ static int transformFkeyGetPrimaryKey(
* assume a primary key cannot have expressional elements)
*/
*attnamelist = NIL;
for (i = 0; i < indexStruct->indnatts; i++) {
for (i = 0; i < indexStruct->indnkeyatts; i++) {
int pkattno = indexStruct->indkey.values[i];
attnums[i] = pkattno;
@ -9931,7 +9947,7 @@ static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16* attnums,
* partial index; forget it if there are any expressions, too. Invalid
* indexes are out as well.
*/
if (indexStruct->indnatts == numattrs && indexStruct->indisunique && IndexIsValid(indexStruct) &&
if (indexStruct->indnkeyatts == numattrs && indexStruct->indisunique && IndexIsValid(indexStruct) &&
heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL)) {
/* Must get indclass the hard way */
@ -10953,7 +10969,7 @@ static void ATExecAlterColumnType(AlteredTableInfo* tab, Relation rel, AlterTabl
case OCLASS_CLASS: {
char relKind = get_rel_relkind(foundObject.objectId);
if (relKind == RELKIND_INDEX) {
if (relKind == RELKIND_INDEX || relKind == RELKIND_GLOBAL_INDEX) {
Assert(foundObject.objectSubId == 0);
if (!list_member_oid(tab->changedIndexOids, foundObject.objectId)) {
/*
@ -11721,6 +11737,7 @@ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE
}
break;
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
if (!recursing) {
/*
* Because ALTER INDEX OWNER used to be allowed, and in fact
@ -11903,13 +11920,14 @@ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE
* don't have their own entries either.
*/
if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE && tuple_class->relkind != RELKIND_INDEX &&
tuple_class->relkind != RELKIND_TOASTVALUE && tuple_class->relnamespace != CSTORE_NAMESPACE)
tuple_class->relkind != RELKIND_GLOBAL_INDEX && tuple_class->relkind != RELKIND_TOASTVALUE &&
tuple_class->relnamespace != CSTORE_NAMESPACE)
changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId);
/*
* Also change the ownership of the table's row type, if it has one
*/
if (tuple_class->relkind != RELKIND_INDEX)
if (tuple_class->relkind != RELKIND_INDEX && tuple_class->relkind != RELKIND_GLOBAL_INDEX)
AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId, tuple_class->relkind == RELKIND_COMPOSITE_TYPE);
/*
@ -11949,7 +11967,8 @@ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE
change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
}
if (tuple_class->relkind == RELKIND_INDEX && tuple_class->relam == PSORT_AM_OID) {
if ((tuple_class->relkind == RELKIND_INDEX || tuple_class->relkind == RELKIND_GLOBAL_INDEX) &&
tuple_class->relam == PSORT_AM_OID) {
/* if it is PSORT index, recurse to change PSORT releateion's ownership */
if (tuple_class->relcudescrelid != InvalidOid)
ATExecChangeOwner(tuple_class->relcudescrelid, newOwnerId, true, lockmode);
@ -11976,6 +11995,19 @@ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE
partCacheList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_PARTITION, relationOid);
} else if (tuple_class->relkind == RELKIND_INDEX) {
partCacheList = searchPgPartitionByParentId(PART_OBJ_TYPE_INDEX_PARTITION, relationOid);
} else if (tuple_class->relkind == RELKIND_GLOBAL_INDEX) {
/* If it has a toast table, recurse to change its ownership */
if (tuple_class->reltoastrelid != InvalidOid)
ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId, true, lockmode);
/* If it has a cudesc table, recurse to change its ownership */
if (tuple_class->relcudescrelid != InvalidOid)
ATExecChangeOwner(tuple_class->relcudescrelid, newOwnerId, true, lockmode);
/* If it has a delta table, recurse to change its ownership */
if (tuple_class->reldeltarelid != InvalidOid)
ATExecChangeOwner(tuple_class->reldeltarelid, newOwnerId, true, lockmode);
partCacheList = NIL;
} else {
partCacheList = NIL;
}
@ -12491,6 +12523,7 @@ static void ATExecSetRelOptions(Relation rel, List* defList, AlterTableType oper
break;
}
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
(void)index_reloptions(rel->rd_am->amoptions, newOptions, true);
break;
default:
@ -14380,7 +14413,7 @@ static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt* stmt, LOCKM
errmsg("cannot use invalid index \"%s\" as replica identity", RelationGetRelationName(indexRel))));
/* Check index for nullable columns. */
for (key = 0; key < indexRel->rd_index->indnatts; key++) {
for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++) {
int16 attno = indexRel->rd_index->indkey.values[key];
Form_pg_attribute attr;
@ -15588,7 +15621,8 @@ static void RangeVarCallbackForAlterRelation(
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a composite type", rv->relname)));
}
if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX && !IsA(stmt, RenameStmt)) {
if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX && relkind != RELKIND_GLOBAL_INDEX &&
!IsA(stmt, RenameStmt)) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", rv->relname)));
}
/*
@ -16547,6 +16581,8 @@ static void ATExecDropPartition(Relation rel, AlterTableCmd* cmd)
Oid changeToRangePartOid = GetNeedDegradToRangePartOid(rel, partOid);
fastDropPartition(rel, partOid, "DROP PARTITION", changeToRangePartOid);
// Unusable Global Index
ATUnusableGlobalIndex(rel);
}
/*
@ -16642,6 +16678,42 @@ static void ATExecUnusableIndexPartition(Relation rel, const char* partition_nam
ATExecSetIndexUsableState(PartitionRelationId, indexPartOid, false);
}
static void ATUnusableGlobalIndex(Relation rel)
{
ListCell* index = NULL;
bool dirty = false;
HeapTuple sysTuple = NULL;
Relation sysTable = NULL;
sysTable = relation_open(IndexRelationId, RowExclusiveLock);
// update the indisusable field
foreach (index, RelationGetSpecificKindIndexList(rel, true)) {
Oid currIndexOid = lfirst_oid(index);
sysTuple = SearchSysCacheCopy1(INDEXRELID, ObjectIdGetDatum(currIndexOid));
if (sysTuple) {
if (((Form_pg_index)GETSTRUCT(sysTuple))->indisusable != false) {
((Form_pg_index)GETSTRUCT(sysTuple))->indisusable = false;
dirty = true;
}
} else {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("could not find tuple for relation %u", currIndexOid)));
}
/* Keep the system catalog indexes current. */
if (dirty) {
simple_heap_update(sysTable, &(sysTuple->t_self), sysTuple);
CatalogUpdateIndexes(sysTable, sysTuple);
}
heap_freetuple_ext(sysTuple);
}
relation_close(sysTable, RowExclusiveLock);
if (dirty) {
CommandCounterIncrement();
}
}
/*
* @@GaussDB@@
* Target : data partition
@ -16942,6 +17014,9 @@ static void ATExecTruncatePartition(Relation rel, AlterTableCmd* cmd)
heap_close(newTableRel, AccessExclusiveLock);
pgstat_report_truncate(newPartOid, newTableRel->rd_id, newTableRel->rd_rel->relisshared);
}
// set global index unusable
ATUnusableGlobalIndex(rel);
}
/*
@ -17593,7 +17668,7 @@ static void ATExecMergePartition(Relation partTableRel, AlterTableCmd* cmd)
RelationOpenSmgr(tempTableRel);
/* lock the index relation on partitioned table and check the usability */
index_list = RelationGetIndexList(partTableRel);
index_list = RelationGetSpecificKindIndexList(partTableRel, false);
foreach (cell, index_list) {
Oid dstIndexPartTblspcOid;
Oid clonedIndexRelationId;
@ -17746,6 +17821,9 @@ static void ATExecMergePartition(Relation partTableRel, AlterTableCmd* cmd)
if (renameTargetPart) {
renamePartitionInternal(partTableRel->rd_id, destPartOid, destPartName);
}
/* step 7: Unusable Global Index */
ATUnusableGlobalIndex(partTableRel);
}
// When merge toast table, values of the first column may be repeat.
@ -17961,6 +18039,9 @@ static void ATExecExchangePartition(Relation partTableRel, AlterTableCmd* cmd)
// Swap relfilenode of table and toast table
finishPartitionHeapSwap(partOid, ordTableRel->rd_id, false, relfrozenxid);
// Unusable global index of partTableRel
ATUnusableGlobalIndex(partTableRel);
// Swap relfilenode of index
Assert(list_length(partIndexList) == list_length(ordIndexList));
if (0 != list_length(partIndexList)) {
@ -17968,7 +18049,7 @@ static void ATExecExchangePartition(Relation partTableRel, AlterTableCmd* cmd)
list_free_ext(partIndexList);
list_free_ext(ordIndexList);
}
heap_close(ordTableRel, NoLock);
}
@ -18198,11 +18279,14 @@ bool checkRelationLocalIndexesUsable(Relation relation)
while (HeapTupleIsValid(htup = systable_getnext(indscan))) {
Form_pg_index index = (Form_pg_index)GETSTRUCT(htup);
Relation index_relation = index_open(index->indexrelid, AccessShareLock);
if (!IndexIsUsable(index)) {
if (!IndexIsUsable(index) && !RelationIsGlobalIndex(index_relation)) {
index_close(index_relation, AccessShareLock);
ret = false;
break;
}
index_close(index_relation, AccessShareLock);
}
systable_endscan(indscan);
@ -18486,7 +18570,7 @@ static void checkIndexForExchange(
HeapTuple ordTableIndexTuple = NULL;
List* ordTableIndexTupleList = NIL;
bool* matchFlag = NULL;
partTableIndexOidList = RelationGetIndexList(partTableRel);
partTableIndexOidList = RelationGetSpecificKindIndexList(partTableRel, false);
ordTableIndexOidList = RelationGetIndexList(ordTableRel);
if (list_length(partTableIndexOidList) == 0 && list_length(ordTableIndexOidList) == 0) {
return;
@ -19253,6 +19337,9 @@ static void ATExecSplitPartition(Relation partTableRel, AlterTableCmd* cmd)
#endif
list_free_ext(newPartOidList);
// set global index unusable
ATUnusableGlobalIndex(partTableRel);
}
// check split point
@ -20660,7 +20747,7 @@ static void ExecOnlyTestCStorePartitionedTable(AlteredTableInfo* tab)
*/
static void ForbidToRewriteOrTestCstoreIndex(AlteredTableInfo* tab)
{
if (tab->relkind == RELKIND_INDEX) {
if (tab->relkind == RELKIND_INDEX || tab->relkind == RELKIND_GLOBAL_INDEX) {
Relation rel = index_open(tab->relid, AccessShareLock);
if (rel->rd_rel->relam == PSORT_AM_OID) {
index_close(rel, AccessShareLock);
@ -20724,7 +20811,7 @@ static void ExecChangeTableSpaceForRowTable(AlteredTableInfo* tab, LOCKMODE lock
ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
/* handle a special index type: PSORT index */
if (tab->relkind == RELKIND_INDEX) {
if (tab->relkind == RELKIND_INDEX || tab->relkind == RELKIND_GLOBAL_INDEX) {
Relation rel = index_open(tab->relid, lockmode);
if (rel->rd_rel->relam == PSORT_AM_OID) {
PSortChangeTableSpace(rel->rd_rel->relcudescrelid, /* psort oid */
@ -20897,7 +20984,7 @@ static void ExecChangeTableSpaceForRowPartition(AlteredTableInfo* tab, LOCKMODE
ATExecSetTableSpaceForPartitionP3(tab->relid, tab->partid, tab->newTableSpace, partitionLock);
/* handle a special index type: PSORT index */
if (tab->relkind == RELKIND_INDEX) {
if (tab->relkind == RELKIND_INDEX || tab->relkind == RELKIND_GLOBAL_INDEX) {
Relation rel = index_open(tab->relid, NoLock);
if (rel->rd_rel->relam == PSORT_AM_OID) {
Partition part = partitionOpen(rel, tab->partid, partitionLock);

View File

@ -440,6 +440,7 @@ Oid CreateTrigger(CreateTrigStmt* stmt, const char* queryString, Oid relOid, Oid
RelationGetRelid(rel),
NULL, /* no conkey */
0,
0,
InvalidOid, /* no domain */
InvalidOid, /* no index */
InvalidOid, /* no foreign key */

View File

@ -2786,6 +2786,7 @@ static char* domainAddConstraint(
InvalidOid, /* not a relation constraint */
NULL,
0,
0,
domainOid, /* domain constraint */
InvalidOid, /* no associated index */
InvalidOid, /* Foreign key fields */

View File

@ -96,6 +96,11 @@ const char* sql_templates[] = {
"COMMIT;", /* with schema */
};
typedef struct {
Bitmapset* invisMap; /* cache invisible tuple's partOid in global partition index */
Bitmapset* visMap; /* cache visible tuple's partOid in global partition index */
} VacStates;
extern void exec_query_for_merge(const char* query_string);
extern void do_delta_merge(List* infos, VacuumStmt* stmt);
@ -104,10 +109,14 @@ extern void do_delta_merge(List* infos, VacuumStmt* stmt);
static void free_merge_info(List* infos);
static void DropEmptyPartitionDirectories(Oid relid);
/* A few variables that don't seem worth passing around as parameters */
static THR_LOCAL BufferAccessStrategy vac_strategy;
static THR_LOCAL int elevel = -1;
static void vac_truncate_clog(TransactionId frozenXID);
static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast);
static void GPIVacuumMainPartition(
Relation onerel, const VacuumStmt* vacstmt, LOCKMODE lockmode, BufferAccessStrategy bstrategy);
#define TryOpenCStoreInternalRelation(r, lmode, r1, r2) \
do { \
@ -312,8 +321,9 @@ void vacuum(
* do NOT vacuum partitioned table,
* as vacuum is an operation related with tuple and storage page reorganization
*/
if (!vacuumMainPartition(vacstmt->flags) && (vacstmt->options & VACOPT_VACUUM)) {
if (vacuumPartition(vacstmt->flags) || vacuumRelation(vacstmt->flags)) {
if (vacstmt->options & VACOPT_VACUUM) {
if (vacuumPartition(vacstmt->flags) || vacuumRelation(vacstmt->flags) ||
vacuumMainPartition(vacstmt->flags)) {
if (!vacuum_rel(relOid, vacstmt, do_toast))
continue;
} else {
@ -1481,8 +1491,6 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast)
gstrace_entry(GS_TRC_ID_vacuum_rel);
StartTransactionCommand();
Assert(!vacuumMainPartition(vacstmt->flags));
if (!(vacstmt->options & VACOPT_FULL)) {
/*
* In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
@ -1604,6 +1612,21 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast)
proc_snapshot_and_transaction();
return false;
}
if (rel != NULL && relid == PartitionRelationId && PartitionMetadataDisabledClean(rel)) {
if (vacstmt->options & VACOPT_VERBOSE) {
messageLevel = VERBOSEMESSAGE;
} else {
messageLevel = WARNING;
}
ereport(messageLevel,
(errcode(ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
errmsg("skipping \"%s\" --- only table or database can vacuum it",
RelationGetRelationName(rel))));
relation_close(rel, AccessShareLock);
proc_snapshot_and_transaction();
return false;
}
}
if (rel != NULL)
relation_close(rel, AccessShareLock);
@ -1651,8 +1674,27 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast)
}
GetLock = true;
} else if (vacuumRelation(vacstmt->flags) && ConditionalLockRelationOid(relid, lmode)) {
} else if (vacuumMainPartition(vacstmt->flags) && !(vacstmt->options & VACOPT_NOWAIT)) {
/*
* Coordinator needs guarantee the old select statement must finish when
* run vacuum full table
*/
CNGuardOldQueryForVacuumFull(vacstmt, relid);
onerel = try_relation_open(relid, lmode);
GetLock = true;
/*
* We block vacuum operation while the target table is in redistribution
* read only mode. For redistribution IUD mode, we will block vacuum before
* coming to this point.
*/
if (!u_sess->attr.attr_sql.enable_cluster_resize && onerel != NULL &&
RelationInClusterResizingReadOnly(onerel)) {
ereport(ERROR,
(errcode(ERRCODE_E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
errmsg("%s is redistributing, please retry later.", onerel->rd_rel->relname.data)));
}
} else if (vacuumRelation(vacstmt->flags) && ConditionalLockRelationOid(relid, lmode)) {
if (relid > FirstNormalObjectId && !checkGroup(relid, true)) {
proc_snapshot_and_transaction();
return false;
@ -1694,6 +1736,10 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast)
}
} else
GetLock = true;
} else if (vacuumMainPartition(vacstmt->flags) && ConditionalLockRelationOid(relationid, lmodePartTable)) {
Assert(!(vacstmt->options & VACOPT_FULL));
onerel = try_relation_open(relid, NoLock);
GetLock = true;
}
if (!GetLock) {
@ -1728,7 +1774,6 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast)
pgxc_lock_for_utility_stmt(NULL, RelationIsLocalTemp(onerel));
// Try to open CUDescRel and DeltaRel if needed
//
if (!(vacstmt->options & VACOPT_NOWAIT))
TryOpenCStoreInternalRelation(onerel, lmode, cudescrel, deltarel);
else {
@ -2045,12 +2090,30 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast)
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
pgstat_report_waitstatus_relname(STATE_VACUUM_FULL, get_nsp_relname(relid));
vacuumFullPart(relid, vacstmt, vacstmt->freeze_min_age, vacstmt->freeze_table_age);
} else if ((vacstmt->options & VACOPT_FULL) && (vacstmt->flags & VACFLG_MAIN_PARTITION)) {
if (cudescrel != NULL) {
relation_close(cudescrel, NoLock);
cudescrel = NULL;
}
if (deltarel != NULL) {
relation_close(deltarel, NoLock);
deltarel = NULL;
}
relation_close(onerel, NoLock);
onerel = NULL;
pgstat_report_waitstatus_relname(STATE_VACUUM_FULL, get_nsp_relname(relid));
GpiVacuumFullMainPartiton(relid);
pgstat_report_vacuum(relid, InvalidOid, false, 0);
} else if (!(vacstmt->options & VACOPT_FULL)) {
/* clean hdfs empty directories of value partition just on main CN */
if (vacstmt->options & VACOPT_HDFSDIRECTORY) {
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
DropEmptyPartitionDirectories(relid);
}
} else if (vacuumMainPartition(vacstmt->flags)) {
pgstat_report_waitstatus_relname(STATE_VACUUM, get_nsp_relname(relid));
GPIVacuumMainPartition(onerel, vacstmt, lmode, vac_strategy);
} else {
pgstat_report_waitstatus_relname(STATE_VACUUM, get_nsp_relname(relid));
lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
@ -2319,53 +2382,61 @@ void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tupl
heap_close(rd, RowExclusiveLock);
}
void vac_open_part_indexes(
VacuumStmt* vacstmt, LOCKMODE lockmode, int* nindexes, Relation** Irel, Relation** indexrel, Partition** indexpart)
void vac_open_part_indexes(VacuumStmt* vacstmt, LOCKMODE lockmode, int* nindexes, int* nindexes_global, Relation** Irel,
Relation** indexrel, Partition** indexpart)
{
List* indexoidlist = NIL;
ListCell* indexoidscan = NULL;
int i;
Relation indrel;
List* localIndOidList = NIL;
List* globIndOidList = NIL;
ListCell* localIndCell = NULL;
ListCell* globIndCell = NULL;
int localIndNums;
int globIndNums;
int tolIndNums;
Relation indrel = NULL;
Assert(lockmode != NoLock);
Assert(vacstmt->onepart != NULL);
Assert(vacstmt->onepartrel != NULL);
indexoidlist = PartitionGetPartIndexList(vacstmt->onepart);
i = list_length(indexoidlist);
if (i > 0) {
*Irel = (Relation*)palloc((long)(i) * sizeof(Relation));
*indexrel = (Relation*)palloc((long)(i) * sizeof(Relation));
*indexpart = (Partition*)palloc((long)(i) * sizeof(Partition));
// get local partition indexes
localIndOidList = PartitionGetPartIndexList(vacstmt->onepart);
localIndNums = list_length(localIndOidList);
// get global partition indexes
globIndOidList = RelationGetSpecificKindIndexList(vacstmt->onepartrel, true);
globIndNums = list_length(globIndOidList);
tolIndNums = localIndNums + globIndNums;
if (tolIndNums > 0) {
*Irel = (Relation*)palloc((long)(tolIndNums) * sizeof(Relation));
} else {
*Irel = NULL;
}
if (localIndNums > 0) {
*indexrel = (Relation*)palloc((long)(localIndNums) * sizeof(Relation));
*indexpart = (Partition*)palloc((long)(localIndNums) * sizeof(Partition));
} else {
*indexrel = NULL;
*indexpart = NULL;
}
i = 0;
foreach (indexoidscan, indexoidlist) {
Oid indexParentid;
Oid indexoid = lfirst_oid(indexoidscan);
Partition indpart;
Relation indexPartRel = NULL;
// collect ready local partition indexes
int i = 0;
foreach (localIndCell, localIndOidList) {
Oid localIndOid = lfirst_oid(localIndCell);
/* Get the index partition's parent oid */
indexParentid = partid_get_parentid(indexoid);
Oid indexParentid = partid_get_parentid(localIndOid);
indrel = relation_open(indexParentid, lockmode);
Assert(indrel != NULL);
/* Open the partition */
indpart = partitionOpen(indrel, indexoid, lockmode);
indexPartRel = partitionGetRelation(indrel, indpart);
Partition indpart = partitionOpen(indrel, localIndOid, lockmode);
Relation indexPartRel = partitionGetRelation(indrel, indpart);
if (IndexIsReady(indrel->rd_index) && IndexIsReady(indexPartRel->rd_index) && IndexIsUsable(indrel->rd_index) &&
indpart->pd_part->indisusable) {
(*indexrel)[i] = indrel;
(*indexpart)[i] = indpart;
(*Irel)[i] = indexPartRel;
++i;
} else {
releaseDummyRelation(&indexPartRel);
@ -2373,26 +2444,48 @@ void vac_open_part_indexes(
relation_close(indrel, lockmode);
}
}
*nindexes = i;
list_free(indexoidlist);
// collect ready global partion indexes
int j = 0;
foreach (globIndCell, globIndOidList) {
Oid globIndOid = lfirst_oid(globIndCell);
indrel = index_open(globIndOid, lockmode);
if (IndexIsReady(indrel->rd_index) && IndexIsUsable(indrel->rd_index)) {
(*Irel)[i + j] = indrel;
j++;
} else {
index_close(indrel, lockmode);
}
}
*nindexes_global = j;
*nindexes += j;
list_free(localIndOidList);
list_free(globIndOidList);
}
void vac_close_part_indexes(int nindexes, Relation* Irel, Relation* indexrel, Partition* indexpart, LOCKMODE lockmode)
void vac_close_part_indexes(
int nindexes, int nindexes_global, Relation* Irel, Relation* indexrel, Partition* indexpart, LOCKMODE lockmode)
{
if (Irel == NULL || indexpart == NULL || indexrel == NULL) {
if (Irel == NULL) {
return;
}
int nindexes_local = nindexes - nindexes_global;
while (nindexes--) {
Relation rel = Irel[nindexes];
Relation ind = indexrel[nindexes];
Partition part = indexpart[nindexes];
releaseDummyRelation(&rel);
partitionClose(ind, part, lockmode);
relation_close(ind, lockmode);
if (nindexes < nindexes_local) {
// close local partition indexes
Relation ind = indexrel[nindexes];
Partition part = indexpart[nindexes];
releaseDummyRelation(&rel);
partitionClose(ind, part, lockmode);
relation_close(ind, lockmode);
} else {
// close global partition indexes
index_close(rel, lockmode);
}
}
pfree_ext(indexrel);
pfree_ext(indexpart);
@ -3441,3 +3534,165 @@ void updateTotalRows(Oid relid, double num_tuples)
heap_inplace_update(classRel, ctup);
heap_close(classRel, RowExclusiveLock);
}
// call back func to check current partOid is invisible or visible
static bool GPIIsInvisibleTuple(ItemPointer itemptr, void* state, Oid partOid)
{
VacStates* pvacStates = (VacStates*)state;
Assert(pvacStates != NULL);
if (partOid == InvalidOid) {
ereport(elevel,
(errmsg("global index tuple's oid invalid. partOid = %u", partOid)));
return false;
}
// check partition oid of global partition index tuple
if (bms_is_member(partOid, pvacStates->invisMap)) {
return true;
}
if (bms_is_member(partOid, pvacStates->visMap)) {
return false;
}
PartStatus partStat = PartitionGetMetadataStatus(partOid, true);
if (partStat == PART_METADATA_INVISIBLE) {
pvacStates->invisMap = bms_add_member(pvacStates->invisMap, partOid);
return true;
} else {
// visible include EXIST and NOEXIST
pvacStates->visMap = bms_add_member(pvacStates->visMap, partOid);
return false;
}
}
// clean invisible tuples for global partition index
static void GPICleanInvisibleIndex(Relation indrel, IndexBulkDeleteResult** stats, Bitmapset** cleanedParts)
{
IndexVacuumInfo ivinfo;
PGRUsage ru0;
gstrace_entry(GS_TRC_ID_lazy_vacuum_index);
pg_rusage_init(&ru0);
ivinfo.index = indrel;
ivinfo.analyze_only = false;
ivinfo.estimated_count = true;
ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = indrel->rd_rel->reltuples;
ivinfo.strategy = vac_strategy;
VacStates* pvacStates = (VacStates*)palloc0(sizeof(VacStates));
pvacStates->invisMap = NULL;
pvacStates->visMap = NULL;
/* Do bulk deletion */
*stats = index_bulk_delete(&ivinfo, *stats, GPIIsInvisibleTuple, (void*)pvacStates);
Bitmapset* pIntersect = bms_intersect(pvacStates->invisMap, pvacStates->visMap);
Assert(bms_is_empty(pIntersect));
bms_free_ext(pIntersect);
*cleanedParts = bms_add_members(*cleanedParts, pvacStates->invisMap);
bms_free(pvacStates->invisMap);
bms_free(pvacStates->visMap);
pfree_ext(pvacStates);
ereport(elevel,
(errmsg("scanned index \"%s\" to remove %lf invisible rows",
RelationGetRelationName(indrel),
(*stats)->tuples_removed),
errdetail("%s.", pg_rusage_show(&ru0))));
gstrace_exit(GS_TRC_ID_lazy_vacuum_index);
}
static void GPIOpenGlobalIndexes(Relation onerel, LOCKMODE lockmode, int* nindexes, Relation** iRel)
{
List* globIndOidList = NIL;
ListCell* globIndCell = NULL;
int globIndNums;
Assert(lockmode != NoLock);
Assert(onerel != NULL);
// get global partition indexes
globIndOidList = RelationGetSpecificKindIndexList(onerel, true);
globIndNums = list_length(globIndOidList);
if (globIndNums > 0) {
*iRel = (Relation*)palloc((long)(globIndNums) * sizeof(Relation));
} else {
*iRel = NULL;
}
// collect ready global partion indexes
int i = 0;
foreach (globIndCell, globIndOidList) {
Oid globIndOid = lfirst_oid(globIndCell);
Relation indrel = index_open(globIndOid, lockmode);
if (IndexIsReady(indrel->rd_index) && IndexIsUsable(indrel->rd_index)) {
(*iRel)[i] = indrel;
i++;
} else {
index_close(indrel, lockmode);
}
}
*nindexes = i;
list_free(globIndOidList);
}
// vacuum main partition table to delete invisible tuple in global partition index
static void GPIVacuumMainPartition(
Relation onerel, const VacuumStmt* vacstmt, LOCKMODE lockmode, BufferAccessStrategy bstrategy)
{
Relation* iRel = NULL;
int nindexes;
Bitmapset* cleanedParts = NULL;
Bitmapset* invisibleParts = NULL;
Oid parentOid = RelationGetRelid(onerel);
if (vacstmt->options & VACOPT_VERBOSE) {
elevel = VERBOSEMESSAGE;
} else {
elevel = DEBUG2;
}
/*
* Before clearing the global partition index of a partition table,
* acquire a AccessShareLock on ADD_PARTITION_ACTION, and make sure that the interval partition
* creation process will not be performed concurrently.
*/
if (ConditionalLockPartition(parentOid, ADD_PARTITION_ACTION, AccessShareLock, PARTITION_SEQUENCE_LOCK)) {
PartitionGetAllInvisibleParts(parentOid, &invisibleParts);
UnlockPartition(parentOid, ADD_PARTITION_ACTION, AccessShareLock, PARTITION_SEQUENCE_LOCK);
}
vac_strategy = bstrategy;
// Open all global indexes of the main partition
GPIOpenGlobalIndexes(onerel, lockmode, &nindexes, &iRel);
IndexBulkDeleteResult** indstats = (IndexBulkDeleteResult**)palloc0(nindexes * sizeof(IndexBulkDeleteResult*));
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
for (int i = 0; i < nindexes; i++) {
GPICleanInvisibleIndex(iRel[i], &indstats[i], &cleanedParts);
vac_update_relstats(
iRel[i], classRel, indstats[i]->num_pages, indstats[i]->num_index_tuples, 0, false, InvalidTransactionId);
index_close(iRel[i], lockmode);
}
heap_close(classRel, RowExclusiveLock);
/*
* Before clearing the global partition index of a partition table,
* acquire a AccessShareLock on ADD_PARTITION_ACTION, and make sure that the interval partition
* creation process will not be performed concurrently.
*/
if (ConditionalLockPartition(parentOid, ADD_PARTITION_ACTION, AccessShareLock, PARTITION_SEQUENCE_LOCK)) {
PartitionSetEnabledClean(parentOid, cleanedParts, invisibleParts, true);
UnlockPartition(parentOid, ADD_PARTITION_ACTION, AccessShareLock, PARTITION_SEQUENCE_LOCK);
} else {
/* Updates reloptions of cleanedParts in pg_partition after gpi lazy_vacuum is executed */
PartitionSetEnabledClean(parentOid, cleanedParts, invisibleParts, false);
}
bms_free(cleanedParts);
bms_free(invisibleParts);
pfree_ext(indstats);
pfree_ext(iRel);
}

View File

@ -67,6 +67,7 @@
#include "utils/timestamp.h"
#include "utils/tqual.h"
#include "utils/syscache.h"
#include "utils/partcache.h"
#include "gstrace/gstrace_infra.h"
#include "gstrace/commands_gstrace.h"
@ -112,6 +113,7 @@ typedef struct LVRelStats {
BlockNumber* new_idx_pages;
double* new_idx_tuples;
bool* idx_estimated;
Oid currVacuumPartOid; /* current lazy vacuum partition oid */
} LVRelStats;
typedef struct ValPrefetchList {
@ -147,7 +149,7 @@ static IndexBulkDeleteResult* lazy_cleanup_index(
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, int tupindex, LVRelStats* vacrelstats);
static void lazy_space_alloc(LVRelStats* vacrelstats, BlockNumber relblocks);
static void lazy_record_dead_tuple(LVRelStats* vacrelstats, ItemPointer itemptr);
static bool lazy_tid_reaped(ItemPointer itemptr, void* state);
static bool lazy_tid_reaped(ItemPointer itemptr, void* state, Oid partOid = InvalidOid);
static int vac_cmp_itemptr(const void* left, const void* right);
/*
@ -164,6 +166,7 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
LVRelStats* vacrelstats = NULL;
Relation* Irel = NULL;
int nindexes;
int nindexes_global;
PGRUsage ru0;
TimestampTz starttime = 0;
long secs;
@ -370,9 +373,11 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
Assert(vacstmt->onepart != NULL);
vacrelstats->old_rel_pages = vacstmt->onepart->pd_part->relpages;
vacrelstats->old_rel_tuples = vacstmt->onepart->pd_part->reltuples;
vacrelstats->currVacuumPartOid = RelationGetRelid(onerel);
} else {
vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
vacrelstats->old_rel_tuples = onerel->rd_rel->reltuples;
vacrelstats->currVacuumPartOid = InvalidOid;
}
vacrelstats->num_index_scans = 0;
vacrelstats->pages_removed = 0;
@ -380,7 +385,7 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
/* Open all indexes of the relation */
if (RelationIsPartition(onerel)) {
vac_open_part_indexes(vacstmt, RowExclusiveLock, &nindexes, &Irel, &indexrel, &indexpart);
vac_open_part_indexes(vacstmt, RowExclusiveLock, &nindexes, &nindexes_global, &Irel, &indexrel, &indexpart);
} else {
vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
}
@ -443,7 +448,8 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
vac_update_pgclass_partitioned_table(
vacstmt->onepartrel, vacstmt->onepartrel->rd_rel->relhasindex, new_frozen_xid);
for (int idx = 0; idx < nindexes; idx++) {
// update stats of local partition indexes
for (int idx = 0; idx < nindexes - nindexes_global; idx++) {
if (vacrelstats->idx_estimated[idx]) {
continue;
}
@ -456,6 +462,24 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
vac_update_pgclass_partitioned_table(indexrel[idx], false, InvalidTransactionId);
}
// update stats of global partition indexes
Assert((nindexes - nindexes_global) >= 0);
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
for (int idx = nindexes - nindexes_global; idx < nindexes; idx++) {
if (vacrelstats->idx_estimated[idx]) {
continue;
}
vac_update_relstats(Irel[idx],
classRel,
vacrelstats->new_idx_pages[idx],
vacrelstats->new_idx_tuples[idx],
0,
false,
InvalidTransactionId);
}
heap_close(classRel, RowExclusiveLock);
} else {
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
vac_update_relstats(
@ -480,7 +504,7 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
/* Done with indexes */
if (RelationIsPartition(onerel)) {
vac_close_part_indexes(nindexes, Irel, indexrel, indexpart, NoLock);
vac_close_part_indexes(nindexes, nindexes_global, Irel, indexrel, indexpart, NoLock);
} else {
vac_close_indexes(nindexes, Irel, NoLock);
}
@ -1154,7 +1178,8 @@ static IndexBulkDeleteResult** lazy_scan_heap(
* cheaper to get rid of it in the next pruning pass than
* to treat it like an indexed tuple.
*/
if (HeapTupleIsHotUpdated(&tuple) || HeapTupleIsHeapOnly(&tuple))
if (HeapTupleIsHotUpdated(&tuple) || HeapTupleIsHeapOnly(&tuple) ||
HeapKeepInvisbleTuple(&tuple, RelationGetDescr(onerel)))
nkeep += 1;
else
tupgone = true; /* we can delete the tuple */
@ -1682,17 +1707,20 @@ static void lazy_record_dead_tuple(LVRelStats* vacrelstats, ItemPointer itemptr)
}
/*
* lazy_tid_reaped() -- is a particular tid deletable?
*
* This has the right signature to be an IndexBulkDeleteCallback.
*
* Assumes dead_tuples array is in sorted order.
* lazy_tid_reaped() -- is a particular tid deletable?
* This has the right signature to be an IndexBulkDeleteCallback.
* Assumes dead_tuples array is in sorted order.
* inputparam partOid is valid only when index is global partition index
*/
static bool lazy_tid_reaped(ItemPointer itemptr, void* state)
static bool lazy_tid_reaped(ItemPointer itemptr, void* state, Oid partOid)
{
LVRelStats* vacrelstats = (LVRelStats*)state;
ItemPointer res;
// global partition index tuple need to check the tuple's partOid is same to current partition
if (partOid != InvalidOid && vacrelstats->currVacuumPartOid != partOid) {
return false;
}
res = (ItemPointer)bsearch((void*)itemptr,
(void*)vacrelstats->dead_tuples,
vacrelstats->num_dead_tuples,

View File

@ -2770,18 +2770,40 @@ static void make_partiterator_pathkey(
itrpath->direction = (BTLessStrategyNumber == pk_strategy ? ForwardScanDirection : BackwardScanDirection);
}
/*
* Check scan path for partition table whether use global partition index
*/
static bool CheckPathUseGlobalPartIndex(Path* path)
{
if (path->pathtype == T_IndexScan || path->pathtype == T_IndexOnlyScan) {
IndexPath* indexPath = (IndexPath*)path;
if (indexPath->indexinfo->isGlobal) {
return true;
}
} else if (path->pathtype == T_BitmapHeapScan) {
BitmapHeapPath* bitmapHeapPath = (BitmapHeapPath*)path;
if (CheckBitmapQualIsGlobalIndex(bitmapHeapPath->bitmapqual)) {
return true;
}
} else {
return false;
}
return false;
}
static Path* create_partiterator_path(PlannerInfo* root, RelOptInfo* rel, Path* path, Relation relation)
{
Path* result = NULL;
switch (path->pathtype) {
case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan:
case T_SeqScan:
case T_CStoreScan:
case T_TsStoreScan:
case T_BitmapHeapScan:
case T_TidScan:
case T_IndexScan:
case T_IndexOnlyScan: {
case T_TidScan: {
PartIteratorPath* itrpath = makeNode(PartIteratorPath);
itrpath->subPath = path;
@ -2848,6 +2870,11 @@ static void try_add_partiterator(PlannerInfo* root, RelOptInfo* rel, RangeTblEnt
continue;
}
/* Use globa partition index */
if (CheckPathUseGlobalPartIndex(path)) {
continue;
}
itrPath = create_partiterator_path(root, rel, path, relation);
/* replace entry in pathlist */

View File

@ -1354,6 +1354,7 @@ void cost_bitmap_heap_scan(
double T;
bool ispartitionedindex = path->parent->isPartitionedTable;
bool partition_index_unusable = false;
bool containGlobalOrLocalIndex = false;
/* Should only be applied to base relations */
AssertEreport(IsA(baserel, RelOptInfo),
@ -1380,9 +1381,15 @@ void cost_bitmap_heap_scan(
if (ispartitionedindex) {
if (!check_bitmap_heap_path_index_unusable(bitmapqual, baserel))
partition_index_unusable = true;
/* If the bitmap path contains Global partition index OR local partition index, set enable_bitmapscan to off */
if (CheckBitmapHeapPathContainGlobalOrLocal(bitmapqual)) {
containGlobalOrLocalIndex = true;
}
}
if (!u_sess->attr.attr_sql.enable_bitmapscan || partition_index_unusable) {
if (!u_sess->attr.attr_sql.enable_bitmapscan || partition_index_unusable ||
containGlobalOrLocalIndex) {
startup_cost += g_instance.cost_cxt.disable_cost;
}

View File

@ -76,6 +76,16 @@ typedef struct {
Bitmapset* clauseids; /* quals+preds represented as a bitmapset */
} PathClauseUsage;
/* Per-choose-bitmapand data used within ChooseBitmapAndWithMultiIndex() */
typedef struct {
Cost costsofar; /* path cost for multi-index-path */
List* qualsofar; /* contain quals for multi-index-path */
Bitmapset* clauseidsofar; /* contain clause set for multi-index-path */
ListCell* lastcell; /* lastcell in paths for quick deletions */
List* paths; /* path list for multi-index-path */
int startPath; /* check value for "AND group leader" */
} ChooseBitmapAndInfo;
static void consider_index_join_clauses(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index,
IndexClauseSet* rclauseset, IndexClauseSet* jclauseset, IndexClauseSet* eclauseset, List** bitindexpaths);
static void consider_index_join_outer_rels(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index,
@ -90,9 +100,10 @@ static void get_index_paths(
PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index, IndexClauseSet* clauses, List** bitindexpaths);
static List* build_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index, IndexClauseSet* clauses,
bool useful_predicate, SaOpControl saop_control, ScanTypeControl scantype);
static List* build_paths_for_OR(PlannerInfo* root, RelOptInfo* rel, List* clauses, List* other_clauses);
static List* build_paths_for_OR(
PlannerInfo* root, RelOptInfo* rel, List* clauses, List* other_clauses, bool justUseGloalPartIndex = false);
static List* drop_indexable_join_clauses(RelOptInfo* rel, List* clauses);
static Path* choose_bitmap_and(PlannerInfo* root, RelOptInfo* rel, List* paths);
static Path* choose_bitmap_and(PlannerInfo* root, RelOptInfo* rel, List* paths, List* globalPartIndexPaths = NIL);
static int path_usage_comparator(const void* a, const void* b);
static Cost bitmap_scan_cost_est(PlannerInfo* root, RelOptInfo* rel, Path* ipath);
static Cost bitmap_and_cost_est(PlannerInfo* root, RelOptInfo* rel, List* paths);
@ -242,6 +253,19 @@ void create_index_paths(PlannerInfo* root, RelOptInfo* rel)
indexpaths = generate_bitmap_or_paths(root, rel, joinorclauses, rel->baserestrictinfo, false);
bitjoinpaths = list_concat(bitjoinpaths, indexpaths);
/*
* Generate BitmapOrPaths for any suitable OR-clauses present in the
* restriction list and joinorclauses just use global partition index.
* Add these to bitindexpaths.
*/
if (rel->isPartitionedTable) {
indexpaths = GenerateBitmapOrPathsUseGPI(root, rel, rel->baserestrictinfo, NIL, false);
bitindexpaths = list_concat(bitindexpaths, indexpaths);
indexpaths = GenerateBitmapOrPathsUseGPI(root, rel, joinorclauses, rel->baserestrictinfo, false);
bitjoinpaths = list_concat(bitjoinpaths, indexpaths);
}
/*
* If we found anything usable, generate a BitmapHeapPath for the most
* promising combination of restriction bitmap index paths. Note there
@ -308,7 +332,6 @@ void create_index_paths(PlannerInfo* root, RelOptInfo* rel)
{
Path* path = (Path*)lfirst(lcp);
Relids p_outers = (Relids)lfirst(lco);
if (bms_is_subset(p_outers, max_outers))
this_path_set = lappend(this_path_set, path);
}
@ -374,7 +397,7 @@ static void consider_index_join_clauses(PlannerInfo* root, RelOptInfo* rel, Inde
* relation itself is also included in the relids set. considered_relids
* lists all relids sets we've already tried.
*/
for (indexcol = 0; indexcol < index->ncolumns; indexcol++) {
for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) {
/* Consider each applicable simple join clause */
considered_clauses += list_length(jclauseset->indexclauses[indexcol]);
consider_index_join_outer_rels(root,
@ -521,7 +544,7 @@ static void get_join_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInf
errorno = memset_s(&clauseset, sizeof(IndexClauseSet), 0, sizeof(clauseset));
securec_check(errorno, "\0", "\0");
for (indexcol = 0; indexcol < index->ncolumns; indexcol++) {
for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) {
ListCell* lc = NULL;
/* First find applicable simple join clauses */
@ -670,6 +693,7 @@ static inline bool index_relation_has_bucket(IndexOptInfo* index)
heap_close(rel, NoLock);
return hasBucket;
}
/*
* build_index_paths
* Given an index and a set of index clauses for it, construct zero
@ -771,7 +795,7 @@ static List* build_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo*
found_clause = false;
found_lower_saop_clause = false;
outer_relids = NULL;
for (indexcol = 0; indexcol < index->ncolumns; indexcol++) {
for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) {
ListCell* lc = NULL;
foreach (lc, clauses->indexclauses[indexcol]) {
@ -930,7 +954,8 @@ static List* build_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo*
* 'clauses' is the current list of clauses (RestrictInfo nodes)
* 'other_clauses' is the list of additional upper-level clauses
*/
static List* build_paths_for_OR(PlannerInfo* root, RelOptInfo* rel, List* clauses, List* other_clauses)
static List* build_paths_for_OR(
PlannerInfo* root, RelOptInfo* rel, List* clauses, List* other_clauses, bool justUseGloalPartIndex)
{
List* result = NIL;
List* all_clauses = NIL; /* not computed till needed */
@ -946,6 +971,11 @@ static List* build_paths_for_OR(PlannerInfo* root, RelOptInfo* rel, List* clause
if (!index->amhasgetbitmap)
continue;
/* Ignore global partition index if caller don't set use global part index flag */
if (index->isGlobal != justUseGloalPartIndex) {
continue;
}
/*
* Ignore partial indexes that do not match the query. If a partial
* index is marked predOK then we know it's OK. Otherwise, we have to
@ -1051,6 +1081,7 @@ List* generate_bitmap_or_paths(
foreach (j, ((BoolExpr*)rinfo->orclause)->args) {
Node* orarg = (Node*)lfirst(j);
List* indlist = NIL;
List* globalIndexList = NIL;
/* OR arguments should be ANDs or sub-RestrictInfos */
if (and_clause(orarg)) {
@ -1059,11 +1090,24 @@ List* generate_bitmap_or_paths(
if (restriction_only)
andargs = drop_indexable_join_clauses(rel, andargs);
indlist = build_paths_for_OR(root, rel, andargs, all_clauses);
indlist = build_paths_for_OR(root, rel, andargs, all_clauses, false);
/* Recurse in case there are sub-ORs */
indlist =
list_concat(indlist, generate_bitmap_or_paths(root, rel, andargs, all_clauses, restriction_only));
indlist = list_concat(
indlist, generate_bitmap_or_paths(root, rel, andargs, all_clauses, restriction_only));
/* If nothing matched this arm, we can't do anything with this OR clause */
if (indlist == NIL) {
pathlist = NIL;
break;
}
if (rel->isPartitionedTable) {
globalIndexList = build_paths_for_OR(root, rel, andargs, all_clauses, true);
/* Recurse in case there are sub-ORs */
globalIndexList = list_concat(globalIndexList,
GenerateBitmapOrPathsUseGPI(root, rel, andargs, all_clauses, restriction_only));
}
} else {
List* orargs = NIL;
@ -1077,14 +1121,124 @@ List* generate_bitmap_or_paths(
if (restriction_only)
orargs = drop_indexable_join_clauses(rel, orargs);
indlist = build_paths_for_OR(root, rel, orargs, all_clauses);
indlist = build_paths_for_OR(root, rel, orargs, all_clauses, false);
/* If nothing matched this arm, we can't do anything with this OR clause */
if (indlist == NIL) {
pathlist = NIL;
break;
}
if (rel->isPartitionedTable) {
globalIndexList = build_paths_for_OR(root, rel, orargs, all_clauses, true);
}
}
/*
* OK, pick the most promising AND combination, and add it to
* pathlist.
*/
bitmapqual = choose_bitmap_and(root, rel, indlist, globalIndexList);
pathlist = lappend(pathlist, bitmapqual);
}
/*
* If we have a match for every arm, then turn them into a
* BitmapOrPath, and add to result list.
*/
if (pathlist != NIL) {
bitmapqual = (Path*)create_bitmap_or_path(root, rel, pathlist);
result = lappend(result, bitmapqual);
}
}
return result;
}
/*
* GenerateBitmapOrPathsUseGlobalPartIndex
* Look through the list of clauses to find OR clauses, and generate
* a BitmapOrPath for each one we can handle that way. Return a list
* of the generated BitmapOrPaths.
*
* other_clauses is a list of additional clauses that can be assumed true
* for the purpose of generating indexquals, but are not to be searched for
* ORs. (See build_paths_for_OR() for motivation.)
*
* If restriction_only is true, ignore OR elements that are join clauses.
* When using this feature it is caller's responsibility that neither clauses
* nor other_clauses contain any join clauses that are not ORs, as we do not
* re-filter those lists.
*
* Notes: Just for partition table and just use global partition index.
*/
List* GenerateBitmapOrPathsUseGPI(
PlannerInfo* root, RelOptInfo* rel, const List* clauses, List* other_clauses, bool restriction_only)
{
List* result = NIL;
List* all_clauses = NIL;
ListCell* lc = NULL;
AssertEreport(rel->isPartitionedTable, MOD_OPT, "rel is incorrect");
/*
* We can use both the current and other clauses as context for
* build_paths_for_OR; no need to remove ORs from the lists.
*/
all_clauses = list_concat(list_copy(clauses), other_clauses);
foreach (lc, clauses) {
RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc);
List* pathlist = NIL;
Path* bitmapqual = NULL;
ListCell* j = NULL;
AssertEreport(IsA(rinfo, RestrictInfo), MOD_OPT, "Restriction clause is incorrect");
/* Ignore RestrictInfos that aren't ORs */
if (!restriction_is_or_clause(rinfo))
continue;
/*
* We must be able to match at least one index to each of the arms of
* the OR, else we can't use it.
*/
pathlist = NIL;
foreach (j, ((BoolExpr*)rinfo->orclause)->args) {
Node* orarg = (Node*)lfirst(j);
List* globalIndexList = NIL;
/* OR arguments should be ANDs or sub-RestrictInfos */
if (and_clause(orarg)) {
List* andargs = ((BoolExpr*)orarg)->args;
if (restriction_only)
andargs = drop_indexable_join_clauses(rel, andargs);
globalIndexList = build_paths_for_OR(root, rel, andargs, all_clauses, true);
/* Recurse in case there are sub-ORs */
globalIndexList = list_concat(
globalIndexList, GenerateBitmapOrPathsUseGPI(root, rel, andargs, all_clauses, restriction_only));
} else {
List* orargs = NIL;
AssertEreport(IsA(orarg, RestrictInfo), MOD_OPT, "Restriction clause is incorrect");
AssertEreport(restriction_is_or_clause((RestrictInfo*)orarg) == false,
MOD_OPT,
"Restriction clause does not contain OR");
orargs = list_make1(orarg);
if (restriction_only)
orargs = drop_indexable_join_clauses(rel, orargs);
globalIndexList = build_paths_for_OR(root, rel, orargs, all_clauses, true);
}
/*
* If nothing matched this arm, we can't do anything with this OR
* clause.
*/
if (indlist == NIL) {
if (globalIndexList == NIL) {
pathlist = NIL;
break;
}
@ -1093,7 +1247,7 @@ List* generate_bitmap_or_paths(
* OK, pick the most promising AND combination, and add it to
* pathlist.
*/
bitmapqual = choose_bitmap_and(root, rel, indlist);
bitmapqual = choose_bitmap_and(root, rel, globalIndexList, NIL);
pathlist = lappend(pathlist, bitmapqual);
}
@ -1135,6 +1289,112 @@ static List* drop_indexable_join_clauses(RelOptInfo* rel, List* clauses)
return result;
}
/*
* As a heuristic, we first check for paths using exactly the same sets of
* WHERE clauses + index predicate conditions, and reject all but the
* cheapest-to-scan in any such group. This primarily gets rid of indexes
* that include the interesting columns but also irrelevant columns. (In
* situations where the DBA has gone overboard on creating variant
* indexes, this can make for a very large reduction in the number of
* paths considered further.)
*/
static PathClauseUsage** GetPathClauseUsage(List* paths, List* clauselist, int* npaths)
{
int tmpPaths = list_length(paths);
PathClauseUsage** pathinfoarray = NULL;
PathClauseUsage* pathinfo = NULL;
int i;
ListCell* l = NULL;
/* Input paths is NIL */
if (tmpPaths == 0) {
*npaths = 0;
return NULL;
}
pathinfoarray = (PathClauseUsage**)palloc(tmpPaths * sizeof(PathClauseUsage*));
tmpPaths = 0;
foreach (l, paths) {
Path* ipath = (Path*)lfirst(l);
pathinfo = classify_index_clause_usage(ipath, &clauselist);
for (i = 0; i < tmpPaths; i++) {
if (bms_equal(pathinfo->clauseids, pathinfoarray[i]->clauseids))
break;
}
if (i < tmpPaths) {
/* duplicate clauseids, keep the cheaper one */
Cost ncost;
Cost ocost;
Selectivity nselec;
Selectivity oselec;
cost_bitmap_tree_node(pathinfo->path, &ncost, &nselec);
cost_bitmap_tree_node(pathinfoarray[i]->path, &ocost, &oselec);
if (ncost < ocost)
pathinfoarray[i] = pathinfo;
} else {
/* not duplicate clauseids, add to array */
pathinfoarray[tmpPaths++] = pathinfo;
}
}
*npaths = tmpPaths;
return pathinfoarray;
}
/*
* For each surviving index, consider it as an "AND group leader", and see
* whether adding on any of the later indexes results in an AND path with
* cheaper total cost than before. Then take the cheapest AND group.
*/
static void ChooseBitmapAndWithMultiIndex(
PlannerInfo* root, RelOptInfo* rel, ChooseBitmapAndInfo* chooseInfo, PathClauseUsage** pathInfos, int npaths)
{
PathClauseUsage* pathinfo = NULL;
ListCell* l = NULL;
for (int j = chooseInfo->startPath; j < npaths; j++) {
Cost newcost;
pathinfo = pathInfos[j];
/* Check for redundancy */
if (bms_overlap(pathinfo->clauseids, chooseInfo->clauseidsofar))
continue; /* consider it redundant */
if (pathinfo->preds != NIL) {
bool redundant = false;
/* we check each predicate clause separately */
foreach (l, pathinfo->preds) {
Node* np = (Node*)lfirst(l);
if (predicate_implied_by(list_make1(np), chooseInfo->qualsofar)) {
redundant = true;
break; /* out of inner foreach loop */
}
}
if (redundant)
continue;
}
/* tentatively add new path to paths, so we can estimate cost */
chooseInfo->paths = lappend(chooseInfo->paths, pathinfo->path);
newcost = bitmap_and_cost_est(root, rel, chooseInfo->paths);
if (newcost < chooseInfo->costsofar || u_sess->attr.attr_sql.force_bitmapand) {
/* keep new path in paths, update subsidiary variables */
chooseInfo->costsofar = newcost;
chooseInfo->qualsofar = list_concat(chooseInfo->qualsofar, list_copy(pathinfo->quals));
chooseInfo->qualsofar = list_concat(chooseInfo->qualsofar, list_copy(pathinfo->preds));
chooseInfo->clauseidsofar = bms_add_members(chooseInfo->clauseidsofar, pathinfo->clauseids);
chooseInfo->lastcell = lnext(chooseInfo->lastcell);
} else {
/* reject new path, remove it from paths list */
chooseInfo->paths = list_delete_cell(chooseInfo->paths, lnext(chooseInfo->lastcell), chooseInfo->lastcell);
}
AssertEreport(lnext(chooseInfo->lastcell) == NULL, MOD_OPT, "Last cell is NULL");
}
}
/*
* choose_bitmap_and
* Given a nonempty list of bitmap paths, AND them into one path.
@ -1146,7 +1406,7 @@ static List* drop_indexable_join_clauses(RelOptInfo* rel, List* clauses)
* The result is either a single one of the inputs, or a BitmapAndPath
* combining multiple inputs.
*/
static Path* choose_bitmap_and(PlannerInfo* root, RelOptInfo* rel, List* paths)
static Path* choose_bitmap_and(PlannerInfo* root, RelOptInfo* rel, List* paths, List* globalPartIndexPaths)
{
int npaths = list_length(paths);
PathClauseUsage** pathinfoarray;
@ -1154,11 +1414,12 @@ static Path* choose_bitmap_and(PlannerInfo* root, RelOptInfo* rel, List* paths)
List* clauselist = NIL;
List* bestpaths = NIL;
Cost bestcost = 0;
int i, j;
ListCell* l = NULL;
int i;
int globalPartPaths = list_length(globalPartIndexPaths);
PathClauseUsage** globalPathinfoarray;
AssertEreport(npaths > 0, MOD_OPT, "Path number is incorrect");
if (npaths == 1)
if (npaths == 1 && globalPartPaths == 0)
return (Path*)linitial(paths); /* easy case */
/*
@ -1214,106 +1475,60 @@ static Path* choose_bitmap_and(PlannerInfo* root, RelOptInfo* rel, List* paths)
* same set of clauses; keep only the cheapest-to-scan of any such groups.
* The surviving paths are put into an array for qsort'ing.
*/
pathinfoarray = (PathClauseUsage**)palloc(npaths * sizeof(PathClauseUsage*));
clauselist = NIL;
npaths = 0;
foreach (l, paths) {
Path* ipath = (Path*)lfirst(l);
pathinfoarray = GetPathClauseUsage(paths, clauselist, &npaths);
pathinfo = classify_index_clause_usage(ipath, &clauselist);
for (i = 0; i < npaths; i++) {
if (bms_equal(pathinfo->clauseids, pathinfoarray[i]->clauseids))
break;
}
if (i < npaths) {
/* duplicate clauseids, keep the cheaper one */
Cost ncost;
Cost ocost;
Selectivity nselec;
Selectivity oselec;
cost_bitmap_tree_node(pathinfo->path, &ncost, &nselec);
cost_bitmap_tree_node(pathinfoarray[i]->path, &ocost, &oselec);
if (ncost < ocost)
pathinfoarray[i] = pathinfo;
} else {
/* not duplicate clauseids, add to array */
pathinfoarray[npaths++] = pathinfo;
}
}
/* Global part index path and local part index path use same clauselist */
globalPathinfoarray = GetPathClauseUsage(globalPartIndexPaths, clauselist, &globalPartPaths);
/* If only one surviving path, we're done */
if (npaths == 1)
if (npaths == 1 && globalPartPaths == 0)
return pathinfoarray[0]->path;
/* Sort the surviving paths by index access cost */
qsort(pathinfoarray, (size_t)npaths, sizeof(PathClauseUsage*), path_usage_comparator);
/* Sort the surviving paths by index access cost for global partition index paths */
if (globalPartPaths > 1) {
qsort(globalPathinfoarray, (size_t)globalPartPaths, sizeof(PathClauseUsage*), path_usage_comparator);
}
/*
* For each surviving index, consider it as an "AND group leader", and see
* whether adding on any of the later indexes results in an AND path with
* cheaper total cost than before. Then take the cheapest AND group.
*/
for (i = 0; i < npaths; i++) {
Cost costsofar;
List* qualsofar = NIL;
Bitmapset* clauseidsofar = NULL;
ListCell* lastcell = NULL;
ChooseBitmapAndInfo chooseInfo;
pathinfo = pathinfoarray[i];
paths = list_make1(pathinfo->path);
costsofar = bitmap_scan_cost_est(root, rel, pathinfo->path);
qualsofar = list_concat(list_copy(pathinfo->quals), list_copy(pathinfo->preds));
clauseidsofar = bms_copy(pathinfo->clauseids);
lastcell = list_head(paths); /* for quick deletions */
chooseInfo.paths = list_make1(pathinfo->path);
chooseInfo.costsofar = bitmap_scan_cost_est(root, rel, pathinfo->path);
chooseInfo.qualsofar = list_concat(list_copy(pathinfo->quals), list_copy(pathinfo->preds));
chooseInfo.clauseidsofar = bms_copy(pathinfo->clauseids);
chooseInfo.startPath = i + 1;
chooseInfo.lastcell = list_head(chooseInfo.paths); /* for quick deletions */
for (j = i + 1; j < npaths; j++) {
Cost newcost;
ChooseBitmapAndWithMultiIndex(root, rel, &chooseInfo, pathinfoarray, npaths);
pathinfo = pathinfoarray[j];
/* Check for redundancy */
if (bms_overlap(pathinfo->clauseids, clauseidsofar))
continue; /* consider it redundant */
if (pathinfo->preds != NIL) {
bool redundant = false;
/* we check each predicate clause separately */
foreach (l, pathinfo->preds) {
Node* np = (Node*)lfirst(l);
if (predicate_implied_by(list_make1(np), qualsofar)) {
redundant = true;
break; /* out of inner foreach loop */
}
}
if (redundant)
continue;
}
/* tentatively add new path to paths, so we can estimate cost */
paths = lappend(paths, pathinfo->path);
newcost = bitmap_and_cost_est(root, rel, paths);
if (newcost < costsofar || u_sess->attr.attr_sql.force_bitmapand) {
/* keep new path in paths, update subsidiary variables */
costsofar = newcost;
qualsofar = list_concat(qualsofar, list_copy(pathinfo->quals));
qualsofar = list_concat(qualsofar, list_copy(pathinfo->preds));
clauseidsofar = bms_add_members(clauseidsofar, pathinfo->clauseids);
lastcell = lnext(lastcell);
} else {
/* reject new path, remove it from paths list */
paths = list_delete_cell(paths, lnext(lastcell), lastcell);
}
AssertEreport(lnext(lastcell) == NULL, MOD_OPT, "Last cell is NULL");
/*
* The local partition index and global partition index form bitmapAnd,
* the final result is the local partition index.
*
* Notes: For global partition index, the start judgment point is 0.
*/
if (globalPartPaths > 0) {
chooseInfo.startPath = 0;
ChooseBitmapAndWithMultiIndex(root, rel, &chooseInfo, globalPathinfoarray, globalPartPaths);
}
/* Keep the cheapest AND-group (or singleton) */
if (i == 0 || costsofar < bestcost) {
bestpaths = paths;
bestcost = costsofar;
if (i == 0 || chooseInfo.costsofar < bestcost) {
bestpaths = chooseInfo.paths;
bestcost = chooseInfo.costsofar;
}
/* some easy cleanup (we don't try real hard though) */
list_free_ext(qualsofar);
list_free_ext(chooseInfo.qualsofar);
if (u_sess->attr.attr_sql.force_bitmapand)
break;
@ -1750,7 +1965,7 @@ static void match_eclass_clauses_to_index(PlannerInfo* root, IndexOptInfo* index
if (!index->rel->has_eclass_joins)
return;
for (indexcol = 0; indexcol < index->ncolumns; indexcol++) {
for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) {
List* clauses = NIL;
clauses = generate_implied_equalities_for_indexcol(root, index, indexcol);
@ -1818,7 +2033,7 @@ static void match_clause_to_index(IndexOptInfo* index, RestrictInfo* rinfo, Inde
return;
/* OK, check each index column for a match */
for (indexcol = 0; indexcol < index->ncolumns; indexcol++) {
for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) {
if (match_clause_to_indexcol(index, indexcol, rinfo)) {
clauseset->indexclauses[indexcol] = list_append_unique_ptr(clauseset->indexclauses[indexcol], rinfo);
clauseset->nonempty = true;
@ -1894,8 +2109,8 @@ static bool match_clause_to_indexcol(IndexOptInfo* index, int indexcol, Restrict
{
Expr* clause = rinfo->clause;
Index index_relid = index->rel->relid;
Oid opfamily = index->opfamily[indexcol];
Oid idxcollation = index->indexcollations[indexcol];
Oid opfamily;
Oid idxcollation;
Node* leftop = NULL;
Node* rightop = NULL;
Relids left_relids;
@ -1904,6 +2119,9 @@ static bool match_clause_to_indexcol(IndexOptInfo* index, int indexcol, Restrict
Oid expr_coll;
bool plain_op = false;
Assert(indexcol < index->nkeycolumns);
opfamily = index->opfamily[indexcol];
idxcollation = index->indexcollations[indexcol];
/*
* Never match pseudoconstants to indexes. (Normally this could not
* happen anyway, since a pseudoconstant clause couldn't contain a Var,
@ -2152,7 +2370,7 @@ static void match_pathkeys_to_index(
* amcanorderbyop. We might need different logic in future for
* other implementations.
*/
for (indexcol = 0; indexcol < index->ncolumns; indexcol++) {
for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) {
Expr* expr = NULL;
expr = match_clause_to_ordering_op(index, indexcol, member->em_expr, pathkey->pk_opfamily);
@ -2203,8 +2421,8 @@ static void match_pathkeys_to_index(
*/
static Expr* match_clause_to_ordering_op(IndexOptInfo* index, int indexcol, Expr* clause, Oid pk_opfamily)
{
Oid opfamily = index->opfamily[indexcol];
Oid idxcollation = index->indexcollations[indexcol];
Oid opfamily;
Oid idxcollation;
Node* leftop = NULL;
Node* rightop = NULL;
Oid expr_op;
@ -2212,6 +2430,10 @@ static Expr* match_clause_to_ordering_op(IndexOptInfo* index, int indexcol, Expr
Oid sortfamily;
bool commuted = false;
Assert(indexcol < index->nkeycolumns);
opfamily = index->opfamily[indexcol];
idxcollation = index->indexcollations[indexcol];
/*
* Clause must be a binary opclause.
*/
@ -2376,8 +2598,12 @@ void check_partial_indexes(PlannerInfo* root, RelOptInfo* rel)
*/
bool eclass_member_matches_indexcol(EquivalenceClass* ec, EquivalenceMember* em, IndexOptInfo* index, int indexcol)
{
Oid curFamily = index->opfamily[indexcol];
Oid curCollation = index->indexcollations[indexcol];
Oid curFamily;
Oid curCollation;
Assert(indexcol < index->nkeycolumns);
curFamily = index->opfamily[indexcol];
curCollation = index->indexcollations[indexcol];
/*
* If it's a btree index, we can reject it if its opfamily isn't
@ -2485,7 +2711,7 @@ bool relation_has_unique_index_for(
* Try to find each index column in the lists of conditions. This is
* O(N^2) or worse, but we expect all the lists to be short.
*/
for (c = 0; c < ind->ncolumns; c++) {
for (c = 0; c < ind->nkeycolumns; c++) {
bool matched = false;
ListCell* lc = NULL;
ListCell* lc2 = NULL;
@ -2556,7 +2782,7 @@ bool relation_has_unique_index_for(
}
/* Matched all columns of this index? */
if (c == ind->ncolumns)
if (c == ind->nkeycolumns)
return true;
}
@ -2916,8 +3142,11 @@ void expand_indexqual_conditions(
RestrictInfo* rinfo = (RestrictInfo*)lfirst(lcc);
int indexcol = lfirst_int(lci);
Expr* clause = rinfo->clause;
Oid curFamily = index->opfamily[indexcol];
Oid curCollation = index->indexcollations[indexcol];
Oid curFamily;
Oid curCollation;
Assert(indexcol < index->nkeycolumns);
curFamily = index->opfamily[indexcol];
curCollation = index->indexcollations[indexcol];
/* First check for boolean cases */
if (IsBooleanOpfamily(curFamily)) {
@ -3248,13 +3477,13 @@ Expr* adjust_rowcompare_for_index(
/*
* The Var side can match any column of the index.
*/
for (i = 0; i < index->ncolumns; i++) {
for (i = 0; i < index->nkeycolumns; i++) {
if (match_index_to_operand(varop, i, index) &&
get_op_opfamily_strategy(expr_op, index->opfamily[i]) == op_strategy &&
IndexCollMatchesExprColl(index->indexcollations[i], lfirst_oid(collids_cell)))
break;
}
if (i >= index->ncolumns)
if (i >= index->nkeycolumns)
break; /* no match found */
/* Add column number to returned list */

View File

@ -112,6 +112,11 @@ bool create_or_index_quals(PlannerInfo* root, RelOptInfo* rel)
orpaths = generate_bitmap_or_paths(root, rel, list_make1(rinfo), rel->baserestrictinfo, true);
if (rel->isPartitionedTable) {
orpaths = list_concat(
orpaths, GenerateBitmapOrPathsUseGPI(root, rel, list_make1(rinfo), rel->baserestrictinfo, true));
}
/* Locate the cheapest OR path */
foreach (k, orpaths) {
BitmapOrPath* path = (BitmapOrPath*)lfirst(k);

View File

@ -391,8 +391,10 @@ Path* get_cheapest_fractional_path_for_pathkeys(List* paths, List* pathkeys, Rel
* If 'scandir' is BackwardScanDirection, build pathkeys representing a
* backwards scan of the index.
*
* The result is canonical, meaning that redundant pathkeys are removed;
* it may therefore have fewer entries than there are index columns.
* We iterate only key columns of covering indexes, since non-key columns
* don't influence index ordering. The result is canonical, meaning that
* redundant pathkeys are removed; it may therefore have fewer entries than
* there are key columns in the index.
*
* Another reason for stopping early is that we may be able to tell that
* an index column's sort order is uninteresting for this query. However,
@ -417,6 +419,14 @@ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection
bool nulls_first = false;
PathKey* cpathkey = NULL;
/*
* INCLUDE columns are stored in index unordered, so they don't
* support ordered index scan.
*/
if (i >= index->nkeycolumns) {
break;
}
/* We assume we don't need to make a copy of the tlist item */
indexkey = indextle->expr;

View File

@ -2843,7 +2843,8 @@ static Plan* create_bitmap_subplan(PlannerInfo* root, Path* bitmapqual, List** q
BitmapIndexScan* btindexscan = (BitmapIndexScan*)plan;
btindexscan->scan.bucketInfo = bitmapqual->parent->bucketInfo;
if (root->isPartIteratorPlanning) {
/* Global partition index don't need set part interator infomartition */
if (root->isPartIteratorPlanning && !CheckIndexPathUseGPI(ipath)) {
btindexscan = (BitmapIndexScan*)plan;
btindexscan->scan.isPartTbl = true;
btindexscan->scan.itrs = root->curItrs;

View File

@ -1582,6 +1582,90 @@ Path* create_tsstorescan_path(PlannerInfo *root, RelOptInfo *rel, int dop)
return pathnode;
}
/*
* Check whether the bitmap heap path just use global partition index.
*/
bool CheckBitmapQualIsGlobalIndex(Path* bitmapqual)
{
bool bitmapqualIsGlobal = true;
if (IsA(bitmapqual, IndexPath)) {
IndexPath* ipath = (IndexPath*)bitmapqual;
bitmapqualIsGlobal = ipath->indexinfo->isGlobal;
} else if (IsA(bitmapqual, BitmapAndPath)) {
BitmapAndPath* apath = (BitmapAndPath*)bitmapqual;
ListCell* l = NULL;
bool allIsGlobal = true;
foreach (l, apath->bitmapquals) {
if (CheckBitmapQualIsGlobalIndex((Path*)lfirst(l)) != allIsGlobal) {
bitmapqualIsGlobal = !allIsGlobal;
break;
}
}
} else if (IsA(bitmapqual, BitmapOrPath)) {
BitmapOrPath* opath = (BitmapOrPath*)bitmapqual;
ListCell* l = NULL;
bool allIsGlobal = true;
foreach (l, opath->bitmapquals) {
if (CheckBitmapQualIsGlobalIndex((Path*)lfirst(l)) != allIsGlobal) {
bitmapqualIsGlobal = !allIsGlobal;
break;
}
}
} else {
ereport(ERROR,
(errmodule(MOD_OPT),
errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized node type: %d", nodeTag(bitmapqual))));
}
return bitmapqualIsGlobal;
}
/*
* Check whether have global partition index or local partition index in bitmap heap path,
* Contains at least one, return true.
*/
bool CheckBitmapHeapPathContainGlobalOrLocal(Path* bitmapqual)
{
bool containGlobalOrLocal = false;
if (IsA(bitmapqual, BitmapAndPath)) {
BitmapAndPath* apath = (BitmapAndPath*)bitmapqual;
ListCell* l = NULL;
foreach (l, apath->bitmapquals) {
containGlobalOrLocal = CheckBitmapHeapPathContainGlobalOrLocal((Path*)lfirst(l));
if (containGlobalOrLocal)
break;
}
} else if (IsA(bitmapqual, BitmapOrPath)) {
BitmapOrPath* opath = (BitmapOrPath*)bitmapqual;
ListCell* head = list_head(opath->bitmapquals);
ListCell* l = NULL;
bool allIsGlobal = CheckBitmapQualIsGlobalIndex((Path*)lfirst(head));
foreach (l, opath->bitmapquals) {
if (l == head) {
continue;
}
if (CheckBitmapQualIsGlobalIndex((Path*)lfirst(l)) != allIsGlobal) {
containGlobalOrLocal = true;
break;
}
}
} else if (IsA(bitmapqual, IndexPath)) {
containGlobalOrLocal = false;
} else {
ereport(ERROR,
(errmodule(MOD_OPT),
errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized node type: %d", nodeTag(bitmapqual))));
}
return containGlobalOrLocal;
}
/*
* Support partiton index unusable.
* Check if the index in bitmap heap path is unusable. Contains at least one, return false.

View File

@ -213,6 +213,7 @@ void get_relation_info(PlannerInfo* root, Oid relationObjectId, bool inhparent,
Form_pg_index index;
IndexOptInfo* info = NULL;
int ncolumns;
int nkeycolumns;
int i;
/*
@ -257,16 +258,22 @@ void get_relation_info(PlannerInfo* root, Oid relationObjectId, bool inhparent,
info->reltablespace = RelationGetForm(indexRelation)->reltablespace;
info->rel = rel;
info->ncolumns = ncolumns = index->indnatts;
info->nkeycolumns = nkeycolumns = index->indnkeyatts;
info->indexkeys = (int*)palloc(sizeof(int) * ncolumns);
info->indexcollations = (Oid*)palloc(sizeof(Oid) * ncolumns);
info->opfamily = (Oid*)palloc(sizeof(Oid) * ncolumns);
info->opcintype = (Oid*)palloc(sizeof(Oid) * ncolumns);
info->indexcollations = (Oid*)palloc(sizeof(Oid) * nkeycolumns);
info->opfamily = (Oid*)palloc(sizeof(Oid) * nkeycolumns);
info->opcintype = (Oid*)palloc(sizeof(Oid) * nkeycolumns);
info->isGlobal = RelationIsGlobalIndex(indexRelation);
for (i = 0; i < ncolumns; i++) {
info->indexkeys[i] = index->indkey.values[i];
info->indexcollations[i] = indexRelation->rd_indcollation[i];
}
for (i = 0; i < nkeycolumns; i++) {
info->opfamily[i] = indexRelation->rd_opfamily[i];
info->opcintype[i] = indexRelation->rd_opcintype[i];
info->indexcollations[i] = indexRelation->rd_indcollation[i];
}
info->relam = indexRelation->rd_rel->relam;
@ -290,10 +297,10 @@ void get_relation_info(PlannerInfo* root, Oid relationObjectId, bool inhparent,
AssertEreport(indexRelation->rd_am->amcanorder, MOD_OPT, "amcanorder is NULL.");
info->sortopfamily = info->opfamily;
info->reverse_sort = (bool*)palloc(sizeof(bool) * ncolumns);
info->nulls_first = (bool*)palloc(sizeof(bool) * ncolumns);
info->reverse_sort = (bool*)palloc(sizeof(bool) * nkeycolumns);
info->nulls_first = (bool*)palloc(sizeof(bool) * nkeycolumns);
for (i = 0; i < ncolumns; i++) {
for (i = 0; i < nkeycolumns; i++) {
int16 opt = indexRelation->rd_indoption[i];
info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
@ -314,11 +321,11 @@ void get_relation_info(PlannerInfo* root, Oid relationObjectId, bool inhparent,
* of current or foreseeable amcanorder index types, it's not
* worth expending more effort on now.
*/
info->sortopfamily = (Oid*)palloc(sizeof(Oid) * ncolumns);
info->reverse_sort = (bool*)palloc(sizeof(bool) * ncolumns);
info->nulls_first = (bool*)palloc(sizeof(bool) * ncolumns);
info->sortopfamily = (Oid*)palloc(sizeof(Oid) * nkeycolumns);
info->reverse_sort = (bool*)palloc(sizeof(bool) * nkeycolumns);
info->nulls_first = (bool*)palloc(sizeof(bool) * nkeycolumns);
for (i = 0; i < ncolumns; i++) {
for (i = 0; i < nkeycolumns; i++) {
int16 opt = indexRelation->rd_indoption[i];
Oid ltopr;
Oid btopfamily;
@ -388,10 +395,10 @@ void get_relation_info(PlannerInfo* root, Oid relationObjectId, bool inhparent,
info->pages = indexRelation->rd_rel->relpages;
} else {
#endif
// non-partitioned index
if (!RelationIsPartitioned(indexRelation)) {
// non-partitioned index or global partition index
if (!RelationIsPartitioned(indexRelation) || RelationIsGlobalIndex(indexRelation)) {
info->pages = RelationGetNumberOfBlocks(indexRelation);
} else { // partitioned index
} else { // partitioned index
ListCell* cell = NULL;
BlockNumber partIndexPages = 0;
int partitionNum = getNumberOfPartitions(relation);
@ -526,6 +533,7 @@ void estimate_rel_size(Relation rel, int32* attr_widths, RelPageType* pages, dou
#endif
/* fall through */
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
case RELKIND_MATVIEW:
/* fall through */
case RELKIND_TOASTVALUE:
@ -536,7 +544,7 @@ void estimate_rel_size(Relation rel, int32* attr_widths, RelPageType* pages, dou
* ESTIMATE_PARTITION_NUMBER non-zero-pages partitions
* multiply total number of partitions
*/
if (RelationIsPartitioned(rel) && !RelationIsColStore(rel)) {
if (RelationIsPartitioned(rel) && !RelationIsColStore(rel) && !RelationIsGlobalIndex(rel)) {
acquireSamplesForPartitionedRelation(rel, AccessShareLock, &curpages, sampledPartitionIds);
} else if (RelationIsValuePartitioned(rel)) {
/*
@ -1300,7 +1308,7 @@ bool has_unique_index(RelOptInfo* rel, AttrNumber attno)
* Also, a multicolumn unique index doesn't allow us to conclude that
* just the specified attr is unique.
*/
if (index->unique && index->ncolumns == 1 && index->indexkeys[0] == attno &&
if (index->unique && index->nkeycolumns == 1 && index->indexkeys[0] == attno &&
(index->indpred == NIL || index->predOK))
return true;
}

View File

@ -139,6 +139,13 @@ bool checkPartitionIndexUnusable(Oid indexOid, int partItrs, PruningResult* prun
heapRelOid = IndexGetRelation(indexOid, false);
heapRel = relation_open(heapRelOid, NoLock);
indexRel = relation_open(indexOid, NoLock);
if (RelationIsGlobalIndex(indexRel)) {
partitionIndexUnusable = indexRel->rd_index->indisusable;
relation_close(heapRel, NoLock);
relation_close(indexRel, NoLock);
return partitionIndexUnusable;
}
if (!RelationIsPartitioned(heapRel) || !RelationIsPartitioned(indexRel) ||
(heapRel->partMap->type != PART_TYPE_RANGE && heapRel->partMap->type != PART_TYPE_INTERVAL)) {
ereport(ERROR,
@ -226,6 +233,14 @@ IndexesUsableType eliminate_partition_index_unusable(Oid indexOid, PruningResult
heapRel = relation_open(heapRelOid, NoLock);
indexRel = relation_open(indexOid, NoLock);
/* Global partition index Just return FULL or NONE */
if (RelationIsGlobalIndex(indexRel)) {
ret = indexRel->rd_index->indisusable ? INDEXES_FULL_USABLE : INDEXES_NONE_USABLE;
relation_close(heapRel, NoLock);
relation_close(indexRel, NoLock);
return ret;
}
if (!RelationIsPartitioned(heapRel) || !RelationIsPartitioned(indexRel)) {
ereport(ERROR,
(errmodule(MOD_OPT),

View File

@ -2030,7 +2030,6 @@ static void do_autovacuum(void)
* nothing worth vacuuming in the database.
*/
DEBUG_MOD_START_TIMER(MOD_AUTOVAC);
;
pgstat_vacuum_stat();
DEBUG_MOD_STOP_TIMER(MOD_AUTOVAC, "AUTOVAC TIMER: Clean up dead statistics collector entries for current DB");

View File

@ -1825,7 +1825,7 @@ void pgstat_initstats(Relation rel)
/* We only count stats for things that have storage */
if (!(relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW || relkind == RELKIND_INDEX ||
relkind == RELKIND_TOASTVALUE || relkind == RELKIND_SEQUENCE)) {
relkind == RELKIND_TOASTVALUE || relkind == RELKIND_SEQUENCE || relkind == RELKIND_GLOBAL_INDEX)) {
rel->pgstat_info = NULL;
return;
}

View File

@ -2916,7 +2916,8 @@ void standard_ProcessUtility(Node* parse_tree, const char* query_string, ParamLi
rel_id = RangeVarGetRelid(rel, lockmode, ((DropStmt*)parse_tree)->missing_ok);
if (OidIsValid(rel_id)) {
Oid check_id = rel_id;
if (get_rel_relkind(rel_id) == RELKIND_INDEX) {
char relkind = get_rel_relkind(rel_id);
if (relkind == RELKIND_INDEX || relkind == RELKIND_GLOBAL_INDEX) {
check_id = IndexGetRelation(rel_id, false);
}
Oid group_oid = get_pgxc_class_groupoid(check_id);
@ -10696,7 +10697,7 @@ ExecNodes* RelidGetExecNodes(Oid rel_id, bool isutility)
/* Binding group_oid for none system table */
group_oid = get_pgxc_class_groupoid(rel_id);
} else if (relkind == RELKIND_INDEX) {
} else if (relkind == RELKIND_INDEX || relkind == RELKIND_GLOBAL_INDEX) {
/*
* For index, there is enry in pgxc_class, so we first get index's
* base rel_id and then fetch group list from pgxc_class

View File

@ -1582,6 +1582,7 @@ void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc
resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
resultRelInfo->ri_RelationDesc = resultRelationDesc;
resultRelInfo->ri_NumIndices = 0;
resultRelInfo->ri_ContainGPI = false;
resultRelInfo->ri_IndexRelationDescs = NULL;
resultRelInfo->ri_IndexRelationInfo = NULL;
/* make a copy so as not to depend on relcache info not changing... */

View File

@ -1112,6 +1112,7 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
IndexInfo** indexInfoArray;
resultRelInfo->ri_NumIndices = 0;
resultRelInfo->ri_ContainGPI = false;
/* fast path if no indexes */
if (!RelationGetForm(resultRelation)->relhasindex)
@ -1155,6 +1156,12 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
index_close(indexDesc, RowExclusiveLock);
continue;
}
/* Check index whether is global parition index, and save */
if (RelationIsGlobalIndex(indexDesc)) {
resultRelInfo->ri_ContainGPI = true;
}
/* extract index key information from the index's pg_index info */
ii = BuildIndexInfo(indexDesc);
@ -1397,6 +1404,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
bool isnull[INDEX_MAX_KEYS];
Relation actualheap;
bool ispartitionedtable = false;
bool containGPI;
List* partitionIndexOidList = NIL;
/*
@ -1407,6 +1415,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
relationDescs = resultRelInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
heapRelation = resultRelInfo->ri_RelationDesc;
containGPI = resultRelInfo->ri_ContainGPI;
/*
* We will use the EState's per-tuple context for evaluating predicates
@ -1427,7 +1436,8 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
if (p == NULL || p->pd_part == NULL) {
return NIL;
}
if (!p->pd_part->indisusable) {
/* If the global partition index is included, the index insertion process needs to continue */
if (!p->pd_part->indisusable && !containGPI) {
numIndices = 0;
}
} else {
@ -1437,6 +1447,15 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
if (bucketId != InvalidBktId) {
searchHBucketFakeRelation(estate->esfRelations, estate->es_query_cxt, actualheap, bucketId, actualheap);
}
/* Partition create in current transaction, set partition and rel reloption wait_clean_gpi */
if (RelationCreateInCurrXact(actualheap) && containGPI && !PartitionEnableWaitCleanGpi(p)) {
/* partition create not set wait_clean_gpi, must use update, and we ensure no concurrency */
PartitionSetWaitCleanGpi(RelationGetRelid(actualheap), true, false);
/* Partitioned create set wait_clean_gpi=n, and we want save it, so just use inplace */
PartitionedSetWaitCleanGpi(RelationGetRelationName(heapRelation), RelationGetRelid(heapRelation), true, true);
}
/*
* for each index, form and insert the index tuple
*/
@ -1461,28 +1480,36 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
continue;
}
if (ispartitionedtable) {
partitionedindexid = RelationGetRelid(indexRelation);
if (!PointerIsValid(partitionIndexOidList)) {
partitionIndexOidList = PartitionGetPartIndexList(p);
// no local indexes available
if (!PointerIsValid(partitionIndexOidList)) {
return NIL;
if (ispartitionedtable && !RelationIsGlobalIndex(indexRelation)) {
/* The GPI index insertion is the same as that of a common table */
if (RelationIsGlobalIndex(indexRelation)) {
if (indexRelation->rd_index->indisusable == false) {
continue;
}
actualindex = indexRelation;
} else {
partitionedindexid = RelationGetRelid(indexRelation);
if (!PointerIsValid(partitionIndexOidList)) {
partitionIndexOidList = PartitionGetPartIndexList(p);
// no local indexes available
if (!PointerIsValid(partitionIndexOidList)) {
return NIL;
}
}
}
indexpartitionid = searchPartitionIndexOid(partitionedindexid, partitionIndexOidList);
indexpartitionid = searchPartitionIndexOid(partitionedindexid, partitionIndexOidList);
searchFakeReationForPartitionOid(estate->esfRelations,
estate->es_query_cxt,
indexRelation,
indexpartitionid,
actualindex,
indexpartition,
RowExclusiveLock);
// skip unusable index
if (false == indexpartition->pd_part->indisusable) {
continue;
searchFakeReationForPartitionOid(estate->esfRelations,
estate->es_query_cxt,
indexRelation,
indexpartitionid,
actualindex,
indexpartition,
RowExclusiveLock);
// skip unusable index
if (false == indexpartition->pd_part->indisusable) {
continue;
}
}
} else {
actualindex = indexRelation;
@ -1617,7 +1644,7 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
Oid* constr_procs = indexInfo->ii_ExclusionProcs;
uint16* constr_strats = indexInfo->ii_ExclusionStrats;
Oid* index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
IndexScanDesc index_scan;
HeapTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
@ -1633,7 +1660,7 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
* If any of the input values are NULL, the constraint check is assumed to
* pass (i.e., we assume the operators are strict).
*/
for (i = 0; i < index_natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
if (isnull[i]) {
return true;
}
@ -1652,7 +1679,7 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
*/
InitDirtySnapshot(DirtySnapshot);
for (i = 0; i < index_natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
ScanKeyEntryInitialize(
&scankeys[i], 0, i + 1, constr_strats[i], InvalidOid, index_collations[i], constr_procs[i], values[i]);
}
@ -1677,8 +1704,8 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
retry:
conflict = false;
found_self = false;
index_scan = index_beginscan(heap, index, &DirtySnapshot, index_natts, 0);
index_rescan(index_scan, scankeys, index_natts, NULL, 0);
index_scan = index_beginscan(heap, index, &DirtySnapshot, indnkeyatts, 0);
index_rescan(index_scan, scankeys, indnkeyatts, NULL, 0);
while ((tup = index_getnext(index_scan, ForwardScanDirection)) != NULL) {
TransactionId xwait;
@ -1795,10 +1822,10 @@ retry:
static bool index_recheck_constraint(
Relation index, Oid* constr_procs, Datum* existing_values, const bool* existing_isnull, Datum* new_values)
{
int index_natts = index->rd_index->indnatts;
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
int i;
for (i = 0; i < index_natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
/* Assume the exclusion operators are strict */
if (existing_isnull[i]) {
return false;

View File

@ -125,6 +125,18 @@ Node* MultiExecBitmapAnd(BitmapAndState* node)
if (result == NULL) {
result = subresult; /* first subplan */
} else {
/*
* If the global tbm intersect with non-global tbm,
* set the final result to non-global tbm.
*
* Notes: This scenario means that the two filter criteria used in the where
* condition of the sql statement, one uses the local partitioned index and
* the other uses the global partitioned index
*/
if (tbm_is_global(result) != tbm_is_global(subresult)) {
tbm_set_global(result, false);
}
tbm_intersect(result, subresult);
tbm_free(subresult);
}

View File

@ -58,6 +58,12 @@ static void bitgetpage(HeapScanDesc scan, TBMIterateResult* tbmres);
static void ExecInitPartitionForBitmapHeapScan(BitmapHeapScanState* scanstate, EState* estate);
static void ExecInitNextPartitionForBitmapHeapScan(BitmapHeapScanState* node);
/* This struct is used for partition switch while prefetch pages */
typedef struct PrefetchNode {
BlockNumber blockNum;
Oid partOid;
} PrefetchNode;
void BitmapHeapFree(BitmapHeapScanState* node)
{
if (node->tbmiterator != NULL) {
@ -176,6 +182,19 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
break;
}
/* Check whether switch partition-fake-rel, use rd_rel save */
if (BitmapNodeNeedSwitchPartRel(node)) {
GPISetCurrPartOid(node->gpi_scan, node->tbmres->partitionOid);
if (!GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
/* If the current partition is invalid, the next page is directly processed */
tbmres = NULL;
continue;
} else {
scan->rs_rd = node->gpi_scan->fakePartRelation;
scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
}
}
#ifdef USE_PREFETCH
if (node->prefetch_pages > 0) {
/* The main iterator has closed the distance by one page */
@ -281,11 +300,17 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
{
BlockNumber* blockList = NULL;
BlockNumber* blockListPtr = NULL;
PrefetchNode* prefetchNode = NULL;
PrefetchNode* prefetchNodePtr = NULL;
int prefetchNow = 0;
int prefetchWindow = node->prefetch_target - node->prefetch_pages;
/* We expect to prefetch at most prefetchWindow pages */
if (prefetchWindow > 0) {
if (tbm_is_global(tbm)) {
prefetchNode = (PrefetchNode*)malloc(sizeof(PrefetchNode) * prefetchWindow);
prefetchNodePtr = prefetchNode;
}
blockList = (BlockNumber*)palloc(sizeof(BlockNumber) * prefetchWindow);
blockListPtr = blockList;
}
@ -299,7 +324,12 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
break;
}
node->prefetch_pages++;
/* we use PrefetchNode here to store relations between blockno and partition Oid */
if (tbm_is_global(tbm)) {
prefetchNodePtr->blockNum = tbmpre->blockno;
prefetchNodePtr->partOid = tbmpre->partitionOid;
prefetchNodePtr++;
}
/* For Async Direct I/O we accumulate a list and send it */
*blockListPtr++ = tbmpre->blockno;
prefetchNow++;
@ -307,17 +337,51 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
/* Send the list we generated and free it */
if (prefetchNow) {
PageListPrefetch(scan->rs_rd, MAIN_FORKNUM, blockList, prefetchNow, 0, 0);
if (tbm_is_global(tbm)) {
/*
* we must save part Oid before switch relation, and recover it after prefetch.
* The reason for this is to assure correctness while getting a new tbmres.
*/
Oid oldOid = GPIGetCurrPartOid(node->gpi_scan);
int blkCount = 0;
Oid prevOid = prefetchNode[0].partOid;
for (int i = 0; i < prefetchNow; i++) {
if (prefetchNode[i].partOid == prevOid) {
blockList[blkCount++] = prefetchNode[i].blockNum;
} else {
GPISetCurrPartOid(node->gpi_scan, prevOid);
if (GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
PageListPrefetch(
node->gpi_scan->fakePartRelation, MAIN_FORKNUM, blockList, blkCount, 0, 0);
}
blkCount = 0;
prevOid = prefetchNode[i].partOid;
blockList[blkCount++] = prefetchNode[i].blockNum;
}
}
GPISetCurrPartOid(node->gpi_scan, prevOid);
if (GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
PageListPrefetch(node->gpi_scan->fakePartRelation, MAIN_FORKNUM, blockList, blkCount, 0, 0);
}
/* recover old oid after prefetch switch */
GPISetCurrPartOid(node->gpi_scan, oldOid);
} else {
PageListPrefetch(scan->rs_rd, MAIN_FORKNUM, blockList, prefetchNow, 0, 0);
}
}
if (prefetchWindow > 0) {
pfree_ext(blockList);
if (tbm_is_global(tbm)) {
pfree_ext(prefetchNode);
}
}
}
ADIO_ELSE()
{
Oid oldOid = GPIGetCurrPartOid(node->gpi_scan);
while (node->prefetch_pages < node->prefetch_target) {
TBMIterateResult* tbmpre = tbm_iterate(prefetch_iterator);
Relation prefetchRel = scan->rs_rd;
if (tbmpre == NULL) {
/* No more pages to prefetch */
tbm_end_iterate(prefetch_iterator);
@ -325,10 +389,21 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
break;
}
node->prefetch_pages++;
if (tbm_is_global(node->tbm) && GPIScanCheckPartOid(node->gpi_scan, tbmpre->partitionOid)) {
GPISetCurrPartOid(node->gpi_scan, tbmpre->partitionOid);
if (!GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
/* If the current partition is invalid, the next page is directly processed */
tbmpre = NULL;
continue;
} else {
prefetchRel = node->gpi_scan->fakePartRelation;
}
}
/* For posix_fadvise() we just send the one request */
PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre->blockno);
PrefetchBuffer(prefetchRel, MAIN_FORKNUM, tbmpre->blockno);
}
/* recover old oid after prefetch switch */
GPISetCurrPartOid(node->gpi_scan, oldOid);
}
ADIO_END();
}
@ -542,9 +617,9 @@ void ExecReScanBitmapHeapScan(BitmapHeapScanState* node)
*/
abs_tbl_endscan(node->ss.ss_currentScanDesc);
/* switch to next partition for scan */
ExecInitNextPartitionForBitmapHeapScan(node);
} else {
/* switch to next partition for scan */
ExecInitNextPartitionForBitmapHeapScan(node);
} else {
/* rescan to release any page pin */
abs_tbl_rescan(node->ss.ss_currentScanDesc, NULL);
}
@ -598,20 +673,26 @@ void ExecEndBitmapHeapScan(BitmapHeapScanState* node)
if (node->ss.ss_currentScanDesc != NULL) {
abs_tbl_endscan(node->ss.ss_currentScanDesc);
}
if (node->gpi_scan != NULL) {
GPIScanEnd(node->gpi_scan);
}
/* close heap scan */
if (node->ss.isPartTbl && PointerIsValid(node->ss.partitions)) {
/* close table partition */
Assert(node->ss.ss_currentPartition);
releaseDummyRelation(&(node->ss.ss_currentPartition));
Assert(node->ss.ss_currentPartition);
releaseDummyRelation(&(node->ss.ss_currentPartition));
releasePartitionList(node->ss.ss_currentRelation, &(node->ss.partitions), NoLock);
releasePartitionList(node->ss.ss_currentRelation, &(node->ss.partitions), NoLock);
}
/*
* close the heap relation.
*/
ExecCloseScanRelation(relation);
}
static inline void InitBitmapHeapScanNextMtd(BitmapHeapScanState* bmstate)
{
@ -659,6 +740,9 @@ BitmapHeapScanState* ExecInitBitmapHeapScan(BitmapHeapScan* node, EState* estate
scanstate->ss.currentSlot = 0;
scanstate->ss.partScanDirection = node->scan.partScanDirection;
/* initilize Global partition index scan information */
GPIScanInit(&scanstate->gpi_scan);
/*
* Miscellaneous initialization
*
@ -687,6 +771,7 @@ BitmapHeapScanState* ExecInitBitmapHeapScan(BitmapHeapScan* node, EState* estate
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
scanstate->ss.ss_currentRelation = currentRelation;
scanstate->gpi_scan->parentRelation = currentRelation;
InitBitmapHeapScanNextMtd(scanstate);
/*

View File

@ -92,6 +92,11 @@ Node* MultiExecBitmapIndexScan(BitmapIndexScanState* node)
} else {
/* XXX should we use less than u_sess->attr.attr_memory.work_mem for this? */
tbm = tbm_create(u_sess->attr.attr_memory.work_mem * 1024L);
/* If bitmapscan uses global partition index, set tbm to global */
if (RelationIsGlobalIndex(node->biss_RelationDesc)) {
tbm_set_global(tbm, true);
}
}
if (hbkt_idx_need_switch_bkt(scandesc, node->ss.ps.hbktScanSlot.currSlot)) {

View File

@ -126,6 +126,10 @@ Node* MultiExecBitmapOr(BitmapOrState* node)
if (result == NULL) {
/* XXX should we use less than u_sess->attr.attr_memory.work_mem for this? */
result = tbm_create(u_sess->attr.attr_memory.work_mem * 1024L);
/* If bitmapscan uses global partition index, set tbm to global */
if (RelationIsGlobalIndex(((BitmapIndexScanState*)subnode)->biss_RelationDesc)) {
tbm_set_global(result, true);
}
}
((BitmapIndexScanState*)subnode)->biss_result = result;
@ -148,6 +152,12 @@ Node* MultiExecBitmapOr(BitmapOrState* node)
if (result == NULL) {
result = subresult; /* first subplan */
} else {
if (tbm_is_global(result) != tbm_is_global(subresult)) {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg(
"do not support bitmap index scan for global index and local index simultaneously.")));
}
tbm_union(result, subresult);
tbm_free(subresult);
}

View File

@ -97,6 +97,15 @@ static TupleTableSlot* IndexOnlyNext(IndexOnlyScanState* node)
* reading the TID; and (2) is satisfied by the acquisition of the
* buffer content lock in order to insert the TID.
*/
if (IndexScanNeedSwitchPartRel(indexScan)) {
/*
* Change the heapRelation in indexScanDesc to Partition Relation of current index
*/
if (!GPIGetNextPartRelation(indexScan->xs_gpi_scan, CurrentMemoryContext, AccessShareLock)) {
continue;
}
indexScan->heapRelation = indexScan->xs_gpi_scan->fakePartRelation;
}
if (!visibilitymap_test(indexScan->heapRelation, ItemPointerGetBlockNumber(tid), &node->ioss_VMBuffer)) {
/*
* Rats, we have to visit the heap to check visibility.
@ -286,16 +295,16 @@ void ExecReScanIndexOnlyScan(IndexOnlyScanState* node)
if (!PointerIsValid(node->ss.partitions)) {
return;
}
Assert(PointerIsValid(node->ioss_ScanDesc));
Assert(PointerIsValid(node->ioss_ScanDesc));
abs_idx_endscan(node->ioss_ScanDesc);
/* initialize to scan the next partition */
ExecInitNextIndexPartitionForIndexScanOnly(node);
/* initialize to scan the next partition */
ExecInitNextIndexPartitionForIndexScanOnly(node);
ExecScanReScan(&node->ss);
/*
* give up rescaning the index if there is no partition to scan
*/
return;
/*
* give up rescaning the index if there is no partition to scan
*/
return;
}
}

View File

@ -41,7 +41,6 @@
#include "gstrace/executer_gstrace.h"
static TupleTableSlot* IndexNext(IndexScanState* node);
static void ExecInitNextPartitionForIndexScan(IndexScanState* node);
/* ----------------------------------------------------------------
@ -818,7 +817,9 @@ void ExecIndexBuildScanKeys(PlanState* plan_state, Relation index, List* quals,
Expr* leftop = NULL; /* expr on lhs of operator */
Expr* rightop = NULL; /* expr on rhs ... */
AttrNumber varattno; /* att number used in scan */
int indnkeyatts;
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
if (IsA(clause, OpExpr)) {
/* indexkey op const or indexkey op expression */
uint32 flags = 0;
@ -839,7 +840,7 @@ void ExecIndexBuildScanKeys(PlanState* plan_state, Relation index, List* quals,
(errcode(ERRCODE_INDEX_CORRUPTED), errmsg("indexqual for OpExpr doesn't have key on left side")));
varattno = ((Var*)leftop)->varattno;
if (varattno < 1 || varattno > index->rd_index->indnatts)
if (varattno < 1 || varattno > indnkeyatts)
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("bogus index qualification for OpExpr, attribute number is %d.", varattno)));
@ -1050,7 +1051,7 @@ void ExecIndexBuildScanKeys(PlanState* plan_state, Relation index, List* quals,
errmsg("indexqual for ScalarArray doesn't have key on left side")));
varattno = ((Var*)leftop)->varattno;
if (varattno < 1 || varattno > index->rd_index->indnatts)
if (varattno < 1 || varattno > indnkeyatts)
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("bogus index qualification for ScalarArray, attribute number is %d.", varattno)));

View File

@ -1405,6 +1405,10 @@ bool UpdateFusion::execute(long max_rows, char* completionTag)
while ((oldtup = m_scan->getTuple()) != NULL) {
if (RelationIsPartitioned(m_scan->m_rel)) {
rel = m_scan->getCurrentRel();
}
CHECK_FOR_INTERRUPTS();
HTSU_Result result;
ItemPointerData update_ctid;
@ -1589,6 +1593,10 @@ bool DeleteFusion::execute(long max_rows, char* completionTag)
m_tupDesc = RelationGetDescr(rel);
while ((oldtup = m_scan->getTuple()) != NULL) {
if (RelationIsPartitioned(m_scan->m_rel)) {
rel = m_scan->getCurrentRel();
}
HTSU_Result result;
ItemPointerData update_ctid;
TransactionId update_xmax;
@ -1806,6 +1814,10 @@ bool SelectForUpdateFusion::execute(long max_rows, char* completionTag)
}
while (nprocessed < (unsigned long)get_rows && (tuple = m_scan->getTuple()) != NULL) {
if (RelationIsPartitioned(m_scan->m_rel)) {
rel = m_scan->getCurrentRel();
}
CHECK_FOR_INTERRUPTS();
heap_deform_tuple(tuple, RelationGetDescr(rel), m_values, m_isnull);

View File

@ -295,6 +295,19 @@ bool IndexFusion::EpqCheck(Datum* values, const bool* isnull)
return true;
}
Relation IndexFusion::getCurrentRel()
{
IndexScanDesc indexScan = GetIndexScanDesc(m_scandesc);
if (indexScan->xs_gpi_scan) {
return indexScan->xs_gpi_scan->fakePartRelation;
} else {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("partitioned relation dose not use global partition index")));
return NULL;
}
}
void IndexFusion::setAttrNo()
{
ListCell* lc = NULL;
@ -326,7 +339,6 @@ IndexScanFusion::IndexScanFusion(IndexScan* node, PlannedStmt* planstmt, ParamLi
m_node = node;
m_keyInit = false;
m_keyNum = list_length(node->indexqual);
;
m_scanKeys = (ScanKey)palloc0(m_keyNum * sizeof(ScanKeyData));
/* init params */
@ -594,6 +606,15 @@ TupleTableSlot* IndexOnlyScanFusion::getTupleSlot()
while ((tid = abs_idx_getnext_tid(m_scandesc, *m_direction)) != NULL) {
HeapTuple tuple = NULL;
IndexScanDesc indexdesc = GetIndexScanDesc(m_scandesc);
if (IndexScanNeedSwitchPartRel(indexdesc)) {
/*
* Change the heapRelation in indexScanDesc to Partition Relation of current index
*/
if (!GPIGetNextPartRelation(indexdesc->xs_gpi_scan, CurrentMemoryContext, AccessShareLock)) {
continue;
}
indexdesc->heapRelation = indexdesc->xs_gpi_scan->fakePartRelation;
}
if (!visibilitymap_test(indexdesc->heapRelation, ItemPointerGetBlockNumber(tid), &m_VMBuffer)) {
tuple = index_fetch_heap(indexdesc);
if (tuple == NULL) {

View File

@ -2755,3 +2755,38 @@ static void slot_deform_cmprs_tuple(TupleTableSlot* slot, uint32 natts)
slot->tts_meta_off = cmprs_off;
slot->tts_slow = true;
}
/*
* Checks whether a dead tuple can be retained
*
* Note: Only the dead tuple of pg_partition needs to be verified in the current code.
*/
bool HeapKeepInvisbleTuple(HeapTuple tuple, TupleDesc tupleDesc, KeepInvisbleTupleFunc checkKeepFunc)
{
static KeepInvisbleOpt keepInvisibleArray[] = {
{PartitionRelationId, Anum_pg_partition_reloptions, PartitionInvisibleMetadataKeep}};
for (int i = 0; i < (int)lengthof(keepInvisibleArray); i++) {
bool isNull = false;
KeepInvisbleOpt keepOpt = keepInvisibleArray[i];
if (keepOpt.tableOid != tuple->t_tableOid) {
return false;
}
Datum checkDatum = fastgetattr(tuple, keepOpt.checkAttnum, tupleDesc, &isNull);
if (isNull) {
return false;
}
if (checkKeepFunc != NULL) {
return checkKeepFunc(checkDatum);
} else if (keepOpt.checkKeepFunc != NULL) {
return keepOpt.checkKeepFunc(checkDatum);
} else {
return false;
}
}
return false;
}

View File

@ -20,6 +20,7 @@
#include "access/heapam.h"
#include "access/itup.h"
#include "access/tuptoaster.h"
#include "utils/rel.h"
/* ----------------------------------------------------------------
* index_ tuple interface routines
@ -387,3 +388,31 @@ IndexTuple CopyIndexTuple(IndexTuple source)
securec_check(rc, "\0", "\0");
return result;
}
/*
* Truncate tailing attributes from given index tuple leaving it with
* new_indnatts number of attributes.
*/
IndexTuple index_truncate_tuple(TupleDesc tupleDescriptor, IndexTuple olditup, int new_indnatts)
{
TupleDesc itupdesc = CreateTupleDescCopyConstr(tupleDescriptor);
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
IndexTuple newitup;
int indnatts = tupleDescriptor->natts;
Assert(indnatts <= INDEX_MAX_KEYS);
Assert(new_indnatts > 0);
Assert(new_indnatts < indnatts);
index_deform_tuple(olditup, tupleDescriptor, values, isnull);
/* form new tuple that will contain only key attributes */
itupdesc->natts = new_indnatts;
newitup = index_form_tuple(itupdesc, values, isnull);
newitup->t_tid = olditup->t_tid;
FreeTupleDesc(itupdesc);
Assert(IndexTupleSize(newitup) <= IndexTupleSize(olditup));
return newitup;
}

View File

@ -356,6 +356,13 @@ static relopt_string string_rel_opts[] = {
NULL,
"",
},
{
{"wait_clean_gpi", "Whether to wait for gpi cleanup", RELOPT_KIND_HEAP },
1,
false,
CheckWaitCleanGpi,
"n",
},
/* list terminator */
{{NULL}}};
@ -867,6 +874,7 @@ bytea* extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
options = heap_reloptions(classForm->relkind, datum, false);
break;
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
options = index_reloptions(amoptions, datum, false);
break;
case RELKIND_FOREIGN_TABLE:
@ -1493,7 +1501,8 @@ bytea* default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
{"end_ctid_internal", RELOPT_TYPE_STRING, offsetof(StdRdOptions, end_ctid_internal)},
{"user_catalog_table", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, user_catalog_table)},
{"hashbucket", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, hashbucket)},
{"on_commit_delete_rows", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, on_commit_delete_rows)}};
{"on_commit_delete_rows", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, on_commit_delete_rows)},
{"wait_clean_gpi", RELOPT_TYPE_STRING, offsetof(StdRdOptions, wait_clean_gpi)}};
options = parseRelOptions(reloptions, validate, kind, &numoptions);
@ -1755,6 +1764,21 @@ void check_append_mode(const char* value)
}
}
/*
* check parameter of wait_clean_gpi . Allows "y", "n"
* and "auto" values.
*/
void CheckWaitCleanGpi(const char* value)
{
if (value == NULL || (strcmp(value, "y") != 0 && strcmp(value, "n") != 0)) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for \"wait_clean_gpi\" option"),
errdetail("Valid values are \"y\", \"n\".")));
}
}
/*
* Brief : Check the compression mode for tablespace.
* Input : val, the compression algorithm value.
@ -1940,7 +1964,7 @@ void ForbidUserToSetDefinedOptions(List* options)
void ForbidOutUsersToSetInnerOptions(List* user_options)
{
static const char* innnerOpts[] = {
"internal_mask", "start_ctid_internal", "end_ctid_internal", "append_mode_internal"};
"internal_mask", "start_ctid_internal", "end_ctid_internal", "append_mode_internal", "wait_clean_gpi"};
if (user_options != NULL) {
int first_invalid_opt = -1;

View File

@ -55,7 +55,7 @@ ItemPointer ginVacuumItemPointers(GinVacuumState* gvs, ItemPointerData* items, i
* Iterate over TIDs array
*/
for (i = 0; i < nitem; i++) {
if (gvs->callback(items + i, gvs->callback_state)) {
if (gvs->callback(items + i, gvs->callback_state, InvalidOid)) {
gvs->result->tuples_removed += 1;
if (!tmpitems) {
/*

View File

@ -186,7 +186,7 @@ Datum gistbulkdelete(PG_FUNCTION_ARGS)
iid = PageGetItemId(page, i);
idxtuple = (IndexTuple)PageGetItem(page, iid);
if (callback(&(idxtuple->t_tid), callback_state)) {
if (callback(&(idxtuple->t_tid), callback_state, InvalidOid)) {
todelete[ntodelete] = i - ntodelete;
ntodelete++;
stats->tuples_removed += 1;

View File

@ -543,7 +543,7 @@ loop_top:
itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, offno));
htup = &(itup->t_tid);
if (callback(htup, callback_state)) {
if (callback(htup, callback_state, InvalidOid)) {
/* mark the item for deletion */
deletable[ndeletable++] = offno;
tuples_removed += 1;

View File

@ -161,7 +161,12 @@ static void initscan(HeapScanDesc scan, ScanKey key, bool is_rescan)
* results for a non-MVCC snapshot, the caller must hold some higher-level
* lock that ensures the interesting tuple(s) won't change.)
*/
nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
if (RelationIsPartitioned(scan->rs_rd)) {
/* partition table just set Initial Value, in BitmapHeapTblNext will update */
nblocks = InvalidBlockNumber;
} else {
nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
}
if (nblocks > 0 && is_range_scan_in_redis) {
ItemPointerData start_ctid;
ItemPointerData end_ctid;
@ -1460,7 +1465,7 @@ Relation heap_open(Oid relationId, LOCKMODE lockmode, int2 bucketid)
Relation r;
r = relation_open(relationId, lockmode, bucketid);
if (r->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(r)) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", RelationGetRelationName(r))));
} else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) {
ereport(ERROR,
@ -1482,7 +1487,7 @@ Relation heap_openrv(const RangeVar* relation, LOCKMODE lockmode)
Relation r;
r = relation_openrv(relation, lockmode);
if (r->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(r)) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", RelationGetRelationName(r))));
} else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) {
ereport(ERROR,
@ -1509,7 +1514,7 @@ Relation heap_openrv_extended(
if (r) {
if (isSupportSynonym && detailInfo != NULL && detailInfo->len > 0) {
/* If has some error detail infos, report it. */
if (r->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(r)) {
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an index", RelationGetRelationName(r)),
@ -1521,7 +1526,7 @@ Relation heap_openrv_extended(
errdetail("%s", detailInfo->data)));
}
} else {
if (r->rd_rel->relkind == RELKIND_INDEX) {
if (RelationIsIndex(r)) {
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", RelationGetRelationName(r))));
}
@ -1601,7 +1606,16 @@ static HeapScanDesc heap_beginscan_internal(Relation relation, Snapshot snapshot
* the scan has a pointer to it. Caller should be holding the rel open
* anyway, so this is redundant in all normal scenarios...
*/
RelationIncrementReferenceCount(relation);
if (!RelationIsPartitioned(relation)) {
RelationIncrementReferenceCount(relation);
} else {
/*
* If the table is a partition table, the current scan must be used by
* bitmapscan to scan tuples using GPI. Therefore,
* the value of rs_rd in the scan is used to store partition-fake-relation.
*/
Assert(is_bitmapscan);
}
/*
* allocate and initialize scan descriptor
@ -1700,7 +1714,9 @@ void heap_endscan(HeapScanDesc scan)
}
/* decrement relation reference count and free scan descriptor storage */
RelationDecrementReferenceCount(scan->rs_rd);
if (!RelationIsPartitioned(scan->rs_rd)) {
RelationDecrementReferenceCount(scan->rs_rd);
}
if (scan->rs_key != NULL) {
pfree(scan->rs_key);
@ -6315,7 +6331,6 @@ static HeapTuple ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool ke
TupleDesc desc = RelationGetDescr(relation);
Oid replidindex;
Relation idx_rel;
TupleDesc idx_desc;
char relreplident;
HeapTuple key_tuple = NULL;
bool nulls[MaxHeapAttributeNumber];
@ -6377,7 +6392,6 @@ static HeapTuple ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool ke
}
idx_rel = RelationIdGetRelation(replidindex);
idx_desc = RelationGetDescr(idx_rel);
/* deform tuple, so we have fast access to columns */
heap_deform_tuple(tp, desc, values, nulls);
@ -6390,7 +6404,7 @@ static HeapTuple ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool ke
* Now set all columns contained in the index to NOT NULL, they cannot
* currently be NULL.
*/
for (natt = 0; natt < idx_desc->natts; natt++) {
for (natt = 0; natt < IndexRelationGetNumberOfKeyAttributes(idx_rel); natt++) {
int attno = idx_rel->rd_index->indkey.values[natt];
if (attno < 0) {

View File

@ -393,6 +393,11 @@ static int heap_prune_chain(
if (HeapTupleSatisfiesVacuum(&tup, oldest_xmin, buffer) == HEAPTUPLE_DEAD &&
!HeapTupleHeaderIsHotUpdated(htup)) {
if (HeapKeepInvisbleTuple(&tup, RelationGetDescr(relation))) {
return ndeleted;
}
heap_prune_record_unused(prstate, rootoffnum);
HeapTupleHeaderAdvanceLatestRemovedXid(&tup, &prstate->latestRemovedXid);
ndeleted++;
@ -485,7 +490,9 @@ static int heap_prune_chain(
}
switch (HeapTupleSatisfiesVacuum(&tup, oldest_xmin, buffer)) {
case HEAPTUPLE_DEAD:
tupdead = true;
if (!HeapKeepInvisbleTuple(&tup, RelationGetDescr(relation))) {
tupdead = true;
}
break;
case HEAPTUPLE_RECENTLY_DEAD:

View File

@ -82,6 +82,14 @@ IndexScanDesc RelationGetIndexScan(Relation index_relation, int nkeys, int norde
scan->numberOfKeys = nkeys;
scan->numberOfOrderBys = norderbys;
/* Initializes global partition index scan's information */
scan->xs_want_ext_oid = RelationIsGlobalIndex(index_relation);
if (scan->xs_want_ext_oid) {
GPIScanInit(&scan->xs_gpi_scan);
} else {
scan->xs_gpi_scan = NULL;
}
/*
* We allocate key workspace here, but it won't get filled until amrescan.
*/
@ -150,7 +158,8 @@ void IndexScanEnd(IndexScanDesc scan)
*
* Construct a string describing the contents of an index entry, in the
* form "(key_name, ...)=(key_value, ...)". This is currently used
* for building unique-constraint and exclusion-constraint error messages.
* for building unique-constraint and exclusion-constraint error messages,
* so only key columns of the index are checked and printed.
*
* Note that if the user does not have permissions to view all of the
* columns involved then a NULL is returned. Returning a partial key seems
@ -166,13 +175,14 @@ char* BuildIndexValueDescription(Relation index_relation, Datum* values, const b
StringInfoData buf;
Form_pg_index idxrec;
HeapTuple ht_idx;
int natts = index_relation->rd_rel->relnatts;
int indnkeyatts;
int i;
int keyno;
Oid indexrelid;
Oid indrelid;
AclResult aclresult;
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index_relation);
/*
* if this relation is a construct from a partition ,we
* should use the parent Oid of Relation
@ -204,7 +214,7 @@ char* BuildIndexValueDescription(Relation index_relation, Datum* values, const b
* No table-level access, so step through the columns in the
* index and make sure the user has SELECT rights on all of them.
*/
for (keyno = 0; keyno < idxrec->indnatts; keyno++) {
for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++) {
AttrNumber attnum = idxrec->indkey.values[keyno];
aclresult = pg_attribute_aclcheck(indrelid, attnum, GetUserId(), ACL_SELECT);
if (aclresult != ACLCHECK_OK) {
@ -220,7 +230,7 @@ char* BuildIndexValueDescription(Relation index_relation, Datum* values, const b
appendStringInfo(&buf, "(%s)=(", pg_get_indexdef_columns(indexrelid, true));
for (i = 0; i < natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
char* val = NULL;
if (isnull[i]) {
@ -321,13 +331,13 @@ SysScanDesc systable_beginscan(
for (i = 0; i < nkeys; i++) {
int j;
for (j = 0; j < irel->rd_index->indnatts; j++) {
for (j = 0; j < IndexRelationGetNumberOfAttributes(irel); j++) {
if (key[i].sk_attno == irel->rd_index->indkey.values[j]) {
key[i].sk_attno = j + 1;
break;
}
}
if (j == irel->rd_index->indnatts)
if (j == IndexRelationGetNumberOfAttributes(irel))
ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("column is not in index")));
}
@ -474,13 +484,13 @@ SysScanDesc systable_beginscan_ordered(
for (i = 0; i < nkeys; i++) {
int j;
for (j = 0; j < index_relation->rd_index->indnatts; j++) {
for (j = 0; j < IndexRelationGetNumberOfAttributes(index_relation); j++) {
if (key[i].sk_attno == index_relation->rd_index->indkey.values[j]) {
key[i].sk_attno = j + 1;
break;
}
}
if (j == index_relation->rd_index->indnatts)
if (j == IndexRelationGetNumberOfAttributes(index_relation))
ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("column is not in index")));
}
@ -536,3 +546,145 @@ HeapTuple systable_getnext_back(SysScanDesc sysscan)
return htup;
}
/* Use global-partition-index-scan access to partition tables */
/* Create hash table for global partition index scan */
static void GPIInitFakeRelTable(GPIScanDesc gpiScan, MemoryContext cxt)
{
HASHCTL ctl;
errno_t errorno;
errorno = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check_c(errorno, "\0", "\0");
ctl.keysize = sizeof(PartRelIdCacheKey);
ctl.entrysize = sizeof(PartRelIdCacheEnt);
ctl.hash = tag_hash;
ctl.hcxt = cxt;
gpiScan->fakeRelationTable = hash_create(
"GPI fakeRelationCache by OID", FAKERELATIONCACHESIZE, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
}
/* Lookup partition information from hash table use key for global partition index scan */
static void GPILookupFakeRelCache(GPIScanDesc gpiScan, PartRelIdCacheKey fakeRelKey)
{
HTAB* fakeRels = gpiScan->fakeRelationTable;
FakeRelationIdCacheLookup(fakeRels, fakeRelKey, gpiScan->fakePartRelation, gpiScan->partition);
}
/* Lookup partition information from hash table use key for global partition index scan */
static void GPIInsertFakeRelCache(GPIScanDesc gpiScan, MemoryContext cxt, LOCKMODE lmode)
{
Oid currPartOid = gpiScan->currPartOid;
Relation parentRel = gpiScan->parentRelation;
HTAB* fakeRels = gpiScan->fakeRelationTable;
Partition partition = NULL;
/* Save search fake relation in gpiScan->fakeRelation */
searchFakeReationForPartitionOid(
fakeRels, cxt, parentRel, currPartOid, gpiScan->fakePartRelation, partition, lmode);
}
/* destroy partition information from hash table */
static void GPIDestroyFakeRelCache(GPIScanDesc gpiScan)
{
FakeRelationCacheDestroy(gpiScan->fakeRelationTable);
gpiScan->fakeRelationTable = NULL;
}
/* Create and fill an GPIScanDesc */
void GPIScanInit(GPIScanDesc* gpiScan)
{
GPIScanDesc gpiInfo = (GPIScanDesc)palloc(sizeof(GPIScanDescData));
gpiInfo->currPartOid = InvalidOid;
gpiInfo->fakePartRelation = NULL;
gpiInfo->invisiblePartMap = NULL;
gpiInfo->parentRelation = NULL;
gpiInfo->fakeRelationTable = NULL;
gpiInfo->partition = NULL;
*gpiScan = gpiInfo;
}
/* Release fake-relation's hash table and GPIScanDesc */
void GPIScanEnd(GPIScanDesc gpiScan)
{
if (gpiScan == NULL) {
return;
}
if (gpiScan->fakeRelationTable != NULL) {
GPIDestroyFakeRelCache(gpiScan);
}
if (gpiScan->invisiblePartMap != NULL) {
bms_free_ext(gpiScan->invisiblePartMap);
}
pfree_ext(gpiScan);
}
/* Set global partition index work partition oid */
void GPISetCurrPartOid(GPIScanDesc gpiScan, Oid partOid)
{
if (gpiScan == NULL) {
ereport(ERROR, (errmsg("gpiScan is null, when set partition oid")));
}
gpiScan->currPartOid = partOid;
}
/* Get global partition index work partition oid */
Oid GPIGetCurrPartOid(const GPIScanDesc gpiScan)
{
if (gpiScan == NULL) {
ereport(ERROR, (errmsg("gpiScan is null, when get partition oid")));
}
return gpiScan->currPartOid;
}
/*
* This gpiScan is used to switch the fake-relation of a partition
* based on the partoid in the GPI when the GPI is used to scan data.
*
* Notes: return true means partition's fake-relation can use gpiScan->fakeRelationTable switch,
* return false means current parition is invisible, shoud not switch.
*/
bool GPIGetNextPartRelation(GPIScanDesc gpiScan, MemoryContext cxt, LOCKMODE lmode)
{
bool result = true;
PartStatus currStatus;
PartRelIdCacheKey fakeRelKey = {gpiScan->currPartOid, InvalidBktId};
Assert(OidIsValid(gpiScan->currPartOid));
if (!PointerIsValid(gpiScan->fakeRelationTable)) {
GPIInitFakeRelTable(gpiScan, cxt);
}
/* First check invisible partition oid's bitmapset */
if (bms_is_member(gpiScan->currPartOid, gpiScan->invisiblePartMap)) {
gpiScan->fakePartRelation = NULL;
gpiScan->partition = NULL;
return false;
}
/* Obtains information about the current partition from the hash table */
GPILookupFakeRelCache(gpiScan, fakeRelKey);
/* If the fakePartRelation field is empty, need get partition information from pg_partition */
if (!RelationIsValid(gpiScan->fakePartRelation)) {
Assert(gpiScan->partition == NULL);
/* Get current partition status in GPI */
currStatus = PartitionGetMetadataStatus(gpiScan->currPartOid, false);
/* Just save partition status if current partition metadata is invisible */
if (currStatus == PART_METADATA_INVISIBLE) {
/* If current partition metadata is invisible, add current partition oid into invisiblePartMap */
gpiScan->invisiblePartMap = bms_add_member(gpiScan->invisiblePartMap, gpiScan->currPartOid);
result = false;
} else {
/* If current partition metadata is invisible, add current partition oid into fakeRelationTable */
GPIInsertFakeRelCache(gpiScan, cxt, lmode);
result = true;
}
}
return result;
}

View File

@ -177,8 +177,7 @@ Relation index_open(Oid relation_id, LOCKMODE lockmode, int2 bucket_id)
Relation r;
r = relation_open(relation_id, lockmode, bucket_id);
if (r->rd_rel->relkind != RELKIND_INDEX)
if (r->rd_rel->relkind != RELKIND_INDEX && r->rd_rel->relkind != RELKIND_GLOBAL_INDEX)
ereport(
ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", RelationGetRelationName(r))));
return r;
@ -261,6 +260,10 @@ IndexScanDesc index_beginscan(
scan->heapRelation = heap_relation;
scan->xs_snapshot = snapshot;
if (scan->xs_want_ext_oid) {
scan->xs_gpi_scan->parentRelation = heap_relation;
}
return scan;
}
@ -378,6 +381,10 @@ void index_endscan(IndexScanDesc scan)
/* Release index refcount acquired by index_beginscan */
RelationDecrementReferenceCount(scan->indexRelation);
if (scan->xs_gpi_scan != NULL) {
GPIScanEnd(scan->xs_gpi_scan);
}
/* Release the scan data structure itself */
IndexScanEnd(scan);
}
@ -608,8 +615,19 @@ HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction)
/* Time to fetch the next TID from the index */
tid = index_getnext_tid(scan, direction);
/* If we're out of index entries, we're done */
if (tid == NULL)
if (tid == NULL) {
break;
}
if (IndexScanNeedSwitchPartRel(scan)) {
/*
* Change the heapRelation in indexScanDesc to Partition Relation of current index
*/
if (!GPIGetNextPartRelation(scan->xs_gpi_scan, CurrentMemoryContext, AccessShareLock)) {
continue;
}
scan->heapRelation = scan->xs_gpi_scan->fakePartRelation;
}
} else {
/*
* We are resuming scan of a HOT chain after having returned an

View File

@ -474,6 +474,23 @@ original search scankey is consulted as each index entry is sequentially
scanned to decide whether to return the entry and whether the scan can
stop (see _bt_checkkeys()).
We use term "pivot" index tuples to distinguish tuples which don't point
to heap tuples, but rather used for tree navigation. Pivot tuples includes
all tuples on non-leaf pages and high keys on leaf pages. Note that pivot
index tuples are only used to represent which part of the key space belongs
on each page, and can have attribute values copied from non-pivot tuples
that were deleted and killed by VACUUM some time ago. In principle, we could
truncate away attributes that are not needed for a page high key during a leaf
page split, provided that the remaining attributes distinguish the last index
tuple on the post-split left page as belonging on the left page, and the first
index tuple on the post-split right page as belonging on the right page. This
optimization is sometimes called suffix truncation, and may appear in a future
release. Since the high key is subsequently reused as the downlink in the
parent page for the new right page, suffix truncation can increase index
fan-out considerably by keeping pivot tuples short. INCLUDE indexes similarly
truncate away non-key attributes at the time of a leaf page split,
increasing fan-out.
Notes About Data Representation
-------------------------------

View File

@ -22,6 +22,7 @@
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "access/nbtree.h"
#include "access/genam.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
@ -51,7 +52,7 @@ typedef struct {
static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, Buffer buf, OffsetNumber offset,
ScanKey itup_scankey, IndexUniqueCheck checkUnique, bool* is_unique);
ScanKey itup_scankey, IndexUniqueCheck checkUnique, bool* is_unique, GPIScanDesc gpiDesc);
static void _bt_findinsertloc(Relation rel, Buffer* bufptr, OffsetNumber* offsetptr, int keysz, ScanKey scankey,
IndexTuple newtup, BTStack stack, Relation heapRel);
static void _bt_insertonpg(
@ -63,7 +64,7 @@ static OffsetNumber _bt_findsplitloc(
static void _bt_checksplitloc(FindSplitData* state, OffsetNumber firstoldonright, bool newitemonleft,
int dataitemstoleft, Size firstoldonrightsz);
static bool _bt_pgaddtup(Page page, Size itemsize, IndexTuple itup, OffsetNumber itup_off);
static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum, int keysz, ScanKey scankey);
static bool _bt_isequal(Relation idxrel, Page page, OffsetNumber offnum, int keysz, ScanKey scankey);
static void _bt_vacuum_one_page(Relation rel, Buffer buffer, Relation heapRel);
static void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf, BTStack stack, bool is_root, bool is_only);
@ -88,18 +89,26 @@ static void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf, BTStack sta
bool _bt_doinsert(Relation rel, IndexTuple itup, IndexUniqueCheck checkUnique, Relation heapRel)
{
bool is_unique = false;
int natts = rel->rd_rel->relnatts;
int indnkeyatts;
ScanKey itup_scankey;
BTStack stack;
Buffer buf;
OffsetNumber offset;
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
Assert(indnkeyatts != 0);
/* we need an insertion scan key to do our search, so build one */
itup_scankey = _bt_mkscankey(rel, itup);
GPIScanDesc gpiScan = NULL;
if (RelationIsGlobalIndex(rel)) {
GPIScanInit(&gpiScan);
gpiScan->parentRelation = relation_open(heapRel->parentId, AccessShareLock);
}
top:
/* find the first page containing this key */
stack = _bt_search(rel, natts, itup_scankey, false, &buf, BT_WRITE);
stack = _bt_search(rel, indnkeyatts, itup_scankey, false, &buf, BT_WRITE);
offset = InvalidOffsetNumber;
@ -114,7 +123,7 @@ top:
* move right in the tree. See Lehman and Yao for an excruciatingly
* precise description.
*/
buf = _bt_moveright(rel, buf, natts, itup_scankey, false, true, stack, BT_WRITE);
buf = _bt_moveright(rel, buf, indnkeyatts, itup_scankey, false, true, stack, BT_WRITE);
/*
* If we're not allowing duplicates, make sure the key isn't already in
@ -140,8 +149,8 @@ top:
if (checkUnique != UNIQUE_CHECK_NO) {
TransactionId xwait;
offset = _bt_binsrch(rel, buf, natts, itup_scankey, false);
xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey, checkUnique, &is_unique);
offset = _bt_binsrch(rel, buf, indnkeyatts, itup_scankey, false);
xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey, checkUnique, &is_unique, gpiScan);
if (TransactionIdIsValid(xwait)) {
/* Have to wait for the other guy ... */
@ -152,7 +161,7 @@ top:
goto top;
}
}
if (checkUnique != UNIQUE_CHECK_EXISTING) {
/*
* The only conflict predicate locking cares about for indexes is when
@ -160,20 +169,26 @@ top:
* actual location of the insert is hard to predict because of the
* random search used to prevent O(N^2) performance when there are
* many duplicate entries, we can just use the "first valid" page.
* This reasoning also applies to INCLUDE indexes, whose extra
* attributes are not considered part of the key space.
*/
CheckForSerializableConflictIn(rel, NULL, buf);
/* do the insertion */
_bt_findinsertloc(rel, &buf, &offset, natts, itup_scankey, itup, stack, heapRel);
_bt_findinsertloc(rel, &buf, &offset, indnkeyatts, itup_scankey, itup, stack, heapRel);
_bt_insertonpg(rel, buf, InvalidBuffer, stack, itup, offset, false);
} else {
/* just release the buffer */
_bt_relbuf(rel, buf);
}
/* be tidy */
_bt_freestack(stack);
_bt_freeskey(itup_scankey);
if (gpiScan != NULL) { // means rel switch happened
relation_close(gpiScan->parentRelation, AccessShareLock);
GPIScanEnd(gpiScan);
}
return is_unique;
}
@ -194,17 +209,16 @@ top:
* core code must redo the uniqueness check later.
*/
static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, Buffer buf, OffsetNumber offset,
ScanKey itup_scankey, IndexUniqueCheck checkUnique, bool* is_unique)
ScanKey itup_scankey, IndexUniqueCheck checkUnique, bool* is_unique, GPIScanDesc gpiScan)
{
TupleDesc itupdesc = RelationGetDescr(rel);
int natts = rel->rd_rel->relnatts;
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
SnapshotData SnapshotDirty;
OffsetNumber maxoff;
Page page;
BTPageOpaqueInternal opaque;
Buffer nbuf = InvalidBuffer;
bool found = false;
Relation tarRel = heapRel;
/* Assume unique until we find a duplicate */
*is_unique = true;
@ -221,6 +235,7 @@ static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation he
ItemId curitemid;
IndexTuple curitup;
BlockNumber nblkno;
bool isNull;
/*
* make sure the offset points to an actual item before trying to
@ -252,21 +267,53 @@ static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation he
* in real comparison, but only for ordering/finding items on
* pages. - vadim 03/24/97
*/
if (!_bt_isequal(itupdesc, page, offset, natts, itup_scankey))
if (!_bt_isequal(rel, page, offset, indnkeyatts, itup_scankey))
break; /* we're past all the equal tuples */
/* okay, we gotta fetch the heap tuple ... */
curitup = (IndexTuple)PageGetItem(page, curitemid);
htid = curitup->t_tid;
Oid curPartOid;
Datum datum;
if (RelationIsGlobalIndex(rel)) {
datum =
index_getattr(curitup, IndexRelationGetNumberOfAttributes(rel), RelationGetDescr(rel), &isNull);
curPartOid = DatumGetUInt32(datum);
Assert(isNull == false);
if (curPartOid != gpiScan->currPartOid) {
GPISetCurrPartOid(gpiScan, curPartOid);
if (!GPIGetNextPartRelation(gpiScan, CurrentMemoryContext, AccessShareLock)) {
ItemIdMarkDead(curitemid);
opaque->btpo_flags |= BTP_HAS_GARBAGE;
if (nbuf != InvalidBuffer) {
MarkBufferDirtyHint(nbuf, true);
} else {
MarkBufferDirtyHint(buf, true);
}
goto next;
} else {
tarRel = gpiScan->fakePartRelation;
}
}
}
/*
* If we are doing a recheck, we expect to find the tuple we
* are rechecking. It's not a duplicate, but we have to keep
* scanning.
* scanning. For global partition index, part oid in index tuple
* is supposed to be same as heapRel oid, add check in case
* abnormal condition.
*/
if (checkUnique == UNIQUE_CHECK_EXISTING && ItemPointerCompare(&htid, &itup->t_tid) == 0) {
if (RelationIsGlobalIndex(rel)) {
if (curPartOid != heapRel->rd_id) {
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("failed to re-find tuple within GPI \"%s\"", RelationGetRelationName(rel))));
}
}
found = true;
} else if (heap_hot_search(&htid, heapRel, &SnapshotDirty, &all_dead)) {
} else if (heap_hot_search(&htid, tarRel, &SnapshotDirty, &all_dead)) {
/*
* We check the whole HOT-chain to see if there is any tuple
* that satisfies SnapshotDirty. This is necessary because we
@ -366,6 +413,8 @@ static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation he
* everyone, so we may as well mark the index entry
* killed.
*/
/* okay, we gotta fetch the heap tuple ... */
curitup = (IndexTuple)PageGetItem(page, curitemid);
ItemIdMarkDead(curitemid);
opaque->btpo_flags |= BTP_HAS_GARBAGE;
@ -381,6 +430,7 @@ static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation he
}
}
next:
/*
* Advance to next tuple to continue checking.
*/
@ -390,7 +440,7 @@ static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation he
/* If scankey == hikey we gotta check the next page too */
if (P_RIGHTMOST(opaque))
break;
if (!_bt_isequal(itupdesc, page, P_HIKEY, natts, itup_scankey))
if (!_bt_isequal(rel, page, P_HIKEY, indnkeyatts, itup_scankey))
break;
/* Advance to next non-dead page --- there must be one */
for (;;) {
@ -520,7 +570,6 @@ static void _bt_findinsertloc(Relation rel, Buffer* bufptr, OffsetNumber* offset
*/
if (P_ISLEAF(lpageop) && P_HAS_GARBAGE(lpageop)) {
_bt_vacuum_one_page(rel, buf, heapRel);
/*
* remember that we vacuumed this page, because that makes the
* hint supplied by the caller invalid
@ -842,6 +891,9 @@ static Buffer _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firs
bool isroot = false;
bool isleaf = false;
errno_t rc;
IndexTuple lefthikey;
int indnatts = IndexRelationGetNumberOfAttributes(rel);
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
/* Acquire a new page to split into */
rbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
@ -909,6 +961,7 @@ static Buffer _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firs
itemid = PageGetItemId(origpage, P_HIKEY);
itemsz = ItemIdGetLength(itemid);
item = (IndexTuple)PageGetItem(origpage, itemid);
Assert(BTreeTupleGetNAtts(item, rel) == indnkeyatts);
if (PageAddItem(rightpage, (Item)item, itemsz, rightoff, false, false) == InvalidOffsetNumber) {
rc = memset_s(rightpage, BLCKSZ, 0, BufferGetPageSize(rbuf));
securec_check(rc, "", "");
@ -937,7 +990,23 @@ static Buffer _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firs
itemsz = ItemIdGetLength(itemid);
item = (IndexTuple)PageGetItem(origpage, itemid);
}
if (PageAddItem(leftpage, (Item)item, itemsz, leftoff, false, false) == InvalidOffsetNumber) {
/*
* We must truncate included attributes of the "high key" item, before
* insert it onto the leaf page. It's the only point in insertion
* process, where we perform truncation. All other functions work with
* this high key and do not change it.
*/
if (indnatts != indnkeyatts && isleaf) {
lefthikey = _bt_nonkey_truncate(rel, item);
itemsz = IndexTupleSize(lefthikey);
itemsz = MAXALIGN(itemsz);
} else {
lefthikey = item;
}
Assert(BTreeTupleGetNAtts(lefthikey, rel) == indnkeyatts);
if (PageAddItem(leftpage, (Item)lefthikey, itemsz, leftoff, false, false) == InvalidOffsetNumber) {
rc = memset_s(rightpage, BLCKSZ, 0, BufferGetPageSize(rbuf));
securec_check(rc, "", "");
ereport(ERROR,
@ -948,6 +1017,11 @@ static Buffer _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firs
}
leftoff = OffsetNumberNext(leftoff);
/* be tidy */
if (lefthikey != item) {
pfree(lefthikey);
}
/*
* Now transfer all the data items to the appropriate page.
*
@ -1179,10 +1253,11 @@ static Buffer _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firs
(char*)rightpage + ((PageHeader)rightpage)->pd_upper,
((PageHeader)rightpage)->pd_special - ((PageHeader)rightpage)->pd_upper);
if (isroot)
if (isroot) {
xlinfo = newitemonleft ? XLOG_BTREE_SPLIT_L_ROOT : XLOG_BTREE_SPLIT_R_ROOT;
else
} else {
xlinfo = newitemonleft ? XLOG_BTREE_SPLIT_L : XLOG_BTREE_SPLIT_R;
}
recptr = XLogInsert(RM_BTREE_ID, xlinfo);
@ -1386,7 +1461,12 @@ static void _bt_checksplitloc(FindSplitData* state, OffsetNumber firstoldonright
/*
* The first item on the right page becomes the high key of the left page;
* therefore it counts against left space as well as right space.
* therefore it counts against left space as well as right space. When
* index has included attribues, then those attributes of left page high
* key will be truncate leaving that page with slightly more free space.
* However, that shouldn't affect our ability to find valid split
* location, because anyway split location should exists even without high
* key truncation.
*/
leftfree -= firstrightitemsz;
@ -1497,19 +1577,19 @@ static void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf, BTStack sta
stack = &fakestack;
stack->bts_blkno = BufferGetBlockNumber(pbuf);
stack->bts_offset = InvalidOffsetNumber;
/* bts_btentry will be initialized below */
stack->bts_btentry = InvalidBlockNumber;
stack->bts_parent = NULL;
_bt_relbuf(rel, pbuf);
}
/* get high key from left page == lowest key on new right page */
/* get high key from left page == lower bound for new right page */
ritem = (IndexTuple)PageGetItem(page, PageGetItemId(page, P_HIKEY));
/* form an index tuple that points at the new right page
* assure that memory is properly allocated, prevent from missing log of insert parent */
START_CRIT_SECTION();
new_item = CopyIndexTuple(ritem);
ItemPointerSet(&(new_item->t_tid), rbknum, P_HIKEY);
BTreeInnerTupleSetDownLink(new_item, rbknum);
END_CRIT_SECTION();
/*
@ -1519,7 +1599,7 @@ static void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf, BTStack sta
* want to find parent pointing to where we are, right ? - vadim
* 05/27/97
*/
ItemPointerSet(&(stack->bts_btentry.t_tid), bknum, P_HIKEY);
stack->bts_btentry = bknum;
pbuf = _bt_getstackbuf(rel, stack, BT_WRITE);
/* Now we can unlock the right child. The left child will be unlocked
@ -1663,7 +1743,7 @@ Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access)
for (offnum = start; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) {
itemid = PageGetItemId(page, offnum);
item = (IndexTuple)PageGetItem(page, itemid);
if (BTEntrySame(item, &stack->bts_btentry)) {
if (BTreeInnerTupleGetDownLink(item) == stack->bts_btentry) {
/* Return accurate pointer to where link is now */
stack->bts_blkno = blkno;
stack->bts_offset = offnum;
@ -1675,7 +1755,7 @@ Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access)
for (offnum = OffsetNumberPrev(start); offnum >= minoff; offnum = OffsetNumberPrev(offnum)) {
itemid = PageGetItemId(page, offnum);
item = (IndexTuple)PageGetItem(page, itemid);
if (BTEntrySame(item, &stack->bts_btentry)) {
if (BTreeInnerTupleGetDownLink(item) == stack->bts_btentry) {
/* Return accurate pointer to where link is now */
stack->bts_blkno = blkno;
stack->bts_offset = offnum;
@ -1779,7 +1859,8 @@ static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
left_item_sz = sizeof(IndexTupleData);
left_item = (IndexTuple)palloc(left_item_sz);
left_item->t_info = (unsigned short)left_item_sz;
ItemPointerSet(&(left_item->t_tid), lbkno, P_HIKEY);
BTreeInnerTupleSetDownLink(left_item, lbkno);
BTreeTupleSetNAtts(left_item, 0);
/*
* Create downlink item for right page. The key for it is obtained from
@ -1789,7 +1870,7 @@ static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
right_item_sz = ItemIdGetLength(itemid);
item = (IndexTuple)PageGetItem(lpage, itemid);
right_item = CopyIndexTuple(item);
ItemPointerSet(&(right_item->t_tid), rbkno, P_HIKEY);
BTreeInnerTupleSetDownLink(right_item, rbkno);
/* set btree special data */
rootopaque = (BTPageOpaqueInternal)PageGetSpecialPointer(rootpage);
@ -1908,6 +1989,7 @@ static bool _bt_pgaddtup(Page page, Size itemsize, IndexTuple itup, OffsetNumber
if (!P_ISLEAF(opaque) && itup_off == P_FIRSTDATAKEY(opaque)) {
trunctuple = *itup;
trunctuple.t_info = sizeof(IndexTupleData);
BTreeTupleSetNAtts(&trunctuple, 0);
itup = &trunctuple;
itemsize = sizeof(IndexTupleData);
}
@ -1924,8 +2006,9 @@ static bool _bt_pgaddtup(Page page, Size itemsize, IndexTuple itup, OffsetNumber
* This is very similar to _bt_compare, except for NULL handling.
* Rule is simple: NOT_NULL not equal NULL, NULL not equal NULL too.
*/
static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum, int keysz, ScanKey scankey)
static bool _bt_isequal(Relation idxrel, Page page, OffsetNumber offnum, int keysz, ScanKey scankey)
{
TupleDesc itupdesc = RelationGetDescr(idxrel);
IndexTuple itup;
int i;
@ -1933,6 +2016,15 @@ static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum, int
Assert(P_ISLEAF((BTPageOpaqueInternal)PageGetSpecialPointer(page)));
itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, offnum));
/*
* Index tuple shouldn't be truncated. Despite we technically could
* compare truncated tuple as well, this function should be only called
* for regular non-truncated leaf tuples and P_HIKEY tuple on
* rightmost leaf page.
*/
Assert((P_RIGHTMOST((BTPageOpaqueInternal)PageGetSpecialPointer(page)) || offnum != P_HIKEY)
? BTreeTupleGetNAtts(itup, idxrel) == itupdesc->natts
: true);
for (i = 1; i <= keysz; i++) {
AttrNumber attno;
@ -1996,4 +2088,3 @@ static void _bt_vacuum_one_page(Relation rel, Buffer buffer, Relation heapRel)
* the page.
*/
}

View File

@ -776,7 +776,11 @@ void _bt_delitems_delete(const Relation rel, Buffer buf, OffsetNumber* itemnos,
XLogRecPtr recptr;
xl_btree_delete xlrec_delete;
RelFileNodeRelCopy(xlrec_delete.hnode, heapRel->rd_node);
if (RelationIsValid(heapRel)) {
RelFileNodeRelCopy(xlrec_delete.hnode, heapRel->rd_node);
} else {
xlrec_delete.hnode = {InvalidOid, InvalidOid, InvalidOid};
}
xlrec_delete.nitems = nitems;
@ -790,9 +794,11 @@ void _bt_delitems_delete(const Relation rel, Buffer buf, OffsetNumber* itemnos,
* server.
*/
XLogRegisterData((char*)itemnos, nitems * sizeof(OffsetNumber));
recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_DELETE, false, heapRel->rd_node.bucketNode);
if (RelationIsValid(heapRel)) {
recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_DELETE, false, heapRel->rd_node.bucketNode);
} else {
recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_DELETE, false, InvalidBktId);
}
PageSetLSN(page, recptr);
}
@ -833,7 +839,7 @@ static bool _bt_parent_deletion_safe(Relation rel, BlockNumber target, BTStack s
return true;
/* Locate the parent's downlink (updating the stack entry if needed) */
ItemPointerSet(&(stack->bts_btentry.t_tid), target, P_HIKEY);
stack->bts_btentry = target;
pbuf = _bt_getstackbuf(rel, stack, BT_READ);
if (pbuf == InvalidBuffer)
ereport(ERROR,
@ -973,7 +979,7 @@ int _bt_pagedel(Relation rel, Buffer buf, BTStack stack)
/* we need an insertion scan key to do our search, so build one */
itup_scankey = _bt_mkscankey(rel, targetkey);
/* find the leftmost leaf page containing this key */
stack = _bt_search(rel, rel->rd_rel->relnatts, itup_scankey, false, &lbuf, BT_READ);
stack = _bt_search(rel, IndexRelationGetNumberOfKeyAttributes(rel), itup_scankey, false, &lbuf, BT_READ);
/* don't need a pin on that either */
_bt_relbuf(rel, lbuf);
@ -1107,7 +1113,7 @@ int _bt_pagedel(Relation rel, Buffer buf, BTStack stack)
* Next find and write-lock the current parent of the target page. This is
* essentially the same as the corresponding step of splitting.
*/
ItemPointerSet(&(stack->bts_btentry.t_tid), target, P_HIKEY);
stack->bts_btentry = target;
pbuf = _bt_getstackbuf(rel, stack, BT_WRITE);
if (pbuf == InvalidBuffer)
ereport(ERROR,
@ -1195,7 +1201,7 @@ int _bt_pagedel(Relation rel, Buffer buf, BTStack stack)
#ifdef USE_ASSERT_CHECKING
itemid = PageGetItemId(page, poffset);
itup = (IndexTuple)PageGetItem(page, itemid);
Assert(ItemPointerGetBlockNumber(&(itup->t_tid)) == target);
Assert(BTreeInnerTupleGetDownLink(itup) == target);
#endif
if (!parent_half_dead) {
@ -1204,13 +1210,13 @@ int _bt_pagedel(Relation rel, Buffer buf, BTStack stack)
nextoffset = OffsetNumberNext(poffset);
itemid = PageGetItemId(page, nextoffset);
itup = (IndexTuple)PageGetItem(page, itemid);
if (ItemPointerGetBlockNumber(&(itup->t_tid)) != rightsib)
if (BTreeInnerTupleGetDownLink(itup) != rightsib)
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("right sibling %u of block %u is not next child %u of block %u in index \"%s\"",
rightsib,
target,
ItemPointerGetBlockNumber(&(itup->t_tid)),
BTreeInnerTupleGetDownLink(itup),
parent,
RelationGetRelationName(rel))));
}
@ -1236,7 +1242,7 @@ int _bt_pagedel(Relation rel, Buffer buf, BTStack stack)
itemid = PageGetItemId(page, poffset);
itup = (IndexTuple)PageGetItem(page, itemid);
ItemPointerSet(&(itup->t_tid), rightsib, P_HIKEY);
BTreeInnerTupleSetDownLink(itup, rightsib);
nextoffset = OffsetNumberNext(poffset);
PageIndexTupleDelete(page, nextoffset);

View File

@ -64,7 +64,7 @@ Datum btbuild(PG_FUNCTION_ARGS)
Relation index = (Relation)PG_GETARG_POINTER(1);
IndexInfo* indexInfo = (IndexInfo*)PG_GETARG_POINTER(2);
IndexBuildResult* result = NULL;
double reltuples;
double reltuples = 0;
BTBuildState buildstate;
buildstate.isUnique = indexInfo->ii_Unique;
@ -97,7 +97,12 @@ Datum btbuild(PG_FUNCTION_ARGS)
buildstate.spool = _bt_spoolinit(index, indexInfo->ii_Unique, false, &indexInfo->ii_desc);
/* do the heap scan */
reltuples = IndexBuildHeapScan(heap, index, indexInfo, true, btbuildCallback, (void*)&buildstate);
double* globalIndexTuples = NULL;
if (RelationIsGlobalIndex(index)) {
globalIndexTuples = GlobalIndexBuildHeapScan(heap, index, indexInfo, btbuildCallback, (void*)&buildstate);
} else {
reltuples = IndexBuildHeapScan(heap, index, indexInfo, true, btbuildCallback, (void*)&buildstate);
}
/* okay, all heap tuples are indexed */
if (buildstate.spool2 && !buildstate.haveDead) {
@ -129,6 +134,7 @@ Datum btbuild(PG_FUNCTION_ARGS)
result->heap_tuples = reltuples;
result->index_tuples = buildstate.indtuples;
result->global_index_tuples = globalIndexTuples;
PG_RETURN_POINTER(result);
}
@ -296,7 +302,8 @@ Datum btgetbitmap(PG_FUNCTION_ARGS)
if (_bt_first(scan, ForwardScanDirection)) {
/* Save tuple ID, and continue scanning */
heapTid = &scan->xs_ctup.t_self;
tbm_add_tuples(tbm, heapTid, 1, false);
Oid currPartOid = so->currPos.items[so->currPos.itemIndex].partitionOid;
tbm_add_tuples(tbm, heapTid, 1, false, currPartOid);
ntids++;
for (;;) {
@ -313,7 +320,8 @@ Datum btgetbitmap(PG_FUNCTION_ARGS)
/* Save tuple ID, and continue scanning */
heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
currPartOid = so->currPos.items[so->currPos.itemIndex].partitionOid;
tbm_add_tuples(tbm, heapTid, 1, false, currPartOid);
ntids++;
}
}
@ -931,6 +939,8 @@ restart:
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
if (callback) {
AttrNumber partitionOidAttr = IndexRelationGetNumberOfAttributes(rel);
TupleDesc tupdesc = RelationGetDescr(rel);
for (offnum = minoff; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) {
IndexTuple itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, offnum));
ItemPointer htup = &(itup->t_tid);
@ -956,7 +966,13 @@ restart:
* applies to *any* type of index that marks index tuples as
* killed.
*/
if (callback(htup, callback_state)) {
Oid partOid = InvalidOid;
if (RelationIsGlobalIndex(rel)) {
bool isnull = false;
partOid = index_getattr(itup, partitionOidAttr, tupdesc, &isnull);
Assert(!isnull);
}
if (callback(htup, callback_state, partOid)) {
deletable[ndeletable++] = offnum;
}
}

View File

@ -30,7 +30,7 @@
#include "catalog/pg_proc.h"
static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum);
static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup);
static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup, Oid partOid);
static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
@ -103,7 +103,7 @@ BTStack _bt_search(Relation rel, int keysz, ScanKey scankey, bool nextkey, Buffe
offnum = _bt_binsrch(rel, *bufP, keysz, scankey, nextkey);
itemid = PageGetItemId(page, offnum);
itup = (IndexTuple)PageGetItem(page, itemid);
blkno = ItemPointerGetBlockNumber(&(itup->t_tid));
blkno = BTreeInnerTupleGetDownLink(itup);
par_blkno = BufferGetBlockNumber(*bufP);
/*
@ -120,7 +120,7 @@ BTStack _bt_search(Relation rel, int keysz, ScanKey scankey, bool nextkey, Buffe
new_stack = (BTStack)palloc(sizeof(BTStackData));
new_stack->bts_blkno = par_blkno;
new_stack->bts_offset = offnum;
new_stack->bts_btentry = *itup;
new_stack->bts_btentry = blkno;
new_stack->bts_parent = stack_in;
}
@ -360,6 +360,15 @@ int32 _bt_compare(Relation rel, int keysz, ScanKey scankey, Page page, OffsetNum
IndexTuple itup;
BTPageOpaqueInternal opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);
/*
* Check tuple has correct number of attributes.
*/
if (unlikely(!_bt_check_natts(rel, page, offnum))) {
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("tuple has wrong number of attributes in index \"%s\"", RelationGetRelationName(rel))));
}
/*
* Force result ">" if target item is first data item on an internal page
* --- see NOTE above.
@ -940,8 +949,12 @@ bool _bt_first(IndexScanDesc scan, ScanDirection dir)
/* OK, itemIndex says what to return */
currItem = &so->currPos.items[so->currPos.itemIndex];
scan->xs_ctup.t_self = currItem->heapTid;
if (scan->xs_want_itup)
if (scan->xs_want_itup) {
scan->xs_itup = (IndexTuple)(so->currTuples + currItem->tupleOffset);
}
if (scan->xs_want_ext_oid && GPIScanCheckPartOid(scan->xs_gpi_scan, currItem->partitionOid)) {
GPISetCurrPartOid(scan->xs_gpi_scan, currItem->partitionOid);
}
return true;
}
@ -997,6 +1010,10 @@ bool _bt_next(IndexScanDesc scan, ScanDirection dir)
if (scan->xs_want_itup)
scan->xs_itup = (IndexTuple)(so->currTuples + currItem->tupleOffset);
if (scan->xs_want_ext_oid && GPIScanCheckPartOid(scan->xs_gpi_scan, currItem->partitionOid)) {
GPISetCurrPartOid(scan->xs_gpi_scan, currItem->partitionOid);
}
return true;
}
@ -1025,9 +1042,17 @@ static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber off
int itemIndex;
IndexTuple itup;
bool continuescan = true;
TupleDesc tupdesc;
AttrNumber PartitionOidAttr;
Oid partOid = InvalidOid;
Oid heapOid = IndexScanGetPartHeapOid(scan);
bool isnull = false;
gstrace_entry(GS_TRC_ID__bt_readpage);
tupdesc = RelationGetDescr(scan->indexRelation);
PartitionOidAttr = IndexRelationGetNumberOfAttributes(scan->indexRelation);
/* we must have the buffer pinned and locked */
Assert(BufferIsValid(so->currPos.buf));
/* We've pinned the buffer, nobody can prune this buffer, check whether snapshot is valid. */
@ -1057,8 +1082,14 @@ static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber off
while (offnum <= maxoff) {
itup = _bt_checkkeys(scan, page, offnum, dir, &continuescan);
if (itup != NULL) {
/* Get partition oid for global partition index */
isnull = false;
partOid = scan->xs_want_ext_oid
? DatumGetUInt32(index_getattr(itup, PartitionOidAttr, tupdesc, &isnull))
: heapOid;
Assert(!isnull);
/* tuple passes all scan key conditions, so remember it */
_bt_saveitem(so, itemIndex, offnum, itup);
_bt_saveitem(so, itemIndex, offnum, itup, partOid);
itemIndex++;
}
if (!continuescan) {
@ -1083,9 +1114,14 @@ static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber off
while (offnum >= minoff) {
itup = _bt_checkkeys(scan, page, offnum, dir, &continuescan);
if (itup != NULL) {
isnull = false;
partOid = scan->xs_want_ext_oid
? DatumGetUInt32(index_getattr(itup, PartitionOidAttr, tupdesc, &isnull))
: heapOid;
Assert(!isnull);
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
_bt_saveitem(so, itemIndex, offnum, itup);
_bt_saveitem(so, itemIndex, offnum, itup, partOid);
}
if (!continuescan) {
/* there can't be any more matches, so stop */
@ -1107,12 +1143,13 @@ static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber off
}
/* Save an index item into so->currPos.items[itemIndex] */
static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, const IndexTuple itup)
static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, const IndexTuple itup, Oid partOid)
{
BTScanPosItem* currItem = &so->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
currItem->partitionOid = partOid;
if (so->currTuples) {
Size itupsz = IndexTupleSize(itup);
@ -1431,7 +1468,7 @@ Buffer _bt_get_endpoint(Relation rel, uint32 level, bool rightmost)
offnum = P_FIRSTDATAKEY(opaque);
itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, offnum));
blkno = ItemPointerGetBlockNumber(&(itup->t_tid));
blkno = BTreeInnerTupleGetDownLink(itup);
buf = _bt_relandgetbuf(rel, buf, blkno, BT_READ);
page = BufferGetPage(buf);
@ -1528,6 +1565,10 @@ static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
if (scan->xs_want_itup)
scan->xs_itup = (IndexTuple)(so->currTuples + currItem->tupleOffset);
if (scan->xs_want_ext_oid && GPIScanCheckPartOid(scan->xs_gpi_scan, currItem->partitionOid)) {
GPISetCurrPartOid(scan->xs_gpi_scan, currItem->partitionOid);
}
return true;
}
@ -1597,3 +1638,43 @@ bool _bt_gettuple_internal(IndexScanDesc scan, ScanDirection dir)
return res;
}
/*
* Check if index tuple have appropriate number of attributes.
*/
bool _bt_check_natts(const Relation index, Page page, OffsetNumber offnum)
{
int16 natts = IndexRelationGetNumberOfAttributes(index);
int16 nkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
ItemId itemid;
IndexTuple itup;
BTPageOpaqueInternal opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);
/*
* Assert that mask allocated for number of keys in index tuple can fit
* maximum number of index keys.
*/
StaticAssertStmt(BT_N_KEYS_OFFSET_MASK >= INDEX_MAX_KEYS, "BT_N_KEYS_OFFSET_MASK can't fit INDEX_MAX_KEYS");
itemid = PageGetItemId(page, offnum);
itup = (IndexTuple)PageGetItem(page, itemid);
if (P_ISLEAF(opaque) && offnum >= P_FIRSTDATAKEY(opaque)) {
/*
* Regular leaf tuples have as every index attributes
*/
return (BTreeTupleGetNAtts(itup, index) == natts);
} else if (!P_ISLEAF(opaque) && offnum == P_FIRSTDATAKEY(opaque)) {
/*
* Leftmost tuples on non-leaf pages have no attributes, or haven't
* INDEX_ALT_TID_MASK set in pg_upgraded indexes.
*/
return (BTreeTupleGetNAtts(itup, index) == 0 || ((itup->t_info & INDEX_ALT_TID_MASK) == 0));
} else {
/*
* Pivot tuples stored in non-leaf pages and hikeys of leaf pages
* contain only key attributes
*/
return (BTreeTupleGetNAtts(itup, index) == nkeyatts);
}
}

View File

@ -407,6 +407,7 @@ static void _bt_sortaddtup(Page page, Size itemsize, IndexTuple itup, OffsetNumb
if (!P_ISLEAF(opaque) && itup_off == P_FIRSTKEY) {
trunctuple = *itup;
trunctuple.t_info = sizeof(IndexTupleData);
BTreeTupleSetNAtts(&trunctuple, 0);
itup = &trunctuple;
itemsize = sizeof(IndexTupleData);
}
@ -455,6 +456,9 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup)
OffsetNumber last_off;
Size pgspc;
Size itupsz;
BTPageOpaqueInternal pageop;
int indnatts = IndexRelationGetNumberOfAttributes(wstate->index);
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(wstate->index);
/*
* This is a handy place to check for cancel interrupts during the btree
@ -506,6 +510,8 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup)
ItemId ii;
ItemId hii;
IndexTuple oitup;
IndexTuple keytup;
BTPageOpaqueInternal opageop = (BTPageOpaqueInternal) PageGetSpecialPointer(opage);
/* Create new page of same level */
npage = _bt_blnewpage(state->btps_level);
@ -533,6 +539,25 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup)
ItemIdSetUnused(ii); /* redundant */
((PageHeader)opage)->pd_lower -= sizeof(ItemIdData);
if (indnkeyatts != indnatts && P_ISLEAF(opageop)) {
/*
* We truncate included attributes of high key here. Subsequent
* insertions assume that hikey is already truncated, and so they
* need not worry about it, when copying the high key into the
* parent page as a downlink.
*
* The code above have just rearranged item pointers, but it
* didn't save any space. In order to save the space on page we
* have to truly shift index tuples on the page. But that's not
* so bad for performance, because we operating pd_upper and don't
* have to shift much of tuples memory. Shift of ItemId's is
* rather cheap, because they are small.
*/
keytup = _bt_nonkey_truncate(wstate->index, oitup);
/* delete "wrong" high key, insert keytup as P_HIKEY. */
PageIndexTupleDelete(opage, P_HIKEY);
_bt_sortaddtup(opage, IndexTupleSize(keytup), keytup, P_HIKEY);
}
/*
* Link the old page into its parent, using its minimum key. If we
* don't have a parent, we have to create one; this adds a new btree
@ -542,7 +567,13 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup)
state->btps_next = _bt_pagestate(wstate, state->btps_level + 1);
Assert(state->btps_minkey != NULL);
ItemPointerSet(&(state->btps_minkey->t_tid), oblkno, P_HIKEY);
Assert(BTreeTupleGetNAtts(state->btps_minkey, wstate->index) ==
IndexRelationGetNumberOfKeyAttributes(wstate->index) ||
P_LEFTMOST(opageop));
Assert(BTreeTupleGetNAtts(state->btps_minkey, wstate->index) == 0 ||
!P_LEFTMOST(opageop));
BTreeInnerTupleSetDownLink(state->btps_minkey, oblkno);
_bt_buildadd(wstate, state->btps_next, state->btps_minkey);
pfree(state->btps_minkey);
state->btps_minkey = NULL;
@ -550,8 +581,11 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup)
/*
* Save a copy of the minimum key for the new page. We have to copy
* it off the old page, not the new one, in case we are not at leaf
* level.
* level. Despite oitup is already initialized, it's important to get
* high key from the page, since we could have replaced it with
* truncated copy. See comment above.
*/
oitup = (IndexTuple) PageGetItem(opage, PageGetItemId(opage, P_HIKEY));
state->btps_minkey = CopyIndexTuple(oitup);
/*
@ -578,15 +612,20 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup)
last_off = P_FIRSTKEY;
}
pageop = (BTPageOpaqueInternal) PageGetSpecialPointer(npage);
/*
* If the new item is the first for its page, stash a copy for later. Note
* this will only happen for the first item on a level; on later pages,
* the first item for a page is copied from the prior page in the code
* above.
* above. Since the minimum key for an entire level is only used as a
* minus infinity downlink, and never as a high key, there is no need to
* truncate away non-key attributes at this point.
*/
if (last_off == P_HIKEY) {
Assert(state->btps_minkey == NULL);
state->btps_minkey = CopyIndexTuple(itup);
/* _bt_sortaddtup() will perform full truncation later */
BTreeTupleSetNAtts(state->btps_minkey, 0);
}
/*
@ -634,7 +673,11 @@ void _bt_uppershutdown(BTWriteState* wstate, BTPageState* state)
rootlevel = s->btps_level;
} else {
Assert(s->btps_minkey != NULL);
ItemPointerSet(&(s->btps_minkey->t_tid), blkno, P_HIKEY);
Assert(BTreeTupleGetNAtts(s->btps_minkey, wstate->index) ==
IndexRelationGetNumberOfKeyAttributes(wstate->index) ||
P_LEFTMOST(opaque));
Assert(BTreeTupleGetNAtts(s->btps_minkey, wstate->index) == 0 || !P_LEFTMOST(opaque));
BTreeInnerTupleSetDownLink(s->btps_minkey, blkno);
_bt_buildadd(wstate, s->btps_next, s->btps_minkey);
pfree(s->btps_minkey);
s->btps_minkey = NULL;
@ -683,7 +726,7 @@ static void _bt_load(BTWriteState* wstate, BTSpool* btspool, BTSpool* btspool2)
bool should_free2 = false;
bool load1 = false;
TupleDesc tupdes = RelationGetDescr(wstate->index);
int keysz = RelationGetNumberOfAttributes(wstate->index);
int keysz = IndexRelationGetNumberOfKeyAttributes(wstate->index);
ScanKey indexScanKey = NULL;
if (merge) {

View File

@ -52,16 +52,25 @@ ScanKey _bt_mkscankey(Relation rel, IndexTuple itup)
{
ScanKey skey;
TupleDesc itupdesc;
int natts;
int indnatts PG_USED_FOR_ASSERTS_ONLY;
int indnkeyatts;
int16* indoption = NULL;
int i;
itupdesc = RelationGetDescr(rel);
natts = RelationGetNumberOfAttributes(rel);
indnatts = IndexRelationGetNumberOfAttributes(rel);
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
indoption = rel->rd_indoption;
skey = (ScanKey)palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++) {
Assert(indnkeyatts != 0);
Assert(indnkeyatts <= indnatts);
Assert(BTreeTupleGetNAtts(itup, rel) == indnatts || BTreeTupleGetNAtts(itup, rel) == indnkeyatts);
/*
* We'll execute search using ScanKey constructed on key columns. Non key
* (included) columns must be omitted.
*/
skey = (ScanKey)palloc(indnkeyatts * sizeof(ScanKeyData));
for (i = 0; i < indnkeyatts; i++) {
FmgrInfo* procinfo = NULL;
Datum arg;
bool null = false;
@ -95,16 +104,16 @@ ScanKey _bt_mkscankey(Relation rel, IndexTuple itup)
ScanKey _bt_mkscankey_nodata(Relation rel)
{
ScanKey skey;
int natts;
int indnkeyatts;
int16* indoption = NULL;
int i;
natts = RelationGetNumberOfAttributes(rel);
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
indoption = rel->rd_indoption;
skey = (ScanKey)palloc(natts * sizeof(ScanKeyData));
skey = (ScanKey) palloc(indnkeyatts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
FmgrInfo* procinfo = NULL;
uint32 flags;
@ -1577,6 +1586,9 @@ void _bt_killitems(IndexScanDesc scan, bool haveLock)
OffsetNumber maxoff;
int i;
bool killedsomething = false;
AttrNumber partitionOidAttr;
TupleDesc tupdesc;
Oid heapOid = IndexScanGetPartHeapOid(scan);
Assert(BufferIsValid(so->currPos.buf));
@ -1588,11 +1600,14 @@ void _bt_killitems(IndexScanDesc scan, bool haveLock)
opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
tupdesc = RelationGetDescr(scan->indexRelation);
partitionOidAttr = IndexRelationGetNumberOfAttributes(scan->indexRelation);
for (i = 0; i < so->numKilled; i++) {
int itemIndex = so->killedItems[i];
BTScanPosItem* kitem = &so->currPos.items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
Oid partOid = kitem->partitionOid;
Assert(itemIndex >= so->currPos.firstItem && itemIndex <= so->currPos.lastItem);
if (offnum < minoff) {
@ -1601,7 +1616,12 @@ void _bt_killitems(IndexScanDesc scan, bool haveLock)
while (offnum <= maxoff) {
ItemId iid = PageGetItemId(page, offnum);
IndexTuple ituple = (IndexTuple)PageGetItem(page, iid);
if (ItemPointerEquals(&ituple->t_tid, &kitem->heapTid)) {
bool isNull = false;
Oid currPartOid = scan->xs_want_ext_oid
? DatumGetUInt32(index_getattr(ituple, partitionOidAttr, tupdesc, &isNull))
: heapOid;
Assert(!isNull);
if (ItemPointerEquals(&ituple->t_tid, &kitem->heapTid) && currPartOid == partOid) {
/* found the item */
ItemIdMarkDead(iid);
killedsomething = true;
@ -1836,3 +1856,27 @@ Datum btoptions(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
/*
* _bt_nonkey_truncate() -- remove non-key (INCLUDE) attributes from index
* tuple.
*
* Transforms an ordinal B-tree leaf index tuple into pivot tuple to be used
* as hikey or non-leaf page tuple with downlink. Note that t_tid offset
* will be overritten in order to represent number of present tuple attributes.
*/
IndexTuple _bt_nonkey_truncate(Relation idxrel, IndexTuple olditup)
{
IndexTuple truncated;
int nkeyattrs = IndexRelationGetNumberOfKeyAttributes(idxrel);
/*
* We're assuming to truncate only regular leaf index tuples which have
* both key and non-key attributes.
*/
Assert(BTreeTupleGetNAtts(olditup, idxrel) == IndexRelationGetNumberOfAttributes(idxrel));
truncated = index_truncate_tuple(RelationGetDescr(idxrel), olditup, nkeyattrs);
BTreeTupleSetNAtts(truncated, nkeyattrs);
return truncated;
}

View File

@ -392,7 +392,7 @@ void btree_xlog_delete_page_operator_parentpage(RedoBufferInfo* buffer, void* re
Assert(info != XLOG_BTREE_DELETE_PAGE_HALF);
itemid = PageGetItemId(page, poffset);
itup = (IndexTuple)PageGetItem(page, itemid);
ItemPointerSet(&(itup->t_tid), xlrec->rightblk, P_HIKEY);
BTreeInnerTupleSetDownLink(itup, xlrec->rightblk);
nextoffset = OffsetNumberNext(poffset);
PageIndexTupleDelete(page, nextoffset);
}

View File

@ -147,7 +147,7 @@ static void vacuumLeafPage(spgBulkDeleteState* bds, Relation index, Buffer buffe
if (lt->tupstate == SPGIST_LIVE) {
Assert(ItemPointerIsValid(&lt->heapPtr));
if (bds->callback(&lt->heapPtr, bds->callback_state)) {
if (bds->callback(&lt->heapPtr, bds->callback_state, InvalidOid)) {
bds->stats->tuples_removed += 1;
deletable[i] = true;
nDeletable++;
@ -401,7 +401,7 @@ static void vacuumLeafRoot(spgBulkDeleteState* bds, Relation index, Buffer buffe
if (lt->tupstate == SPGIST_LIVE) {
Assert(ItemPointerIsValid(&lt->heapPtr));
if (bds->callback(&lt->heapPtr, bds->callback_state)) {
if (bds->callback(&lt->heapPtr, bds->callback_state, InvalidOid)) {
bds->stats->tuples_removed += 1;
toDelete[xlrec.nDelete] = i;
xlrec.nDelete++;
@ -847,7 +847,7 @@ Datum spgbulkdelete(PG_FUNCTION_ARGS)
}
/* Dummy callback to delete no tuples during spgvacuumcleanup */
static bool dummy_callback(ItemPointer itemptr, void* state)
static bool dummy_callback(ItemPointer itemptr, void* state, Oid partOid = InvalidOid)
{
return false;
}

View File

@ -28,6 +28,7 @@
typedef struct IndexBuildResult {
double heap_tuples; /* # of tuples seen in parent table */
double index_tuples; /* # of tuples inserted into index */
double* global_index_tuples;
} IndexBuildResult;
/*
@ -75,7 +76,7 @@ typedef struct IndexBulkDeleteResult {
} IndexBulkDeleteResult;
/* Typedef for callback function to determine if a tuple is bulk-deletable */
typedef bool (*IndexBulkDeleteCallback)(ItemPointer itemptr, void* state);
typedef bool (*IndexBulkDeleteCallback)(ItemPointer itemptr, void* state, Oid partOid);
/* struct definitions appear in relscan.h */
typedef struct IndexScanDescData* IndexScanDesc;
@ -166,4 +167,33 @@ extern void systable_endscan_ordered(SysScanDesc sysscan);
HeapTuple systable_getnext_back(SysScanDesc sysscan);
/*
* global partition index access method support routines (in genam.c)
*/
typedef struct GPIScanDescData {
HTAB* fakeRelationTable; /* fake partition relation and partition hash table */
Bitmapset* invisiblePartMap; /* cache invisible partition oid in GPI */
Relation parentRelation; /* parent relation of partition */
Relation fakePartRelation; /* fake-relation using partition */
Partition partition; /* partition use to fake partition rel */
Oid currPartOid; /* current partition oid in GPI */
} GPIScanDescData;
typedef GPIScanDescData* GPIScanDesc;
/* Check input partition oid is same as global-partition-index current work partition oid */
inline bool GPIScanCheckPartOid(GPIScanDesc gpiScan, Oid currScanPartOid)
{
if (!PointerIsValid(gpiScan)) {
return false;
}
return gpiScan->currPartOid != currScanPartOid;
}
extern void GPIScanInit(GPIScanDesc* gpiScan);
extern void GPIScanEnd(GPIScanDesc gpiScan);
extern bool GPIGetNextPartRelation(GPIScanDesc gpiScan, MemoryContext cxt, LOCKMODE lmode);
extern void GPISetCurrPartOid(GPIScanDesc gpiScan, Oid partOid);
extern Oid GPIGetCurrPartOid(const GPIScanDesc gpiScan);
#endif /* GENAM_H */

View File

@ -171,6 +171,8 @@ typedef HashMetaPageData* HashMetaPage;
MAXALIGN_DOWN( \
PageGetPageSize(page) - SizeOfPageHeaderData - sizeof(ItemIdData) - MAXALIGN(sizeof(HashPageOpaqueData)))
#define INDEX_MOVED_BY_SPLIT_MASK INDEX_AM_RESERVED_BIT
#define HASH_MIN_FILLFACTOR 10
#define HASH_DEFAULT_FILLFACTOR 75

View File

@ -1125,6 +1125,16 @@ extern MinimalTuple heapFormMinimalTuple(HeapTuple tuple, TupleDesc tuple_desc);
extern MinimalTuple heapFormMinimalTuple(HeapTuple tuple, TupleDesc tuple_desc, Page page);
/* for GPI clean up metadata */
typedef bool (*KeepInvisbleTupleFunc)(Datum checkDatum);
typedef struct KeepInvisbleOpt {
Oid tableOid;
int checkAttnum;
KeepInvisbleTupleFunc checkKeepFunc;
} KeepInvisbleOpt;
bool HeapKeepInvisbleTuple(HeapTuple tuple, TupleDesc tupleDesc, KeepInvisbleTupleFunc checkKeepFunc = NULL);
// for ut test
extern HeapTuple test_HeapUncompressTup2(HeapTuple tuple, TupleDesc tuple_desc, Page dict_page);

View File

@ -40,7 +40,7 @@ typedef struct IndexTupleData {
*
* 15th (high) bit: has nulls
* 14th bit: has var-width attributes
* 13th bit: unused
* 13th bit: AM-defined meaning
* 12-0 bit: size of tuple
* ---------------
*/
@ -61,7 +61,7 @@ typedef IndexAttributeBitMapData* IndexAttributeBitMap;
* t_info manipulation macros
*/
#define INDEX_SIZE_MASK 0x1FFF
/* bit 0x2000 is not used at present */
#define INDEX_AM_RESERVED_BIT 0x2000 /* reserved for index-AM specific usage */
#define INDEX_VAR_MASK 0x4000
#define INDEX_NULL_MASK 0x8000
@ -113,6 +113,7 @@ typedef IndexAttributeBitMapData* IndexAttributeBitMap;
extern IndexTuple index_form_tuple(TupleDesc tuple_descriptor, Datum* values, const bool* isnull);
extern Datum nocache_index_getattr(IndexTuple tup, uint32 attnum, TupleDesc tuple_desc);
extern void index_deform_tuple(IndexTuple tup, TupleDesc tuple_descriptor, Datum* values, bool* isnull);
extern IndexTuple index_truncate_tuple(TupleDesc tupleDescriptor, IndexTuple olditup, int new_indnatts);
extern IndexTuple CopyIndexTuple(IndexTuple source);
#endif /* ITUP_H */

View File

@ -134,29 +134,6 @@ typedef struct BTMetaPageData {
#define BTREE_DEFAULT_FILLFACTOR 90
#define BTREE_NONLEAF_FILLFACTOR 70
/*
* Test whether two btree entries are "the same".
*
* Old comments:
* In addition, we must guarantee that all tuples in the index are unique,
* in order to satisfy some assumptions in Lehman and Yao. The way that we
* do this is by generating a new OID for every insertion that we do in the
* tree. This adds eight bytes to the size of btree index tuples. Note
* that we do not use the OID as part of a composite key; the OID only
* serves as a unique identifier for a given index tuple (logical position
* within a page).
*
* New comments:
* actually, we must guarantee that all tuples in A LEVEL
* are unique, not in ALL INDEX. So, we can use the t_tid
* as unique identifier for a given index tuple (logical position
* within a level). - vadim 04/09/97
*/
#define BTTidSame(i1, i2) \
((i1).ip_blkid.bi_hi == (i2).ip_blkid.bi_hi && (i1).ip_blkid.bi_lo == (i2).ip_blkid.bi_lo && \
(i1).ip_posid == (i2).ip_posid)
#define BTEntrySame(i1, i2) BTTidSame((i1)->t_tid, (i2)->t_tid)
/*
* In general, the btree code tries to localize its knowledge about
* page layout to a couple of routines. However, we need a special
@ -266,10 +243,11 @@ typedef struct xl_btree_insert {
* Note: the four XLOG_BTREE_SPLIT xl_info codes all use this data record.
* The _L and _R variants indicate whether the inserted tuple went into the
* left or right split page (and thus, whether newitemoff and the new item
* are stored or not). The _ROOT variants indicate that we are splitting
* the root page, and thus that a newroot record rather than an insert or
* split record should follow. Note that a split record never carries a
* metapage update --- we'll do that in the parent-level update.
* are stored or not). The _HIGHKEY variants indicate that we've logged
* explicitly left page high key value, otherwise redo should use right page
* leftmost key as a left page high key. _HIGHKEY is specified for internal
* pages where right page leftmost key is suppressed, and for leaf pages
* of covering indexes where high key have non-key attributes truncated.
*
* Backup Blk 0: original page / new left page
*
@ -392,6 +370,74 @@ typedef struct xl_btree_newroot {
#define SizeOfBtreeNewroot (offsetof(xl_btree_newroot, level) + sizeof(uint32))
/*
* INCLUDE B-Tree indexes have non-key attributes. These are extra
* attributes that may be returned by index-only scans, but do not influence
* the order of items in the index (formally, non-key attributes are not
* considered to be part of the key space). Non-key attributes are only
* present in leaf index tuples whose item pointers actually point to heap
* tuples. All other types of index tuples (collectively, "pivot" tuples)
* only have key attributes, since pivot tuples only ever need to represent
* how the key space is separated. In general, any B-Tree index that has
* more than one level (i.e. any index that does not just consist of a
* metapage and a single leaf root page) must have some number of pivot
* tuples, since pivot tuples are used for traversing the tree.
*
* We store the number of attributes present inside pivot tuples by abusing
* their item pointer offset field, since pivot tuples never need to store a
* real offset (downlinks only need to store a block number). The offset
* field only stores the number of attributes when the INDEX_ALT_TID_MASK
* bit is set (we never assume that pivot tuples must explicitly store the
* number of attributes, and currently do not bother storing the number of
* attributes unless indnkeyatts actually differs from indnatts).
* INDEX_ALT_TID_MASK is only used for pivot tuples at present, though it's
* possible that it will be used within non-pivot tuples in the future. Do
* not assume that a tuple with INDEX_ALT_TID_MASK set must be a pivot
* tuple.
*
* The 12 least significant offset bits are used to represent the number of
* attributes in INDEX_ALT_TID_MASK tuples, leaving 4 bits that are reserved
* for future use (BT_RESERVED_OFFSET_MASK bits). BT_N_KEYS_OFFSET_MASK should
* be large enough to store any number <= INDEX_MAX_KEYS.
*/
#define INDEX_ALT_TID_MASK INDEX_AM_RESERVED_BIT
#define BT_RESERVED_OFFSET_MASK 0xF000
#define BT_N_KEYS_OFFSET_MASK 0x0FFF
/* Get/set downlink block number */
#define BTreeInnerTupleGetDownLink(itup) ItemPointerGetBlockNumberNoCheck(&((itup)->t_tid))
#define BTreeInnerTupleSetDownLink(itup, blkno) ItemPointerSetBlockNumber(&((itup)->t_tid), (blkno))
/*
* Get/set leaf page highkey's link. During the second phase of deletion, the
* target leaf page's high key may point to an ancestor page (at all other
* times, the leaf level high key's link is not used). See the nbtree README
* for full details.
*/
#define BTreeTupleGetTopParent(itup) ItemPointerGetBlockNumberNoCheck(&((itup)->t_tid))
#define BTreeTupleSetTopParent(itup, blkno) \
do { \
ItemPointerSetBlockNumber(&((itup)->t_tid), (blkno)); \
BTreeTupleSetNAtts((itup), 0); \
} while (0)
/*
* Get/set number of attributes within B-tree index tuple. Asserts should be
* removed when BT_RESERVED_OFFSET_MASK bits will be used.
*/
#define BTreeTupleGetNAtts(itup, rel) \
((itup)->t_info & INDEX_ALT_TID_MASK \
? (AssertMacro((ItemPointerGetOffsetNumberNoCheck(&(itup)->t_tid) & BT_RESERVED_OFFSET_MASK) == 0), \
ItemPointerGetOffsetNumberNoCheck(&(itup)->t_tid) & BT_N_KEYS_OFFSET_MASK) \
: IndexRelationGetNumberOfAttributes(rel))
#define BTreeTupleSetNAtts(itup, n) \
do { \
(itup)->t_info |= INDEX_ALT_TID_MASK; \
Assert(((n) & BT_RESERVED_OFFSET_MASK) == 0); \
ItemPointerSetOffsetNumber(&(itup)->t_tid, (n) & BT_N_KEYS_OFFSET_MASK); \
} while (0)
/*
* Operator strategy numbers for B-tree have been moved to access/skey.h,
* because many places need to use them in ScanKeyInit() calls.
@ -437,7 +483,7 @@ typedef struct xl_btree_newroot {
typedef struct BTStackData {
BlockNumber bts_blkno;
OffsetNumber bts_offset;
IndexTupleData bts_btentry;
BlockNumber bts_btentry;
struct BTStackData* bts_parent;
} BTStackData;
@ -473,6 +519,7 @@ typedef struct BTScanPosItem { /* what we remember about each match */
ItemPointerData heapTid; /* TID of referenced heap item */
OffsetNumber indexOffset; /* index item's location within page */
LocationIndex tupleOffset; /* IndexTuple's offset in workspace, if any */
Oid partitionOid; /* partition table oid in workspace, if any */
} BTScanPosItem;
typedef struct BTScanPosData {
@ -675,6 +722,7 @@ extern bool _bt_first(IndexScanDesc scan, ScanDirection dir);
extern bool _bt_next(IndexScanDesc scan, ScanDirection dir);
extern Buffer _bt_get_endpoint(Relation rel, uint32 level, bool rightmost);
extern bool _bt_gettuple_internal(IndexScanDesc scan, ScanDirection dir);
extern bool _bt_check_natts(const Relation index, Page page, OffsetNumber offnum);
/*
* prototypes for functions in nbtutils.c
@ -699,6 +747,7 @@ extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
extern void _bt_finish_split(Relation rel, Buffer lbuf, BTStack stack);
extern IndexTuple _bt_nonkey_truncate(Relation idxrel, IndexTuple olditup);
/*
* prototypes for functions in nbtsort.c

View File

@ -259,6 +259,7 @@ extern int8 heaprel_get_compression_from_modes(int16 modes);
extern void CheckGetServerIpAndPort(const char* Address, List** AddrList, bool IsCheck, int real_addr_max);
extern void CheckFoldernameOrFilenamesOrCfgPtah(const char* OptStr, char* OptType);
extern void CheckWaitCleanGpi(const char* value);
extern void ForbidToSetOptionsForPSort(List* options);
extern void ForbidOutUsersToSetInnerOptions(List* user_options);

View File

@ -124,14 +124,16 @@ typedef HBktTblScanDescData* HBktTblScanDesc;
typedef struct IndexScanDescData {
AbsIdxScanDescData sd;
/* scan parameters */
Relation heapRelation; /* heap relation descriptor, or NULL */
Relation indexRelation; /* index relation descriptor */
Snapshot xs_snapshot; /* snapshot to see */
int numberOfKeys; /* number of index qualifier conditions */
int numberOfOrderBys; /* number of ordering operators */
ScanKey keyData; /* array of index qualifier descriptors */
ScanKey orderByData; /* array of ordering op descriptors */
bool xs_want_itup; /* caller requests index tuples */
Relation heapRelation; /* heap relation descriptor, or NULL */
Relation indexRelation; /* index relation descriptor */
GPIScanDesc xs_gpi_scan; /* global partition index scan use information */
Snapshot xs_snapshot; /* snapshot to see */
int numberOfKeys; /* number of index qualifier conditions */
int numberOfOrderBys; /* number of ordering operators */
ScanKey keyData; /* array of index qualifier descriptors */
ScanKey orderByData; /* array of ordering op descriptors */
bool xs_want_itup; /* caller requests index tuples */
bool xs_want_ext_oid; /* global partition index need partition oid */
/* signaling to index AM about killing index tuples */
bool kill_prior_tuple; /* last-returned tuple is dead */
@ -162,6 +164,20 @@ typedef struct IndexScanDescData {
#define SizeofIndexScanDescData (offsetof(IndexScanDescData, xs_ctbuf_hdr) + SizeofHeapTupleHeader)
/* Get partition heap oid for bitmap index scan */
#define IndexScanGetPartHeapOid(scan) \
((scan)->indexRelation != NULL \
? (RelationIsPartition((scan)->indexRelation) ? (scan)->indexRelation->rd_partHeapOid : InvalidOid) \
: InvalidOid)
/*
* When the global partition index is used for index scanning,
* checks whether the partition table needs to be
* switched each time an indextuple is obtained.
*/
#define IndexScanNeedSwitchPartRel(scan) \
((scan)->xs_want_ext_oid && GPIScanCheckPartOid((scan)->xs_gpi_scan, (scan)->heapRelation->rd_id))
typedef struct HBktIdxScanDescData {
AbsIdxScanDescData sd;
Relation rs_rd; /* heap relation descriptor */

View File

@ -106,6 +106,9 @@ typedef struct tupleDesc {
int tdrefcount; /* reference count, or -1 if not counting */
} * TupleDesc;
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) ((tupdesc)->attrs[(i)])
extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
extern TupleDesc CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute* attrs);
@ -149,7 +152,4 @@ extern bool tupledesc_have_pck(TupleConstr* constr);
extern void copyDroppedAttribute(Form_pg_attribute target, Form_pg_attribute source);
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
#endif /* TUPDESC_H */

View File

@ -112,6 +112,24 @@
#define dngettext(d, s, p, n) ((n) == 1 ? (s) : (p))
#endif
/* only GCC supports the unused attribute */
#ifdef __GNUC__
#define pg_attribute_unused() __attribute__((unused))
#else
#define pg_attribute_unused()
#endif
/*
* Append PG_USED_FOR_ASSERTS_ONLY to definitions of variables that are only
* used in assert-enabled builds, to avoid compiler warnings about unused
* variables in assert-disabled builds.
*/
#ifdef USE_ASSERT_CHECKING
#define PG_USED_FOR_ASSERTS_ONLY
#else
#define PG_USED_FOR_ASSERTS_ONLY pg_attribute_unused()
#endif
/*
* Use this to mark string constants as needing translation at some later
* time, rather than immediately. This is useful for cases where you need

View File

@ -59,8 +59,22 @@ extern void index_check_primary_key(Relation heapRel, IndexInfo *indexInfo, bool
typedef struct {
Oid existingPSortOid;
bool isPartitionedIndex;
bool isGlobalPartitionedIndex;
} IndexCreateExtraArgs;
typedef enum
{
INDEX_CREATE_NONE_PARTITION,
INDEX_CREATE_LOCAL_PARTITION,
INDEX_CREATE_GLOBAL_PARTITION
} IndexCreatePartitionType;
typedef enum { ALL_KIND, GLOBAL_INDEX, LOCAL_INDEX } IndexKind;
#define PARTITION_TYPE(extra) \
(extra->isPartitionedIndex == false ? INDEX_CREATE_NONE_PARTITION : \
(extra->isGlobalPartitionedIndex == false ? INDEX_CREATE_LOCAL_PARTITION : INDEX_CREATE_GLOBAL_PARTITION))
extern Oid index_create(Relation heapRelation, const char *indexRelationName, Oid indexRelationId,
Oid relFileNode, IndexInfo *indexInfo, List *indexColNames, Oid accessMethodObjectId,
Oid tableSpaceId, Oid *collationObjectId, Oid *classObjectId, int16 *coloptions,
@ -83,10 +97,12 @@ extern void BuildSpeculativeIndexInfo(Relation index, IndexInfo* ii);
extern void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull);
extern void index_build(Relation heapRelation, Partition heapPartition, Relation indexRelation,
Partition indexPartition, IndexInfo *indexInfo, bool isprimary,
bool isreindex, bool isPartition);
bool isreindex, IndexCreatePartitionType partitionType);
extern double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo,
bool allow_sync, IndexBuildCallback callback, void *callback_state);
extern double* GlobalIndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo,
IndexBuildCallback callback, void* callbackState);
extern double IndexBuildVectorBatchScan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo,
VectorBatch *vecScanBatch, Snapshot snapshot,
@ -104,6 +120,8 @@ extern void reindex_indexpart_internal(Relation heapRelation,
extern void reindex_index(Oid indexId, Oid indexPartId,
bool skip_constraint_checks, AdaptMem *memInfo,
bool dbWide, char persistence);
extern void ReindexGlobalIndexInternal(Relation heapRelation, Relation iRel, IndexInfo* indexInfo);
/* Flag bits for reindex_relation(): */
#define REINDEX_REL_PROCESS_TOAST 0x01
@ -111,7 +129,8 @@ extern void reindex_index(Oid indexId, Oid indexPartId,
#define REINDEX_REL_CHECK_CONSTRAINTS 0x04
extern bool reindex_relation(Oid relid, int flags, int reindexType,
AdaptMem *memInfo = NULL, bool dbWide = false);
AdaptMem *memInfo = NULL, bool dbWide = false,
IndexKind indexKind = ALL_KIND);
extern bool ReindexIsProcessingHeap(Oid heapOid);
extern bool ReindexIsProcessingIndex(Oid indexOid);
@ -148,4 +167,5 @@ extern void PartitionNameCallbackForIndexPartition(Oid partitionedRelationOid,
extern void reindex_partIndex(Relation heapRel, Partition heapPart, Relation indexRel , Partition indexPart);
extern bool reindexPartition(Oid relid, Oid partOid, int flags, int reindexType);
extern void mergeBTreeIndexes(List* mergingBtreeIndexes, List* srcPartMergeOffset);
extern void SetIndexCreateExtraArgs(IndexCreateExtraArgs* extra, Oid psortOid, bool isPartition, bool isGlobal);
#endif /* INDEX_H */

View File

@ -52,6 +52,7 @@ CATALOG(pg_am,2601) BKI_SCHEMA_MACRO
bool amstorage; /* can storage type differ from column type? */
bool amclusterable; /* does AM support cluster command? */
bool ampredlocks; /* does AM handle predicate locks? */
bool amcaninclude; /* does AM support create index xxx include? */
Oid amkeytype; /* type of data in index, or InvalidOid */
regproc aminsert; /* "insert this tuple" function */
regproc ambeginscan; /* "prepare for index scan" function */
@ -82,7 +83,7 @@ typedef FormData_pg_am *Form_pg_am;
* compiler constants for pg_am
* ----------------
*/
#define Natts_pg_am 31
#define Natts_pg_am 32
#define Anum_pg_am_amname 1
#define Anum_pg_am_amstrategies 2
#define Anum_pg_am_amsupport 3
@ -97,54 +98,55 @@ typedef FormData_pg_am *Form_pg_am;
#define Anum_pg_am_amstorage 12
#define Anum_pg_am_amclusterable 13
#define Anum_pg_am_ampredlocks 14
#define Anum_pg_am_amkeytype 15
#define Anum_pg_am_aminsert 16
#define Anum_pg_am_ambeginscan 17
#define Anum_pg_am_amgettuple 18
#define Anum_pg_am_amgetbitmap 19
#define Anum_pg_am_amrescan 20
#define Anum_pg_am_amendscan 21
#define Anum_pg_am_ammarkpos 22
#define Anum_pg_am_amrestrpos 23
#define Anum_pg_am_ammerge 24
#define Anum_pg_am_ambuild 25
#define Anum_pg_am_ambuildempty 26
#define Anum_pg_am_ambulkdelete 27
#define Anum_pg_am_amvacuumcleanup 28
#define Anum_pg_am_amcanreturn 29
#define Anum_pg_am_amcostestimate 30
#define Anum_pg_am_amoptions 31
#define Anum_pg_am_amcaninclude 15
#define Anum_pg_am_amkeytype 16
#define Anum_pg_am_aminsert 17
#define Anum_pg_am_ambeginscan 18
#define Anum_pg_am_amgettuple 19
#define Anum_pg_am_amgetbitmap 20
#define Anum_pg_am_amrescan 21
#define Anum_pg_am_amendscan 22
#define Anum_pg_am_ammarkpos 23
#define Anum_pg_am_amrestrpos 24
#define Anum_pg_am_ammerge 25
#define Anum_pg_am_ambuild 26
#define Anum_pg_am_ambuildempty 27
#define Anum_pg_am_ambulkdelete 28
#define Anum_pg_am_amvacuumcleanup 29
#define Anum_pg_am_amcanreturn 30
#define Anum_pg_am_amcostestimate 31
#define Anum_pg_am_amoptions 32
/* ----------------
* initial contents of pg_am
* ----------------
*/
DATA(insert OID = 403 ( btree 5 2 t f t t t t t t f t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btmerge btbuild btbuildempty btbulkdelete btvacuumcleanup btcanreturn btcostestimate btoptions ));
DATA(insert OID = 403 ( btree 5 2 t f t t t t t t f t t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btmerge btbuild btbuildempty btbulkdelete btvacuumcleanup btcanreturn btcostestimate btoptions ));
DESCR("b-tree index access method");
#define BTREE_AM_OID 403
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashmerge hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions ));
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashmerge hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions ));
DESCR("hash index access method");
#define HASH_AM_OID 405
DATA(insert OID = 783 ( gist 0 8 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistmerge gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup - gistcostestimate gistoptions ));
DATA(insert OID = 783 ( gist 0 8 f t f f t t f t t t f f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistmerge gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup - gistcostestimate gistoptions ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
DATA(insert OID = 2742 ( gin 0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginmerge ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
DATA(insert OID = 2742 ( gin 0 6 f f f f t t f f t f f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginmerge ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742
DATA(insert OID = 4000 ( spgist 0 5 f f f f f t f t f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgmerge spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcanreturn spgcostestimate spgoptions ));
DATA(insert OID = 4000 ( spgist 0 5 f f f f f t f t f f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgmerge spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcanreturn spgcostestimate spgoptions ));
DESCR("SP-GiST index access method");
#define SPGIST_AM_OID 4000
DATA(insert OID = 4039 ( psort 5 1 f f f f t t f t f f f 0 - - psortgettuple psortgetbitmap - - - - - psortbuild - - - psortcanreturn psortcostestimate psortoptions ));
DATA(insert OID = 4039 ( psort 5 1 f f f f t t f t f f f f 0 - - psortgettuple psortgetbitmap - - - - - psortbuild - - - psortcanreturn psortcostestimate psortoptions ));
DESCR("psort index access method");
#define PSORT_AM_OID 4039
DATA(insert OID = 4239 ( cbtree 5 1 f f f f t t f t f f t 0 btinsert btbeginscan cbtreegettuple cbtreegetbitmap btrescan btendscan - - - cbtreebuild btbuildempty - - cbtreecanreturn cbtreecostestimate cbtreeoptions ));
DATA(insert OID = 4239 ( cbtree 5 1 f f f f t t f t f f t f 0 btinsert btbeginscan cbtreegettuple cbtreegetbitmap btrescan btendscan - - - cbtreebuild btbuildempty - - cbtreecanreturn cbtreecostestimate cbtreeoptions ));
DESCR("cstore btree index access method");
#define CBTREE_AM_OID 4239
DATA(insert OID = 4444 ( cgin 0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - cgingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginmerge cginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
DATA(insert OID = 4444 ( cgin 0 6 f f f f t t f f t f f f 0 gininsert ginbeginscan - cgingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginmerge cginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
DESCR("cstore GIN index access method");
#define CGIN_AM_OID 4444

View File

@ -163,6 +163,7 @@ DESCR("");
#define RELKIND_RELATION 'r' /* ordinary table */
#define RELKIND_INDEX 'i' /* secondary index */
#define RELKIND_GLOBAL_INDEX 'I' /* GLOBAL partitioned index */
#define RELKIND_SEQUENCE 'S' /* sequence object */
#define RELKIND_TOASTVALUE 't' /* for out-of-line values */
#define RELKIND_VIEW 'v' /* view */

View File

@ -104,6 +104,13 @@ CATALOG(pg_constraint,2606) BKI_SCHEMA_MACRO
*/
int2 conkey[1];
/*
* Columns of conrelid that the constraint does not apply to, but included
* into the same index with key columns.
*/
int2 conincluding[1];
/*
* If a foreign key, the referenced columns of confrelid
*/
@ -156,7 +163,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
#define Natts_pg_constraint 26
#define Natts_pg_constraint 27
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@ -176,13 +183,14 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_consoft 17
#define Anum_pg_constraint_conopt 18
#define Anum_pg_constraint_conkey 19
#define Anum_pg_constraint_confkey 20
#define Anum_pg_constraint_conpfeqop 21
#define Anum_pg_constraint_conppeqop 22
#define Anum_pg_constraint_conffeqop 23
#define Anum_pg_constraint_conexclop 24
#define Anum_pg_constraint_conbin 25
#define Anum_pg_constraint_consrc 26
#define Anum_pg_constraint_conincluding 20
#define Anum_pg_constraint_confkey 21
#define Anum_pg_constraint_conpfeqop 22
#define Anum_pg_constraint_conppeqop 23
#define Anum_pg_constraint_conffeqop 24
#define Anum_pg_constraint_conexclop 25
#define Anum_pg_constraint_conbin 26
#define Anum_pg_constraint_consrc 27
/* Valid values for contype */
@ -224,6 +232,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid relId,
const int16 *constraintKey,
int constraintNKeys,
int constraintNTotalKeys,
Oid domainId,
Oid indexRelId,
Oid foreignRelId,

View File

@ -33,7 +33,8 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
{
Oid indexrelid; /* OID of the index */
Oid indrelid; /* OID of the relation it indexes */
int2 indnatts; /* number of columns in index */
int2 indnatts; /* total number of columns in index */
int2 indnkeyatts; /* number of key columns in index */
bool indisunique; /* is this a unique index? */
bool indisprimary; /* is this index for primary key? */
bool indisexclusion; /* is this index for exclusion constraint? */
@ -72,26 +73,27 @@ typedef FormData_pg_index *Form_pg_index;
* compiler constants for pg_index
* ----------------
*/
#define Natts_pg_index 19
#define Natts_pg_index 20
#define Anum_pg_index_indexrelid 1
#define Anum_pg_index_indrelid 2
#define Anum_pg_index_indnatts 3
#define Anum_pg_index_indisunique 4
#define Anum_pg_index_indisprimary 5
#define Anum_pg_index_indisexclusion 6
#define Anum_pg_index_indimmediate 7
#define Anum_pg_index_indisclustered 8
#define Anum_pg_index_indisusable 9
#define Anum_pg_index_indisvalid 10
#define Anum_pg_index_indcheckxmin 11
#define Anum_pg_index_indisready 12
#define Anum_pg_index_indkey 13
#define Anum_pg_index_indcollation 14
#define Anum_pg_index_indclass 15
#define Anum_pg_index_indoption 16
#define Anum_pg_index_indexprs 17
#define Anum_pg_index_indpred 18
#define Anum_pg_index_indisreplident 19
#define Anum_pg_index_indnkeyatts 4
#define Anum_pg_index_indisunique 5
#define Anum_pg_index_indisprimary 6
#define Anum_pg_index_indisexclusion 7
#define Anum_pg_index_indimmediate 8
#define Anum_pg_index_indisclustered 9
#define Anum_pg_index_indisusable 10
#define Anum_pg_index_indisvalid 11
#define Anum_pg_index_indcheckxmin 12
#define Anum_pg_index_indisready 13
#define Anum_pg_index_indkey 14
#define Anum_pg_index_indcollation 15
#define Anum_pg_index_indclass 16
#define Anum_pg_index_indoption 17
#define Anum_pg_index_indexprs 18
#define Anum_pg_index_indpred 19
#define Anum_pg_index_indisreplident 20
/*
* Index AMs that support ordered scans must support these two indoption
* bits. Otherwise, the content of the per-column indoption fields is

View File

@ -33,7 +33,7 @@ CATALOG(pg_partition,9016) BKI_ROWTYPE_OID(3790) BKI_SCHEMA_MACRO
{
NameData relname;
char parttype;
Oid parentid;
Oid parentid;
int4 rangenum;
int4 intervalnum;
char partstrategy;

View File

@ -39,6 +39,7 @@ extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_cata
bool check_constraints, TransactionId frozenXid, AdaptMem* memInfo = NULL);
extern void vacuumFullPart(Oid partOid, VacuumStmt* vacstmt, int freeze_min_age, int freeze_table_age);
extern void GpiVacuumFullMainPartiton(Oid parentOid);
extern void updateRelationName(Oid relOid, bool isPartition, const char* relNewName);
#endif /* CLUSTER_H */

View File

@ -386,10 +386,10 @@ extern int compute_attr_target(Form_pg_attribute attr);
extern void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tuples,
BlockNumber num_all_visible_pages, TransactionId frozenxid);
extern void vac_open_part_indexes(
VacuumStmt* vacstmt, LOCKMODE lockmode, int* nindexes, Relation** Irel, Relation** indexrel, Partition** indexpart);
extern void vac_open_part_indexes(VacuumStmt* vacstmt, LOCKMODE lockmode, int* nindexes, int* nindexes_global,
Relation** Irel, Relation** indexrel, Partition** indexpart);
extern void vac_close_part_indexes(
int nindexes, Relation* Irel, Relation* indexrel, Partition* indexpart, LOCKMODE lockmode);
int nindexes, int nindexes_global, Relation* Irel, Relation* indexrel, Partition* indexpart, LOCKMODE lockmode);
extern void vac_update_pgclass_partitioned_table(Relation partitionRel, bool hasIndex, TransactionId newFrozenXid);
extern void CStoreVacUpdateNormalRelStats(Oid relid, TransactionId frozenxid, Relation pgclassRel);

View File

@ -51,9 +51,11 @@ typedef struct UtilityDesc {
* entries for a particular index. Used for both index_build and
* retail creation of index entries.
*
* NumIndexAttrs number of columns in this index
* NumIndexAttrs total number of columns in this index
* NumIndexKeyAttrs number of key columns in index
* KeyAttrNumbers underlying-rel attribute numbers used as keys
* (zeroes indicate expressions)
* (zeroes indicate expressions). It also contains
* info about included columns.
* Expressions expr trees for expression entries, or NIL if none
* ExpressionsState exec state for expressions, or NIL if none
* Predicate partial-index predicate, or NIL if none
@ -75,7 +77,8 @@ typedef struct UtilityDesc {
*/
typedef struct IndexInfo {
NodeTag type;
int ii_NumIndexAttrs;
int ii_NumIndexAttrs; /* total number of columns in index */
int ii_NumIndexKeyAttrs; /* number of key columns in index */
AttrNumber ii_KeyAttrNumbers[INDEX_MAX_KEYS];
List* ii_Expressions; /* list of Expr */
List* ii_ExpressionsState; /* list of ExprState */
@ -391,6 +394,7 @@ typedef struct MergeState {
* RangeTableIndex result relation's range table index
* RelationDesc relation descriptor for result relation
* NumIndices # of indices existing on result relation
* ri_ContainGPI indices whether contain global parition index
* IndexRelationDescs array of relation descriptors for indices
* IndexRelationInfo array of key/attr info for indices
* TrigDesc triggers to be fired, if any
@ -410,6 +414,7 @@ typedef struct ResultRelInfo {
Index ri_RangeTableIndex;
Relation ri_RelationDesc;
int ri_NumIndices;
bool ri_ContainGPI;
RelationPtr ri_IndexRelationDescs;
IndexInfo** ri_IndexRelationInfo;
TriggerDesc* ri_TrigDesc;
@ -1696,6 +1701,7 @@ typedef struct BitmapHeapScanState {
TBMIterator* prefetch_iterator;
int prefetch_pages;
int prefetch_target;
GPIScanDesc gpi_scan; /* global partition index scan use information */
} BitmapHeapScanState;
/* ----------------
@ -2451,6 +2457,16 @@ TupleTableSlot* ExecMakeTupleSlot(HeapTuple tuple, HeapScanDesc heapScan, TupleT
return ExecClearTuple(slot);
}
/*
* When the global partition index is used for bitmap scanning,
* checks whether the partition table needs to be
* switched each time an tbmres is obtained.
*/
inline bool BitmapNodeNeedSwitchPartRel(BitmapHeapScanState* node)
{
return tbm_is_global(node->tbm) && GPIScanCheckPartOid(node->gpi_scan, node->tbmres->partitionOid);
}
extern bool reset_scan_qual(Relation currHeapRel, ScanState *node);
#endif /* EXECNODES_H */

Some files were not shown because too many files have changed in this diff Show More