forked from huawei/openGauss-server
code for Global-Partition-Index feature
Signed-off-by: xiliu <xiliu_h@163.com>
This commit is contained in:
parent
339cd59f26
commit
c040d78287
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ',');
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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... */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ static void vacuumLeafPage(spgBulkDeleteState* bds, Relation index, Buffer buffe
|
|||
if (lt->tupstate == SPGIST_LIVE) {
|
||||
Assert(ItemPointerIsValid(<->heapPtr));
|
||||
|
||||
if (bds->callback(<->heapPtr, bds->callback_state)) {
|
||||
if (bds->callback(<->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(<->heapPtr));
|
||||
|
||||
if (bds->callback(<->heapPtr, bds->callback_state)) {
|
||||
if (bds->callback(<->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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue