diff --git a/contrib/dblink/dblink.cpp b/contrib/dblink/dblink.cpp index 1ed1e77a0a882ae275fa2f2fdff5b06c567581eb..f03d2a4e26716e84d7246a5818e04b489b64e935 100755 --- a/contrib/dblink/dblink.cpp +++ b/contrib/dblink/dblink.cpp @@ -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; } diff --git a/contrib/tcn/tcn.cpp b/contrib/tcn/tcn.cpp index 7412a2dada74a82411c687cd622c247988aaa7aa..e758d49dfec7dd6879c7db9260079f7deda5b988 100644 --- a/contrib/tcn/tcn.cpp +++ b/contrib/tcn/tcn.cpp @@ -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, ','); diff --git a/doc/src/sgml/ref/create_index.sgmlin b/doc/src/sgml/ref/create_index.sgmlin index e023227c13e1fa675bebe2ca3ed4d45b0875dec7..9ff7b1fa5917a27a7c78f7504bb578f0b38f9b1e 100644 --- a/doc/src/sgml/ref/create_index.sgmlin +++ b/doc/src/sgml/ref/create_index.sgmlin @@ -17,7 +17,7 @@ CREATE [ UNIQUE ] INDEX [ [schema_name.] index_name ] ON table_name [ USING meth [ WHERE predicate ]; CREATE [ UNIQUE ] INDEX [ [schema_name.] index_name ] ON table_name [ USING method ] ( {{ column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [ ASC | DESC ] [ NULLS LAST ] }[, ...] ) -LOCAL [ ( { PARTITION index_partition_name [ TABLESPACE index_partition_tablespace ] } [, ...] ) ] +[ LOCAL [ ( { PARTITION index_partition_name [ TABLESPACE index_partition_tablespace ] } [, ...] ) ] | GLOBAL ] [ WITH ( { storage_parameter = value } [, ...] ) ] [ TABLESPACE tablespace_name ]; diff --git a/src/bin/psql/describe.cpp b/src/bin/psql/describe.cpp index 0548ac44612306fa4676dd7fc0d6146a90f887fb..2b6060b4b5c813ea990d96661fcba537460ecafc 100755 --- a/src/bin/psql/describe.cpp +++ b/src/bin/psql/describe.cpp @@ -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("global partition 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) diff --git a/src/common/backend/catalog/aclchk.cpp b/src/common/backend/catalog/aclchk.cpp index 9a8fe101442ff35fc83159cb5e3293bf169d5f92..a6f79df0fb5e7270cd73583e24be4d79596db530 100755 --- a/src/common/backend/catalog/aclchk.cpp +++ b/src/common/backend/catalog/aclchk.cpp @@ -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)))); diff --git a/src/common/backend/catalog/cstore_ctlg.cpp b/src/common/backend/catalog/cstore_ctlg.cpp index 4bc3feb3e43d52896e24cd28a41766b9de462b61..2ecbca25f0e98a13345cb084e82a0dc716547b53 100755 --- a/src/common/backend/catalog/cstore_ctlg.cpp +++ b/src/common/backend/catalog/cstore_ctlg.cpp @@ -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(); diff --git a/src/common/backend/catalog/dependency.cpp b/src/common/backend/catalog/dependency.cpp index b17d24b89c1c17ecb9c2091e9594a08a1a9b38c6..8c3556027bd240f30a818bfaff733adf19537b8c 100755 --- a/src/common/backend/catalog/dependency.cpp +++ b/src/common/backend/catalog/dependency.cpp @@ -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: diff --git a/src/common/backend/catalog/dfsstore_ctlg.cpp b/src/common/backend/catalog/dfsstore_ctlg.cpp index 1c51b9c2f8f35880be07cff9c7393c3bc89370c7..136ce25c11b7678ff24e36e2c2227308f0d0b374 100755 --- a/src/common/backend/catalog/dfsstore_ctlg.cpp +++ b/src/common/backend/catalog/dfsstore_ctlg.cpp @@ -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(); diff --git a/src/common/backend/catalog/heap.cpp b/src/common/backend/catalog/heap.cpp index eda2406eff74a741f6f94123d3013afae907ffa8..95c7004688c496347b90bf3e014901414e71247e 100755 --- a/src/common/backend/catalog/heap.cpp +++ b/src/common/backend/catalog/heap.cpp @@ -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); @@ -5873,7 +5881,8 @@ List* AddRelClusterConstraints(Relation rel, List* clusterKeys) true, /* Is Validated */ RelationGetRelid(rel), /* relation */ attNums, /* attrs in the constraint */ - colNum, /* # attrs in the constraint */ + colNum, /* # key attrs in the constraint */ + colNum, /* total # attrs (include attrs and key attrs) in the constraint */ InvalidOid, /* not a domain constraint */ InvalidOid, /* no associated index */ InvalidOid, /* Foreign key fields */ @@ -6119,3 +6128,25 @@ static void LockSeqConstraints(Relation rel, List* Constraints) return; } + +/* Get index's indnkeyatts value with indexTuple */ +int GetIndexKeyAttsByTuple(Relation relation, HeapTuple indexTuple) +{ + bool isnull = false; + Datum indkeyDatum; + TupleDesc tupleDesc = RelationIsValid(relation) ? RelationGetDescr(relation) : GetDefaultPgIndexDesc(); + Form_pg_index index = (Form_pg_index)GETSTRUCT(indexTuple); + + /* + * This scenario will only occur after the upgrade, This scenario means that + * the indnatts and Indnkeyatts of all current indexes are equal + */ + if (heap_attisnull(indexTuple, Anum_pg_index_indnkeyatts, NULL)) { + return index->indnatts; + } + + indkeyDatum = fastgetattr(indexTuple, Anum_pg_index_indnkeyatts, tupleDesc, &isnull); + Assert(!isnull); + + return DatumGetInt16(indkeyDatum); +} diff --git a/src/common/backend/catalog/index.cpp b/src/common/backend/catalog/index.cpp index 6be9454f3386a4393b1547ee02acde760681b96f..9a4f922aecec3875c29fc25c41bef7463a55266a 100755 --- a/src/common/backend/catalog/index.cpp +++ b/src/common/backend/catalog/index.cpp @@ -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,41 @@ 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 +551,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 @@ -592,6 +616,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo* indexInfo, nulls[Anum_pg_index_indpred - 1] = true; values[Anum_pg_index_indisreplident - 1] = BoolGetDatum(false); + values[Anum_pg_index_indnkeyatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexKeyAttrs); tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls); /* @@ -718,6 +743,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 +859,19 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index } } + char relKind = RELKIND_INDEX; + bool isLocalPart = false; + + if (extra->isGlobalPartitionedIndex) { + relKind = RELKIND_GLOBAL_INDEX; + } + /* + * for normal relation index and global partition index, isLocalPart should be false. + * more description refers to defination of IndexCreateExtraArgs; + */ + if (extra->isPartitionedIndex && !extra->isGlobalPartitionedIndex) { + isLocalPart = true; + } /* * 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 +884,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, + isLocalPart, false, shared_relation, mapped_relation, @@ -894,7 +937,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 +1064,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 +1075,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 +1128,7 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index else Assert(indexRelation->rd_indexcxt != NULL); + indexRelation->rd_indnkeyatts = 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 +1153,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 +1265,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 +1346,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,27 +1840,29 @@ void index_drop(Oid indexId, bool concurrent) */ IndexInfo* BuildIndexInfo(Relation index) { - IndexInfo* ii = makeNode(IndexInfo); Form_pg_index indexStruct = index->rd_index; - int i; - int numKeys; + + IndexInfo* ii = makeIndexInfo(indexStruct->indnatts, + RelationGetIndexExpressions(index), + RelationGetIndexPredicate(index), + indexStruct->indisunique, + IndexIsReady(indexStruct), + false); /* check the number of keys, and copy attr numbers into the IndexInfo */ - numKeys = indexStruct->indnatts; - if (numKeys < 1 || numKeys > INDEX_MAX_KEYS) + int 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", numKeys, RelationGetRelid(index)))); - - ii = makeIndexInfo(indexStruct->indnatts, - RelationGetIndexExpressions(index), - RelationGetIndexPredicate(index), - indexStruct->indisunique, - IndexIsReady(indexStruct), - false); + errmsg("invalid indnatts %d for index %u", numAtts, RelationGetRelid(index)))); + } + ii->ii_NumIndexAttrs = numAtts; + ii->ii_NumIndexKeyAttrs = IndexRelationGetNumberOfKeyAttributes(index); + Assert(ii->ii_NumIndexKeyAttrs != 0); + Assert(ii->ii_NumIndexKeyAttrs <= ii->ii_NumIndexAttrs); /* fill in attribute numbers */ - for (i = 0; i < numKeys; i++) { + for (int i = 0; i < numAtts; i++) { ii->ii_KeyAttrNumbers[i] = indexStruct->indkey.values[i]; } /* fetch exclusion constraint info if any */ @@ -2110,10 +2164,11 @@ 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 */ + } else { /* don't bother for indexes */ relallvisible = 0; + } if (is_gtt) { rel->rd_rel->relpages = static_cast(relpages); @@ -2314,47 +2369,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_ext(results); + } + return stats; +} + +void UpdateStatsForGlobalIndex( + Relation heapRelation, Relation indexRelation, IndexBuildResult* stats, bool isPrimary, Oid cudescIdxOid) +{ + ListCell* partitionCell = NULL; + Oid partitionOid; + Partition partition = NULL; + List* partitionOidList = NIL; + partitionOidList = relationGetPartitionOidList(heapRelation); + int partitionIdx = 0; + + Assert(PointerIsValid(stats->all_part_tuples)); + + foreach (partitionCell, partitionOidList) { + partitionOid = lfirst_oid(partitionCell); + partition = partitionOpen(heapRelation, partitionOid, ShareLock); + partition_index_update_stats(partition, + true, + isPrimary, + (heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ? RelationGetRelid(indexRelation) : InvalidOid, + cudescIdxOid, + stats->all_part_tuples[partitionIdx]); + partitionClose(heapRelation, partition, NoLock); + partitionIdx++; } + releasePartitionOidList(&partitionOidList); + 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); } /* @@ -2366,10 +2453,10 @@ static void index_build_storage_for_bucket(Relation heapRelation, Relation index * entries of the index and heap relation as needed, using statistics * returned by ambuild as well as data passed by the caller. * - * isprimary tells whether to mark the index as a primary-key index. + * isPrimary tells whether to mark the index as a primary-key index. * isreindex indicates we are recreating a previously-existing index. * - * Note: when reindexing an existing index, isprimary can be false even if + * Note: when reindexing an existing index, isPrimary can be false even if * the index is a PK; it's already properly marked and need not be re-marked. * * Note: before Postgres 8.2, the passed-in heap and index Relations @@ -2377,11 +2464,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 +2485,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 +2549,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)); + hasbucket = (partitionType == INDEX_CREATE_NONE_PARTITION && RELATION_CREATE_BUCKET(heapRelation)) || + (partitionType != INDEX_CREATE_NONE_PARTITION && RELATION_OWN_BUCKETKEY(heapRelation)); + + IndexBuildResult* stats = NULL; if (hasbucket) { - index_build_storage_for_bucket(heapRelation, + 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 +2589,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; @@ -2520,10 +2614,10 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR heap_close(pg_index, RowExclusiveLock); } - Oid cudesc_idx_oid = InvalidOid; + Oid cudescIdxOid = InvalidOid; switch (indexInfo->ii_PgClassAttrId) { case Anum_pg_class_relcudescidx: { - cudesc_idx_oid = RelationGetRelid(indexRelation); + cudescIdxOid = RelationGetRelid(indexRelation); break; } default: @@ -2533,29 +2627,34 @@ 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, + isPrimary, (heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ? RelationGetRelid(indexRelation) : InvalidOid, - cudesc_idx_oid, + cudescIdxOid, 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. */ partition_index_update_stats(heapPartition, true, - isprimary, + isPrimary, (heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ? RelationGetRelid(indexRelation) : InvalidOid, - cudesc_idx_oid, + cudescIdxOid, heaptuples); - partition_index_update_stats(indexPartition, false, false, InvalidOid, cudesc_idx_oid, indextuples); + partition_index_update_stats(indexPartition, false, false, InvalidOid, cudescIdxOid, indextuples); + } else if (partitionType == INDEX_CREATE_GLOBAL_PARTITION) { + UpdateStatsForGlobalIndex(heapRelation, indexRelation, stats, isPrimary, cudescIdxOid); + pfree(stats->all_part_tuples); } + pfree(stats); + /* Make the updated catalog row versions visible */ CommandCounterIncrement(); @@ -2578,7 +2677,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 +3069,35 @@ 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++; + } + releasePartitionOidList(&partitionIdList); + return globalIndexTuples; +} + double IndexBuildVectorBatchScan(Relation heapRelation, Relation indexRelation, IndexInfo* indexInfo, VectorBatch* vecScanBatch, Snapshot snapshot, IndexBuildVecBatchScanCallback callback, void* callback_state, void* transferFuncs) @@ -3310,7 +3438,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 +3786,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 +3796,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 +3936,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 +3944,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 +4083,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 +4108,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 +4376,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 +4434,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 @@ -4486,7 +4646,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 @@ -4731,7 +4891,8 @@ Oid psort_create(const char* indexRelationName, Relation indexRelation, Oid tabl true, /* Is Validated */ psortRelationId, /* relation */ attrNums, /* attrs in the constraint */ - natts, /* # attrs in the constraint */ + natts, /* # key attrs in the constraint */ + natts, /* total # attrs (include attrs and key attrs) in the constraint */ InvalidOid, /* not a domain constraint */ InvalidOid, /* no associated index */ InvalidOid, /* Foreign key fields */ @@ -4953,3 +5114,13 @@ static Oid bupgrade_get_next_psort_array_pg_type_oid() return old_psort_array_pg_type_oid; } + +/* + * set IndexCreateExtraArgs, more info refer to defination of IndexCreateExtraArgs + */ +void SetIndexCreateExtraArgs(IndexCreateExtraArgs* extra, Oid psortOid, bool isPartition, bool isGlobal) +{ + extra->existingPSortOid = psortOid; + extra->isPartitionedIndex = isPartition; + extra->isGlobalPartitionedIndex = isGlobal; +} diff --git a/src/common/backend/catalog/indexing.cpp b/src/common/backend/catalog/indexing.cpp index 16f911de0d48da457de20a737da5eff01dde83f9..6ed0deb8a8f945c11a653b167f4622d9c25550a7 100644 --- a/src/common/backend/catalog/indexing.cpp +++ b/src/common/backend/catalog/indexing.cpp @@ -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 diff --git a/src/common/backend/catalog/objectaddress.cpp b/src/common/backend/catalog/objectaddress.cpp index 96a41a5b73b728ca777381a81bf40a355b5046b6..0dbc991d5fd82029a8779c14e5aad769df0cd0f3 100755 --- a/src/common/backend/catalog/objectaddress.cpp +++ b/src/common/backend/catalog/objectaddress.cpp @@ -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)))); diff --git a/src/common/backend/catalog/pg_constraint.cpp b/src/common/backend/catalog/pg_constraint.cpp index d7408a6381403df2708075643c7e10195c9a1fa6..27af27d2aadea26b2d1df00084003d0edd0b99ba 100755 --- a/src/common/backend/catalog/pg_constraint.cpp +++ b/src/common/backend/catalog/pg_constraint.cpp @@ -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,24 @@ 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; + } + + // index attrs have key attrs and include attrs, the num of include attrs maybe 0 + if (constraintNTotalKeys > constraintNKeys) { + int j = 0; + int constraintNIncludedKeys = constraintNTotalKeys - constraintNKeys; + + Datum* 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'); + pfree_ext(conincluding); + } else { + conincludingArray = NULL; + } if (foreignNKeys > 0) { Datum* fkdatums = NULL; @@ -151,6 +169,12 @@ 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 +238,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); diff --git a/src/common/backend/catalog/pg_depend.cpp b/src/common/backend/catalog/pg_depend.cpp index 42edf476bf3c122f2e3d0b73741d592570f03e4a..82dab55792a803a86d688da1d47918ed495791ce 100755 --- a/src/common/backend/catalog/pg_depend.cpp +++ b/src/common/backend/catalog/pg_depend.cpp @@ -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; + } } } diff --git a/src/common/backend/catalog/pg_object.cpp b/src/common/backend/catalog/pg_object.cpp index 4d06c70cbf829b8acbb1c18b149bcbf69e565590..9eec74a3443eabf0d9d3a5b795d314f1b3393beb 100644 --- a/src/common/backend/catalog/pg_object.cpp +++ b/src/common/backend/catalog/pg_object.cpp @@ -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: diff --git a/src/common/backend/catalog/storage_gtt.cpp b/src/common/backend/catalog/storage_gtt.cpp index 4300e82e48868873a990cbf4b15aab8a074da85b..b572261ab4254b5966fb724448fa304b6fb40694 100644 --- a/src/common/backend/catalog/storage_gtt.cpp +++ b/src/common/backend/catalog/storage_gtt.cpp @@ -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); } diff --git a/src/common/backend/catalog/toasting.cpp b/src/common/backend/catalog/toasting.cpp index bb312e45f5eda620972f967a407737600d606a97..7ae76cb8bb3a62e1a8463ba8d626e2c27dd79678 100755 --- a/src/common/backend/catalog/toasting.cpp +++ b/src/common/backend/catalog/toasting.cpp @@ -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, diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 8dc1db1f2784abb24014c5bac2eee867d338de78..5e2a4d6316b1bfd35b329b91e767a82412cb5c7e 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -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); diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 2e9cbe382466ba35d5d24f171942b5a998a1c691..7c1a12bcc5be1f5bcd2a8012ffc578d7daa73471 100755 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -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); diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index 41c48689d9cd06d69e755deba68f96768eab3c43..c4b11f52e3b6b4a02257c60f60e527f32f0bfd18 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -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); diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index 26493109e48723b648f8b25db8f5b49f7a6dd962..44b614579c57ebb0e26b5c8a1b56dba96702638d 100644 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -4619,6 +4619,7 @@ static IndexStmt* _readIndexStmt() READ_STRING_FIELD(accessMethod); READ_STRING_FIELD(tableSpace); READ_NODE_FIELD(indexParams); + READ_NODE_FIELD(indexIncludingParams); READ_NODE_FIELD(options); READ_NODE_FIELD(whereClause); READ_NODE_FIELD(excludeOpNames); diff --git a/src/common/backend/nodes/tidbitmap.cpp b/src/common/backend/nodes/tidbitmap.cpp index b6486f76ebae05d4b80a7dc87c78335f247c7321..647bdcbc293df479f5bf3254fe3332c9f4db6d49 100755 --- a/src/common/backend/nodes/tidbitmap.cpp +++ b/src/common/backend/nodes/tidbitmap.cpp @@ -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; +} diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 201f5b61fc2e9bd1f3c9ea480c68852ff2c673ed..b86c8bb928daed547a59b3d2168eb9723c47679e 100755 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -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 diff --git a/src/common/backend/parser/parse_target.cpp b/src/common/backend/parser/parse_target.cpp index 34154c1487ab7891096a2ee03bb17e4129e1cf1a..b92d7c597ec4528f65c52174357d55900a22f3f8 100755 --- a/src/common/backend/parser/parse_target.cpp +++ b/src/common/backend/parser/parse_target.cpp @@ -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++) { diff --git a/src/common/backend/parser/parse_utilcmd.cpp b/src/common/backend/parser/parse_utilcmd.cpp index 083da321b8822081871c60a0467734762e0f8c3d..9b3dd39c837592dfe5daa456d726bac64c5720f3 100644 --- a/src/common/backend/parser/parse_utilcmd.cpp +++ b/src/common/backend/parser/parse_utilcmd.cpp @@ -1996,6 +1996,7 @@ static IndexStmt* generateClonedIndexStmt( Datum datum; bool isnull = false; bool isResize = false; + int indnkeyatts; /* * Fetch pg_class tuple of source index. We can't use the copy in the @@ -2011,6 +2012,7 @@ static IndexStmt* generateClonedIndexStmt( htIdx = source_idx->rd_indextuple; idxrec = (Form_pg_index)GETSTRUCT(htIdx); indrelid = idxrec->indrelid; + indnkeyatts = IndexRelationGetNumberOfKeyAttributes(source_idx); /* Fetch pg_am tuple for source index from relcache entry */ amrec = source_idx->rd_am; @@ -2149,9 +2151,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 < indnkeyatts; keyno++) { IndexElem* iparam = NULL; AttrNumber attnum = idxrec->indkey.values[keyno]; uint16 opt = (uint16)source_idx->rd_indoption[keyno]; @@ -2240,6 +2243,12 @@ static IndexStmt* generateClonedIndexStmt( } } + /* Handle included columns separately */ + if (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 +2482,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 +2640,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; @@ -2669,6 +2680,7 @@ static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtCon Datum indclassDatum; bool isnull = true; int i; + int indnkeyatts; /* Grammar should not allow this with explicit column list */ AssertEreport(constraint->keys == NIL, MOD_OPT, ""); @@ -2694,6 +2706,7 @@ static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtCon /* Open the index (this will throw an error if it is not an index) */ indexRel = index_open(indexOid, AccessShareLock); indexForm = indexRel->rd_index; + indnkeyatts = IndexRelationGetNumberOfKeyAttributes(indexRel); /* check the conditons for this function, * and verify the index is usable @@ -2724,22 +2737,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 < 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 +2785,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; - 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 (!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; + + foreach (inher, cxt->inhRelations) { + RangeVar* inh = lfirst_node(RangeVar, inher); + Relation rel; + int count; + + 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) { + 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); - if (inhattr->attisdropped) - continue; - if (strcmp(key, inhname) == 0) { - found = true; + 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; + /* + * 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 +2994,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 +3001,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 +3165,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)); diff --git a/src/common/backend/utils/adt/dbsize.cpp b/src/common/backend/utils/adt/dbsize.cpp index 025e687a20768d387bfaa2baafda8bed589746cd..6295f3b8ab95b986f9198551b7fa8641a90ffd17 100755 --- a/src/common/backend/utils/adt/dbsize.cpp +++ b/src/common/backend/utils/adt/dbsize.cpp @@ -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); diff --git a/src/common/backend/utils/adt/ruleutils.cpp b/src/common/backend/utils/adt/ruleutils.cpp index b5ef2c888e64b1a7b8a703f27c930853e2040d35..7d23b9ebd99acc38c0a13edbe6987a40d7218f37 100755 --- a/src/common/backend/utils/adt/ruleutils.cpp +++ b/src/common/backend/utils/adt/ruleutils.cpp @@ -45,6 +45,7 @@ #include "catalog/pg_synonym.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" +#include "catalog/heap.h" #include "commands/comment.h" #include "commands/defrem.h" #include "commands/tablespace.h" @@ -2225,6 +2226,7 @@ static char* pg_get_indexdef_worker( StringInfoData buf; char* str = NULL; char* sep = NULL; + int indnkeyatts; /* * Fetch the pg_index tuple by the Oid of the index @@ -2237,6 +2239,7 @@ static char* pg_get_indexdef_worker( indrelid = idxrec->indrelid; Assert(indexrelid == idxrec->indexrelid); + indnkeyatts = GetIndexKeyAttsByTuple(NULL, ht_idx); /* Must get indcollation, indclass, and indoption the hard way */ indcoll_datum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indcollation, &isnull); @@ -2323,6 +2326,12 @@ static char* pg_get_indexdef_worker( Oid keycoltype; Oid keycolcollation; + /* + * Ignore non-key attributes if told to. + */ + if (keyno >= indnkeyatts) { + break; + } if (!colno) { appendStringInfoString(&buf, sep); } @@ -2364,11 +2373,16 @@ static char* pg_get_indexdef_worker( if (!attrs_only && (!colno || colno == keyno + 1)) { Oid indcoll; + if (keyno >= 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 +2412,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 +2668,15 @@ 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)) { diff --git a/src/common/backend/utils/adt/selfuncs.cpp b/src/common/backend/utils/adt/selfuncs.cpp index a007f26548f4e960eb693852dcec04f3a8147a83..57d0261f947d9bde84d170990691a37a4c1732dc 100644 --- a/src/common/backend/utils/adt/selfuncs.cpp +++ b/src/common/backend/utils/adt/selfuncs.cpp @@ -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; diff --git a/src/common/backend/utils/cache/partcache.cpp b/src/common/backend/utils/cache/partcache.cpp index af16c316dabea131bc5c2f16dbd925a1cc119099..1484067d4d43dbeb78028e443587c296717a7a00 100755 --- a/src/common/backend/utils/cache/partcache.cpp +++ b/src/common/backend/utils/cache/partcache.cpp @@ -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; } @@ -1187,15 +1189,17 @@ Relation partitionGetRelation(Relation rel, Partition part) relation->rd_rel->relcudescidx = part->pd_part->relcudescidx; relation->rd_rel->reldeltarelid = part->pd_part->reldeltarelid; relation->rd_rel->reldeltaidx = part->pd_part->reldeltaidx; - relation->rd_bucketoid = rel->rd_bucketoid; + relation->rd_bucketoid = rel->rd_bucketoid; 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; + relation->rd_indnkeyatts = rel->rd_indnkeyatts; if (!OidIsValid(rel->rd_rel->relam)) { relation->rd_indexcxt = NULL; @@ -1318,7 +1322,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 +1397,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 +1521,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 */ +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 get 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 wait_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; +} diff --git a/src/common/backend/utils/cache/relcache.cpp b/src/common/backend/utils/cache/relcache.cpp index 4a343ca08a9579a3bde92e1ebf12d347f5adde0a..3be4cf835713dec91cf42cb126450e55fda8cb83 100644 --- a/src/common/backend/utils/cache/relcache.cpp +++ b/src/common/backend/utils/cache/relcache.cpp @@ -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,8 +1258,9 @@ 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,8 +1269,9 @@ 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; + } /* * Set up constraint/default info @@ -1278,7 +1280,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; @@ -2004,6 +2006,27 @@ static void relation_init_physical_addr(Relation relation) relation->rd_node.bucketNode = InvalidBktId; } +/* + * Initialize index key number for an index relation + */ +static void IndexRelationInitKeyNums(Relation relation) +{ + int indnkeyatts; + bool isnull = false; + + if (heap_attisnull(relation->rd_indextuple, Anum_pg_index_indnkeyatts, NULL)) { + /* This scenario will only occur after the upgrade */ + indnkeyatts = RelationGetNumberOfAttributes(relation); + } else { + Datum indkeyDatum = + heap_getattr(relation->rd_indextuple, Anum_pg_index_indnkeyatts, get_pg_index_descriptor(), &isnull); + Assert(!isnull); + indnkeyatts = DatumGetInt16(indkeyDatum); + } + + relation->rd_indnkeyatts = indnkeyatts; +} + /* * Initialize index-access-method support data for an index relation */ @@ -2020,7 +2043,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 +2064,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 +2081,21 @@ 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)))); + + IndexRelationInitKeyNums(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 +2112,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 +2130,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 +2142,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 +2160,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 +2168,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 +2588,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 +2713,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 +3006,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 +3021,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 +3859,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 +3897,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); @@ -4513,6 +4549,17 @@ static TupleDesc get_pg_index_descriptor(void) return u_sess->relcache_cxt.pgindexdesc; } +/* + * Replace with get_pg_index_descriptor + * + * Ban to release return value since it is a static value, and it is used + * frequently in relation operation + */ +TupleDesc GetDefaultPgIndexDesc(void) +{ + return get_pg_index_descriptor(); +} + /* * Load any default attribute value definitions for the relation. */ @@ -4649,6 +4696,36 @@ 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 + */ +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 +5456,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 +5519,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 +5531,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 +5589,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 +5606,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 +5620,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 +5835,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; @@ -5775,6 +5862,7 @@ static bool load_relcache_init_file(bool shared) /* Fix up internal pointers in the tuple -- see heap_copytuple */ rel->rd_indextuple->t_data = (HeapTupleHeader)((char*)rel->rd_indextuple + HEAPTUPLESIZE); rel->rd_index = (Form_pg_index)GETSTRUCT(rel->rd_indextuple); + IndexRelationInitKeyNums(rel); /* next, read the access method tuple form */ if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) @@ -6099,7 +6187,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 */ diff --git a/src/common/backend/utils/sort/tuplesort.cpp b/src/common/backend/utils/sort/tuplesort.cpp index bb12ea76cc160dc04b1982345ed6f7dbf3a585a7..552a516d2ae6cf6f6c12219fdfe201e06964bd42 100644 --- a/src/common/backend/utils/sort/tuplesort.cpp +++ b/src/common/backend/utils/sort/tuplesort.cpp @@ -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; } diff --git a/src/common/backend/utils/time/tqual.cpp b/src/common/backend/utils/time/tqual.cpp index 57f0c4b395aa90af8fdb3f8eebc8552f26ef81a0..0457263eb3ff317ed3b946cc6ff1e25d813a8b40 100755 --- a/src/common/backend/utils/time/tqual.cpp +++ b/src/common/backend/utils/time/tqual.cpp @@ -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 */ diff --git a/src/gausskernel/bootstrap/bootparse.y b/src/gausskernel/bootstrap/bootparse.y index 7aab250bbece9f420718a3630b6f5d59f7d1986f..9fd1043d789803483535acb9d19a4cb023f3798a 100755 --- a/src/gausskernel/bootstrap/bootparse.y +++ b/src/gausskernel/bootstrap/bootparse.y @@ -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; diff --git a/src/gausskernel/bootstrap/bootstrap.cpp b/src/gausskernel/bootstrap/bootstrap.cpp index 2dbaabad659f69c9d26505cbac9ef44adfba1412..a418a6e85737ca2749637e12449ee0de9de8bec5 100755 --- a/src/gausskernel/bootstrap/bootstrap.cpp +++ b/src/gausskernel/bootstrap/bootstrap.cpp @@ -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); diff --git a/src/gausskernel/optimizer/README b/src/gausskernel/optimizer/README index af2f77a4d0d1e93eda45622efe37f1404157bd83..c21c1e8173a1fc499207d64742159e1f7d492a11 100644 --- a/src/gausskernel/optimizer/README +++ b/src/gausskernel/optimizer/README @@ -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 diff --git a/src/gausskernel/optimizer/commands/analyze.cpp b/src/gausskernel/optimizer/commands/analyze.cpp index 4112321a32c5262c51c5f7fd5ac6744f05f02468..ab44888ed949cceeeea6dcf740db9299a1bac594 100755 --- a/src/gausskernel/optimizer/commands/analyze.cpp +++ b/src/gausskernel/optimizer/commands/analyze.cpp @@ -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; diff --git a/src/gausskernel/optimizer/commands/cluster.cpp b/src/gausskernel/optimizer/commands/cluster.cpp old mode 100644 new mode 100755 index c5756519ddc68a123d4f60f31540957fe3f849b3..80693379f96a07731baab08b8be7a4e557b09097 --- a/src/gausskernel/optimizer/commands/cluster.cpp +++ b/src/gausskernel/optimizer/commands/cluster.cpp @@ -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 * diff --git a/src/gausskernel/optimizer/commands/indexcmds.cpp b/src/gausskernel/optimizer/commands/indexcmds.cpp index 275f58c25a88978a7eeb6068df86471c7b85a957..5815222291462b93ae8b1fa52941c9b731d16463 100755 --- a/src/gausskernel/optimizer/commands/indexcmds.cpp +++ b/src/gausskernel/optimizer/commands/indexcmds.cpp @@ -90,6 +90,11 @@ 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(HeapTuple tarTuple, const int16* currAttrsArray, int currKeyNum); +static int AttrComparator(const void* a, const void* b); +static void AddIndexColumnForGpi(IndexStmt* stmt); /* * CheckIndexCompatible @@ -115,7 +120,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 +187,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 +233,7 @@ bool CheckIndexCompatible(Oid oldId, char* accessMethodName, List* attributeList } /* Any change in operator class or collation breaks compatibility. */ - old_natts = indexForm->indnatts; + old_natts = GetIndexKeyAttsByTuple(NULL, tuple); Assert(old_natts == numberOfAttributes); d = SysCacheGetAttr(INDEXRELID, tuple, Anum_pg_index_indcollation, &isnull); @@ -310,6 +321,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 +333,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 +363,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 +391,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 +413,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 +433,50 @@ 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"))); + /* Add special index columns tableoid to global partition index */ + if (stmt->isGlobal) { + AddIndexColumnForGpi(stmt); + } + + if (list_intersection(stmt->indexParams, stmt->indexIncludingParams) != NIL) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("included columns must not intersect with key columns"))); + } + + if (list_length(stmt->indexParams) <= 0) { + ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("must specify at least one column"))); + } + if (list_length(stmt->indexParams) > INDEX_MAX_KEYS) { + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("cannot use more than %d columns in an index", INDEX_MAX_KEYS))); + } + + /* + * 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 +524,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 +571,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 +585,39 @@ 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"))); + } else { + if (OidIsValid(tablespaceId) && tablespaceId != u_sess->proc_cxt.MyDatabaseTableSpace) { + AclResult aclresult; + + aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) { + aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tablespaceId)); + } } } /* * 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 +656,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 +700,7 @@ 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 (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -663,6 +734,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 +758,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al collationObjectId, classObjectId, coloptions, - stmt->indexParams, + allIndexParams, stmt->excludeOpNames, relationId, accessMethodName, @@ -699,6 +771,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 +875,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 +960,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 +1189,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 +1478,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 +1529,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 +1581,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 +2316,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 +2383,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); @@ -2586,7 +2707,7 @@ void addIndexForPartition(Relation partitionedRelation, Oid partOid) } /* get oid list of indexRel */ - indelist = RelationGetIndexList(partitionedRelation); + indelist = RelationGetSpecificKindIndexList(partitionedRelation, false); if (!PointerIsValid(indelist)) { return; } @@ -2887,6 +3008,7 @@ static Oid buildInformationalConstraint( true, RelationGetRelid(rel), indexInfo->ii_KeyAttrNumbers, + indexInfo->ii_NumIndexKeyAttrs, indexInfo->ii_NumIndexAttrs, InvalidOid, /* no domain */ indexRelationId, /* InvalidOid */ @@ -2911,3 +3033,126 @@ 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 with 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 = false; + char currIdxKind = isGlobal ? RELKIND_GLOBAL_INDEX : RELKIND_INDEX; + int currSize = sizeof(AttrNumber) * 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); + + AttrNumber* currAttrsArray = (AttrNumber*)palloc0(currSize); + rc = memcpy_s(currAttrsArray, currSize, indexInfo->ii_KeyAttrNumbers, currSize); + securec_check(rc, "\0", "\0"); + qsort(currAttrsArray, currKeyNum, sizeof(AttrNumber), 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; + } + + heap_getattr(tarTuple, Anum_pg_index_indexprs, RelationGetDescr(indexRelation), &isNull); + /* + * check expressions: This condition looks confused, here we judge whether tow index has expressions, + * like XOR operation. if two indexes both have expression or not, we continue to + * check next condition, otherwise, two index could be treated as compatible. + */ + if ((indexInfo->ii_Expressions != NIL) != (!isNull)) { + continue; + } + if (!CheckSimpleAttrsConsistency(tarTuple, 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*)DatumGetPointer(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(HeapTuple tarTuple, const int16* currAttrsArray, int currKeyNum) +{ + Form_pg_index indexTuple = (Form_pg_index)GETSTRUCT(tarTuple); + int tarKeyNum = GetIndexKeyAttsByTuple(NULL, tarTuple); + 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 *(AttrNumber*)a - *(AttrNumber*)b; +} + +/* Set the internal index column partoid for global partition index */ +static void AddIndexColumnForGpi(IndexStmt* stmt) +{ + 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); +} diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index 3ed0004647c388cae9dc516f54cd88cfd826a225..a2fe4f5b75c21fa9db28c6f2d7a44671d4f94f0f 100644 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -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,15 @@ 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 = classform->relkind; + if (classform->relkind == RELKIND_GLOBAL_INDEX) { + expected_relkind = RELKIND_INDEX; + } + + if ((expected_relkind != relkind) && !(u_sess->attr.attr_common.IsInplaceUpgrade && + relkind == RELKIND_RELATION && expected_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 +2826,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 +4207,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 +4654,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 +4989,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 +6676,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 +7193,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: @@ -8135,6 +8149,7 @@ static void ATExecDropNotNull(Relation rel, const char* colName, LOCKMODE lockmo HeapTuple indexTuple; Form_pg_index indexStruct; int i; + int indnkeyatts; indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid)); if (!HeapTupleIsValid(indexTuple)) { @@ -8142,6 +8157,7 @@ static void ATExecDropNotNull(Relation rel, const char* colName, LOCKMODE lockmo ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for index %u", indexoid))); } indexStruct = (Form_pg_index)GETSTRUCT(indexTuple); + indnkeyatts = GetIndexKeyAttsByTuple(NULL, indexTuple); /* If the index is not a primary key, skip the check */ if (indexStruct->indisprimary) { @@ -8149,7 +8165,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 < indnkeyatts; i++) { if (indexStruct->indkey.values[i] == attnum) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), @@ -8290,11 +8306,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 +9561,7 @@ static void ATAddForeignKeyConstraint(AlteredTableInfo* tab, Relation rel, Const RelationGetRelid(rel), fkattnum, numfks, + numfks, InvalidOid, /* not a domain constraint */ indexOid, RelationGetRelid(pkrel), @@ -9817,6 +9834,7 @@ static int transformFkeyGetPrimaryKey( Datum indclassDatum; bool isnull = false; oidvector* indclass = NULL; + int indnkeyatts = 0; int i; /* @@ -9838,6 +9856,7 @@ static int transformFkeyGetPrimaryKey( } indexStruct = (Form_pg_index)GETSTRUCT(indexTuple); + indnkeyatts = GetIndexKeyAttsByTuple(NULL, indexTuple); if (indexStruct->indisprimary && IndexIsValid(indexStruct)) { /* * Refuse to use a deferrable primary key. This is per SQL spec, @@ -9876,7 +9895,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 < indnkeyatts; i++) { int pkattno = indexStruct->indkey.values[i]; attnums[i] = pkattno; @@ -9916,6 +9935,7 @@ static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16* attnums, foreach (indexoidscan, indexoidlist) { HeapTuple indexTuple; Form_pg_index indexStruct; + int indnkeyatts; int i, j; indexoid = lfirst_oid(indexoidscan); @@ -9925,13 +9945,14 @@ static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16* attnums, ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for index %u", indexoid))); } indexStruct = (Form_pg_index)GETSTRUCT(indexTuple); + indnkeyatts = GetIndexKeyAttsByTuple(NULL, indexTuple); /* * Must have the right number of columns; must be unique and not a * 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 (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 +10974,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 +11742,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 +11925,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 +11972,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 +12000,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 +12528,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 +14418,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 +15626,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 +16586,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 +16683,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 +17019,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 +17673,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 +17826,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 +18044,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 +18054,7 @@ static void ATExecExchangePartition(Relation partTableRel, AlterTableCmd* cmd) list_free_ext(partIndexList); list_free_ext(ordIndexList); } - + heap_close(ordTableRel, NoLock); } @@ -18198,11 +18284,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 +18575,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 +19342,9 @@ static void ATExecSplitPartition(Relation partTableRel, AlterTableCmd* cmd) #endif list_free_ext(newPartOidList); + + // set global index unusable + ATUnusableGlobalIndex(partTableRel); } // check split point @@ -20660,7 +20752,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 +20816,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 +20989,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); diff --git a/src/gausskernel/optimizer/commands/trigger.cpp b/src/gausskernel/optimizer/commands/trigger.cpp index c34e47a1e3b658575ad547a7366cbe3d8b298c10..c406f6ed0628abe091446d38a58163b71eb6a965 100644 --- a/src/gausskernel/optimizer/commands/trigger.cpp +++ b/src/gausskernel/optimizer/commands/trigger.cpp @@ -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 */ diff --git a/src/gausskernel/optimizer/commands/typecmds.cpp b/src/gausskernel/optimizer/commands/typecmds.cpp index 03b4110cf5a1edc0517d378b1f11ae9aba2dada3..789f7c07babec2e48b08abf05ab96384d3248b23 100755 --- a/src/gausskernel/optimizer/commands/typecmds.cpp +++ b/src/gausskernel/optimizer/commands/typecmds.cpp @@ -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 */ diff --git a/src/gausskernel/optimizer/commands/vacuum.cpp b/src/gausskernel/optimizer/commands/vacuum.cpp index 7aa70c3a814718f23317ce964877955a53fd9ee1..bf9d553fd0fe2a4c1b5f882097a2245062c070a6 100755 --- a/src/gausskernel/optimizer/commands/vacuum.cpp +++ b/src/gausskernel/optimizer/commands/vacuum.cpp @@ -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* nindexesGlobal, 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); + } + } + *nindexesGlobal = 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 nindexesGlobal, Relation* Irel, Relation* indexrel, Partition* indexpart, LOCKMODE lockmode) { - if (Irel == NULL || indexpart == NULL || indexrel == NULL) { + if (Irel == NULL) { return; } + int nindexes_local = nindexes - nindexesGlobal; 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,164 @@ 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; + } + // 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); +} + diff --git a/src/gausskernel/optimizer/commands/vacuumlazy.cpp b/src/gausskernel/optimizer/commands/vacuumlazy.cpp index 90ad2d8eef7ddb6ec444b90eea3e49fdd304a735..469a2d37476255cb91cecb625b03d84b7f38ccda 100644 --- a/src/gausskernel/optimizer/commands/vacuumlazy.cpp +++ b/src/gausskernel/optimizer/commands/vacuumlazy.cpp @@ -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 nindexesGlobal; 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, &nindexesGlobal, &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 - nindexesGlobal; 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 - nindexesGlobal) >= 0); + Relation classRel = heap_open(RelationRelationId, RowExclusiveLock); + for (int idx = nindexes - nindexesGlobal; 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, nindexesGlobal, 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, diff --git a/src/gausskernel/optimizer/path/allpaths.cpp b/src/gausskernel/optimizer/path/allpaths.cpp index 43d6e5d4a50cca3b0ae02e269fcbd645519ab118..9b1fab1cc0381a50b76ee941dbcadef06abd1e63 100755 --- a/src/gausskernel/optimizer/path/allpaths.cpp +++ b/src/gausskernel/optimizer/path/allpaths.cpp @@ -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 */ diff --git a/src/gausskernel/optimizer/path/costsize.cpp b/src/gausskernel/optimizer/path/costsize.cpp index e17dcb36bc4b920c90bcf37e435bdeb3045ba8c0..d38a1ee049e202e530ff4043691aaae66ca4060e 100644 --- a/src/gausskernel/optimizer/path/costsize.cpp +++ b/src/gausskernel/optimizer/path/costsize.cpp @@ -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; } diff --git a/src/gausskernel/optimizer/path/indxpath.cpp b/src/gausskernel/optimizer/path/indxpath.cpp index 6c0597247fd5109760a470f20d99f4e5528821d9..cc8d8456b30c7f9fc6fe1f8941e41a351a656691 100755 --- a/src/gausskernel/optimizer/path/indxpath.cpp +++ b/src/gausskernel/optimizer/path/indxpath.cpp @@ -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,14 @@ 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 + * Ignore local partition or normal index if caller 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 +1084,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 +1093,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 +1124,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 +1250,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 +1292,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 +1409,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 +1417,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 gpinPaths = list_length(globalPartIndexPaths); + PathClauseUsage** globalPathinfoarray; AssertEreport(npaths > 0, MOD_OPT, "Path number is incorrect"); - if (npaths == 1) + if (npaths == 1 && gpinPaths == 0) return (Path*)linitial(paths); /* easy case */ /* @@ -1214,106 +1478,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); - - 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; + pathinfoarray = GetPathClauseUsage(paths, clauselist, &npaths); - 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, &gpinPaths); /* If only one surviving path, we're done */ - if (npaths == 1) + if (npaths == 1 && gpinPaths == 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 (gpinPaths > 1) { + qsort(globalPathinfoarray, (size_t)gpinPaths, 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 */ - - for (j = i + 1; j < npaths; j++) { - Cost newcost; - - 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"); + 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 */ + + ChooseBitmapAndWithMultiIndex(root, rel, &chooseInfo, pathinfoarray, npaths); + + /* + * 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 (gpinPaths > 0) { + chooseInfo.startPath = 0; + ChooseBitmapAndWithMultiIndex(root, rel, &chooseInfo, globalPathinfoarray, gpinPaths); } /* 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 +1968,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 +2036,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 +2112,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 +2122,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 +2373,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 +2424,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 +2433,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 +2601,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 +2714,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 +2785,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 +3145,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 +3480,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 */ diff --git a/src/gausskernel/optimizer/path/orindxpath.cpp b/src/gausskernel/optimizer/path/orindxpath.cpp index c221ac59c208190add670d809d9c2e1125dafc50..17bd7a022b46ea319cd986aac3023720b61d5463 100755 --- a/src/gausskernel/optimizer/path/orindxpath.cpp +++ b/src/gausskernel/optimizer/path/orindxpath.cpp @@ -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); diff --git a/src/gausskernel/optimizer/path/pathkeys.cpp b/src/gausskernel/optimizer/path/pathkeys.cpp index a437d2d9fc321bd5f625060167aba375a1048a7c..3e773aa9668d929f0f54347f5c18ff73e22aa30c 100755 --- a/src/gausskernel/optimizer/path/pathkeys.cpp +++ b/src/gausskernel/optimizer/path/pathkeys.cpp @@ -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; diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp index d2391db522c4bd815d6db613d4e838122bcae745..d45c1f38d1777e90deb094f95eceabb240caaad4 100644 --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -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; diff --git a/src/gausskernel/optimizer/util/pathnode.cpp b/src/gausskernel/optimizer/util/pathnode.cpp index 741a74ad731117d984dd74aeda428f7ad7083f3e..97d119544b6529b52465c304ac22ad0b1787c5aa 100755 --- a/src/gausskernel/optimizer/util/pathnode.cpp +++ b/src/gausskernel/optimizer/util/pathnode.cpp @@ -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. diff --git a/src/gausskernel/optimizer/util/plancat.cpp b/src/gausskernel/optimizer/util/plancat.cpp index 70b2d7bcbfbee0565091d13b5d61b0e101df8005..6dc7f12bd3f493cc01c84182cc25f4744ecb230f 100644 --- a/src/gausskernel/optimizer/util/plancat.cpp +++ b/src/gausskernel/optimizer/util/plancat.cpp @@ -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 = IndexRelationGetNumberOfKeyAttributes(indexRelation); 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; } diff --git a/src/gausskernel/optimizer/util/pruning.cpp b/src/gausskernel/optimizer/util/pruning.cpp index 682157bc578c996670a6832507645ed7fbfa39d0..3285ca9eb1abe82f70273ba2730f5b2b483ea137 100755 --- a/src/gausskernel/optimizer/util/pruning.cpp +++ b/src/gausskernel/optimizer/util/pruning.cpp @@ -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), diff --git a/src/gausskernel/process/postmaster/autovacuum.cpp b/src/gausskernel/process/postmaster/autovacuum.cpp index 0f7de6aa57b98222a2894d23352e930635090ce3..e9a4ec0761c07689c433063d6e5c0c166a6e5f47 100755 --- a/src/gausskernel/process/postmaster/autovacuum.cpp +++ b/src/gausskernel/process/postmaster/autovacuum.cpp @@ -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"); diff --git a/src/gausskernel/process/postmaster/pgstat.cpp b/src/gausskernel/process/postmaster/pgstat.cpp index 1c9303649cf8abd0d7722051530423cf5f8d0621..e9fb21929200a68533e2c207be4aad0431a10c71 100644 --- a/src/gausskernel/process/postmaster/pgstat.cpp +++ b/src/gausskernel/process/postmaster/pgstat.cpp @@ -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; } diff --git a/src/gausskernel/process/tcop/utility.cpp b/src/gausskernel/process/tcop/utility.cpp index fd4a26241c6ac94bf5769b8f8d28fe6755e1326a..c30c057af5132fe3b4bc97def86d1bef21a02522 100644 --- a/src/gausskernel/process/tcop/utility.cpp +++ b/src/gausskernel/process/tcop/utility.cpp @@ -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 diff --git a/src/gausskernel/runtime/executor/execMain.cpp b/src/gausskernel/runtime/executor/execMain.cpp index d9d527ad4aee912aaedb28e54fa2d9d528ffabf5..7c314ab2be04ef24b0302a5886a66c7242421b3c 100644 --- a/src/gausskernel/runtime/executor/execMain.cpp +++ b/src/gausskernel/runtime/executor/execMain.cpp @@ -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... */ diff --git a/src/gausskernel/runtime/executor/execUtils.cpp b/src/gausskernel/runtime/executor/execUtils.cpp index c624cfc358a2acbd4103697c0c4dea50ec5f0f9f..8736d335481d0d8cb90c6820d6c3f95c06827346 100755 --- a/src/gausskernel/runtime/executor/execUtils.cpp +++ b/src/gausskernel/runtime/executor/execUtils.cpp @@ -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 (ispartitionedtable && !RelationIsGlobalIndex(indexRelation)) { + /* The GPI index insertion is the same as that of a common table */ + if (RelationIsGlobalIndex(indexRelation)) { + if (!indexRelation->rd_index->indisusable) { + continue; + } + actualindex = indexRelation; + } else { + partitionedindexid = RelationGetRelid(indexRelation); if (!PointerIsValid(partitionIndexOidList)) { - return NIL; + partitionIndexOidList = PartitionGetPartIndexList(p); + // no local indexes available + if (!PointerIsValid(partitionIndexOidList)) { + return NIL; + } } - } - - 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; + indexpartitionid = searchPartitionIndexOid(partitionedindexid, partitionIndexOidList); + + searchFakeReationForPartitionOid(estate->esfRelations, + estate->es_query_cxt, + indexRelation, + indexpartitionid, + actualindex, + indexpartition, + RowExclusiveLock); + // skip unusable index + if (!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; diff --git a/src/gausskernel/runtime/executor/nodeBitmapAnd.cpp b/src/gausskernel/runtime/executor/nodeBitmapAnd.cpp index 06440f2e0f5582218f096ceaecb39f72cdd96026..f9261f8fb114ac3f12530b78f5383707154ede1c 100755 --- a/src/gausskernel/runtime/executor/nodeBitmapAnd.cpp +++ b/src/gausskernel/runtime/executor/nodeBitmapAnd.cpp @@ -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); } diff --git a/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp b/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp index f71aeaba7e1aadac5ddfab88a028ea7ea2e86610..7c8753634af4cc91c941c3b59a27ad8f5ea1c4e0 100755 --- a/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp +++ b/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp @@ -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,18 @@ 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; + } + 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 +299,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 +323,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 +336,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 +388,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 +616,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 +672,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 +739,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 +770,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); /* diff --git a/src/gausskernel/runtime/executor/nodeBitmapIndexscan.cpp b/src/gausskernel/runtime/executor/nodeBitmapIndexscan.cpp index 805adae69e57837238a4926882e223c7831f0a13..0fb3a3407f1f6e6529ea40048a5897c5c8f00d31 100755 --- a/src/gausskernel/runtime/executor/nodeBitmapIndexscan.cpp +++ b/src/gausskernel/runtime/executor/nodeBitmapIndexscan.cpp @@ -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)) { diff --git a/src/gausskernel/runtime/executor/nodeBitmapOr.cpp b/src/gausskernel/runtime/executor/nodeBitmapOr.cpp index 3d172203b3ed3b5292af61188cb6276097c3a7ff..0353c8ccc1c223589c965166e0227c36e2a46c78 100755 --- a/src/gausskernel/runtime/executor/nodeBitmapOr.cpp +++ b/src/gausskernel/runtime/executor/nodeBitmapOr.cpp @@ -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); } diff --git a/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp b/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp index 491eb3ed8178884ec6cbffd74fc2c15f2a17702b..421c3d6ab689b5ed1f2c2be3a92c7b2806a0c226 100755 --- a/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp +++ b/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp @@ -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; } } diff --git a/src/gausskernel/runtime/executor/nodeIndexscan.cpp b/src/gausskernel/runtime/executor/nodeIndexscan.cpp index 7d79b06e118367500b1dc9f49580992401b444ba..9db0718cd95ae50b241c143ab0442edc4661e76f 100755 --- a/src/gausskernel/runtime/executor/nodeIndexscan.cpp +++ b/src/gausskernel/runtime/executor/nodeIndexscan.cpp @@ -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))); diff --git a/src/gausskernel/runtime/executor/opfusion.cpp b/src/gausskernel/runtime/executor/opfusion.cpp index a89314f993d55a30c9d2941247a05a228d177b48..6e989db014ef075b671b97c34621b9b38679b919 100644 --- a/src/gausskernel/runtime/executor/opfusion.cpp +++ b/src/gausskernel/runtime/executor/opfusion.cpp @@ -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); diff --git a/src/gausskernel/runtime/executor/opfusion_scan.cpp b/src/gausskernel/runtime/executor/opfusion_scan.cpp index 1fc8cc03e6fea5423a5351c2df63e0a3351d1ec0..67c31660826bf97c07bd9de976eb934a9a1c16f3 100644 --- a/src/gausskernel/runtime/executor/opfusion_scan.cpp +++ b/src/gausskernel/runtime/executor/opfusion_scan.cpp @@ -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) { diff --git a/src/gausskernel/storage/access/common/heaptuple.cpp b/src/gausskernel/storage/access/common/heaptuple.cpp index 8a80675b21f88ab34c881afda9401bc58c0b8ef2..a65de4e5f6c695022cfd64118d51654e1c3b7409 100755 --- a/src/gausskernel/storage/access/common/heaptuple.cpp +++ b/src/gausskernel/storage/access/common/heaptuple.cpp @@ -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; +} diff --git a/src/gausskernel/storage/access/common/indextuple.cpp b/src/gausskernel/storage/access/common/indextuple.cpp index cd5c9a52e74da6f89cc6080228bcad0fe3cea958..671dfaba73f28a27e21c20d8e16f2db6d0f451d5 100644 --- a/src/gausskernel/storage/access/common/indextuple.cpp +++ b/src/gausskernel/storage/access/common/indextuple.cpp @@ -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; +} diff --git a/src/gausskernel/storage/access/common/reloptions.cpp b/src/gausskernel/storage/access/common/reloptions.cpp index c93262ada245a3a61157b11399e58f42487bad87..ed7de5d4f54f961cc1489cf73b0155ca85cdba31 100644 --- a/src/gausskernel/storage/access/common/reloptions.cpp +++ b/src/gausskernel/storage/access/common/reloptions.cpp @@ -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; diff --git a/src/gausskernel/storage/access/gin/ginvacuum.cpp b/src/gausskernel/storage/access/gin/ginvacuum.cpp index 1d02721f4d65219ed1cca86288602f5181ff9584..0271d15ef13d7312663bdc349b3fde574f414a77 100755 --- a/src/gausskernel/storage/access/gin/ginvacuum.cpp +++ b/src/gausskernel/storage/access/gin/ginvacuum.cpp @@ -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) { /* diff --git a/src/gausskernel/storage/access/gist/gistvacuum.cpp b/src/gausskernel/storage/access/gist/gistvacuum.cpp index 8e5f93d0c2f9adc6cfe123fcd3a3a4314e10a1ce..8b5d38886e0f224ae6246979112935701440e41b 100644 --- a/src/gausskernel/storage/access/gist/gistvacuum.cpp +++ b/src/gausskernel/storage/access/gist/gistvacuum.cpp @@ -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; diff --git a/src/gausskernel/storage/access/hash/hash.cpp b/src/gausskernel/storage/access/hash/hash.cpp index e0edf2d6a966cb642c7487a6ef7789399f39c2ed..d3782b43df9e3ab6906e48650304442be4a17f23 100644 --- a/src/gausskernel/storage/access/hash/hash.cpp +++ b/src/gausskernel/storage/access/hash/hash.cpp @@ -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; diff --git a/src/gausskernel/storage/access/heap/heapam.cpp b/src/gausskernel/storage/access/heap/heapam.cpp index e0d71c3a22588f7ae2eec8538c6531830acda080..4a8f5e4572e32c78239cfc8e9ece6aefb9919560 100644 --- a/src/gausskernel/storage/access/heap/heapam.cpp +++ b/src/gausskernel/storage/access/heap/heapam.cpp @@ -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) { diff --git a/src/gausskernel/storage/access/heap/pruneheap.cpp b/src/gausskernel/storage/access/heap/pruneheap.cpp index 04eb0c7c579b7a94a483f317ed9711e99d993fc0..70efe88d552f9bc730c4c82d25db4642761c557a 100755 --- a/src/gausskernel/storage/access/heap/pruneheap.cpp +++ b/src/gausskernel/storage/access/heap/pruneheap.cpp @@ -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: diff --git a/src/gausskernel/storage/access/index/genam.cpp b/src/gausskernel/storage/access/index/genam.cpp index 1b6a59d4e69396c3075e4e2d28a0a5a2dad62d02..1c49f1347fea06371f36722a18ef84f50ac80435 100755 --- a/src/gausskernel/storage/access/index/genam.cpp +++ b/src/gausskernel/storage/access/index/genam.cpp @@ -23,6 +23,7 @@ #include "access/relscan.h" #include "access/transam.h" #include "catalog/index.h" +#include "catalog/heap.h" #include "miscadmin.h" #include "storage/bufmgr.h" #include "utils/acl.h" @@ -82,6 +83,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 +159,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 +176,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 @@ -197,6 +208,7 @@ char* BuildIndexValueDescription(Relation index_relation, Datum* values, const b indrelid = idxrec->indrelid; Assert(indexrelid == idxrec->indexrelid); + int tuplekeyatts = GetIndexKeyAttsByTuple(NULL, ht_idx); /* Table-level SELECT is enough, if the user has it */ aclresult = pg_class_aclcheck(indrelid, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) { @@ -204,7 +216,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 < tuplekeyatts; keyno++) { AttrNumber attnum = idxrec->indkey.values[keyno]; aclresult = pg_attribute_aclcheck(indrelid, attnum, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) { @@ -220,7 +232,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 +333,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 +486,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 +548,144 @@ 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 = 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; +} diff --git a/src/gausskernel/storage/access/index/indexam.cpp b/src/gausskernel/storage/access/index/indexam.cpp index f9cd4827327bd8566d2c0e85f90bb19751aea99c..e6be3876c8c7d71cb5a8cd5a1b3be8f73e423886 100755 --- a/src/gausskernel/storage/access/index/indexam.cpp +++ b/src/gausskernel/storage/access/index/indexam.cpp @@ -177,10 +177,10 @@ 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 (!RelationIsIndex(r)) { ereport( ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", RelationGetRelationName(r)))); + } return r; } @@ -261,6 +261,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 +382,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 +616,18 @@ 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 diff --git a/src/gausskernel/storage/access/nbtree/README b/src/gausskernel/storage/access/nbtree/README index 1605220b2a823efaa756627b861a2cc362a2c15e..fe428834445da71f6ff55086fef94d8b808d4e92 100644 --- a/src/gausskernel/storage/access/nbtree/README +++ b/src/gausskernel/storage/access/nbtree/README @@ -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 ------------------------------- diff --git a/src/gausskernel/storage/access/nbtree/nbtinsert.cpp b/src/gausskernel/storage/access/nbtree/nbtinsert.cpp index 6c8b68c18ad2cc89881e0bf0cff13131990c27bc..6f4d4cfadf6b5b1ec1220bd43d843efc37e984a3 100644 --- a/src/gausskernel/storage/access/nbtree/nbtinsert.cpp +++ b/src/gausskernel/storage/access/nbtree/nbtinsert.cpp @@ -1,7 +1,7 @@ /* ------------------------------------------------------------------------- * * nbtinsert.cpp - * Item insertion in Lehman and Yao btrees for Postgres. + * Item insertion in Lehman and Yao btrees for Postgres. * * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group @@ -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; @@ -252,21 +266,52 @@ 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 = InvalidOid; + Datum datum; + bool isNull = false; + 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) && 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 +411,7 @@ 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 ... */ ItemIdMarkDead(curitemid); opaque->btpo_flags |= BTP_HAS_GARBAGE; @@ -381,6 +427,7 @@ static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation he } } +next: /* * Advance to next tuple to continue checking. */ @@ -390,7 +437,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 +567,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 +888,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 +958,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 +987,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 +1014,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 +1250,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 +1458,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 +1574,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 +1596,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 +1740,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 +1752,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 +1856,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 +1867,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 +1986,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 +2003,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,7 +2013,12 @@ 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. + */ for (i = 1; i <= keysz; i++) { AttrNumber attno; Datum datum; @@ -1996,4 +2081,3 @@ static void _bt_vacuum_one_page(Relation rel, Buffer buffer, Relation heapRel) * the page. */ } - diff --git a/src/gausskernel/storage/access/nbtree/nbtpage.cpp b/src/gausskernel/storage/access/nbtree/nbtpage.cpp index b2aab670c48d18402d1474d3831b64fff997e16e..1af4b06c17d455e1b100c03f3ff3420ff886bfc8 100644 --- a/src/gausskernel/storage/access/nbtree/nbtpage.cpp +++ b/src/gausskernel/storage/access/nbtree/nbtpage.cpp @@ -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); diff --git a/src/gausskernel/storage/access/nbtree/nbtree.cpp b/src/gausskernel/storage/access/nbtree/nbtree.cpp index 03f0ef8a3abee8ea4676614d28db1df34469f5d3..6d3add9bd6ab8dbea3ca6622cc4b543e79ebbb0a 100755 --- a/src/gausskernel/storage/access/nbtree/nbtree.cpp +++ b/src/gausskernel/storage/access/nbtree/nbtree.cpp @@ -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* allPartTuples = NULL; + if (RelationIsGlobalIndex(index)) { + allPartTuples = 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->all_part_tuples = allPartTuples; 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 = DatumGetUInt32(index_getattr(itup, partitionOidAttr, tupdesc, &isnull)); + Assert(!isnull); + } + if (callback(htup, callback_state, partOid)) { deletable[ndeletable++] = offnum; } } diff --git a/src/gausskernel/storage/access/nbtree/nbtsearch.cpp b/src/gausskernel/storage/access/nbtree/nbtsearch.cpp index 8a7d636c186deff6feb4b5719dffc69e11d9e4f9..ac6488ee083c9e84ab9e2690a5537f0d5c569d5a 100755 --- a/src/gausskernel/storage/access/nbtree/nbtsearch.cpp +++ b/src/gausskernel/storage/access/nbtree/nbtsearch.cpp @@ -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); + } +} + diff --git a/src/gausskernel/storage/access/nbtree/nbtsort.cpp b/src/gausskernel/storage/access/nbtree/nbtsort.cpp index 80816ba949c7711e7ef19e2e5146362f35a7ea32..92e7f7c3f69d93247a973446deac18b26fd03f51 100755 --- a/src/gausskernel/storage/access/nbtree/nbtsort.cpp +++ b/src/gausskernel/storage/access/nbtree/nbtsort.cpp @@ -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); } @@ -506,6 +507,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); @@ -532,7 +535,28 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup) *hii = *ii; ItemIdSetUnused(ii); /* redundant */ ((PageHeader)opage)->pd_lower -= sizeof(ItemIdData); - + int indnatts = IndexRelationGetNumberOfAttributes(wstate->index); + int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(wstate->index); + + 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 +566,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 +580,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); /* @@ -582,11 +615,15 @@ void _bt_buildadd(BTWriteState* wstate, BTPageState* state, IndexTuple itup) * 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 +671,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 +724,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) { diff --git a/src/gausskernel/storage/access/nbtree/nbtutils.cpp b/src/gausskernel/storage/access/nbtree/nbtutils.cpp index 65bbf3895cf8eb5279d6823bdda18b9b74674d00..9df27b4aec6cb81891770b81365bfb409f353907 100755 --- a/src/gausskernel/storage/access/nbtree/nbtutils.cpp +++ b/src/gausskernel/storage/access/nbtree/nbtutils.cpp @@ -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; +} + diff --git a/src/gausskernel/storage/access/redo/nbtxlog.cpp b/src/gausskernel/storage/access/redo/nbtxlog.cpp index d51a4dc66f1cacc0d87bb1eaecb4296867f1ce50..c9627e9d36b415f24d53f85d33ad6e00caa56308 100644 --- a/src/gausskernel/storage/access/redo/nbtxlog.cpp +++ b/src/gausskernel/storage/access/redo/nbtxlog.cpp @@ -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); } diff --git a/src/gausskernel/storage/access/spgist/spgvacuum.cpp b/src/gausskernel/storage/access/spgist/spgvacuum.cpp index 1e09d0ccf33833e61e7bd44c0759cfdc374954bc..a92a7c93b5f768fc7aeb0466b83a2cd218acee7a 100644 --- a/src/gausskernel/storage/access/spgist/spgvacuum.cpp +++ b/src/gausskernel/storage/access/spgist/spgvacuum.cpp @@ -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; } diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 7b10f936cc17cf2a3fcde9fb0e67e317511dbfd8..e76a38bb00850c65b0dd3aeb4779d69a3a328459 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -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* all_part_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 */ diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 6bd5211f6f78206577d4f42b91bb22483af4b384..fa7d46ff148509c9fd4f3b9cfe72ab72efdd4b10 100755 --- a/src/include/access/hash.h +++ b/src/include/access/hash.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 diff --git a/src/include/access/htup.h b/src/include/access/htup.h index fc8bf9a78abee339079b2ed8c0b43f795a043d2b..3b11afca8289737b5a34b09e796334b4755af350 100755 --- a/src/include/access/htup.h +++ b/src/include/access/htup.h @@ -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); diff --git a/src/include/access/itup.h b/src/include/access/itup.h index 4f4ee0e37f3b4ea33432fe881c67e56bfd0d990c..95a2abb05b835381bd272967be08779ab3d50b5a 100644 --- a/src/include/access/itup.h +++ b/src/include/access/itup.h @@ -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 */ diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index d56108f41d1b9a51365ec7e27c12020cafbb856d..ae32a3a82ff613205a306611a2e7c49cabf62c8c 100755 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.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 diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index 2a437572e694ddbcb830cc3ae0e4d37fdcdb3554..31f83b499f296fe9cc0f2f9cda5b21c2644ba232 100755 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -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); diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h index 49f5cce5473e591859f2735451ea2c4371870a58..a88a34e5671c0a5a6f9d9214b1603180cba9f9dc 100755 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -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 */ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 3dc09f0efe0e4f3e1f2abe3880b8bc47fd75e4fd..1a87bcec72d0b2caa6e2eb8d58779d44bab73ef9 100755 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -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 */ diff --git a/src/include/c.h b/src/include/c.h index 57dd2a9aaea4928af2697ce8b9a3d7cde020e375..b038aa4e4805607a983341b406c9ef74629339e3 100644 --- a/src/include/c.h +++ b/src/include/c.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 diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index a0df9b3b543f3b293f54a1c7c747fed0a1b75c67..06ffd8373a17eaf725693ca715a2fc789f7004ac 100755 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -146,4 +146,6 @@ extern bool* check_partkey_has_timestampwithzone(Relation partTableRel); extern Oid AddNewIntervalPartition(Relation rel, HeapTuple insertTuple); +extern int GetIndexKeyAttsByTuple(Relation relation, HeapTuple indexTuple); + #endif /* HEAP_H */ diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 1ed6517a08ac1aae090580f4fe2607085e56830c..ddff7eb722c0e9a9d0bfe169ad78192cf7b7ae90 100755 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -56,11 +56,41 @@ typedef enum CheckWaitMode extern void index_check_primary_key(Relation heapRel, IndexInfo *indexInfo, bool is_alter_table); -typedef struct { +/* + * Parameter isPartitionedIndex indicates whether the index is a partition index. + * Parameter isGlobalPartitionedIndex indicates whether the index is a global partition index + * ------------------------------------------------------------------------- + * | isPartitionedIndex | isGlobalPartitionedIndex | Description | + * ------------------------------------------------------------------------- + * | false | false | normal relation index | + * ------------------------------------------------------------------------- + * | true | false | local partition index | + * ------------------------------------------------------------------------- + * | false | true | can not happen | + * ------------------------------------------------------------------------- + * | true | true | global partition index | + * ------------------------------------------------------------------------- + */ +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 +113,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 +136,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 +145,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 +183,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 */ diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index dd0f30e7a2ac9a14eb0d19a77fbd373ddbaa2bd6..f94ee689f685c59f154e97d5d886570460e4cbf2 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -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 */ diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 448c199bcfd4331baf1dae0918993343653add0b..305779598564db24349e5c1ee5ac5a8c0b80e885 100755 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -142,6 +142,12 @@ CATALOG(pg_constraint,2606) BKI_SCHEMA_MACRO * If a check constraint, source-text representation of expression */ text consrc; + + /* + * Columns of conrelid that the constraint does not apply to, but included + * into the same index with key columns. + */ + int2 conincluding[1]; #endif } FormData_pg_constraint; @@ -156,7 +162,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 @@ -183,6 +189,7 @@ typedef FormData_pg_constraint *Form_pg_constraint; #define Anum_pg_constraint_conexclop 24 #define Anum_pg_constraint_conbin 25 #define Anum_pg_constraint_consrc 26 +#define Anum_pg_constraint_conincluding 27 /* Valid values for contype */ @@ -224,6 +231,7 @@ extern Oid CreateConstraintEntry(const char *constraintName, Oid relId, const int16 *constraintKey, int constraintNKeys, + int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h index 03af02c6ab39d87b0bf5b27680a81fe6b345080e..ae552667e57b85b47a90cc9e925b80b4af63f28d 100644 --- a/src/include/catalog/pg_index.h +++ b/src/include/catalog/pg_index.h @@ -33,7 +33,7 @@ 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 */ bool indisunique; /* is this a unique index? */ bool indisprimary; /* is this index for primary key? */ bool indisexclusion; /* is this index for exclusion constraint? */ @@ -58,6 +58,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO pg_node_tree indpred; /* expression tree for predicate, if a partial * index; else NULL */ bool indisreplident; /* is this index the identity for replication? */ + int2 indnkeyatts; /* number of key columns in index */ #endif } FormData_pg_index; @@ -72,7 +73,7 @@ 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 @@ -92,6 +93,8 @@ typedef FormData_pg_index *Form_pg_index; #define Anum_pg_index_indexprs 17 #define Anum_pg_index_indpred 18 #define Anum_pg_index_indisreplident 19 +#define Anum_pg_index_indnkeyatts 20 + /* * Index AMs that support ordered scans must support these two indoption * bits. Otherwise, the content of the per-column indoption fields is diff --git a/src/include/catalog/pg_partition.h b/src/include/catalog/pg_partition.h index 28d9c49014709ec8cf6eb73aba9aa9e1bd675739..9b41239690651f819cf1bac23e0233234c041170 100755 --- a/src/include/catalog/pg_partition.h +++ b/src/include/catalog/pg_partition.h @@ -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; diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index fb7edbb381026ca24ec3be97cc4c98bc69c2875a..e91a3af6098330bbac0dd98e331949fd694c1b49 100755 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -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 */ diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index d4ddfc3c95f42d25cfbaacde6f44353f412e3a57..5a79a640775b639b8de8c2b301308854b6cf7a0f 100755 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.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* nindexesGlobal, + 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 nindexesGlobal, 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); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b474775dd2f6d6286cd6a3488dd36f26eee331e0..5a755f4911c1128bbce4029637538dedccf74d6f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -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 */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index dd3dc1c5b742fbd7d701cf3ec7551101884d1681..2da4118aa40c5a04b63855429576cf8228815917 100755 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2028,7 +2028,8 @@ typedef struct Constraint { char* cooked_expr; /* expr, as nodeToString representation */ /* Fields used for unique constraints (UNIQUE and PRIMARY KEY) or cluster partial key for colstore: */ - List* keys; /* String nodes naming referenced column(s) */ + List* keys; /* String nodes naming referenced key column(s) */ + List* including; /* String nodes naming referenced nonkey column(s) */ /* Fields used for EXCLUSION constraints: */ List* exclusions; /* list of (IndexElem, operator name) pairs */ @@ -2598,6 +2599,7 @@ typedef struct IndexStmt { char* accessMethod; /* name of access method (eg. btree) */ char* tableSpace; /* tablespace, or NULL for default */ List* indexParams; /* columns to index: a list of IndexElem */ + List* indexIncludingParams; /* additional columns to index: a list of IndexElem */ List* options; /* WITH clause options: a list of DefElem */ Node* whereClause; /* qualification (partial-index predicate) */ List* excludeOpNames; /* exclusion operator names, or NIL if none */ @@ -2614,6 +2616,7 @@ typedef struct IndexStmt { * value is false when relation is a foreign table. */ bool isPartitioned; + bool isGlobal; /* is GLOBAL partition index */ bool unique; /* is index unique? */ bool primary; /* is index a primary key? */ bool isconstraint; /* is it for a pkey/unique constraint? */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 3e20514b50e1d6ebe05a0f52ef613f778f29c434..b9ab9a64fba81cd8cefe5e5f8aeed7fde683252d 100755 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -664,6 +664,7 @@ typedef struct IndexOptInfo { /* index descriptor information */ int ncolumns; /* number of columns in index */ + int nkeycolumns; /* number of key columns in index */ int* indexkeys; /* column numbers of index's keys, or 0 */ Oid* indexcollations; /* OIDs of collations of index columns */ Oid* opfamily; /* OIDs of operator families for columns */ @@ -680,6 +681,7 @@ typedef struct IndexOptInfo { List* indextlist; /* targetlist representing index columns */ + bool isGlobal; /* true if index is global partition index */ bool predOK; /* true if predicate matches query */ bool unique; /* true if a unique index */ bool immediate; /* is uniqueness enforced immediately? */ diff --git a/src/include/nodes/tidbitmap.h b/src/include/nodes/tidbitmap.h index 797017343851f730b5281caf9adf990f9648f502..5f2942f1d384bab981be2d8511e85400b92d54ad 100755 --- a/src/include/nodes/tidbitmap.h +++ b/src/include/nodes/tidbitmap.h @@ -36,6 +36,7 @@ typedef struct TBMIterator TBMIterator; /* Result structure for tbm_iterate */ typedef struct { BlockNumber blockno; /* page number containing tuples */ + Oid partitionOid; int ntuples; /* -1 indicates lossy result */ bool recheck; /* should the tuples be rechecked? */ /* Note: recheck is always true if ntuples < 0 */ @@ -46,7 +47,8 @@ typedef struct { extern TIDBitmap* tbm_create(long maxbytes); extern void tbm_free(TIDBitmap* tbm); -extern void tbm_add_tuples(TIDBitmap* tbm, const ItemPointer tids, int ntids, bool recheck); +extern void tbm_add_tuples( + TIDBitmap* tbm, const ItemPointer tids, int ntids, bool recheck, Oid partitionOid = InvalidOid); extern void tbm_add_page(TIDBitmap* tbm, BlockNumber pageno); extern void tbm_union(TIDBitmap* a, const TIDBitmap* b); @@ -57,5 +59,6 @@ extern bool tbm_is_empty(const TIDBitmap* tbm); extern TBMIterator* tbm_begin_iterate(TIDBitmap* tbm); extern TBMIterateResult* tbm_iterate(TBMIterator* iterator); extern void tbm_end_iterate(TBMIterator* iterator); - +extern bool tbm_is_global(const TIDBitmap* tbm); +extern void tbm_set_global(TIDBitmap* tbm, bool isGlobal); #endif /* TIDBITMAP_H */ diff --git a/src/include/opfusion/opfusion_scan.h b/src/include/opfusion/opfusion_scan.h index e47c14ca193490676f2b124aede8137643d23009..be243de8f81fdebc7ffbd3757877ac0aa78fbc27 100644 --- a/src/include/opfusion/opfusion_scan.h +++ b/src/include/opfusion/opfusion_scan.h @@ -56,6 +56,8 @@ public: virtual bool EpqCheck(Datum* values, const bool* isnull) = 0; + virtual Relation getCurrentRel() = 0; + virtual void setAttrNo() = 0; virtual TupleTableSlot* getTupleSlot() = 0; @@ -98,6 +100,8 @@ public: bool EpqCheck(Datum* values, const bool* isnull); + Relation getCurrentRel(); + Relation m_index; /* index relation */ Oid m_reloid; /* relation oid of range table */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 8533330fdb16e99e5878a6e97d32a981813b0278..9dcdef17641322e85836d5a661036fa0912b977a 100755 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -57,6 +57,8 @@ extern IndexPath* create_index_path(PlannerInfo* root, IndexOptInfo* index, List List* indexorderbys, List* indexorderbycols, List* pathkeys, ScanDirection indexscandir, bool indexonly, Relids required_outer, double loop_count); extern Path* build_seqScanPath_by_indexScanPath(PlannerInfo* root, Path* index_path); +extern bool CheckBitmapQualIsGlobalIndex(Path* bitmapqual); +extern bool CheckBitmapHeapPathContainGlobalOrLocal(Path* bitmapqual); extern bool check_bitmap_heap_path_index_unusable(Path* bitmapqual, RelOptInfo* baserel); extern bool is_partitionIndex_Subpath(Path* subpath); extern bool is_pwj_path(Path* pwjpath); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index cf1fcbd85bde0892428c482df8e193b94f08b6a0..b05b105aef74085638912acb1a7896c44f2214bc 100755 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -32,6 +32,8 @@ extern void debug_print_rel(PlannerInfo* root, RelOptInfo* rel); extern void create_index_paths(PlannerInfo* root, RelOptInfo* rel); extern List* generate_bitmap_or_paths( PlannerInfo* root, RelOptInfo* rel, List* clauses, List* other_clauses, bool restriction_only); +extern List* GenerateBitmapOrPathsUseGPI( + PlannerInfo* root, RelOptInfo* rel, const List* clauses, List* other_clauses, bool restriction_only); extern bool relation_has_unique_index_for( PlannerInfo* root, RelOptInfo* rel, List* restrictlist, List* exprlist, List* oprlist); extern bool eclass_member_matches_indexcol( @@ -42,6 +44,14 @@ extern void expand_indexqual_conditions( extern void check_partial_indexes(PlannerInfo* root, RelOptInfo* rel); extern Expr* adjust_rowcompare_for_index( RowCompareExpr* clause, IndexOptInfo* index, int indexcol, List** indexcolnos, bool* var_on_left_p); +/* + * Check index path whether use global partition index to scan + */ +inline bool CheckIndexPathUseGPI(IndexPath* ipath) +{ + return ipath->indexinfo->isGlobal; +} + /* * orindxpath.c diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index ca07883ceed9e6347eaddd0545fb9428d9f3782d..1b5bdf6352ef3e3538de6c2311b0f69d0ea4dbc6 100755 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -243,6 +243,7 @@ PG_KEYWORD("immediate", IMMEDIATE, UNRESERVED_KEYWORD) PG_KEYWORD("immutable", IMMUTABLE, UNRESERVED_KEYWORD) PG_KEYWORD("implicit", IMPLICIT_P, UNRESERVED_KEYWORD) PG_KEYWORD("in", IN_P, RESERVED_KEYWORD) +PG_KEYWORD("include", INCLUDE, UNRESERVED_KEYWORD) PG_KEYWORD("including", INCLUDING, UNRESERVED_KEYWORD) PG_KEYWORD("increment", INCREMENT, UNRESERVED_KEYWORD) PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD) diff --git a/src/include/storage/itemptr.h b/src/include/storage/itemptr.h index 312d0595fb7c3d5439f9b2b294969e954d43e5f9..b72cb35f56264b44e80791096da7717065c277bb 100644 --- a/src/include/storage/itemptr.h +++ b/src/include/storage/itemptr.h @@ -66,6 +66,18 @@ typedef ItemPointerData* ItemPointer; #define ItemPointerGetBlockNumber(pointer) \ (AssertMacro(ItemPointerIsValid(pointer)), BlockIdGetBlockNumber(&(pointer)->ip_blkid)) +/* + * ItemPointerGetBlockNumberNoCheck + * Returns the block number of a disk item pointer. + */ +#define ItemPointerGetBlockNumberNoCheck(pointer) (BlockIdGetBlockNumber(&(pointer)->ip_blkid)) + +/* + * ItemPointerGetOffsetNumberNoCheck + * Returns the offset number of a disk item pointer. + */ +#define ItemPointerGetOffsetNumberNoCheck(pointer) ((pointer)->ip_posid) + /* * ItemPointerGetOffsetNumber * Returns the offset number of a disk item pointer. diff --git a/src/include/utils/partcache.h b/src/include/utils/partcache.h index d70bb5eacb6e3a434d967d88f1d143c833c9e1fe..877cb038738a864e8f824271b92cdfa686613868 100755 --- a/src/include/utils/partcache.h +++ b/src/include/utils/partcache.h @@ -83,5 +83,20 @@ void releaseDummyRelation(Relation* relation); extern void PartitionSetNewRelfilenode(Relation parent, Partition part, TransactionId freezeXid); +/* + * Routines for global partition index open partition + */ +extern PartStatus PartitionGetMetadataStatus(Oid partOid, bool vacuumFlag); +extern Datum SetWaitCleanGpiRelOptions(Datum oldOptions, bool enable); +extern void PartitionedSetWaitCleanGpi(const char* parentName, Oid parentPartOid, bool enable, bool inplace); +extern void PartitionSetWaitCleanGpi(Oid partOid, bool enable, bool inplace); +extern bool PartitionInvisibleMetadataKeep(Datum datumRelOptions); +extern void PartitionedSetEnabledClean(Oid parentOid); +extern void PartitionSetEnabledClean( + Oid parentOid, const Bitmapset* cleanedParts, const Bitmapset* invisibleParts, bool updatePartitioned); +extern void PartitionSetAllEnabledClean(Oid parentOid); +extern void PartitionGetAllInvisibleParts(Oid parentOid, Bitmapset** invisibleParts); +extern bool PartitionMetadataDisabledClean(Relation pgPartition); + #endif /* RELCACHE_H */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h old mode 100644 new mode 100755 index 98b02b6e9121638bb3497d31f67d2562f3c7f03a..e04ffd1d31a8fb93774c9450de4949b86600bf7d --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -154,10 +154,12 @@ typedef struct RelationData { bytea* rd_options; /* parsed pg_class.reloptions */ /* These are non-NULL only for an index relation: */ + Oid rd_partHeapOid; /* partition index's partition oid */ Form_pg_index rd_index; /* pg_index tuple describing this index */ /* use "struct" here to avoid needing to include htup.h: */ struct HeapTupleData* rd_indextuple; /* all of pg_index tuple */ Form_pg_am rd_am; /* pg_am tuple for index's AM */ + int rd_indnkeyatts; /* index relation's indexkey nums */ /* * index access support info (used only for an index relation) @@ -302,6 +304,7 @@ typedef struct StdRdOptions { char* ttl; /* time to live for tsdb data management */ char* period; /* partition range for tsdb data management */ char* version; + char* wait_clean_gpi; /* pg_partition system catalog wait gpi-clean or not */ /* item for online expand */ char* append_mode; char* start_ctid_internal; @@ -388,10 +391,23 @@ typedef struct StdRdOptions { /* * RelationGetNumberOfAttributes - * Returns the number of attributes in a relation. + * Returns the total number of attributes in a relation. */ #define RelationGetNumberOfAttributes(relation) ((relation)->rd_rel->relnatts) +/* + * IndexRelationGetNumberOfAttributes + * Returns the number of attributes in an index. + */ +#define IndexRelationGetNumberOfAttributes(relation) ((relation)->rd_index->indnatts) + +/* + * IndexRelationGetNumberOfKeyAttributes + * Returns the number of key attributes in an index. + */ +#define IndexRelationGetNumberOfKeyAttributes(relation) \ + (AssertMacro((relation)->rd_indnkeyatts != 0), ((relation)->rd_indnkeyatts)) + /* * RelationGetDescr * Returns tuple descriptor for a relation. diff --git a/src/include/utils/rel_gs.h b/src/include/utils/rel_gs.h index f804449a54d8ddfa46080aa5f09fa46425a3001c..d9fff410e4cc5f0ddea7c73fe8c9c5a828fbdafa 100755 --- a/src/include/utils/rel_gs.h +++ b/src/include/utils/rel_gs.h @@ -171,6 +171,9 @@ typedef struct RelationMetaData { #define OptIgnoreEnableHadoopEnv "ignore_enable_hadoop_env" +#define OptEnabledWaitCleanGpi "y" +#define OptDisabledWaitCleanGpi "n" + #define INTERNAL_MASK_DISABLE 0x0 #define INTERNAL_MASK_ENABLE 0x8000 #define INTERNAL_MASK_DINSERT 0x01 // disable insert @@ -329,6 +332,12 @@ static inline RedisHtlAction RelationGetAppendMode(Relation rel) RelationGetMaxBatchRows(relation))) \ : RelDefaultPartialClusterRows) +/* Relation whether create in current xact */ +static inline bool RelationCreateInCurrXact(Relation rel) +{ + return rel->rd_createSubid != InvalidTransactionId; +} + /* RelationGetPgClassOid * return the pg_class OID of this relation. * if this is a PARTITION, return its parent oid; @@ -445,7 +454,9 @@ extern void PartitionDecrementReferenceCount(Partition part); #define RelationIsUnlogged(relation) ((relation)->rd_rel->relnamespace == RELPERSISTENCE_UNLOGGED) -#define RelationIsIndex(relation) (RELKIND_INDEX == (relation)->rd_rel->relkind) +#define RelationIsGlobalIndex(relation) (RELKIND_GLOBAL_INDEX == (relation)->rd_rel->relkind) + +#define RelationIsIndex(relation) (RELKIND_INDEX == (relation)->rd_rel->relkind || RelationIsGlobalIndex(relation)) #define RelationIsSequnce(relation) (RELKIND_SEQUENCE == (relation)->rd_rel->relkind) @@ -569,6 +580,31 @@ static inline bool IsCompressedByCmprsInPgclass(const RelCompressType cmprInPgcl #define RelationGetRelMergeList(relation) \ StdRdOptionsGetStringData((relation)->rd_options, merge_list, NULL) +#define ParitionGetWaitCleanGpi(partition) StdRdOptionsGetStringData((partition)->rd_options, wait_clean_gpi, OptDisabledWaitCleanGpi) +#define RelationGetWaitCleanGpi(relation) StdRdOptionsGetStringData((relation)->rd_options, wait_clean_gpi, OptDisabledWaitCleanGpi) + +/* Partition get reloptions whether have wait_clean_gpi for parition */ +static inline bool PartitionEnableWaitCleanGpi(Partition partition) +{ + if (PointerIsValid(partition) && partition->rd_options != NULL && + pg_strcasecmp(OptEnabledWaitCleanGpi, ParitionGetWaitCleanGpi(partition)) == 0) { + return true; + } + + return false; +} + +/* Relation get reloptions whether have wait_clean_gpi for relation */ +static inline bool RelationEnableWaitCleanGpi(Relation relation) +{ + if (PointerIsValid(relation) && relation->rd_options != NULL && + pg_strcasecmp(OptEnabledWaitCleanGpi, RelationGetWaitCleanGpi(relation)) == 0) { + return true; + } + + return false; +} + /* routines in utils/cache/relcache.c */ extern bool RelationIsDfsStore(Relation relatioin); extern bool RelationIsPaxFormatByOid(Oid relid); diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 4c9aa108ede45046b9b6064132e17d7657f7b385..f0dd4085a9e8eff33e16d9268ce2925d5c3d6de1 100755 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -16,6 +16,7 @@ #include "access/tupdesc.h" #include "nodes/bitmapset.h" +#include "utils/hsearch.h" typedef struct RelationData* Relation; typedef struct PartitionData* Partition; @@ -51,12 +52,12 @@ extern void RelationClose(Relation relation); */ extern List* PartitionGetPartIndexList(Partition part); extern List* RelationGetIndexList(Relation relation); +extern List* RelationGetSpecificKindIndexList(Relation relation, bool isGlobal); extern List* RelationGetIndexInfoList(Relation relation); extern int RelationGetIndexNum(Relation relation); extern Oid RelationGetOidIndex(Relation relation); extern Oid RelationGetReplicaIndex(Relation relation); extern List* RelationGetIndexExpressions(Relation relation); -extern List* RelationGetIndexExpressions(Relation relation); extern List* RelationGetDummyIndexExpressions(Relation relation); extern List* RelationGetIndexPredicate(Relation relation); @@ -67,6 +68,13 @@ typedef enum IndexAttrBitmapKind { INDEX_ATTR_BITMAP_IDENTITY_KEY } IndexAttrBitmapKind; +typedef enum PartitionMetadataStatus { + PART_METADATA_NOEXIST, /* partition is not exists */ + PART_METADATA_LIVE, /* partition is live, normal use */ + PART_METADATA_CREATING, /* partition is being created */ + PART_METADATA_INVISIBLE /* partition is invisible, being droped */ +} PartStatus; + extern Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind keyAttrs); extern void RelationGetExclusionInfo(Relation indexRelation, Oid** operators, Oid** procs, uint16** strategies); @@ -123,6 +131,7 @@ extern void RelationCacheInitFileRemove(void); extern TupleDesc BuildHardcodedDescriptor(int natts, const FormData_pg_attribute* attrs, bool hasoids); extern TupleDesc GetDefaultPgClassDesc(void); +extern TupleDesc GetDefaultPgIndexDesc(void); extern bool CheckRelationInRedistribution(Oid rel_oid); diff --git a/src/test/regress/expected/gpi_bitmapscan.out b/src/test/regress/expected/gpi_bitmapscan.out new file mode 100644 index 0000000000000000000000000000000000000000..309eb2d5c4026227d8773ac69e993a7726c4ffc7 --- /dev/null +++ b/src/test/regress/expected/gpi_bitmapscan.out @@ -0,0 +1,229 @@ +DROP TABLE if exists gpi_bitmap_table1; +NOTICE: table "gpi_bitmap_table1" does not exist, skipping +DROP TABLE if exists gpi_bitmap_table2; +NOTICE: table "gpi_bitmap_table2" does not exist, skipping +SET enable_seqscan=off; +SET enable_indexscan=off; +CREATE TABLE gpi_bitmap_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_bitmap_table1 VALUES less than (10000), + partition p1_gpi_bitmap_table1 VALUES less than (20000), + partition p2_gpi_bitmap_table1 VALUES less than (30000), + partition p3_gpi_bitmap_table1 VALUES less than (maxvalue) +); +INSERT INTO gpi_bitmap_table1 + SELECT (r%53), (r%59), (r%56) + FROM generate_series(1,70000) r; +--ok +CREATE INDEX idx1_gpi_bitmap_table1 ON gpi_bitmap_table1 (c1) GLOBAL; +--ok +CREATE INDEX idx2_gpi_bitmap_table1 ON gpi_bitmap_table1 (c2) GLOBAL; +--ok +-- test bitmap-and +SELECT count(*) FROM gpi_bitmap_table1 WHERE c1 = 1 AND c2 = 1; + count +------- + 23 +(1 row) + +SELECT * FROM gpi_bitmap_table1 WHERE c1 = 1 AND c2 = 1; + c1 | c2 | c3 +----+----+---- + 1 | 1 | 1 + 1 | 1 | 48 + 1 | 1 | 39 + 1 | 1 | 30 + 1 | 1 | 21 + 1 | 1 | 12 + 1 | 1 | 3 + 1 | 1 | 50 + 1 | 1 | 41 + 1 | 1 | 32 + 1 | 1 | 23 + 1 | 1 | 14 + 1 | 1 | 5 + 1 | 1 | 52 + 1 | 1 | 43 + 1 | 1 | 34 + 1 | 1 | 25 + 1 | 1 | 16 + 1 | 1 | 7 + 1 | 1 | 54 + 1 | 1 | 45 + 1 | 1 | 36 + 1 | 1 | 27 +(23 rows) + +SELECT * FROM gpi_bitmap_table1 WHERE c1 = 1 AND c2 = 1 ORDER BY c3; + c1 | c2 | c3 +----+----+---- + 1 | 1 | 1 + 1 | 1 | 3 + 1 | 1 | 5 + 1 | 1 | 7 + 1 | 1 | 12 + 1 | 1 | 14 + 1 | 1 | 16 + 1 | 1 | 21 + 1 | 1 | 23 + 1 | 1 | 25 + 1 | 1 | 27 + 1 | 1 | 30 + 1 | 1 | 32 + 1 | 1 | 34 + 1 | 1 | 36 + 1 | 1 | 39 + 1 | 1 | 41 + 1 | 1 | 43 + 1 | 1 | 45 + 1 | 1 | 48 + 1 | 1 | 50 + 1 | 1 | 52 + 1 | 1 | 54 +(23 rows) + +-- test bitmap-or +SELECT count(*) FROM gpi_bitmap_table1 WHERE c1 = 1 OR c2 = 1; + count +------- + 2485 +(1 row) + +CREATE TABLE gpi_bitmap_table2 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_bitmap_table2 VALUES less than (10000), + partition p1_gpi_bitmap_table2 VALUES less than (20000), + partition p2_gpi_bitmap_table2 VALUES less than (30000), + partition p3_gpi_bitmap_table2 VALUES less than (maxvalue) +); +--ok +INSERT INTO gpi_bitmap_table2 + SELECT r, (r+1), 500 + FROM generate_series(1,70000) r; +--ok +CREATE INDEX idx1_gpi_bitmap_table2 ON gpi_bitmap_table2 (c1) GLOBAL; +--ok +CREATE INDEX idx2_gpi_bitmap_table2 ON gpi_bitmap_table2 (c2) GLOBAL; +--ok +EXPLAIN (COSTS OFF) SELECT count(*) FROM gpi_bitmap_table2 WHERE c1 > 9990 AND c1 < 10010; + QUERY PLAN +---------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on gpi_bitmap_table2 + Recheck Cond: ((c1 > 9990) AND (c1 < 10010)) + -> Bitmap Index Scan on idx1_gpi_bitmap_table2 + Index Cond: ((c1 > 9990) AND (c1 < 10010)) +(5 rows) + +SELECT count(*) FROM gpi_bitmap_table2 WHERE c1 > 9990 AND c1 < 10010; + count +------- + 19 +(1 row) + +SELECT * FROM gpi_bitmap_table2 WHERE c1 > 9990 AND c1 < 10010; + c1 | c2 | c3 +-------+-------+----- + 9991 | 9992 | 500 + 9992 | 9993 | 500 + 9993 | 9994 | 500 + 9994 | 9995 | 500 + 9995 | 9996 | 500 + 9996 | 9997 | 500 + 9997 | 9998 | 500 + 9998 | 9999 | 500 + 9999 | 10000 | 500 + 10000 | 10001 | 500 + 10001 | 10002 | 500 + 10002 | 10003 | 500 + 10003 | 10004 | 500 + 10004 | 10005 | 500 + 10005 | 10006 | 500 + 10006 | 10007 | 500 + 10007 | 10008 | 500 + 10008 | 10009 | 500 + 10009 | 10010 | 500 +(19 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 AND c2 = 10000; + QUERY PLAN +--------------------------------------------------------- + Bitmap Heap Scan on gpi_bitmap_table2 + Recheck Cond: ((c2 = 10000) AND (c1 = 10000)) + -> BitmapAnd + -> Bitmap Index Scan on idx2_gpi_bitmap_table2 + Index Cond: (c2 = 10000) + -> Bitmap Index Scan on idx1_gpi_bitmap_table2 + Index Cond: (c1 = 10000) +(7 rows) + +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 AND c2 = 10000; + c1 | c2 | c3 +----+----+---- +(0 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 10000; + QUERY PLAN +--------------------------------------------------------- + Bitmap Heap Scan on gpi_bitmap_table2 + Recheck Cond: ((c1 = 10000) OR (c2 = 10000)) + -> BitmapOr + -> Bitmap Index Scan on idx1_gpi_bitmap_table2 + Index Cond: (c1 = 10000) + -> Bitmap Index Scan on idx2_gpi_bitmap_table2 + Index Cond: (c2 = 10000) +(7 rows) + +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 10000; + c1 | c2 | c3 +-------+-------+----- + 9999 | 10000 | 500 + 10000 | 10001 | 500 +(2 rows) + +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 9999 OR c2 = 10001; + c1 | c2 | c3 +-------+-------+----- + 9999 | 10000 | 500 + 10000 | 10001 | 500 +(2 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 30000; + QUERY PLAN +--------------------------------------------------------- + Bitmap Heap Scan on gpi_bitmap_table2 + Recheck Cond: ((c1 = 10000) OR (c2 = 30000)) + -> BitmapOr + -> Bitmap Index Scan on idx1_gpi_bitmap_table2 + Index Cond: (c1 = 10000) + -> Bitmap Index Scan on idx2_gpi_bitmap_table2 + Index Cond: (c2 = 30000) +(7 rows) + +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 30000; + c1 | c2 | c3 +-------+-------+----- + 10000 | 10001 | 500 + 29999 | 30000 | 500 +(2 rows) + +DROP TABLE idx1_gpi_bitmap_table1; +ERROR: "idx1_gpi_bitmap_table1" is not a table +HINT: Use DROP INDEX to remove an global partition index. +DROP TABLE idx1_gpi_bitmap_table2; +ERROR: "idx1_gpi_bitmap_table2" is not a table +HINT: Use DROP INDEX to remove an global partition index. +SET enable_seqscan=on; +SET enable_indexscan=on; diff --git a/src/test/regress/expected/gpi_build_index.out b/src/test/regress/expected/gpi_build_index.out new file mode 100644 index 0000000000000000000000000000000000000000..fcf4f2a1f2af4f738ce56e78a019115c3daed0f2 --- /dev/null +++ b/src/test/regress/expected/gpi_build_index.out @@ -0,0 +1,266 @@ +-- +---- test global index +-- +drop table if exists hw_global_index_rp; +NOTICE: table "hw_global_index_rp" does not exist, skipping +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (50), + partition hw_global_index_rp_p1 values less than (100), + partition hw_global_index_rp_p2 values less than (150) +); +--succeed +insert into hw_global_index_rp values(1, 1); +insert into hw_global_index_rp values(49, 2); +insert into hw_global_index_rp values(50, 3); +insert into hw_global_index_rp values(100, 4); +insert into hw_global_index_rp values(149, 5); +create index rp_index_global1 on hw_global_index_rp (c1) global; +--succeed +create index on hw_global_index_rp (c1) global; +--fail +create unique index rp_index_global2 on hw_global_index_rp (c1) global; +--succeed +create unique index on hw_global_index_rp (c1) global; +--fail +--expression +create index rp_index_global3 on hw_global_index_rp ((c1+c2)) global; +ERROR: Global partition index does not support EXPRESSION index +--fail +create index rp_index_global4 on hw_global_index_rp ((c1-c2)) global; +ERROR: Global partition index does not support EXPRESSION index +--succeed +create index rp_index_global15 on hw_global_index_rp ((c1-c2),c1,c2) global; +ERROR: Global partition index does not support EXPRESSION index +--succeed +create index rp_index_global16 on hw_global_index_rp using hash (c1) global; +ERROR: Global partition index only support btree. +--fail ERROR: access method "HASH" does not support global indexes +create index rp_index_global17 on hw_global_index_rp using gin ((c1-c2),c1,c2) global; +ERROR: Global partition index only support btree. +--fail ERROR: data type INTEGER has no default operator class for access method "GIN" ??? +create unique index CONCURRENTLY rp_index_global18 on hw_global_index_rp (c1) global; +ERROR: cannot create concurrent partitioned indexes +--fail ERROR: not support CONCURRENTLY +create unique index rp_index_global19 on hw_global_index_rp (c1); +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'rp_index_global1' and ind.indexrelid = class.oid; + relname | reltuples | parttype +------------------+-----------+---------- + rp_index_global1 | 5 | n +(1 row) + +drop index rp_index_global1; +drop table hw_global_index_rp; +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (50), + partition hw_global_index_rp_p1 values less than (100), + partition hw_global_index_rp_p2 values less than (150) +); +insert into hw_global_index_rp values(1, 1); +insert into hw_global_index_rp values(1, 1); +insert into hw_global_index_rp values(2, 1); +create unique index rp_index_global21 on hw_global_index_rp (c1) global; +ERROR: could not create unique index "rp_index_global21" +DETAIL: Key (c1)=(1) is duplicated. +-- fail +delete from hw_global_index_rp where c1 = '1'; +create unique index rp_index_global21 on hw_global_index_rp (c1) global; +--succeed +create index rp_index_global22 on hw_global_index_rp(c1 DESC NULLS LAST); +-- succeed +create index rp_index_globa23 on hw_global_index_rp(c1) where c2 > 0; +ERROR: Global partition index does not support WHERE clause. +--fail +alter index rp_index_global22 rename to rp_index_global24; +-- succeed +create index rp_index_globa25 on hw_global_index_rp(c1); +alter index if exists rp_index_global24 unusable; +-- succeed +create index rp_index_globa26 on hw_global_index_rp(c1) tablespace pg_default; +-- succeed +drop table hw_global_index_rp; +Create table hw_global_index_t1 +( + C1 int, + C2 int +); +Create unique index t1_unique_index_1 on hw_global_index_t1 (c1) global; +ERROR: non-partitioned table does not support local partitioned indexes +--fail non-partitioned table does not support global global index +drop table hw_global_index_t1; +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); +Create index rp_global_index on hw_global_index_rp (c1) global; +COMMENT ON INDEX rp_global_index IS 'global index comment'; +\di+ rp_global_index; +--?.* +--? Schema | Name | Type | .* | Table | Size | Storage | Description +--?.* +--? public | rp_global_index | index | .* | hw_global_index_rp | 8192 bytes | | global index comment +(1 row) + +\d rp_global_index; +Index "public.rp_global_index" + Column | Type | Definition +--------+---------+------------ + c1 | integer | c1 +btree, for table "public.hw_global_index_rp" + +\d hw_global_index_rp; +Table "public.hw_global_index_rp" + Column | Type | Modifiers +--------+---------+----------- + c1 | integer | + c2 | integer | +Indexes: + "rp_global_index" btree (c1) TABLESPACE pg_default +Range partition by(c1) +Number of partition: 5 (View pg_partition to check each partition range.) + +select pg_get_indexdef('rp_global_index'::regclass); + pg_get_indexdef +------------------------------------------------------------------------------------------- + CREATE INDEX rp_global_index ON hw_global_index_rp USING btree (c1) TABLESPACE pg_default +(1 row) + +COMMENT ON INDEX rp_global_index IS NULL; +\di+ rp_global_index; +--?.* +--? Schema | Name | Type | .* | Table | Size | Storage | Description +--?.* +--? public | rp_global_index | index | .* | hw_global_index_rp | 8192 bytes | | +(1 row) + +drop table hw_global_index_rp; +create table hw_global_index_rp +( + c1 int, + name text +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); +Create index rp_global_index on hw_global_index_rp (name) global; +drop table hw_global_index_rp; +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); +insert into hw_global_index_rp values(generate_series(0,9999), 1); +\parallel on +create index rp_index_global1 on hw_global_index_rp (c1) global; +select count(*) from hw_global_index_rp; +\parallel off + count +------- + 10000 +(1 row) + +drop table hw_global_index_rp; +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); +insert into hw_global_index_rp values(generate_series(0,9999), 1); +create index on hw_global_index_rp(c1); +create index on hw_global_index_rp(c1); +create index on hw_global_index_rp(c1, c2); +create index index_for_create_like on hw_global_index_rp(c2) global; +create table test_gpi_create_like (like hw_global_index_rp including all); +\d test_gpi_create_like; +Table "public.test_gpi_create_like" + Column | Type | Modifiers +--------+---------+----------- + c1 | integer | + c2 | integer | +Indexes: + "test_gpi_create_like_c1_c2_tableoid_idx" btree (c1, c2) TABLESPACE pg_default + "test_gpi_create_like_c1_tableoid_idx" btree (c1) TABLESPACE pg_default + "test_gpi_create_like_c2_tableoid_idx" btree (c2) TABLESPACE pg_default +Range partition by(c1) +Number of partition: 5 (View pg_partition to check each partition range.) + +insert into test_gpi_create_like values(generate_series(0,9999), 1); +explain (costs off) select * from test_gpi_create_like where c1 = 0; + QUERY PLAN +----------------------------------------------------------------- + Bitmap Heap Scan on test_gpi_create_like + Recheck Cond: (c1 = 0) + -> Bitmap Index Scan on test_gpi_create_like_c1_tableoid_idx + Index Cond: (c1 = 0) +(4 rows) + +select * from test_gpi_create_like where c1 = 0; + c1 | c2 +----+---- + 0 | 1 +(1 row) + +drop table test_gpi_create_like; +drop table if exists gpi_multi_cols; +NOTICE: table "gpi_multi_cols" does not exist, skipping +create table gpi_multi_cols(a0 int,a1 int,a2 int,a3 int,a4 int,a5 int,a6 int,a7 int,a8 int,a9 int,a10 int,a11 int,a12 int,a13 int,a14 int,a15 int,a16 int,a17 int,a18 int,a19 int,a20 int,a21 int,a22 int,a23 int,a24 int,a25 int,a26 int,a27 int,a28 int,a29 int,a30 int,a31 int,a32 int, a33 int) partition by range(a0) (partition p1 values less than (1001), partition p2 values less than (2001), partition p3 values less than (3001)); +-- success +create index gpi_multi_cols_index on gpi_multi_cols(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30) global; +--failed +create index gpi_multi_cols_index on gpi_multi_cols(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30, a31) global; +ERROR: cannot use more than 32 columns in an index +--success +create index gpi_multi_cols_index_local on gpi_multi_cols(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30, a31) local; +insert into gpi_multi_cols select r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r from generate_series(1,3000) as r; +vacuum analyze gpi_multi_cols; +set enable_seqscan = off; +select * from gpi_multi_cols where a1 = 200; + a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9 | a10 | a11 | a12 | a13 | a14 | a15 | a16 | a17 | a18 | a19 | a20 | a21 | a22 | a23 | a24 | a25 | a26 | a27 | a28 | a29 | a30 | a31 | a32 | a33 +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----- + 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | 200 | +(1 row) + +reset enable_seqscan; +--clean data +drop table gpi_multi_cols; diff --git a/src/test/regress/expected/gpi_build_index_01.out b/src/test/regress/expected/gpi_build_index_01.out new file mode 100644 index 0000000000000000000000000000000000000000..cb454a93b242f0678a9bf661155371db01c3f2ce --- /dev/null +++ b/src/test/regress/expected/gpi_build_index_01.out @@ -0,0 +1,4 @@ +-- +---- test global index +-- +create index rp_index_global1 on hw_global_index_rp (c1) global; diff --git a/src/test/regress/expected/gpi_build_index_02.out b/src/test/regress/expected/gpi_build_index_02.out new file mode 100644 index 0000000000000000000000000000000000000000..9548f04403491b8d469461ee869140327e736ff6 --- /dev/null +++ b/src/test/regress/expected/gpi_build_index_02.out @@ -0,0 +1,9 @@ +-- +---- test global index +-- +select count(*) from hw_global_index_rp; + count +------- + 10000 +(1 row) + diff --git a/src/test/regress/expected/gpi_clean_wait.out b/src/test/regress/expected/gpi_clean_wait.out new file mode 100644 index 0000000000000000000000000000000000000000..4e5f9564fcb4b332618174124a0469e1d7f72933 --- /dev/null +++ b/src/test/regress/expected/gpi_clean_wait.out @@ -0,0 +1,598 @@ +drop table if exists test_gpi_more_invalid; +NOTICE: table "test_gpi_more_invalid" does not exist, skipping +create table test_gpi_more_invalid(a int, b int, c int) partition by range(a) (partition p1 values less than (1001), partition p2 values less than (2001), partition p3 values less than (3001)); +insert into test_gpi_more_invalid select r,r,100 from generate_series(1,1000) as r; +insert into test_gpi_more_invalid select r,r,200 from generate_series(1001,2000) as r; +insert into test_gpi_more_invalid select r,r,300 from generate_series(2001,3000) as r; +create index global_index_gpi_more_invalid_b on test_gpi_more_invalid (a) global; +create index global_index_gpi_more_invalid_c on test_gpi_more_invalid (c) global; +create unique index global_index_gpi_more_invalid_a on test_gpi_more_invalid (b) global; +create index local_index_gpi_more_invalid_a_b on test_gpi_more_invalid (a,b) local; +create index global_index_gpi_more_invalid_a_b_c on test_gpi_more_invalid(a,b,c) global; +vacuum analyze test_gpi_more_invalid; +start transaction; +alter table test_gpi_more_invalid add partition p6 values less than (4001); +update test_gpi_more_invalid set a = 3000 + a where a <= 100; +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + p6 | p | {orientation=row,compression=no,wait_clean_gpi=y} + test_gpi_more_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(2 rows) + +explain (costs off) select * from test_gpi_more_invalid where a >= 3000; + QUERY PLAN +--------------------------------------------------------------------------- + [Bypass] + Index Scan using global_index_gpi_more_invalid_b on test_gpi_more_invalid + Index Cond: (a >= 3000) +(3 rows) + +--100 rows +select count(*) from test_gpi_more_invalid where a > 3000; + count +------- + 100 +(1 row) + +abort; +-- delete 100 +delete test_gpi_more_invalid where b % 10 = 0 and c = 100; +-- delete 100 +delete test_gpi_more_invalid where b % 10 = 0 and c = 200; +-- delete 100 +delete test_gpi_more_invalid where b % 10 = 0 and c = 300; +-- after select's where condition +insert into test_gpi_more_invalid values(10, 100000, 100); +insert into test_gpi_more_invalid values(1020, 200000, 200); +insert into test_gpi_more_invalid values(2030, 300000, 300); +explain (costs off) select count(*) from test_gpi_more_invalid where a <= 100 and b = 100000; + QUERY PLAN +--------------------------------------------------------------------------------- + Aggregate + -> Index Scan using global_index_gpi_more_invalid_a on test_gpi_more_invalid + Index Cond: (b = 100000) + Filter: (a <= 100) +(4 rows) + +explain (costs off) select count(*) from test_gpi_more_invalid where a > 3000; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_b on test_gpi_more_invalid + Index Cond: (a > 3000) +(3 rows) + +-- 1 rows +select count(*) from test_gpi_more_invalid where a <= 100 and b = 100000; + count +------- + 1 +(1 row) + +-- 0 rows +select count(*) from test_gpi_more_invalid where a >= 3000; + count +------- + 0 +(1 row) + +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + test_gpi_more_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +vacuum analyze test_gpi_more_invalid; +-- test_gpi_more_invalid have wait_clean_gpi=n +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +---------+----------+------------ +(0 rows) + +explain (costs off) select count(*) from test_gpi_more_invalid where a <= 100; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_b on test_gpi_more_invalid + Index Cond: (a <= 100) +(3 rows) + +explain (costs off) select count(*) from test_gpi_more_invalid where a >= 4000; + QUERY PLAN +------------------------------------------------------------------------------------------ + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_a_b_c on test_gpi_more_invalid + Index Cond: (a >= 4000) +(3 rows) + +-- 91 rows +select count(*) from test_gpi_more_invalid where a <= 100; + count +------- + 91 +(1 row) + +-- 0 rows +select count(*) from test_gpi_more_invalid where a >= 4000; + count +------- + 0 +(1 row) + +explain (costs off) select count(*) from test_gpi_more_invalid where c = 100; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_c on test_gpi_more_invalid + Index Cond: (c = 100) +(3 rows) + +-- 900 rows +select count(*) from test_gpi_more_invalid where c = 100; + count +------- + 901 +(1 row) + +explain (costs off) select count(*) from test_gpi_more_invalid where c = 200; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_c on test_gpi_more_invalid + Index Cond: (c = 200) +(3 rows) + +-- 900 rows +select count(*) from test_gpi_more_invalid where c = 200; + count +------- + 901 +(1 row) + +explain (costs off) select count(*) from test_gpi_more_invalid where c = 300; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_c on test_gpi_more_invalid + Index Cond: (c = 300) +(3 rows) + +-- 900 rows +select count(*) from test_gpi_more_invalid where c = 300; + count +------- + 901 +(1 row) + +explain (costs off) delete test_gpi_more_invalid where b%5 != 0 and (c = 100 or c = 200 or c = 300); + QUERY PLAN +---------------------------------------------------------------------------------- + Delete on test_gpi_more_invalid + -> Partition Iterator + Iterations: 3 + -> Partitioned Seq Scan on test_gpi_more_invalid + Filter: (((b % 5) <> 0) AND ((c = 100) OR (c = 200) OR (c = 300))) + Selected Partitions: 1..3 +(6 rows) + +delete test_gpi_more_invalid where b%5 != 0 and (c = 100 or c = 200 or c = 300); +start transaction; +alter table test_gpi_more_invalid add partition p6 values less than (4001); +insert into test_gpi_more_invalid select r,r,300 from generate_series(3001,4000) as r; +--failed +update test_gpi_more_invalid set b = 1001 where c = 200; +ERROR: duplicate key value violates unique constraint "global_index_gpi_more_invalid_a" +DETAIL: Key (b)=(1001) already exists. +commit; +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + test_gpi_more_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +set enable_seqscan = off; +-- rows 100 +explain (costs off) select count(*) from test_gpi_more_invalid where c = 200; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_c on test_gpi_more_invalid + Index Cond: (c = 200) +(3 rows) + +select count(*) from test_gpi_more_invalid where c = 200; + count +------- + 101 +(1 row) + +explain (costs off) select count(*) from test_gpi_more_invalid where c = 300; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_c on test_gpi_more_invalid + Index Cond: (c = 300) +(3 rows) + +select count(*) from test_gpi_more_invalid where c = 300; + count +------- + 101 +(1 row) + +explain (costs off) update test_gpi_more_invalid set a = a - 500 where a < 1500 and a >= 1000; + QUERY PLAN +--------------------------------------------------------------------------------- + Update on test_gpi_more_invalid + -> Index Scan using global_index_gpi_more_invalid_b on test_gpi_more_invalid + Index Cond: ((a < 1500) AND (a >= 1000)) +(3 rows) + +update test_gpi_more_invalid set a = a - 500 where a < 1500 and a >= 1000; +explain (costs off) update test_gpi_more_invalid set a = a + 500 where a < 1000 and a >= 500; + QUERY PLAN +--------------------------------------------------------------------------------- + Update on test_gpi_more_invalid + -> Index Scan using global_index_gpi_more_invalid_b on test_gpi_more_invalid + Index Cond: ((a < 1000) AND (a >= 500)) +(3 rows) + +update test_gpi_more_invalid set a = a + 500 where a < 1000 and a >= 500; +explain (costs off) select count(*) from test_gpi_more_invalid where a < 1500 and a >= 1000; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_b on test_gpi_more_invalid + Index Cond: ((a < 1500) AND (a >= 1000)) +(3 rows) + +select count(*) from test_gpi_more_invalid where a < 1500 and a >= 1000; + count +------- + 101 +(1 row) + +explain (costs off) select count(*) from test_gpi_more_invalid where a <= 1000; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_more_invalid_b on test_gpi_more_invalid + Index Cond: (a <= 1000) +(3 rows) + +select count(*) from test_gpi_more_invalid where a <= 1000; + count +------- + 51 +(1 row) + +set force_bitmapand = on; +-- partition 1 +explain (costs off) select * from test_gpi_more_invalid where b = 100000 and c = 100; + QUERY PLAN +--------------------------------------------------------------------------- + Index Scan using global_index_gpi_more_invalid_a on test_gpi_more_invalid + Index Cond: (b = 100000) + Filter: (c = 100) +(3 rows) + +select * from test_gpi_more_invalid where b = 100000 and c = 100; + a | b | c +----+--------+----- + 10 | 100000 | 100 +(1 row) + +--partition 2 +explain (costs off) select * from test_gpi_more_invalid where b = 200000 and c = 200; + QUERY PLAN +--------------------------------------------------------------------------- + Index Scan using global_index_gpi_more_invalid_a on test_gpi_more_invalid + Index Cond: (b = 200000) + Filter: (c = 200) +(3 rows) + +select * from test_gpi_more_invalid where b = 200000 and c = 200; + a | b | c +------+--------+----- + 1020 | 200000 | 200 +(1 row) + +reset force_bitmapand; +alter table test_gpi_more_invalid DISABLE ROW MOVEMENT; +update test_gpi_more_invalid set a = a - 500 where a < 2000 and a > 1500; +-- failed +update test_gpi_more_invalid set a = a - 500 where a <= 1500; +ERROR: fail to update partitioned table "test_gpi_more_invalid" +DETAIL: disable row movement +alter table test_gpi_more_invalid ENABLE ROW MOVEMENT; +set force_bitmapand = on; +--partition 2 +explain (costs off) select * from test_gpi_more_invalid where b = 200000 and c = 200; + QUERY PLAN +--------------------------------------------------------------------------- + Index Scan using global_index_gpi_more_invalid_a on test_gpi_more_invalid + Index Cond: (b = 200000) + Filter: (c = 200) +(3 rows) + +select * from test_gpi_more_invalid where b = 200000 and c = 200; + a | b | c +------+--------+----- + 1020 | 200000 | 200 +(1 row) + +reset force_bitmapand; +start transaction; +alter table test_gpi_more_invalid add partition p5 values less than (4001); +select * from test_gpi_more_invalid where b = 300000 and c = 700 for update; + a | b | c +---+---+--- +(0 rows) + +update test_gpi_more_invalid set a = a + 1000 where a > 1000 or a < 500; +select count(*) from test_gpi_more_invalid where c = 100 and b = 2000; + count +------- + 0 +(1 row) + +-- test_gpi_more_invalid/p5 have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + p5 | p | {orientation=row,compression=no,wait_clean_gpi=y} + test_gpi_more_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(2 rows) + +abort; +--failed +vacuum full pg_partition; +WARNING: system table pg_partition contain relation test_gpi_more_invalid have reloptions wait_clean_gpi=y,must run the vacuum (full) test_gpi_more_invalid first +WARNING: skipping "pg_partition" --- only table or database can vacuum it +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + test_gpi_more_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +vacuum analyze test_gpi_more_invalid; +-- test_gpi_more_invalid have wait_clean_gpi=n +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +---------+----------+------------ +(0 rows) + +-- success +vacuum full pg_partition; +set force_bitmapand = on; +-- partition 1 +explain (costs off) select * from test_gpi_more_invalid where b = 100000 and c = 100; + QUERY PLAN +--------------------------------------------------------------------------- + Index Scan using global_index_gpi_more_invalid_a on test_gpi_more_invalid + Index Cond: (b = 100000) + Filter: (c = 100) +(3 rows) + +select * from test_gpi_more_invalid where b = 100000 and c = 100; + a | b | c +----+--------+----- + 10 | 100000 | 100 +(1 row) + +--partition 2 +explain (costs off) select * from test_gpi_more_invalid where b = 200000 and c = 200; + QUERY PLAN +--------------------------------------------------------------------------- + Index Scan using global_index_gpi_more_invalid_a on test_gpi_more_invalid + Index Cond: (b = 200000) + Filter: (c = 200) +(3 rows) + +select * from test_gpi_more_invalid where b = 200000 and c = 200; + a | b | c +------+--------+----- + 1020 | 200000 | 200 +(1 row) + +--partition 3 +explain (costs off) select * from test_gpi_more_invalid where b = 300000 and c = 300; + QUERY PLAN +--------------------------------------------------------------------------- + Index Scan using global_index_gpi_more_invalid_a on test_gpi_more_invalid + Index Cond: (b = 300000) + Filter: (c = 300) +(3 rows) + +select * from test_gpi_more_invalid where b = 300000 and c = 300; + a | b | c +------+--------+----- + 2030 | 300000 | 300 +(1 row) + +reset force_bitmapand; +start transaction; +alter table test_gpi_more_invalid add partition p5 values less than (4001); +update test_gpi_more_invalid set a = a + 1000 where a > 1000 or a < 500; +alter table test_gpi_more_invalid drop partition p5; +commit; +-- all global index unusuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; + relname | indisusable +-------------------------------------+------------- + global_index_gpi_more_invalid_a | f + global_index_gpi_more_invalid_a_b_c | f + global_index_gpi_more_invalid_b | f + global_index_gpi_more_invalid_c | f + local_index_gpi_more_invalid_a_b | t +(5 rows) + +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + test_gpi_more_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +vacuum full test_gpi_more_invalid; +-- all global index unusuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; + relname | indisusable +-------------------------------------+------------- + global_index_gpi_more_invalid_a | f + global_index_gpi_more_invalid_a_b_c | f + global_index_gpi_more_invalid_b | f + global_index_gpi_more_invalid_c | f + local_index_gpi_more_invalid_a_b | t +(5 rows) + +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + test_gpi_more_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +alter index local_index_gpi_more_invalid_a_b unusable; +reindex table test_gpi_more_invalid; +-- all global index unusuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; + relname | indisusable +-------------------------------------+------------- + global_index_gpi_more_invalid_a | f + global_index_gpi_more_invalid_a_b_c | f + global_index_gpi_more_invalid_b | f + global_index_gpi_more_invalid_c | f + local_index_gpi_more_invalid_a_b | t +(5 rows) + +alter index global_index_gpi_more_invalid_a rebuild; +alter index global_index_gpi_more_invalid_a_b_c rebuild; +alter index global_index_gpi_more_invalid_b rebuild; +alter index global_index_gpi_more_invalid_c rebuild; +-- all global index usuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; + relname | indisusable +-------------------------------------+------------- + global_index_gpi_more_invalid_a | t + global_index_gpi_more_invalid_a_b_c | t + global_index_gpi_more_invalid_b | t + global_index_gpi_more_invalid_c | t + local_index_gpi_more_invalid_a_b | t +(5 rows) + +reset enable_seqscanï¼› +drop table test_gpi_more_invalid; +ERROR: syntax error at or near "drop table" +LINE 2: drop table test_gpi_more_invalid; + ^ +drop table if exists interval_normal_date; +NOTICE: table "interval_normal_date" does not exist, skipping +CREATE TABLE interval_normal_date (logdate date not null, b int, c int) +PARTITION BY RANGE (logdate) +INTERVAL ('1 day') +( + PARTITION interval_normal_date_p1 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_normal_date_p2 VALUES LESS THAN ('2020-05-01'), + PARTITION interval_normal_date_p3 VALUES LESS THAN ('2020-06-01') +); +create index global_interval_index on interval_normal_date(b) global; +insert into interval_normal_date select '2020-6-01', 1000,r from generate_series(0, 10000) as r; +vacuum interval_normal_date; +start transaction; +insert into interval_normal_date select '2020-6-02', r,r from generate_series(0,500) as r; +explain (costs off) select count(*) from interval_normal_date where b <= 500; + QUERY PLAN +-------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on interval_normal_date + Recheck Cond: (b <= 500) + -> Bitmap Index Scan on global_interval_index + Index Cond: (b <= 500) +(5 rows) + +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_normal_date' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +----------------------+----------+--------------------------------------------------- + interval_normal_date | r | {orientation=row,compression=no,wait_clean_gpi=y} + sys_p2 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(2 rows) + +alter table interval_normal_date drop partition sys_p2; +commit; +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_normal_date' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +----------------------+----------+--------------------------------------------------- + interval_normal_date | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +vacuum analyze interval_normal_date; +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_normal_date' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + relname | parttype | reloptions +---------+----------+------------ +(0 rows) + +drop table if exists interval_partition_table_vacuum; +NOTICE: table "interval_partition_table_vacuum" does not exist, skipping +create table interval_partition_table_vacuum +( + c1 int, + c2 int, + logdate date not null, + PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 day') +( + PARTITION interval_partition_table_004_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_004_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_004_p2 VALUES LESS THAN ('2020-05-01') +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "interval_partition_table_vacuum_pkey" for table "interval_partition_table_vacuum" +create index global_index_interval_partition_table_vacuum_a on interval_partition_table_vacuum(c1) global; +\parallel on +insert into interval_partition_table_vacuum values (generate_series(1,10), generate_series(1,10), generate_series(TO_DATE('1990-01-01', 'YYYY-MM-DD'),TO_DATE('2020-12-01', 'YYYY-MM-DD'),'1 day')); +vacuum interval_partition_table_vacuum; +vacuum interval_partition_table_vacuum; +vacuum analyze interval_partition_table_vacuum; +\parallel off +set enable_bitmapscan = off; +explain (costs off) select count(*) from interval_partition_table_vacuum where c1 = 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_interval_partition_table_vacuum_a on interval_partition_table_vacuum + Index Cond: (c1 = 1) +(3 rows) + +-- 11293 rows +select count(*) from interval_partition_table_vacuum where c1 = 1; + count +------- + 11293 +(1 row) + +-- all new add partition have wait_clean_gpi=y return true +select true from (select count(*) as count from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_partition_table_vacuum' and a.reloptions[3] like '%wait_clean_gpi=y%') wait_clean_gpi where wait_clean_gpi.count = 0 or wait_clean_gpi.count = 216; + bool +------ + t +(1 row) + +vacuum analyze interval_partition_table_vacuum; +-- 0 rows +select count(*) from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_partition_table_vacuum' and a.reloptions[3] like '%wait_clean_gpi=y%'; + count +------- + 0 +(1 row) + +reset enable_bitmapscan; +-- clean table +drop table interval_partition_table_vacuum; +drop table interval_normal_date; +drop table test_gpi_more_invalid; diff --git a/src/test/regress/expected/gpi_cluster_01.out b/src/test/regress/expected/gpi_cluster_01.out new file mode 100644 index 0000000000000000000000000000000000000000..922ed313d1ceb221c8df51a6001018d53e53d976 --- /dev/null +++ b/src/test/regress/expected/gpi_cluster_01.out @@ -0,0 +1,127 @@ +-- +---- test cluster on global index +-- +--drop table and index +drop index if exists global_inventory_table_01_index1; +NOTICE: index "global_inventory_table_01_index1" does not exist, skipping +drop index if exists global_inventory_table_01_index2; +NOTICE: index "global_inventory_table_01_index2" does not exist, skipping +drop table if exists inventory_table_01; +NOTICE: table "inventory_table_01" does not exist, skipping +create table inventory_table_01 +( + inv_date_sk integer not null, + inv_item_sk numeric not null, + inv_warehouse_sk integer not null, + inv_quantity_on_hand integer +) +partition by range(inv_date_sk) +( + partition p1 values less than(10000), + partition p2 values less than(20000), + partition p3 values less than(30000), + partition p4 values less than(40000), + partition p5 values less than(50000), + partition p6 values less than(60000), + partition p7 values less than(maxvalue) +); +--succeed +insert into inventory_table_01 values ( 1, 0.205546309705824, 1 ); +insert into inventory_table_01 values ( 2, 0.635608241427690, 2 ); +insert into inventory_table_01 values ( 3, 0.868086945265532, 3 ); +insert into inventory_table_01 values ( 4, 0.513698554132134, 4 ); +insert into inventory_table_01 values ( 5, 0.172576570883393, 5 ); +insert into inventory_table_01 values ( 6, 0.537533693015575, 6 ); +insert into inventory_table_01 values ( 7, 0.967260550707579, 7 ); +insert into inventory_table_01 values ( 8, 0.515049927402288, 8 ); +insert into inventory_table_01 values ( 9, 0.583286581560969, 9 ); +insert into inventory_table_01 values (10, 0.313009374774992, 10 ); +vacuum analyze inventory_table_01; +select ctid,* from inventory_table_01; + ctid | inv_date_sk | inv_item_sk | inv_warehouse_sk | inv_quantity_on_hand +--------+-------------+------------------+------------------+---------------------- + (0,1) | 1 | .205546309705824 | 1 | + (0,2) | 2 | .635608241427690 | 2 | + (0,3) | 3 | .868086945265532 | 3 | + (0,4) | 4 | .513698554132134 | 4 | + (0,5) | 5 | .172576570883393 | 5 | + (0,6) | 6 | .537533693015575 | 6 | + (0,7) | 7 | .967260550707579 | 7 | + (0,8) | 8 | .515049927402288 | 8 | + (0,9) | 9 | .583286581560969 | 9 | + (0,10) | 10 | .313009374774992 | 10 | +(10 rows) + +create index global_inventory_table_01_index1 on inventory_table_01(inv_date_sk) global; +create index global_inventory_table_01_index2 on inventory_table_01(inv_item_sk) global; +cluster inventory_table_01 using global_inventory_table_01_index2; +vacuum analyze inventory_table_01; +select ctid,* from inventory_table_01; + ctid | inv_date_sk | inv_item_sk | inv_warehouse_sk | inv_quantity_on_hand +--------+-------------+------------------+------------------+---------------------- + (0,1) | 5 | .172576570883393 | 5 | + (0,2) | 1 | .205546309705824 | 1 | + (0,3) | 10 | .313009374774992 | 10 | + (0,4) | 4 | .513698554132134 | 4 | + (0,5) | 8 | .515049927402288 | 8 | + (0,6) | 6 | .537533693015575 | 6 | + (0,7) | 9 | .583286581560969 | 9 | + (0,8) | 2 | .635608241427690 | 2 | + (0,9) | 3 | .868086945265532 | 3 | + (0,10) | 7 | .967260550707579 | 7 | +(10 rows) + +cluster inventory_table_01 using global_inventory_table_01_index1; +vacuum analyze inventory_table_01; +select ctid,* from inventory_table_01; + ctid | inv_date_sk | inv_item_sk | inv_warehouse_sk | inv_quantity_on_hand +--------+-------------+------------------+------------------+---------------------- + (0,1) | 1 | .205546309705824 | 1 | + (0,2) | 2 | .635608241427690 | 2 | + (0,3) | 3 | .868086945265532 | 3 | + (0,4) | 4 | .513698554132134 | 4 | + (0,5) | 5 | .172576570883393 | 5 | + (0,6) | 6 | .537533693015575 | 6 | + (0,7) | 7 | .967260550707579 | 7 | + (0,8) | 8 | .515049927402288 | 8 | + (0,9) | 9 | .583286581560969 | 9 | + (0,10) | 10 | .313009374774992 | 10 | +(10 rows) + +cluster inventory_table_01 using global_inventory_table_01_index2; +\dS+ inventory_table_01; + Table "public.inventory_table_01" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | numeric | not null | main | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_01_index1" btree (inv_date_sk) TABLESPACE pg_default + "global_inventory_table_01_index2" btree (inv_item_sk) TABLESPACE pg_default CLUSTER +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +vacuum analyze inventory_table_01; +select ctid,* from inventory_table_01; + ctid | inv_date_sk | inv_item_sk | inv_warehouse_sk | inv_quantity_on_hand +--------+-------------+------------------+------------------+---------------------- + (0,1) | 5 | .172576570883393 | 5 | + (0,2) | 1 | .205546309705824 | 1 | + (0,3) | 10 | .313009374774992 | 10 | + (0,4) | 4 | .513698554132134 | 4 | + (0,5) | 8 | .515049927402288 | 8 | + (0,6) | 6 | .537533693015575 | 6 | + (0,7) | 9 | .583286581560969 | 9 | + (0,8) | 2 | .635608241427690 | 2 | + (0,9) | 3 | .868086945265532 | 3 | + (0,10) | 7 | .967260550707579 | 7 | +(10 rows) + +--clean +drop index if exists global_inventory_table_01_index1; +drop index if exists global_inventory_table_01_index2; +drop table if exists inventory_table_01; diff --git a/src/test/regress/expected/gpi_cluster_02.out b/src/test/regress/expected/gpi_cluster_02.out new file mode 100644 index 0000000000000000000000000000000000000000..0e4e6343b13f4c8f6b1462617bab1d68f6ba3e34 --- /dev/null +++ b/src/test/regress/expected/gpi_cluster_02.out @@ -0,0 +1,153 @@ +-- +---- test cluster on global index +-- +--drop table and index +drop index if exists global_inventory_table_02_index1; +NOTICE: index "global_inventory_table_02_index1" does not exist, skipping +drop index if exists global_inventory_table_02_index2; +NOTICE: index "global_inventory_table_02_index2" does not exist, skipping +drop table if exists inventory_table_02; +NOTICE: table "inventory_table_02" does not exist, skipping +create table inventory_table_02 +( + inv_date_sk integer not null, + inv_item_sk numeric not null, + inv_warehouse_sk integer not null, + inv_quantity_on_hand integer +) +partition by range(inv_date_sk) +( + partition p1 values less than(10000), + partition p2 values less than(20000), + partition p3 values less than(30000), + partition p4 values less than(40000), + partition p5 values less than(50000), + partition p6 values less than(60000), + partition p7 values less than(maxvalue) +); +--succeed +insert into inventory_table_02 values (generate_series(1,100000), random(), generate_series(1,100000)); +--succeed +vacuum analyze inventory_table_02; +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; + bool +------ + t +(1 row) + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + bool +------ +(0 rows) + +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_item_sk=(select min(inv_item_sk) from inventory_table_02)) where ctid = '(0,1)'; + bool +------ +(0 rows) + +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_date_sk=(select min(inv_date_sk) from inventory_table_02)) where ctid = '(0,1)'; + bool +------ + t +(1 row) + +create index global_inventory_table_02_index1 on inventory_table_02(inv_date_sk) global; +create index global_inventory_table_02_index2 on inventory_table_02(inv_item_sk) global; +cluster inventory_table_02 using global_inventory_table_02_index2; +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_item_sk=(select min(inv_item_sk) from inventory_table_02)) where ctid = '(0,1)'; + bool +------ + t +(1 row) + +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_date_sk=(select min(inv_date_sk) from inventory_table_02)) where ctid = '(0,1)'; + bool +------ +(0 rows) + +vacuum analyze inventory_table_02; +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; + bool +------ +(0 rows) + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + bool +------ +(0 rows) + +cluster inventory_table_02 using global_inventory_table_02_index1; +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_item_sk=(select min(inv_item_sk) from inventory_table_02)) where ctid = '(0,1)'; + bool +------ +(0 rows) + +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_date_sk=(select min(inv_date_sk) from inventory_table_02)) where ctid = '(0,1)'; + bool +------ + t +(1 row) + +vacuum analyze inventory_table_02; +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; + bool +------ + t +(1 row) + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + bool +------ +(0 rows) + +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.partkey, part.interval, part.boundaries, part.reltuples +from pg_class class, pg_partition part, pg_index ind where class.relname = 'inventory_table_02' and ind.indrelid = class.oid and part.parentid = ind.indexrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + relname | parttype | rangenum | intervalnum | partstrategy | relallvisible | partkey | interval | boundaries | reltuples +---------+----------+----------+-------------+--------------+---------------+---------+----------+------------+----------- +(0 rows) + +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.partkey, part.interval, part.boundaries, part.reltuples +from pg_class class, pg_partition part, pg_index ind where class.relname = 'inventory_table_02' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + relname | parttype | rangenum | intervalnum | partstrategy | relallvisible | partkey | interval | boundaries | reltuples +--------------------+----------+----------+-------------+--------------+---------------+---------+----------+------------+----------- + inventory_table_02 | r | 0 | 0 | r | 0 | 1 | | | 0 + inventory_table_02 | r | 0 | 0 | r | 0 | 1 | | | 0 + p1 | p | 0 | 0 | r | 0 | | | {10000} | 9999 + p1 | p | 0 | 0 | r | 0 | | | {10000} | 9999 + p2 | p | 0 | 0 | r | 0 | | | {20000} | 10000 + p2 | p | 0 | 0 | r | 0 | | | {20000} | 10000 + p3 | p | 0 | 0 | r | 0 | | | {30000} | 10000 + p3 | p | 0 | 0 | r | 0 | | | {30000} | 10000 + p4 | p | 0 | 0 | r | 0 | | | {40000} | 10000 + p4 | p | 0 | 0 | r | 0 | | | {40000} | 10000 + p5 | p | 0 | 0 | r | 0 | | | {50000} | 10000 + p5 | p | 0 | 0 | r | 0 | | | {50000} | 10000 + p6 | p | 0 | 0 | r | 0 | | | {60000} | 10000 + p6 | p | 0 | 0 | r | 0 | | | {60000} | 10000 + p7 | p | 0 | 0 | r | 0 | | | {NULL} | 40001 + p7 | p | 0 | 0 | r | 0 | | | {NULL} | 40001 +(16 rows) + +cluster inventory_table_02 using global_inventory_table_02_index2; +\dS+ inventory_table_02; + Table "public.inventory_table_02" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | numeric | not null | main | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_02_index1" btree (inv_date_sk) TABLESPACE pg_default + "global_inventory_table_02_index2" btree (inv_item_sk) TABLESPACE pg_default CLUSTER +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +--clean +drop index if exists global_inventory_table_02_index1; +drop index if exists global_inventory_table_02_index2; +drop table if exists inventory_table_02; diff --git a/src/test/regress/expected/gpi_cluster_03.out b/src/test/regress/expected/gpi_cluster_03.out new file mode 100644 index 0000000000000000000000000000000000000000..99ec60807979ff5e845ee8ff0d4a31fcd8e47fa0 --- /dev/null +++ b/src/test/regress/expected/gpi_cluster_03.out @@ -0,0 +1,317 @@ +-- +---- test cluster on global index +-- +--drop table and index +drop index if exists global_inventory_table_index1; +NOTICE: index "global_inventory_table_index1" does not exist, skipping +drop index if exists global_inventory_table_index2; +NOTICE: index "global_inventory_table_index2" does not exist, skipping +drop index if exists inventory_table_index1; +NOTICE: index "inventory_table_index1" does not exist, skipping +drop index if exists inventory_table_index2; +NOTICE: index "inventory_table_index2" does not exist, skipping +drop table if exists inventory_table; +NOTICE: table "inventory_table" does not exist, skipping +CREATE TABLE inventory_table +( + INV_DATE_SK INTEGER NOT NULL, + INV_ITEM_SK INTEGER NOT NULL, + INV_WAREHOUSE_SK INTEGER NOT NULL, + INV_QUANTITY_ON_HAND INTEGER +) +PARTITION BY RANGE(INV_DATE_SK) +( + PARTITION P1 VALUES LESS THAN(10000), + PARTITION P2 VALUES LESS THAN(20000), + PARTITION P3 VALUES LESS THAN(30000), + PARTITION P4 VALUES LESS THAN(40000), + PARTITION P5 VALUES LESS THAN(50000), + PARTITION P6 VALUES LESS THAN(60000), + PARTITION P7 VALUES LESS THAN(MAXVALUE) +); +--succeed +insert into inventory_table values (generate_series(1,40000), generate_series(1,40000), generate_series(1,40000)); +--succeed +create index inventory_table_index1 on inventory_table(INV_DATE_SK) local; +--succeed +create index inventory_table_index2 on inventory_table(INV_DATE_SK) local; +--succeed +create index global_inventory_table_index1 on inventory_table(INV_ITEM_SK) global; +--succeed +create index global_inventory_table_index2 on inventory_table(INV_ITEM_SK) global; +--succeed +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +cluster inventory_table using global_inventory_table_index1; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default CLUSTER + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +cluster inventory_table PARTITION(P3) USING inventory_table_index1; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default CLUSTER + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +cluster inventory_table using global_inventory_table_index1; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default CLUSTER + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +alter table inventory_table cluster on inventory_table_index1; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default CLUSTER + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +alter table inventory_table cluster on global_inventory_table_index1; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default CLUSTER + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +alter table inventory_table cluster on global_inventory_table_index2; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default CLUSTER + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +cluster inventory_table; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default CLUSTER + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +cluster inventory_table using global_inventory_table_index1; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default CLUSTER + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +cluster inventory_table; +\dS+ inventory_table; + Table "public.inventory_table" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+---------+-----------+---------+--------------+------------- + inv_date_sk | integer | not null | plain | | + inv_item_sk | integer | not null | plain | | + inv_warehouse_sk | integer | not null | plain | | + inv_quantity_on_hand | integer | | plain | | +Indexes: + "global_inventory_table_index1" btree (inv_item_sk) TABLESPACE pg_default CLUSTER + "global_inventory_table_index2" btree (inv_item_sk) TABLESPACE pg_default + "inventory_table_index1" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default + "inventory_table_index2" btree (inv_date_sk) LOCAL(PARTITION p1_inv_date_sk_idx, PARTITION p2_inv_date_sk_idx, PARTITION p3_inv_date_sk_idx, PARTITION p4_inv_date_sk_idx, PARTITION p5_inv_date_sk_idx, PARTITION p6_inv_date_sk_idx, PARTITION p7_inv_date_sk_idx) TABLESPACE pg_default +Range partition by(inv_date_sk) +Number of partition: 7 (View pg_partition to check each partition range.) +Has OIDs: no +Options: orientation=row, compression=no + +SELECT conname FROM pg_constraint WHERE conrelid = 'inventory_table'::regclass +ORDER BY 1; + conname +--------- +(0 rows) + +SELECT relname, relkind, + EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast +FROM pg_class c WHERE relname = 'inventory_table' ORDER BY relname; + relname | relkind | hastoast +-----------------+---------+---------- + inventory_table | r | f +(1 row) + +-- Verify that indisclustered is correctly set +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'inventory_table' + AND indisclustered; + relname +------------------------------- + global_inventory_table_index1 +(1 row) + +-- Try changing indisclustered +ALTER TABLE inventory_table CLUSTER ON global_inventory_table_index2; +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'inventory_table' + AND indisclustered; + relname +------------------------------- + global_inventory_table_index2 +(1 row) + +-- Try turning off all clustering +ALTER TABLE inventory_table SET WITHOUT cluster; +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'inventory_table' + AND indisclustered; + relname +--------- +(0 rows) + +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +\parallel off +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index2; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index2; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index2; +\parallel off +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +\parallel off +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +\parallel off +--clean +drop index if exists global_inventory_table_index1; +drop index if exists global_inventory_table_index2; +drop index if exists inventory_table_index1; +drop index if exists inventory_table_index2; +drop table if exists inventory_table; diff --git a/src/test/regress/expected/gpi_create_constraint.out b/src/test/regress/expected/gpi_create_constraint.out new file mode 100644 index 0000000000000000000000000000000000000000..248c17517412b47b8744c72af7544b42edc53622 --- /dev/null +++ b/src/test/regress/expected/gpi_create_constraint.out @@ -0,0 +1,142 @@ +DROP TABLE if exists gpi_table1; +NOTICE: table "gpi_table1" does not exist, skipping +DROP TABLE if exists gpi_table2; +NOTICE: table "gpi_table2" does not exist, skipping +CREATE TABLE gpi_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_table1 VALUES less than (10000), + partition p1_gpi_table1 VALUES less than (20000), + partition p2_gpi_table1 VALUES less than (30000), + partition p3_gpi_table1 VALUES less than (maxvalue) +); +--ok +INSERT INTO gpi_table1 SELECT r, r, r FROM generate_series(0,10000) AS r; +--ok +CREATE INDEX ON gpi_table1 (c1) GLOBAL; +--error +CREATE INDEX idx1_gpi_table1 ON gpi_table1 (c1) GLOBAL; +--ok +CREATE INDEX idx2_gpi_table1 ON gpi_table1 (c1) LOCAL; +ERROR: Global and local partition index should not be on same column +--error +CREATE INDEX idx3_gpi_table1 ON gpi_table1 (c2) LOCAL; +--ok +CREATE INDEX idx4_gpi_table1 ON gpi_table1 ((c1+10)) LOCAL; +--ok +CREATE INDEX idx5_gpi_table1 ON gpi_table1 (c1, c2) LOCAL; +--ok +CREATE INDEX idx6_gpi_table1 ON gpi_table1 (c1, c2) GLOBAL; +ERROR: Global and local partition index should not be on same column +--error +CREATE INDEX idx7_gpi_table1 ON gpi_table1 ((c1+10)) GLOBAL; +ERROR: Global partition index does not support EXPRESSION index +--error +CREATE INDEX idx8_gpi_table1 ON gpi_table1 ((c1+10), c2) GLOBAL; +ERROR: Global partition index does not support EXPRESSION index +--error +CREATE INDEX idx9_gpi_table1 ON gpi_table1 USING hash (c1, c2) GLOBAL; +ERROR: Global partition index only support btree. +--error +CREATE INDEX idx10_gpi_table1 ON gpi_table1 (c1); +--ok +\d gpi_table1; + Table "public.gpi_table1" + Column | Type | Modifiers +--------+---------+----------- + c1 | integer | + c2 | integer | + c3 | integer | +Indexes: + "gpi_table1_c1_tableoid_idx" btree (c1) TABLESPACE pg_default + "idx10_gpi_table1" btree (c1) TABLESPACE pg_default + "idx1_gpi_table1" btree (c1) TABLESPACE pg_default + "idx3_gpi_table1" btree (c2) LOCAL(PARTITION p0_gpi_table1_c2_idx, PARTITION p1_gpi_table1_c2_idx, PARTITION p2_gpi_table1_c2_idx, PARTITION p3_gpi_table1_c2_idx) TABLESPACE pg_default + "idx4_gpi_table1" btree ((c1 + 10)) LOCAL(PARTITION p0_gpi_table1_expr_idx, PARTITION p1_gpi_table1_expr_idx, PARTITION p2_gpi_table1_expr_idx, PARTITION p3_gpi_table1_expr_idx) TABLESPACE pg_default + "idx5_gpi_table1" btree (c1, c2) LOCAL(PARTITION p0_gpi_table1_c1_c2_idx, PARTITION p1_gpi_table1_c1_c2_idx, PARTITION p2_gpi_table1_c1_c2_idx, PARTITION p3_gpi_table1_c1_c2_idx) TABLESPACE pg_default +Range partition by(c1) +Number of partition: 4 (View pg_partition to check each partition range.) + +CREATE TABLE gpi_table2 +( + c1 int, + c2 int, + c3 int +) WITH (orientation = column) +partition by range (c1) +( + partition p0_gpi_table2 VALUES less than (10000), + partition p1_gpi_table2 VALUES less than (20000), + partition p2_gpi_table2 VALUES less than (30000), + partition p3_gpi_table2 VALUES less than (maxvalue) +); +--ok +CREATE INDEX idx1_gpi_table2 ON gpi_table2 USING btree (c1) GLOBAL; +ERROR: Global partition index does not support column store. +--error +CREATE INDEX idx1_gpi_table2 ON gpi_table2 (c1) GLOBAL; +ERROR: Global partition index does not support column store. +--error +CREATE INDEX idx1_gpi_table2 ON gpi_table2 (c2) GLOBAL; +ERROR: Global partition index does not support column store. +drop table gpi_table2; +CREATE TABLE gpi_table2 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_table2 VALUES less than (10000), + partition p1_gpi_table2 VALUES less than (20000), + partition p2_gpi_table2 VALUES less than (30000), + partition p3_gpi_table2 VALUES less than (maxvalue) +); +--ok +CREATE INDEX idx1_gpi_table2 ON gpi_table2 USING btree (c1) GLOBAL; +--error +CREATE INDEX idx1_gpi_table2 ON gpi_table2 (c1) GLOBAL; +ERROR: relation "idx1_gpi_table2" already exists +--error +CREATE INDEX idx2_gpi_table2_local_c1 ON gpi_table2 (c1) LOCAL; +ERROR: Global and local partition index should not be on same column +--ok +CREATE INDEX idx1_gpi_table2_global_c2 ON gpi_table2 (c2) GLOBAL; +\d gpi_table2 + Table "public.gpi_table2" + Column | Type | Modifiers +--------+---------+----------- + c1 | integer | + c2 | integer | + c3 | integer | +Indexes: + "idx1_gpi_table2" btree (c1) TABLESPACE pg_default + "idx1_gpi_table2_global_c2" btree (c2) TABLESPACE pg_default +Range partition by(c1) +Number of partition: 4 (View pg_partition to check each partition range.) + +alter table gpi_table2 drop column c2; +\d gpi_table2 + Table "public.gpi_table2" + Column | Type | Modifiers +--------+---------+----------- + c1 | integer | + c3 | integer | +Indexes: + "idx1_gpi_table2" btree (c1) TABLESPACE pg_default +Range partition by(c1) +Number of partition: 4 (View pg_partition to check each partition range.) + +create table gpi_table2_inherits (c4 int) INHERITS (gpi_table2); +ERROR: CREATE TABLE ... INHERITS is not yet supported. +-- error +alter table gpi_table2 set (wait_clean_gpi=y); +ERROR: Un-support feature +DETAIL: Forbid to set or change inner option "wait_clean_gpi" +drop table gpi_table2; diff --git a/src/test/regress/expected/gpi_hw_partition_vacuum_full.out b/src/test/regress/expected/gpi_hw_partition_vacuum_full.out new file mode 100644 index 0000000000000000000000000000000000000000..4d2b3047bae6fdb6aa5ae700a3921bd405baddd3 --- /dev/null +++ b/src/test/regress/expected/gpi_hw_partition_vacuum_full.out @@ -0,0 +1,112 @@ +-- +-- test vacuum full partition +-- +--i1. create table +create table hw_partition_vacuum_full_partition_table(id int,name text, city text) +partition by range(id) +( + partition hw_partition_vacuum_full_partition_table_p1 values less than(1000), + partition hw_partition_vacuum_full_partition_table_p2 values less than(2000) +); +--i2. create btree index +create index inx_part0_id on hw_partition_vacuum_full_partition_table(id) global; +--i3. insert data +create or replace function insert_part0_data() returns void as $$ +declare + times integer :=1; +begin + loop + insert into hw_partition_vacuum_full_partition_table values(times, 'xian', 'beijing'); + times = times + 1; + if times > 1998 then + exit; + end if; + end loop; +end; +$$ language plpgsql; +select insert_part0_data(); + insert_part0_data +------------------- + +(1 row) + +--i4. delete data +delete from hw_partition_vacuum_full_partition_table where id%3=0; +select pg_sleep(2); + pg_sleep +---------- + +(1 row) + +select count(*) from hw_partition_vacuum_full_partition_table where id%3=2; + count +------- + 666 +(1 row) + +--i5. vacuum full hw_partition_vacuum_full_partition_table_p1 +analyze hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p1); +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p1'; + relpages | reltuples +----------+----------- + 0 | 0 +(1 row) + +vacuum full hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p1); +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p1'; + relpages | reltuples +----------+----------- + 0 | 0 +(1 row) + +--i6. vacuum full hw_partition_vacuum_full_partition_table_p2 +analyze hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p2); +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p2'; + relpages | reltuples +----------+----------- + 0 | 0 +(1 row) + +vacuum full hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p2); +select pg_sleep(2); + pg_sleep +---------- + +(1 row) + +select count(*) from hw_partition_vacuum_full_partition_table where id%3=2; + count +------- + 666 +(1 row) + +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p2'; + relpages | reltuples +----------+----------- + 0 | 0 +(1 row) + +--i7. delete all the data +delete from hw_partition_vacuum_full_partition_table where id%3=1; +--i8. vacuum full hw_partition_vacuum_full_partition_table +vacuum full hw_partition_vacuum_full_partition_table; +select pg_sleep(2); + pg_sleep +---------- + +(1 row) + +select count(*) from hw_partition_vacuum_full_partition_table where id%3=2; + count +------- + 666 +(1 row) + +select relpages > 0 as relpagesgtzero, reltuples > 0 as reltuplesgtzero from pg_class where relname='hw_partition_vacuum_full_partition_table'; + relpagesgtzero | reltuplesgtzero +----------------+----------------- + f | f +(1 row) + +--i9. drop table +drop table hw_partition_vacuum_full_partition_table; diff --git a/src/test/regress/expected/gpi_hw_partition_vacuum_full_01.out b/src/test/regress/expected/gpi_hw_partition_vacuum_full_01.out new file mode 100644 index 0000000000000000000000000000000000000000..5a4599634c8d3473cb1aa9f3dabaa83418eb8a2f --- /dev/null +++ b/src/test/regress/expected/gpi_hw_partition_vacuum_full_01.out @@ -0,0 +1,92 @@ +-- +-- test vacuum full partition +-- +--i1. create table +create table hw_partition_vacuum_full_partition_table(id int,name text, city text) +partition by range(id) +( + partition hw_partition_vacuum_full_partition_table_p1 values less than(1000), + partition hw_partition_vacuum_full_partition_table_p2 values less than(2000) +); +--i2. create btree index +create index inx_part0_id on hw_partition_vacuum_full_partition_table(id) global; +--i3. insert data +create or replace function insert_part0_data() returns void as $$ +declare + times integer :=1; +begin + loop + insert into hw_partition_vacuum_full_partition_table values(times, 'xian', 'beijing'); + times = times + 1; + if times > 1998 then + exit; + end if; + end loop; +end; +$$ language plpgsql; +select insert_part0_data(); + insert_part0_data +------------------- + +(1 row) + +--i4. delete data +delete from hw_partition_vacuum_full_partition_table where id%10=1; +\parallel on +vacuum full hw_partition_vacuum_full_partition_table; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +\parallel off + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + + count +------- + 199 +(1 row) + +drop table hw_partition_vacuum_full_partition_table; diff --git a/src/test/regress/expected/gpi_index.out b/src/test/regress/expected/gpi_index.out new file mode 100644 index 0000000000000000000000000000000000000000..0925f6b28562e0a7e915d13996592a37bf25476a --- /dev/null +++ b/src/test/regress/expected/gpi_index.out @@ -0,0 +1,627 @@ +--clean and create data +drop table if exists gpi_index_test; +NOTICE: table "gpi_index_test" does not exist, skipping +create table gpi_index_test(a int, b int, c text) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500), partition p4 values less than (maxvalue)); +insert into gpi_index_test select r,r,'gpi_index_test' from generate_series(0,1000) as r; +create index gpi_index_test_global_a on gpi_index_test(a) global; +vacuum analyze gpi_index_test; +-- select in parition key and with global index +-- use global index +set enable_bitmapscan = off; +explain (costs off) select * from gpi_index_test where a < 200; + QUERY PLAN +------------------------------------------------------------ + [Bypass] + Index Scan using gpi_index_test_global_a on gpi_index_test + Index Cond: (a < 200) +(3 rows) + +explain (costs off) select * from gpi_index_test where a < 200 order by a; + QUERY PLAN +------------------------------------------------------------ + [Bypass] + Index Scan using gpi_index_test_global_a on gpi_index_test + Index Cond: (a < 200) +(3 rows) + +select count(*) from gpi_index_test where a < 200; + count +------- + 200 +(1 row) + +explain (costs off) select avg(b), a from gpi_index_test where a < 10 group by (a) having avg(b) < 100 order by a; + QUERY PLAN +------------------------------------------------------------------ + GroupAggregate + Group By Key: a + Filter: (avg(b) < 100::numeric) + -> Index Scan using gpi_index_test_global_a on gpi_index_test + Index Cond: (a < 10) +(5 rows) + +select avg(b), a from gpi_index_test where a < 10 group by (a) having avg(b) < 100 order by a; + avg | a +------------------------+--- + 0.00000000000000000000 | 0 + 1.00000000000000000000 | 1 + 2.0000000000000000 | 2 + 3.0000000000000000 | 3 + 4.0000000000000000 | 4 + 5.0000000000000000 | 5 + 6.0000000000000000 | 6 + 7.0000000000000000 | 7 + 8.0000000000000000 | 8 + 9.0000000000000000 | 9 +(10 rows) + +-- multi index with global index +create index gpi_index_test_global_b on gpi_index_test(b) global; +explain (costs off) select * from gpi_index_test where a < 200 and b > 100; + QUERY PLAN +------------------------------------------------------------ + Index Scan using gpi_index_test_global_a on gpi_index_test + Index Cond: (a < 200) + Filter: (b > 100) +(3 rows) + +explain (costs off) select * from gpi_index_test where a < 200 and b > 100 order by a; + QUERY PLAN +------------------------------------------------------------ + Index Scan using gpi_index_test_global_a on gpi_index_test + Index Cond: (a < 200) + Filter: (b > 100) +(3 rows) + +select * from gpi_index_test where a < 200 and b > 190 order by a; + a | b | c +-----+-----+---------------- + 191 | 191 | gpi_index_test + 192 | 192 | gpi_index_test + 193 | 193 | gpi_index_test + 194 | 194 | gpi_index_test + 195 | 195 | gpi_index_test + 196 | 196 | gpi_index_test + 197 | 197 | gpi_index_test + 198 | 198 | gpi_index_test + 199 | 199 | gpi_index_test +(9 rows) + +explain (costs off) select * from gpi_index_test where a < 200 or b > 100; + QUERY PLAN +---------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test + Filter: ((a < 200) OR (b > 100)) + Selected Partitions: 1..4 +(5 rows) + +explain (costs off) select * from gpi_index_test where a < 200 or b > 100 order by a; + QUERY PLAN +------------------------------------------------------------ + Index Scan using gpi_index_test_global_a on gpi_index_test + Filter: ((a < 200) OR (b > 100)) +(2 rows) + +select * from gpi_index_test where a < 200 and b > 190 order by a; + a | b | c +-----+-----+---------------- + 191 | 191 | gpi_index_test + 192 | 192 | gpi_index_test + 193 | 193 | gpi_index_test + 194 | 194 | gpi_index_test + 195 | 195 | gpi_index_test + 196 | 196 | gpi_index_test + 197 | 197 | gpi_index_test + 198 | 198 | gpi_index_test + 199 | 199 | gpi_index_test +(9 rows) + +-- multi column index with global index +create index gpi_index_test_global_a_b on gpi_index_test(a,b) global; +explain (costs off) select * from gpi_index_test where a = 200 and b = 100; + QUERY PLAN +------------------------------------------------------------ + Index Scan using gpi_index_test_global_b on gpi_index_test + Index Cond: (b = 100) + Filter: (a = 200) +(3 rows) + +select * from gpi_index_test where a = 100 and b = 100 order by a; + a | b | c +-----+-----+---------------- + 100 | 100 | gpi_index_test +(1 row) + +-- select with local index and global index +drop index gpi_index_test_global_a_b; +drop index gpi_index_test_global_a; +drop index gpi_index_test_global_b; +create index gpi_index_test_local_a on gpi_index_test(a) local; +create index gpi_index_test_global_b on gpi_index_test(b) global; +vacuum analyze gpi_index_test; +explain (costs off) select * from gpi_index_test where (a < 200 or a < 300) and b > 100; + QUERY PLAN +---------------------------------------------------------- + Partition Iterator + Iterations: 3 + -> Partitioned Seq Scan on gpi_index_test + Filter: ((b > 100) AND ((a < 200) OR (a < 300))) + Selected Partitions: 1..3 +(5 rows) + +select * from gpi_index_test where a < 200 and b > 190 order by b; + a | b | c +-----+-----+---------------- + 191 | 191 | gpi_index_test + 192 | 192 | gpi_index_test + 193 | 193 | gpi_index_test + 194 | 194 | gpi_index_test + 195 | 195 | gpi_index_test + 196 | 196 | gpi_index_test + 197 | 197 | gpi_index_test + 198 | 198 | gpi_index_test + 199 | 199 | gpi_index_test +(9 rows) + +-- use bitmapscan +reset enable_bitmapscan; +set force_bitmapand = on; +set enable_indexscan = off; +-- use bitmapand local index and global index and use Partition Iterator +explain (costs off) select * from gpi_index_test where a = 800 and b%4 <> 0 and b = 50; + QUERY PLAN +--------------------------------------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Bitmap Heap Scan on gpi_index_test + Recheck Cond: ((b = 50) AND (a = 800)) + Filter: ((b % 4) <> 0) + Selected Partitions: 4 + -> BitmapAnd + -> Bitmap Index Scan on gpi_index_test_global_b + Index Cond: (b = 50) + -> Partitioned Bitmap Index Scan on gpi_index_test_local_a + Index Cond: (a = 800) + Selected Partitions: 4 +(12 rows) + +select * from gpi_index_test where a < 800 and b%4 <> 0 and b <= 50; + a | b | c +----+----+---------------- + 1 | 1 | gpi_index_test + 2 | 2 | gpi_index_test + 3 | 3 | gpi_index_test + 5 | 5 | gpi_index_test + 6 | 6 | gpi_index_test + 7 | 7 | gpi_index_test + 9 | 9 | gpi_index_test + 10 | 10 | gpi_index_test + 11 | 11 | gpi_index_test + 13 | 13 | gpi_index_test + 14 | 14 | gpi_index_test + 15 | 15 | gpi_index_test + 17 | 17 | gpi_index_test + 18 | 18 | gpi_index_test + 19 | 19 | gpi_index_test + 21 | 21 | gpi_index_test + 22 | 22 | gpi_index_test + 23 | 23 | gpi_index_test + 25 | 25 | gpi_index_test + 26 | 26 | gpi_index_test + 27 | 27 | gpi_index_test + 29 | 29 | gpi_index_test + 30 | 30 | gpi_index_test + 31 | 31 | gpi_index_test + 33 | 33 | gpi_index_test + 34 | 34 | gpi_index_test + 35 | 35 | gpi_index_test + 37 | 37 | gpi_index_test + 38 | 38 | gpi_index_test + 39 | 39 | gpi_index_test + 41 | 41 | gpi_index_test + 42 | 42 | gpi_index_test + 43 | 43 | gpi_index_test + 45 | 45 | gpi_index_test + 46 | 46 | gpi_index_test + 47 | 47 | gpi_index_test + 49 | 49 | gpi_index_test + 50 | 50 | gpi_index_test +(38 rows) + +-- must use seqscan +explain (costs off) select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); + QUERY PLAN +-------------------------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test + Filter: ((a = 200) OR (((b % 4) = 0) AND (b <= 50))) + Selected Partitions: 1..4 +(5 rows) + +select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); + a | b | c +-----+-----+---------------- + 0 | 0 | gpi_index_test + 4 | 4 | gpi_index_test + 8 | 8 | gpi_index_test + 12 | 12 | gpi_index_test + 16 | 16 | gpi_index_test + 20 | 20 | gpi_index_test + 24 | 24 | gpi_index_test + 28 | 28 | gpi_index_test + 32 | 32 | gpi_index_test + 36 | 36 | gpi_index_test + 40 | 40 | gpi_index_test + 44 | 44 | gpi_index_test + 48 | 48 | gpi_index_test + 200 | 200 | gpi_index_test +(14 rows) + +drop index gpi_index_test_global_b; +create index gpi_index_test_local_b on gpi_index_test(b) local; +-- use bitmapor +explain (costs off) select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); + QUERY PLAN +--------------------------------------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on gpi_index_test + Recheck Cond: ((a = 200) OR (b <= 50)) + Filter: ((a = 200) OR (((b % 4) = 0) AND (b <= 50))) + Selected Partitions: 1..4 + -> BitmapOr + -> Partitioned Bitmap Index Scan on gpi_index_test_local_a + Index Cond: (a = 200) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on gpi_index_test_local_b + Index Cond: (b <= 50) + Selected Partitions: 1..4 +(13 rows) + +select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); + a | b | c +-----+-----+---------------- + 0 | 0 | gpi_index_test + 4 | 4 | gpi_index_test + 8 | 8 | gpi_index_test + 12 | 12 | gpi_index_test + 16 | 16 | gpi_index_test + 20 | 20 | gpi_index_test + 24 | 24 | gpi_index_test + 28 | 28 | gpi_index_test + 32 | 32 | gpi_index_test + 36 | 36 | gpi_index_test + 40 | 40 | gpi_index_test + 44 | 44 | gpi_index_test + 48 | 48 | gpi_index_test + 200 | 200 | gpi_index_test +(14 rows) + +drop index gpi_index_test_local_b; +reset enable_indexscan; +reset force_bitmapand; +--two table join use global index scan +drop table if exists gpi_index_test1; +NOTICE: table "gpi_index_test1" does not exist, skipping +create table gpi_index_test1(a int, b int, c text) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500), partition p4 values less than (maxvalue)); +insert into gpi_index_test1 select r,r,'gpi_index_test' from generate_series(0,1000) as r; +create index gpi_index_test1_global_a on gpi_index_test1(a) global; +vacuum analyze gpi_index_test1; +-- single global index +--hash join +explain (costs off) select * from gpi_index_test1 a, gpi_index_test b where a.a = b.a; + QUERY PLAN +------------------------------------------------------------ + Hash Join + Hash Cond: (a.a = b.a) + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test1 a + Selected Partitions: 1..4 + -> Hash + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test b + Selected Partitions: 1..4 +(11 rows) + +select count(*) from gpi_index_test1 a, gpi_index_test b where a.a = b.a; + count +------- + 1001 +(1 row) + +-- merge join +explain (costs off) select * from gpi_index_test1 a, gpi_index_test b where a.a = b.a and (a.b < 10 or b.b > 9990) order by a.a; + QUERY PLAN +------------------------------------------------------------------------------------- + Nested Loop + -> Partition Iterator + Iterations: 4 + -> Partitioned Index Scan using gpi_index_test_local_a on gpi_index_test b + Selected Partitions: 1..4 + -> Index Scan using gpi_index_test1_global_a on gpi_index_test1 a + Index Cond: (a = b.a) + Filter: ((b < 10) OR (b.b > 9990)) +(8 rows) + +select * from gpi_index_test1 a, gpi_index_test b where a.a = b.a and (a.b < 10 or b.b > 9990) order by a.a; + a | b | c | a | b | c +---+---+----------------+---+---+---------------- + 0 | 0 | gpi_index_test | 0 | 0 | gpi_index_test + 1 | 1 | gpi_index_test | 1 | 1 | gpi_index_test + 2 | 2 | gpi_index_test | 2 | 2 | gpi_index_test + 3 | 3 | gpi_index_test | 3 | 3 | gpi_index_test + 4 | 4 | gpi_index_test | 4 | 4 | gpi_index_test + 5 | 5 | gpi_index_test | 5 | 5 | gpi_index_test + 6 | 6 | gpi_index_test | 6 | 6 | gpi_index_test + 7 | 7 | gpi_index_test | 7 | 7 | gpi_index_test + 8 | 8 | gpi_index_test | 8 | 8 | gpi_index_test + 9 | 9 | gpi_index_test | 9 | 9 | gpi_index_test +(10 rows) + +select count(*) from gpi_index_test1 a, gpi_index_test b where a.a = b.a and (a.b < 10 or b.b > 10); + count +------- + 1000 +(1 row) + +-- nestloop +explain (costs off) select * from gpi_index_test1 a, gpi_index_test b where a.a < b.a and (a.b < 10 or b.b > 9990) order by a.a; + QUERY PLAN +------------------------------------------------------------------------------------- + Nested Loop + -> Index Scan using gpi_index_test1_global_a on gpi_index_test1 a + -> Partition Iterator + Iterations: 4 + -> Partitioned Index Scan using gpi_index_test_local_a on gpi_index_test b + Index Cond: (a.a < a) + Filter: ((a.b < 10) OR (b > 9990)) + Selected Partitions: 1..4 +(8 rows) + +select count(*) from gpi_index_test1 a, gpi_index_test b where a.a < b.a and (a.b < 10 or b.b > 9990); + count +------- + 9955 +(1 row) + +create index gpi_index_test_global_b on gpi_index_test(b) global; +explain (costs off) select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a; + QUERY PLAN +------------------------------------------------------------------------------ + Aggregate + -> Hash Join + Hash Cond: (t2.a = gpi_index_test.a) + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test1 t2 + Selected Partitions: 1..4 + -> Hash + -> Index Scan using gpi_index_test_global_b on gpi_index_test + Index Cond: (b < 100) +(10 rows) + +select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a; + count +------- + 100 +(1 row) + +explain (costs off) select count(*) from gpi_index_test as t1 NATURAL inner join gpi_index_test1 t2 where t1.b < 200 and t2.a = 50; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate + -> Nested Loop + Join Filter: ((t1.b = t2.b) AND (t1.c = t2.c)) + -> Partition Iterator + Iterations: 1 + -> Partitioned Index Scan using gpi_index_test_local_a on gpi_index_test t1 + Index Cond: (a = 50) + Filter: (b < 200) + Selected Partitions: 1 + -> Index Scan using gpi_index_test1_global_a on gpi_index_test1 t2 + Index Cond: (a = 50) + Filter: (b < 200) +(12 rows) + +select count(*) from gpi_index_test as t1 NATURAL inner join gpi_index_test1 t2 where t1.b < 200 and t2.a = 50; + count +------- + 1 +(1 row) + +explain (costs off) select * from gpi_index_test t1 full join gpi_index_test1 t2 on t1.b >= t2.a where t1.b < 200 and t2.a = 50 and t1.a = 100; + QUERY PLAN +----------------------------------------------------------------------- + Nested Loop + -> Index Scan using gpi_index_test1_global_a on gpi_index_test1 t2 + Index Cond: (a = 50) + -> Bitmap Heap Scan on gpi_index_test t1 + Recheck Cond: ((b >= t2.a) AND (b < 200)) + Filter: (a = 100) + -> Bitmap Index Scan on gpi_index_test_global_b + Index Cond: ((b >= t2.a) AND (b < 200)) +(8 rows) + +select * from gpi_index_test t1 full join gpi_index_test1 t2 on t1.b >= t2.a where t1.b < 200 and t2.a = 50 and t1.a = 100; + a | b | c | a | b | c +-----+-----+----------------+----+----+---------------- + 100 | 100 | gpi_index_test | 50 | 50 | gpi_index_test +(1 row) + +explain (costs off) select * from gpi_index_test t1 left join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a != 60 or t1.b != 30)) where t1.b < 200 and t2.a < 1000; + QUERY PLAN +----------------------------------------------------------------------- + Nested Loop + -> Index Scan using gpi_index_test_global_b on gpi_index_test t1 + Index Cond: (b < 200) + Filter: ((a <> 60) OR (b <> 30)) + -> Index Scan using gpi_index_test1_global_a on gpi_index_test1 t2 + Index Cond: ((t1.b >= a) AND (a < 1000)) +(6 rows) + +select count(*) from gpi_index_test t1 left join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a != 60 or t1.b != 30)) where t1.b < 200 and t2.a < 1000; + count +------- + 20100 +(1 row) + +explain (costs off) select * from gpi_index_test t1 right join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a < 60 or t2.b != 30)) where t1.b < 200 and t2.a < 100; + QUERY PLAN +--------------------------------------------------------------------------- + Nested Loop + Join Filter: ((t1.b >= t2.a) AND ((t1.a < 60) OR (t2.b <> 30))) + -> Index Scan using gpi_index_test1_global_a on gpi_index_test1 t2 + Index Cond: (a < 100) + -> Materialize + -> Index Scan using gpi_index_test_global_b on gpi_index_test t1 + Index Cond: (b < 200) +(7 rows) + +select count(*) from gpi_index_test t1 left join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a != 60 or t1.b != 30)) where t1.b < 200 and t2.a < 100; + count +------- + 15050 +(1 row) + +--three table join use global index scan +drop table if exists gpi_index_test2; +NOTICE: table "gpi_index_test2" does not exist, skipping +create table gpi_index_test2(a int, b int, c text) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500), partition p4 values less than (maxvalue)); +insert into gpi_index_test2 select r,r,'gpi_index_test' from generate_series(0,1000) as r; +create index gpi_index_test2_global_b on gpi_index_test2(b) global; +create index gpi_index_test2_global_a on gpi_index_test2(a) global; +vacuum analyze gpi_index_test2; +explain (costs off) select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b <= 10; + QUERY PLAN +----------------------------------------------------------------------------------------- + Aggregate + -> Nested Loop + -> Hash Join + Hash Cond: (t2.a = t3.a) + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test1 t2 + Selected Partitions: 1..4 + -> Hash + -> Index Scan using gpi_index_test2_global_b on gpi_index_test2 t3 + Index Cond: (b <= 10) + -> Partition Iterator + Iterations: 4 + -> Partitioned Index Scan using gpi_index_test_local_a on gpi_index_test + Index Cond: (a = t2.a) + Filter: (b < 100) + Selected Partitions: 1..4 +(17 rows) + +select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b <= 10; + count +------- + 11 +(1 row) + +create index gpi_index_test1_global_b on gpi_index_test1(b) global; +explain (costs off) select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.a <= 100); + QUERY PLAN +------------------------------------------------------------------------------------------ + Aggregate + -> Nested Loop + Join Filter: (gpi_index_test.a = t2.a) + -> Hash Semi Join + Hash Cond: (t3.b = gpi_index_test1.b) + -> Hash Join + Hash Cond: (t3.a = gpi_index_test.a) + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test2 t3 + Selected Partitions: 1..4 + -> Hash + -> Index Scan using gpi_index_test_global_b on gpi_index_test + Index Cond: (b < 100) + -> Hash + -> Index Scan using gpi_index_test1_global_a on gpi_index_test1 + Index Cond: (a <= 100) + -> Index Only Scan using gpi_index_test1_global_a on gpi_index_test1 t2 + Index Cond: (a = t3.a) +(19 rows) + +select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.a <= 100); + count +------- + 100 +(1 row) + +insert into gpi_index_test1 values(100, 100, 'test_my'); +insert into gpi_index_test2 values(100, 100, 'test_my'); +insert into gpi_index_test values(100, 100, 'test_my'); +explain (costs off) select * from (select a from gpi_index_test where gpi_index_test.b <= 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.b <= 100 and t3.c = 'test_my'); + QUERY PLAN +------------------------------------------------------------------------------------ + Hash Right Join + Hash Cond: (t3.a = gpi_index_test.a) + Filter: (SubPlan 1) + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test2 t3 + Selected Partitions: 1..4 + -> Hash + -> Hash Join + Hash Cond: (t2.a = gpi_index_test.a) + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test1 t2 + Selected Partitions: 1..4 + -> Hash + -> Index Scan using gpi_index_test_global_b on gpi_index_test + Index Cond: (b <= 100) + SubPlan 1 + -> Result + One-Time Filter: (t3.c = 'test_my'::text) + -> Index Only Scan using gpi_index_test1_global_b on gpi_index_test1 + Index Cond: (b <= 100) +(22 rows) + +select * from (select a from gpi_index_test where gpi_index_test.b <= 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.b <= 100 and t3.c = 'test_my'); + a | a | b | c | a | b | c +-----+-----+-----+----------------+-----+-----+--------- + 100 | 100 | 100 | test_my | 100 | 100 | test_my + 100 | 100 | 100 | test_my | 100 | 100 | test_my + 100 | 100 | 100 | gpi_index_test | 100 | 100 | test_my + 100 | 100 | 100 | gpi_index_test | 100 | 100 | test_my +(4 rows) + +insert into gpi_index_test1 values(50, 40, 'test_my'); +insert into gpi_index_test values(60, 60, 'test_my'); +explain (costs off) select * from gpi_index_test t1 cross join gpi_index_test1 t2 where t1.a > t2.b and (t1.a + t2.b = 100) and t1.c = 'test_my' order by t1.b; + QUERY PLAN +----------------------------------------------------------------------------- + Sort + Sort Key: t1.b + -> Nested Loop + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_index_test t1 + Filter: (c = 'test_my'::text) + Selected Partitions: 1..4 + -> Index Scan using gpi_index_test1_global_b on gpi_index_test1 t2 + Index Cond: (t1.a > b) + Filter: ((t1.a + b) = 100) +(11 rows) + +select * from gpi_index_test t1 cross join gpi_index_test1 t2 where t1.a > t2.b and (t1.a + t2.b = 100) and t1.c = 'test_my' order by t1.b; + a | b | c | a | b | c +-----+-----+---------+----+----+---------------- + 60 | 60 | test_my | 50 | 40 | test_my + 60 | 60 | test_my | 40 | 40 | gpi_index_test + 100 | 100 | test_my | 0 | 0 | gpi_index_test +(3 rows) + +-- clean data +drop table gpi_index_test; +drop table gpi_index_tes1; +ERROR: table "gpi_index_tes1" does not exist +drop table gpi_index_test2; diff --git a/src/test/regress/expected/gpi_index_only.out b/src/test/regress/expected/gpi_index_only.out new file mode 100644 index 0000000000000000000000000000000000000000..1c4416d1bcb8dfeee70d9dcb820014594b3625e0 --- /dev/null +++ b/src/test/regress/expected/gpi_index_only.out @@ -0,0 +1,657 @@ +-- +---- test partitioned index +-- +set client_min_messages=error; +drop table if exists global_part_indexonly_table; +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(0,50), generate_series(0,50); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table order by c2,c3; + QUERY PLAN +------------------------------------------------------------------------------------------- + Unique + -> Index Only Scan using test_global_nonpartition_index on global_part_indexonly_table +(2 rows) + +select distinct(c2),c3 from global_part_indexonly_table order by c2,c3; + c2 | c3 +----+---- + 0 | 0 + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 10 | 10 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 + 16 | 16 + 17 | 17 + 18 | 18 + 19 | 19 + 20 | 20 + 21 | 21 + 22 | 22 + 23 | 23 + 24 | 24 + 25 | 25 + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 + 31 | 31 + 32 | 32 + 33 | 33 + 34 | 34 + 35 | 35 + 36 | 36 + 37 | 37 + 38 | 38 + 39 | 39 + 40 | 40 + 41 | 41 + 42 | 42 + 43 | 43 + 44 | 44 + 45 | 45 + 46 | 46 + 47 | 47 + 48 | 48 + 49 | 49 + 50 | 50 +(51 rows) + +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(-50,50), generate_series(-20,30); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; + QUERY PLAN +------------------------------------------------------------------------------------------- + Unique + -> Index Only Scan using test_global_nonpartition_index on global_part_indexonly_table + Index Cond: (c2 = (-19)) + Filter: (((c2 + c3) < (-10)) AND ((c2 + c3) > (-30))) +(4 rows) + +select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; + c2 | c3 +-----+----- + -19 | -10 + -19 | -9 + -19 | -8 + -19 | -7 + -19 | -6 + -19 | -5 + -19 | -4 + -19 | -3 + -19 | -2 + -19 | -1 + -19 | 0 + -19 | 1 + -19 | 2 + -19 | 3 + -19 | 4 + -19 | 5 + -19 | 6 + -19 | 7 + -19 | 8 +(19 rows) + +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(-50,50), generate_series(-20,30); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +update global_part_indexonly_table set c1=c1+5; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; + QUERY PLAN +------------------------------------------------------------------------------------------- + Unique + -> Index Only Scan using test_global_nonpartition_index on global_part_indexonly_table + Index Cond: (c2 = (-19)) + Filter: (((c2 + c3) < (-10)) AND ((c2 + c3) > (-30))) +(4 rows) + +select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; + c2 | c3 +-----+----- + -19 | -10 + -19 | -9 + -19 | -8 + -19 | -7 + -19 | -6 + -19 | -5 + -19 | -4 + -19 | -3 + -19 | -2 + -19 | -1 + -19 | 0 + -19 | 1 + -19 | 2 + -19 | 3 + -19 | 4 + -19 | 5 + -19 | 6 + -19 | 7 + -19 | 8 +(19 rows) + +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(-50,50), generate_series(-20,30); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +update global_part_indexonly_table set c2=c2+35; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; + QUERY PLAN +------------------------------------------------------------------------------------------- + Unique + -> Index Only Scan using test_global_nonpartition_index on global_part_indexonly_table + Index Cond: (c2 = (-19)) + Filter: (((c2 + c3) < (-10)) AND ((c2 + c3) > (-30))) +(4 rows) + +select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; + c2 | c3 +----+---- +(0 rows) + +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; +drop table if exists gpi_J1_TBL; +drop table if exists gpi_J2_TBL; +CREATE TABLE gpi_J1_TBL ( + i integer, + j integer, + t integer +) +partition by range (i) +( + partition gpi_J1_TBL_p0 values less than (10), + partition gpi_J1_TBL_p1 values less than (30), + partition gpi_J1_TBL_p2 values less than (maxvalue) +); +CREATE TABLE gpi_J2_TBL ( + i integer, + k integer, + t integer +) +partition by range (i) +( + partition gpi_J2_TBL_p0 values less than (10000), + partition gpi_J2_TBL_p1 values less than (20000), + partition gpi_J2_TBL_p2 values less than (30000), + partition gpi_J2_TBL_p3 values less than (40000), + partition gpi_J2_TBL_p4 values less than (50000), + partition gpi_J2_TBL_p5 values less than (60000), + partition gpi_J2_TBL_p6 values less than (maxvalue) +); +create index gpi_J1_TBL_index on gpi_J1_TBL(i) local; +create index gpi_J2_TBL_index on gpi_J2_TBL(i) local; +INSERT INTO gpi_J1_TBL select generate_series(0,50),generate_series(-10,30),generate_series(-50,40); +INSERT INTO gpi_J2_TBL select r,r,r from generate_series(0,90000) as r; +create index gpi_J1_TBL_nonp_j_index on gpi_J1_TBL(j) global; +create index gpi_J1_TBL_nonp_t_index on gpi_J1_TBL(t) global; +create index gpi_J2_TBL_nonp_k_index on gpi_J2_TBL(k) global; +create index gpi_J2_TBL_nonp_t_index on gpi_J2_TBL(t) global; +set enable_bitmapscan=off; +set enable_seqscan=off; +vacuum analyze gpi_J1_TBL; +vacuum analyze gpi_J2_TBL; +explain (costs false) SELECT distinct(t1.b), t2.e + FROM gpi_J1_TBL t1 (a, b, c), gpi_J2_TBL t2 (d, e) + WHERE t1.b > t2.e and t1.b = 5 + ORDER BY b, e; + QUERY PLAN +---------------------------------------------------------------------------------- + Sort + Sort Key: t2.e + -> HashAggregate + Group By Key: t1.b, t2.e + -> Nested Loop + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl t1 + Index Cond: (b = 5) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl t2 + Index Cond: (e < t1.b) +(9 rows) + +SELECT distinct(t1.b), t2.e + FROM gpi_J1_TBL t1 (a, b, c), gpi_J2_TBL t2 (d, e) + WHERE t1.b > t2.e and t1.b = 5 + ORDER BY b, e; + b | e +---+--- + 5 | 0 + 5 | 1 + 5 | 2 + 5 | 3 + 5 | 4 +(5 rows) + +explain (costs false) SELECT distinct(j), k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL + WHERE j > k and j = 5 + ORDER BY j, k; + QUERY PLAN +------------------------------------------------------------------------------- + Sort + Sort Key: gpi_j2_tbl.k + -> HashAggregate + Group By Key: gpi_j1_tbl.j, gpi_j2_tbl.k + -> Nested Loop + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl + Index Cond: (j = 5) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl + Index Cond: (k < gpi_j1_tbl.j) +(9 rows) + +SELECT distinct(j), k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL + WHERE j > k and j = 5 + ORDER BY j, k; + j | k +---+--- + 5 | 0 + 5 | 1 + 5 | 2 + 5 | 3 + 5 | 4 +(5 rows) + +explain (costs false) SELECT distinct(jj), kk + FROM (gpi_J1_TBL CROSS JOIN gpi_J2_TBL) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; + QUERY PLAN +------------------------------------------------------------------------------- + Sort + Sort Key: gpi_j2_tbl.k + -> HashAggregate + Group By Key: gpi_j1_tbl.j, gpi_j2_tbl.k + -> Nested Loop + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl + Index Cond: (j = 5) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl + Index Cond: (k < gpi_j1_tbl.j) +(9 rows) + +SELECT distinct(jj), kk + FROM (gpi_J1_TBL CROSS JOIN gpi_J2_TBL) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; + jj | kk +----+---- + 5 | 0 + 5 | 1 + 5 | 2 + 5 | 3 + 5 | 4 +(5 rows) + +explain (costs false) SELECT distinct(jj), kk + FROM (gpi_J1_TBL t1 (a, b, c) CROSS JOIN gpi_J2_TBL t2 (d, e)) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; + QUERY PLAN +---------------------------------------------------------------------------------- + Sort + Sort Key: t2.e + -> HashAggregate + Group By Key: t1.b, t2.e + -> Nested Loop + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl t1 + Index Cond: (b = 5) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl t2 + Index Cond: (e < t1.b) +(9 rows) + +SELECT distinct(jj), kk + FROM (gpi_J1_TBL t1 (a, b, c) CROSS JOIN gpi_J2_TBL t2 (d, e)) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; + jj | kk +----+---- + 5 | 0 + 5 | 1 + 5 | 2 + 5 | 3 + 5 | 4 +(5 rows) + +explain (costs false) SELECT distinct(j),a.k,b.k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL a CROSS JOIN gpi_J2_TBL b + WHERE j > a.k and j = 5 and a.k>b.k + ORDER BY j,a.k,b.k; + QUERY PLAN +--------------------------------------------------------------------------------------- + Sort + Sort Key: a.k, b.k + -> HashAggregate + Group By Key: gpi_j1_tbl.j, a.k, b.k + -> Nested Loop + -> Nested Loop + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl + Index Cond: (j = 5) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl a + Index Cond: (k < gpi_j1_tbl.j) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl b + Index Cond: (k < a.k) +(12 rows) + +SELECT distinct(j),a.k,b.k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL a CROSS JOIN gpi_J2_TBL b + WHERE j > a.k and j = 5 and a.k>b.k + ORDER BY j,a.k,b.k; + j | k | k +---+---+--- + 5 | 1 | 0 + 5 | 2 | 0 + 5 | 2 | 1 + 5 | 3 | 0 + 5 | 3 | 1 + 5 | 3 | 2 + 5 | 4 | 0 + 5 | 4 | 1 + 5 | 4 | 2 + 5 | 4 | 3 +(10 rows) + +explain (costs false) SELECT distinct(t) + FROM gpi_J1_TBL INNER JOIN gpi_J2_TBL USING (t) + WHERE t>0 and t<5 + ORDER BY t; + QUERY PLAN +------------------------------------------------------------------------- + Unique + -> Nested Loop + -> Index Only Scan using gpi_j2_tbl_nonp_t_index on gpi_j2_tbl + Index Cond: ((t > 0) AND (t < 5)) + -> Index Only Scan using gpi_j1_tbl_nonp_t_index on gpi_j1_tbl + Index Cond: ((t = gpi_j2_tbl.t) AND (t > 0) AND (t < 5)) +(6 rows) + +SELECT distinct(t) + FROM gpi_J1_TBL INNER JOIN gpi_J2_TBL USING (t) + WHERE t>0 and t<5 + ORDER BY t; + t +--- + 1 + 2 + 3 + 4 +(4 rows) + +explain (costs false) SELECT distinct(b) + FROM gpi_J1_TBL t1 (a, b, c) NATURAL JOIN gpi_J2_TBL t2 (e, b, d) + ORDER BY b; + QUERY PLAN +---------------------------------------------------------------------------- + Unique + -> Merge Join + Merge Cond: (t2.b = t1.b) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl t2 + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl t1 +(5 rows) + +SELECT distinct(b) + FROM gpi_J1_TBL t1 (a, b, c) NATURAL JOIN gpi_J2_TBL t2 (e, b, d) + ORDER BY b; + b +---- + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +(31 rows) + +explain (costs false) SELECT distinct(j),k + FROM gpi_J1_TBL JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; + QUERY PLAN +------------------------------------------------------------------------- + Unique + -> Merge Join + Merge Cond: (gpi_j2_tbl.k = gpi_j1_tbl.j) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl +(5 rows) + +SELECT distinct(j),k + FROM gpi_J1_TBL JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; + j | k +----+---- + 0 | 0 + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 10 | 10 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 + 16 | 16 + 17 | 17 + 18 | 18 + 19 | 19 + 20 | 20 + 21 | 21 + 22 | 22 + 23 | 23 + 24 | 24 + 25 | 25 + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 +(31 rows) + +explain (costs false) SELECT distinct(j),k + FROM gpi_J1_TBL LEFT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; + QUERY PLAN +------------------------------------------------------------------------------- + Unique + -> Sort + Sort Key: gpi_j1_tbl.j, gpi_j2_tbl.k + -> Merge Right Join + Merge Cond: (gpi_j2_tbl.k = gpi_j1_tbl.j) + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl +(7 rows) + +SELECT distinct(j),k + FROM gpi_J1_TBL LEFT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; + j | k +-----+---- + -10 | + -9 | + -8 | + -7 | + -6 | + -5 | + -4 | + -3 | + -2 | + -1 | + 0 | 0 + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 10 | 10 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 + 16 | 16 + 17 | 17 + 18 | 18 + 19 | 19 + 20 | 20 + 21 | 21 + 22 | 22 + 23 | 23 + 24 | 24 + 25 | 25 + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 +(41 rows) + +explain (costs false) SELECT distinct(j),k + FROM gpi_J1_TBL RIGHT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + WHERE k>25 and k<35 + ORDER BY j,k; + QUERY PLAN +------------------------------------------------------------------------------- + Sort + Sort Key: gpi_j1_tbl.j, gpi_j2_tbl.k + -> HashAggregate + Group By Key: gpi_j1_tbl.j, gpi_j2_tbl.k + -> Nested Loop Left Join + -> Index Only Scan using gpi_j2_tbl_nonp_k_index on gpi_j2_tbl + Index Cond: ((k > 25) AND (k < 35)) + -> Index Only Scan using gpi_j1_tbl_nonp_j_index on gpi_j1_tbl + Index Cond: (j = gpi_j2_tbl.k) +(9 rows) + +SELECT distinct(j),k + FROM gpi_J1_TBL RIGHT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + WHERE k>25 and k<35 + ORDER BY j,k; + j | k +----+---- + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 + | 31 + | 32 + | 33 + | 34 +(9 rows) + +drop table if exists gpi_J1_TBL; +drop table if exists gpi_J2_TBL; +set client_min_messages=notice; diff --git a/src/test/regress/expected/gpi_interval.out b/src/test/regress/expected/gpi_interval.out new file mode 100644 index 0000000000000000000000000000000000000000..010edba83dc9c50dd06a3531d64a735c546ba78e --- /dev/null +++ b/src/test/regress/expected/gpi_interval.out @@ -0,0 +1,492 @@ +-- +---- test interval partitioned global index +-- +--drop table and index +drop index if exists ip_index_global; +NOTICE: index "ip_index_global" does not exist, skipping +drop index if exists ip_index_local; +NOTICE: index "ip_index_local" does not exist, skipping +drop table if exists gpi_partition_global_index_interval; +NOTICE: table "gpi_partition_global_index_interval" does not exist, skipping +drop index if exists index_interval_partition_table_001; +NOTICE: index "index_interval_partition_table_001" does not exist, skipping +drop table if exists interval_partition_table_001; +NOTICE: table "interval_partition_table_001" does not exist, skipping +drop table if exists interval_partition_table_002; +NOTICE: table "interval_partition_table_002" does not exist, skipping +drop table if exists interval_partition_table_003; +NOTICE: table "interval_partition_table_003" does not exist, skipping +drop table if exists interval_partition_table_004; +NOTICE: table "interval_partition_table_004" does not exist, skipping +create table gpi_partition_global_index_interval +( + c1 int, + c2 int, + logdate date not null +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION gpi_partition_global_index_interval_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION gpi_partition_global_index_interval_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION gpi_partition_global_index_interval_p2 VALUES LESS THAN ('2020-05-01') +); +--succeed +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + relname | parttype | rangenum | intervalnum | partstrategy | relallvisible | reltoastrelid | partkey | interval | boundaries +---------+----------+----------+-------------+--------------+---------------+---------------+---------+----------+------------ +(0 rows) + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + count +------- + 0 +(1 row) + +create index ip_index_global on gpi_partition_global_index_interval(c1) global; +--succeed +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + relname | parttype | rangenum | intervalnum | partstrategy | relallvisible | reltoastrelid | partkey | interval | boundaries +----------------------------------------+----------+----------+-------------+--------------+---------------+---------------+---------+-------------+-------------- + gpi_partition_global_index_interval | r | 0 | 0 | i | 0 | 0 | 3 | {"1 month"} | + gpi_partition_global_index_interval_p0 | p | 0 | 0 | r | 0 | 0 | | | {2020-03-01} + gpi_partition_global_index_interval_p1 | p | 0 | 0 | r | 0 | 0 | | | {2020-04-01} + gpi_partition_global_index_interval_p2 | p | 0 | 0 | r | 0 | 0 | | | {2020-05-01} +(4 rows) + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + count +------- + 4 +(1 row) + +create index ip_index_local on gpi_partition_global_index_interval(c2) local; +--succeed +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + relname | parttype | rangenum | intervalnum | partstrategy | relallvisible | reltoastrelid | partkey | interval | boundaries +----------------------------------------+----------+----------+-------------+--------------+---------------+---------------+---------+-------------+-------------- + gpi_partition_global_index_interval | r | 0 | 0 | i | 0 | 0 | 3 | {"1 month"} | + gpi_partition_global_index_interval | r | 0 | 0 | i | 0 | 0 | 3 | {"1 month"} | + gpi_partition_global_index_interval_p0 | p | 0 | 0 | r | 0 | 0 | | | {2020-03-01} + gpi_partition_global_index_interval_p0 | p | 0 | 0 | r | 0 | 0 | | | {2020-03-01} + gpi_partition_global_index_interval_p1 | p | 0 | 0 | r | 0 | 0 | | | {2020-04-01} + gpi_partition_global_index_interval_p1 | p | 0 | 0 | r | 0 | 0 | | | {2020-04-01} + gpi_partition_global_index_interval_p2 | p | 0 | 0 | r | 0 | 0 | | | {2020-05-01} + gpi_partition_global_index_interval_p2 | p | 0 | 0 | r | 0 | 0 | | | {2020-05-01} +(8 rows) + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + count +------- + 8 +(1 row) + +--insert into table +insert into gpi_partition_global_index_interval values(7,2,'2020-03-01'); +insert into gpi_partition_global_index_interval values(3,1,'2020-04-01'); +insert into gpi_partition_global_index_interval values(5,3,'2020-05-01'); +insert into gpi_partition_global_index_interval values(7,5,'2020-06-01'); +insert into gpi_partition_global_index_interval values(1,4,'2020-07-01'); +--succeed +explain (costs off, nodes off) select * from gpi_partition_global_index_interval where c1 = 7 order by 1, 2; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: c2 + -> Bitmap Heap Scan on gpi_partition_global_index_interval + Recheck Cond: (c1 = 7) + -> Bitmap Index Scan on ip_index_global + Index Cond: (c1 = 7) +(6 rows) + +select * from gpi_partition_global_index_interval where c1 = 7 order by 1, 2; + c1 | c2 | logdate +----+----+-------------------------- + 7 | 2 | Sun Mar 01 00:00:00 2020 + 7 | 5 | Mon Jun 01 00:00:00 2020 +(2 rows) + +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + relname | parttype | rangenum | intervalnum | partstrategy | relallvisible | reltoastrelid | partkey | interval | boundaries +----------------------------------------+----------+----------+-------------+--------------+---------------+---------------+---------+-------------+------------------------------ + gpi_partition_global_index_interval | r | 0 | 0 | i | 0 | 0 | 3 | {"1 month"} | + gpi_partition_global_index_interval | r | 0 | 0 | i | 0 | 0 | 3 | {"1 month"} | + gpi_partition_global_index_interval_p0 | p | 0 | 0 | r | 0 | 0 | | | {2020-03-01} + gpi_partition_global_index_interval_p0 | p | 0 | 0 | r | 0 | 0 | | | {2020-03-01} + gpi_partition_global_index_interval_p1 | p | 0 | 0 | r | 0 | 0 | | | {2020-04-01} + gpi_partition_global_index_interval_p1 | p | 0 | 0 | r | 0 | 0 | | | {2020-04-01} + gpi_partition_global_index_interval_p2 | p | 0 | 0 | r | 0 | 0 | | | {2020-05-01} + gpi_partition_global_index_interval_p2 | p | 0 | 0 | r | 0 | 0 | | | {2020-05-01} + sys_p1 | p | 0 | 0 | i | 0 | 0 | | | {"Mon Jun 01 00:00:00 2020"} + sys_p1 | p | 0 | 0 | i | 0 | 0 | | | {"Mon Jun 01 00:00:00 2020"} + sys_p2 | p | 0 | 0 | i | 0 | 0 | | | {"Wed Jul 01 00:00:00 2020"} + sys_p2 | p | 0 | 0 | i | 0 | 0 | | | {"Wed Jul 01 00:00:00 2020"} + sys_p3 | p | 0 | 0 | i | 0 | 0 | | | {"Sat Aug 01 00:00:00 2020"} + sys_p3 | p | 0 | 0 | i | 0 | 0 | | | {"Sat Aug 01 00:00:00 2020"} +(14 rows) + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + count +------- + 14 +(1 row) + +-- +---- test input for unique index +-- +create table interval_partition_table_001 +( + c1 int, + logdate date not null +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_001_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_001_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_001_p2 VALUES LESS THAN ('2020-05-01') +); +create unique index index_interval_partition_table_001 on interval_partition_table_001(logdate) global; +--fail: unique index +insert into interval_partition_table_001 values(10, '2020-06-01'); +insert into interval_partition_table_001 values(10, '2020-06-01'); +ERROR: duplicate key value violates unique constraint "index_interval_partition_table_001" +DETAIL: Key (logdate)=(Mon Jun 01 00:00:00 2020) already exists. +-- +---- test with primary key +-- +create table interval_partition_table_002 +( + c1 int, + c2 int, + logdate date not null, + CONSTRAINT interval_partition_table_CONSTRAINT PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_002_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_002_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_002_p2 VALUES LESS THAN ('2020-05-01') +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "interval_partition_table_constraint" for table "interval_partition_table_002" +insert into interval_partition_table_002 values(10, 10, '2020-06-01'); +insert into interval_partition_table_002 values(10, 10, '2020-06-01'); +ERROR: duplicate key value violates unique constraint "interval_partition_table_constraint" +DETAIL: Key (c2, logdate)=(10, Mon Jun 01 00:00:00 2020) already exists. +analyze interval_partition_table_002; +-- +---- test with btree index +-- +create table interval_partition_table_003 +( + c1 int, + c2 int, + logdate date not null, + PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_003_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_003_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_003_p2 VALUES LESS THAN ('2020-05-01') +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "interval_partition_table_003_pkey" for table "interval_partition_table_003" +create index interval_partition_table_003_1 ON interval_partition_table_003 USING BTREE (logdate) global; +create index interval_partition_table_003_2 ON interval_partition_table_003 USING BTREE (c2) global; +create index interval_partition_table_003_3 ON interval_partition_table_003 USING BTREE (c1) global; +select relname from pg_class where relname like '%interval_partition_table_003%' order by 1; + relname +----------------------------------- + interval_partition_table_003 + interval_partition_table_003_1 + interval_partition_table_003_2 + interval_partition_table_003_3 + interval_partition_table_003_pkey +(5 rows) + +select relname, parttype, partstrategy, boundaries from pg_partition +where parentid = (select oid from pg_class where relname = 'interval_partition_table_003') +order by relname; + relname | parttype | partstrategy | boundaries +---------------------------------+----------+--------------+-------------- + interval_partition_table_003 | r | i | + interval_partition_table_003_p0 | p | r | {2020-03-01} + interval_partition_table_003_p1 | p | r | {2020-04-01} + interval_partition_table_003_p2 | p | r | {2020-05-01} +(4 rows) + +insert into interval_partition_table_003 values(1,2,'2020-03-01'); +insert into interval_partition_table_003 values(1,2,'2020-04-01'); +insert into interval_partition_table_003 values(1,2,'2020-05-01'); +insert into interval_partition_table_003 values(1,2,'2020-06-01'); +insert into interval_partition_table_003 values(1,2,'2020-07-01'); +alter table interval_partition_table_003 drop column C2; +insert into interval_partition_table_003 values(1,2,'2020-07-01'); +ERROR: INSERT has more expressions than target columns +LINE 1: ...sert into interval_partition_table_003 values(1,2,'2020-07-0... + ^ +insert into interval_partition_table_003 values(1,'2020-07-01'); +select relname from pg_class where relname like '%interval_partition_table_003%' order by 1; + relname +-------------------------------- + interval_partition_table_003 + interval_partition_table_003_1 + interval_partition_table_003_3 +(3 rows) + +select relname, parttype, partstrategy, boundaries from pg_partition +where parentid = (select oid from pg_class where relname = 'interval_partition_table_003') +order by relname; + relname | parttype | partstrategy | boundaries +---------------------------------+----------+--------------+------------------------------ + interval_partition_table_003 | r | i | + interval_partition_table_003_p0 | p | r | {2020-03-01} + interval_partition_table_003_p1 | p | r | {2020-04-01} + interval_partition_table_003_p2 | p | r | {2020-05-01} + sys_p1 | p | i | {"Mon Jun 01 00:00:00 2020"} + sys_p2 | p | i | {"Wed Jul 01 00:00:00 2020"} + sys_p3 | p | i | {"Sat Aug 01 00:00:00 2020"} +(7 rows) + +-- +---- test partition BTREE index +-- +create table interval_partition_table_004 +( + c1 int, + c2 int, + logdate date not null, + PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_004_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_004_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_004_p2 VALUES LESS THAN ('2020-05-01') +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "interval_partition_table_004_pkey" for table "interval_partition_table_004" +-- expression index +CREATE INDEX interval_partition_table_004_index_01 on interval_partition_table_004 using btree(c2) global; +CREATE INDEX interval_partition_table_004_index_02 on interval_partition_table_004 using btree(logdate) global; +CREATE INDEX interval_partition_table_004_index_03 on interval_partition_table_004 using btree(c1) global; +cluster interval_partition_table_004 using interval_partition_table_004_index_01; +insert into interval_partition_table_004 values(7,1,'2020-03-01'); +insert into interval_partition_table_004 values(3,2,'2020-04-01'); +insert into interval_partition_table_004 values(5,3,'2020-05-01'); +insert into interval_partition_table_004 values(7,4,'2020-06-01'); +insert into interval_partition_table_004 values(1,5,'2020-07-01'); +insert into interval_partition_table_004 values(7,2,'2020-03-01'); +insert into interval_partition_table_004 values(3,3,'2020-04-01'); +insert into interval_partition_table_004 values(5,4,'2020-05-01'); +insert into interval_partition_table_004 values(7,5,'2020-06-01'); +insert into interval_partition_table_004 values(1,1,'2020-07-01'); +insert into interval_partition_table_004 values(7,3,'2020-03-01'); +insert into interval_partition_table_004 values(3,4,'2020-04-01'); +insert into interval_partition_table_004 values(5,5,'2020-05-01'); +insert into interval_partition_table_004 values(7,1,'2020-06-01'); +insert into interval_partition_table_004 values(1,2,'2020-07-01'); +insert into interval_partition_table_004 values(7,4,'2020-03-01'); +insert into interval_partition_table_004 values(3,5,'2020-04-01'); +insert into interval_partition_table_004 values(5,1,'2020-05-01'); +insert into interval_partition_table_004 values(7,2,'2020-06-01'); +insert into interval_partition_table_004 values(1,3,'2020-07-01'); +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on interval_partition_table_004 + Recheck Cond: (c1 = 7) + -> Bitmap Index Scan on interval_partition_table_004_index_03 + Index Cond: (c1 = 7) +(5 rows) + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + count +------- + 8 +(1 row) + +delete from interval_partition_table_004 where c1 = 1; +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on interval_partition_table_004 + Recheck Cond: (c1 = 7) + -> Bitmap Index Scan on interval_partition_table_004_index_03 + Index Cond: (c1 = 7) +(5 rows) + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + count +------- + 8 +(1 row) + +update interval_partition_table_004 set c1 = 100 where c1 = 7; +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 100; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on interval_partition_table_004 + Recheck Cond: (c1 = 100) + -> Bitmap Index Scan on interval_partition_table_004_index_03 + Index Cond: (c1 = 100) +(5 rows) + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 100; + count +------- + 8 +(1 row) + +update interval_partition_table_004 set c1 = 7 where c1 = 100; +reindex table interval_partition_table_004; +cluster; +reindex table interval_partition_table_004; +cluster; +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on interval_partition_table_004 + Recheck Cond: (c1 = 7) + -> Bitmap Index Scan on interval_partition_table_004_index_03 + Index Cond: (c1 = 7) +(5 rows) + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + count +------- + 8 +(1 row) + +start transaction; +insert into interval_partition_table_004 values (generate_series(1,10), generate_series(1,10), generate_series(TO_DATE('2000-01-01', 'YYYY-MM-DD'),TO_DATE('2019-12-01', 'YYYY-MM-DD'),'1 day')); +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + count +------- + 1463 +(1 row) + +commit; +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + count +------- + 1463 +(1 row) + +delete from interval_partition_table_004; +\parallel on +insert into interval_partition_table_004 values (generate_series(1,10), generate_series(1,10), generate_series(TO_DATE('1990-01-01', 'YYYY-MM-DD'),TO_DATE('2020-12-01', 'YYYY-MM-DD'),'1 day')); +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +\parallel off + bool +------ + t +(1 row) + + bool +------ + t +(1 row) + + bool +------ + t +(1 row) + + bool +------ + t +(1 row) + + bool +------ + t +(1 row) + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + count +------- + 11293 +(1 row) + +\parallel on +DELETE from interval_partition_table_004 where c1 = 1; +VACUUM analyze interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 2; +VACUUM interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 3; +ANALYZE interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 4; +VACUUM analyze interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 5; +VACUUM interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 6; +ANALYZE interval_partition_table_004; +\parallel off +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; + QUERY PLAN +------------------------------------------------------------------ + Aggregate + -> Partition Iterator + Iterations: 11 + -> Partitioned Seq Scan on interval_partition_table_004 + Filter: (c1 <= 7) + Selected Partitions: 1..11 +(6 rows) + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; + count +------- + 11293 +(1 row) + +VACUUM full interval_partition_table_004; +set enable_seqscan = off; +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on interval_partition_table_004 + Recheck Cond: (c1 <= 7) + -> Bitmap Index Scan on interval_partition_table_004_index_03 + Index Cond: (c1 <= 7) +(5 rows) + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; + count +------- + 11293 +(1 row) + +reset enable_seqscan; +--drop table and index +drop index if exists ip_index_global; +drop index if exists ip_index_local; +drop table if exists gpi_partition_global_index_interval; +drop index if exists index_interval_partition_table_001; +drop table if exists interval_partition_table_001; +drop table if exists interval_partition_table_002; +drop table if exists interval_partition_table_003; +drop table if exists interval_partition_table_004; diff --git a/src/test/regress/expected/gpi_invalid_part.out b/src/test/regress/expected/gpi_invalid_part.out new file mode 100644 index 0000000000000000000000000000000000000000..aaa3085706321f7853020d5642b6db15ead53322 --- /dev/null +++ b/src/test/regress/expected/gpi_invalid_part.out @@ -0,0 +1,398 @@ +drop table if exists gpi_test_invalid_part; +NOTICE: table "gpi_test_invalid_part" does not exist, skipping +create table gpi_test_invalid_part(a int, b int,c int) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500)); +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + gpi_test_invalid_part | r | {orientation=row,compression=no,wait_clean_gpi=n} + p1 | p | {orientation=row,compression=no} + p2 | p | {orientation=row,compression=no} + p3 | p | {orientation=row,compression=no} +(4 rows) + +insert into gpi_test_invalid_part select r,r,100 from generate_series(0,400) as r; +create index global_index_gpi_test_invalid_part_b on gpi_test_invalid_part (b) global; +create index local_index_gpi_test_invalid_part_c on gpi_test_invalid_part (c) local; +vacuum analyze gpi_test_invalid_part; +--Scenario 1: abort one create partition +start transaction; +alter table gpi_test_invalid_part add partition p6 values less than (900); +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +---------+----------+------------ +(0 rows) + +insert into gpi_test_invalid_part select r,r,100 from generate_series(500,800) as r; +--p6/gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + gpi_test_invalid_part | r | {orientation=row,compression=no,wait_clean_gpi=y} + p6 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(2 rows) + +abort; +--gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + gpi_test_invalid_part | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +start transaction read only; +set enable_show_any_tuples = on; +set enable_indexscan = off; +set enable_bitmapscan = off; +--p6 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p6' and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +---------+----------+--------------------------------------------------- + p6 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +commit; +reset enable_indexscan; +reset enable_bitmapscan; +reset enable_show_any_tuples; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 0 +(1 row) + +--skip vacuum full +vacuum full pg_partition; +WARNING: system table pg_partition contain relation gpi_test_invalid_part have reloptions wait_clean_gpi=y,must run the vacuum (full) gpi_test_invalid_part first +WARNING: skipping "pg_partition" --- only table or database can vacuum it +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 0 +(1 row) + +--Scenario 2: abort one create partition in sub transaction +start transaction; +alter table gpi_test_invalid_part add partition p7 values less than (900); +savepoint s1; +alter table gpi_test_invalid_part add partition p8 values less than (1000); +insert into gpi_test_invalid_part select r,r,100 from generate_series(500,950) as r; +-- gpi_test_invalid_part and p7/p8 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + gpi_test_invalid_part | r | {orientation=row,compression=no,wait_clean_gpi=y} + p7 | p | {orientation=row,compression=no,wait_clean_gpi=y} + p8 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(3 rows) + +rollback to s1; +commit; +--gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + gpi_test_invalid_part | r | {orientation=row,compression=no,wait_clean_gpi=y} + p1 | p | {orientation=row,compression=no} + p2 | p | {orientation=row,compression=no} + p3 | p | {orientation=row,compression=no} + p7 | p | {orientation=row,compression=no} +(5 rows) + +start transaction read only; +set enable_show_any_tuples = on; +set enable_indexscan = off; +set enable_bitmapscan = off; +--p7/p8 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and (a.relname = 'p7' or a.relname = 'p8') and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +---------+----------+--------------------------------------------------- + p7 | p | {orientation=row,compression=no,wait_clean_gpi=y} + p8 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(2 rows) + +commit; +reset enable_indexscan; +reset enable_bitmapscan; +reset enable_show_any_tuples; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 0 +(1 row) + +--skip vacuum full +vacuum full pg_partition; +WARNING: system table pg_partition contain relation gpi_test_invalid_part have reloptions wait_clean_gpi=y,must run the vacuum (full) gpi_test_invalid_part first +WARNING: skipping "pg_partition" --- only table or database can vacuum it +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 0 +(1 row) + +--Scenario 3: drop one create partition +start transaction; +alter table gpi_test_invalid_part add partition p9 values less than (1000); +insert into gpi_test_invalid_part select r,r,100 from generate_series(950,990) as r; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +---------+----------+--------------------------------------------------- + p9 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +alter table gpi_test_invalid_part drop partition p9; +--p9 not exists +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +---------+----------+------------ +(0 rows) + +commit; +-- gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + gpi_test_invalid_part | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +start transaction read only; +set enable_show_any_tuples = on; +set enable_indexscan = off; +set enable_bitmapscan = off; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +---------+----------+--------------------------------------------------- + p9 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +commit; +reset enable_indexscan; +reset enable_bitmapscan; +reset enable_show_any_tuples; +--use seqscan +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_test_invalid_part + Filter: (b > 700) + Selected Partitions: 1..4 +(6 rows) + +--drop partition set global index unusable +reindex index global_index_gpi_test_invalid_part_b; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 0 +(1 row) + +--skip vacuum full +vacuum full pg_partition; +WARNING: system table pg_partition contain relation gpi_test_invalid_part have reloptions wait_clean_gpi=y,must run the vacuum (full) gpi_test_invalid_part first +WARNING: skipping "pg_partition" --- only table or database can vacuum it +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 0 +(1 row) + +--Scenario 4: create one partition, sub transaction drop and rollback +start transaction; +alter table gpi_test_invalid_part add partition p9 values less than (1000); +insert into gpi_test_invalid_part select r,r,100 from generate_series(950,990) as r; +savepoint s1; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +---------+----------+--------------------------------------------------- + p9 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +alter table gpi_test_invalid_part drop partition p9; +--p9 not exists +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +---------+----------+------------ +(0 rows) + +rollback to s1; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +---------+----------+--------------------------------------------------- + p9 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +commit; +-- gpi_test_invalid_part and p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + relname | parttype | reloptions +-----------------------+----------+--------------------------------------------------- + gpi_test_invalid_part | r | {orientation=row,compression=no,wait_clean_gpi=y} + p9 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(2 rows) + +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select insert 41 rows +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 41 +(1 row) + +--skip vacuum full +vacuum full pg_partition; +WARNING: system table pg_partition contain relation gpi_test_invalid_part have reloptions wait_clean_gpi=y,must run the vacuum (full) gpi_test_invalid_part first +WARNING: skipping "pg_partition" --- only table or database can vacuum it +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; + QUERY PLAN +------------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_gpi_test_invalid_part_b on gpi_test_invalid_part + Index Cond: (b > 700) +(3 rows) + +--select insert 41 rows +select count(*) from gpi_test_invalid_part where b > 700; + count +------- + 41 +(1 row) + +--Scenario 5: create partition, sub transaction create one partition, rollback; +drop table if exists gpi_test_create_invalid; +NOTICE: table "gpi_test_create_invalid" does not exist, skipping +start transaction; +create table gpi_test_create_invalid(a int, b int,c int) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500)); +insert into gpi_test_create_invalid select r,r,100 from generate_series(0,400) as r; +--gpi_test_create_invalid have wait_clean_gpi=n +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_create_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +---------+----------+------------ +(0 rows) + +create index global_gpi_test_create_invalid_b on gpi_test_create_invalid (b) global; +create index local_gpi_test_create_invalid_c on gpi_test_create_invalid (c) local; +savepoint s1; +alter table gpi_test_create_invalid add partition p6 values less than (800); +insert into gpi_test_create_invalid select r,r,100 from generate_series(500,790) as r; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p6' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_create_invalid' order by b.relname; + relname | parttype | reloptions +---------+----------+--------------------------------------------------- + p6 | p | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +rollback to s1; +commit; +--gpi_test_create_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_create_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + relname | parttype | reloptions +-------------------------+----------+--------------------------------------------------- + gpi_test_create_invalid | r | {orientation=row,compression=no,wait_clean_gpi=y} +(1 row) + +vacuum analyze gpi_test_create_invalid; +explain (costs off) select count(*) from gpi_test_create_invalid where b > 700; + QUERY PLAN +----------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_gpi_test_create_invalid_b on gpi_test_create_invalid + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_create_invalid where b > 700; + count +------- + 0 +(1 row) + +----skip vacuum full +vacuum full pg_partition; +WARNING: system table pg_partition contain relation gpi_test_invalid_part have reloptions wait_clean_gpi=y,must run the vacuum (full) gpi_test_invalid_part first +WARNING: skipping "pg_partition" --- only table or database can vacuum it +explain (costs off) select count(*) from gpi_test_create_invalid where b > 700; + QUERY PLAN +----------------------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_gpi_test_create_invalid_b on gpi_test_create_invalid + Index Cond: (b > 700) +(3 rows) + +--select nothing +select count(*) from gpi_test_create_invalid where b > 700; + count +------- + 0 +(1 row) + +--clean data +drop table if exists gpi_test_invalid_part; +drop table if exists gpi_test_create_invalid; diff --git a/src/test/regress/expected/gpi_pwj.out b/src/test/regress/expected/gpi_pwj.out new file mode 100644 index 0000000000000000000000000000000000000000..9a2e3e020185488d6446b94c95a23c2f4d88d344 --- /dev/null +++ b/src/test/regress/expected/gpi_pwj.out @@ -0,0 +1,85 @@ +DROP TABLE if exists gpi_pwj_table1; +NOTICE: table "gpi_pwj_table1" does not exist, skipping +DROP TABLE if exists gpi_pwj_table2; +NOTICE: table "gpi_pwj_table2" does not exist, skipping +CREATE TABLE gpi_pwj_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_pwj_table1 VALUES less than (10000), + partition p1_gpi_pwj_table1 VALUES less than (20000), + partition p2_gpi_pwj_table1 VALUES less than (30000), + partition p3_gpi_pwj_table1 VALUES less than (maxvalue) +); +CREATE TABLE gpi_pwj_table2 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_pwj_table2 VALUES less than (10000), + partition p1_gpi_pwj_table2 VALUES less than (20000), + partition p2_gpi_pwj_table2 VALUES less than (30000), + partition p3_gpi_pwj_table2 VALUES less than (maxvalue) +); +INSERT INTO gpi_pwj_table1 SELECT r, r, r FROM generate_series(0,40000) AS r; +INSERT INTO gpi_pwj_table2 SELECT r, r, r FROM generate_series(0,40000) AS r; +CREATE INDEX idx1_gpi_pwj_table1 ON gpi_pwj_table1 (c1) GLOBAL; +CREATE INDEX idx1_gpi_pwj_table2 ON gpi_pwj_table2 (c2) GLOBAL; +explain (costs off, nodes off) +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; + QUERY PLAN +---------------------------------------------------------------- + Aggregate + -> Hash Join + Hash Cond: (gpi_pwj_table2.c1 = gpi_pwj_table1.c1) + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_pwj_table2 + Selected Partitions: 1..4 + -> Hash + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on gpi_pwj_table1 + Filter: (c2 < 15000) + Selected Partitions: 1..4 +(13 rows) + +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; + count +------- + 15000 +(1 row) + +VACUUM analyze; +SET enable_partitionwise=ON; +explain (costs off, nodes off) +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; + QUERY PLAN +------------------------------------------------------------------ + Aggregate + -> Partition Iterator + Iterations: 4 + -> Hash Join + Hash Cond: (gpi_pwj_table1.c1 = gpi_pwj_table2.c1) + -> Partitioned Seq Scan on gpi_pwj_table1 + Filter: (c2 < 15000) + Selected Partitions: 1..4 + -> Hash + -> Partitioned Seq Scan on gpi_pwj_table2 + Selected Partitions: 1..4 +(11 rows) + +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; + count +------- + 15000 +(1 row) + +SET enable_partitionwise=OFF; \ No newline at end of file diff --git a/src/test/regress/expected/gpi_rebuild_index.out b/src/test/regress/expected/gpi_rebuild_index.out new file mode 100644 index 0000000000000000000000000000000000000000..77f39443684699ca80f207709a773db1565df7fd --- /dev/null +++ b/src/test/regress/expected/gpi_rebuild_index.out @@ -0,0 +1,383 @@ +-- +---- test reindex global index +-- +--drop table and index +drop index if exists global_partition_reindex_table_ind1; +NOTICE: index "global_partition_reindex_table_ind1" does not exist, skipping +drop index if exists global_partition_reindex_table_ind2; +NOTICE: index "global_partition_reindex_table_ind2" does not exist, skipping +drop index if exists global_partition_reindex_table_ind3; +NOTICE: index "global_partition_reindex_table_ind3" does not exist, skipping +drop index if exists partition_reindex_table_ind1; +NOTICE: index "partition_reindex_table_ind1" does not exist, skipping +drop index if exists partition_reindex_table_ind2; +NOTICE: index "partition_reindex_table_ind2" does not exist, skipping +drop table if exists partition_reindex_table; +NOTICE: table "partition_reindex_table" does not exist, skipping +drop index if exists partition_reindex_internal_table_ind1; +NOTICE: index "partition_reindex_internal_table_ind1" does not exist, skipping +drop index if exists partition_reindex_internal_table_ind2; +NOTICE: index "partition_reindex_internal_table_ind2" does not exist, skipping +drop index if exists global_partition_reindex_internal_table_ind1; +NOTICE: index "global_partition_reindex_internal_table_ind1" does not exist, skipping +drop table if exists partition_reindex_internal_table; +NOTICE: table "partition_reindex_internal_table" does not exist, skipping +--prepare table and index +create table partition_reindex_table +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_partition_reindex_table values less than (10000), + partition p1_partition_reindex_table values less than (20000), + partition p2_partition_reindex_table values less than (30000), + partition p3_partition_reindex_table values less than (40000), + partition p4_partition_reindex_table values less than (50000), + partition p5_partition_reindex_table values less than (MAXVALUE) +); +--succeed +create index partition_reindex_table_ind1 on partition_reindex_table(c1) local; +--succeed +insert into partition_reindex_table values (generate_series(1,40000), generate_series(1,40000), generate_series(1,40000)); +--succeed +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Sort Key: c1 + -> Partition Iterator + Iterations: 2 + -> Partitioned Bitmap Heap Scan on partition_reindex_table + Recheck Cond: ((c1 >= 19998) AND (c1 <= 20002)) + Selected Partitions: 2..3 + -> Partitioned Bitmap Index Scan on partition_reindex_table_ind1 + Index Cond: ((c1 >= 19998) AND (c1 <= 20002)) + Selected Partitions: 2..3 +(10 rows) + +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + c1 | c2 | c3 +-------+-------+------- + 19998 | 19998 | 19998 + 19999 | 19999 | 19999 + 20000 | 20000 | 20000 + 20001 | 20001 | 20001 + 20002 | 20002 | 20002 +(5 rows) + +create unique index global_partition_reindex_table_ind1 on partition_reindex_table(c1) global; +ERROR: Global and local partition index should not be on same column +drop index partition_reindex_table_ind1; +create index partition_reindex_table_ind1 on partition_reindex_table(c2) local; +--succeed +create index partition_reindex_table_ind2 on partition_reindex_table(c2) local ( + PARTITION p0_partition_reindex_table, + PARTITION p1_partition_reindex_table, + PARTITION p2_partition_reindex_table, + PARTITION p3_partition_reindex_table, + PARTITION p4_partition_reindex_table, + PARTITION p5_partition_reindex_table +); +--succeed +create unique index global_partition_reindex_table_ind1 on partition_reindex_table(c1) global; +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + QUERY PLAN +---------------------------------------------------------------------- + Sort + Sort Key: c1 + -> Bitmap Heap Scan on partition_reindex_table + Recheck Cond: ((c1 >= 19998) AND (c1 <= 20002)) + -> Bitmap Index Scan on global_partition_reindex_table_ind1 + Index Cond: ((c1 >= 19998) AND (c1 <= 20002)) +(6 rows) + +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + c1 | c2 | c3 +-------+-------+------- + 19998 | 19998 | 19998 + 19999 | 19999 | 19999 + 20000 | 20000 | 20000 + 20001 | 20001 | 20001 + 20002 | 20002 | 20002 +(5 rows) + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + relname | relpagesgtzero | reltuplesgtzero | indisunique | indisvalid | indcheckxmin | indisready +-------------------------------------+----------------+-----------------+-------------+------------+--------------+------------ + global_partition_reindex_table_ind1 | t | t | t | t | f | t +(1 row) + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + relname | reltuples | parttype +-------------------------------------+-----------+---------- + global_partition_reindex_table_ind1 | 40000 | n +(1 row) + +reindex index global_partition_reindex_table_ind1; +--succeed +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + QUERY PLAN +---------------------------------------------------------------------- + Sort + Sort Key: c1 + -> Bitmap Heap Scan on partition_reindex_table + Recheck Cond: ((c1 >= 19998) AND (c1 <= 20002)) + -> Bitmap Index Scan on global_partition_reindex_table_ind1 + Index Cond: ((c1 >= 19998) AND (c1 <= 20002)) +(6 rows) + +--the plan before reindex and after reindex should be same +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + c1 | c2 | c3 +-------+-------+------- + 19998 | 19998 | 19998 + 19999 | 19999 | 19999 + 20000 | 20000 | 20000 + 20001 | 20001 | 20001 + 20002 | 20002 | 20002 +(5 rows) + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + relname | relpagesgtzero | reltuplesgtzero | indisunique | indisvalid | indcheckxmin | indisready +-------------------------------------+----------------+-----------------+-------------+------------+--------------+------------ + global_partition_reindex_table_ind1 | t | t | t | t | f | t +(1 row) + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + relname | reltuples | parttype +-------------------------------------+-----------+---------- + global_partition_reindex_table_ind1 | 40000 | n +(1 row) + +alter index global_partition_reindex_table_ind1 rebuild; +--succeed +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + QUERY PLAN +---------------------------------------------------------------------- + Sort + Sort Key: c1 + -> Bitmap Heap Scan on partition_reindex_table + Recheck Cond: ((c1 >= 19998) AND (c1 <= 20002)) + -> Bitmap Index Scan on global_partition_reindex_table_ind1 + Index Cond: ((c1 >= 19998) AND (c1 <= 20002)) +(6 rows) + +--the plan before reindex and after reindex should be same +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + c1 | c2 | c3 +-------+-------+------- + 19998 | 19998 | 19998 + 19999 | 19999 | 19999 + 20000 | 20000 | 20000 + 20001 | 20001 | 20001 + 20002 | 20002 | 20002 +(5 rows) + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + relname | relpagesgtzero | reltuplesgtzero | indisunique | indisvalid | indcheckxmin | indisready +-------------------------------------+----------------+-----------------+-------------+------------+--------------+------------ + global_partition_reindex_table_ind1 | t | t | t | t | f | t +(1 row) + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + relname | reltuples | parttype +-------------------------------------+-----------+---------- + global_partition_reindex_table_ind1 | 40000 | n +(1 row) + +reindex table partition_reindex_table; +--succeed +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + QUERY PLAN +---------------------------------------------------------------------- + Sort + Sort Key: c1 + -> Bitmap Heap Scan on partition_reindex_table + Recheck Cond: ((c1 >= 19998) AND (c1 <= 20002)) + -> Bitmap Index Scan on global_partition_reindex_table_ind1 + Index Cond: ((c1 >= 19998) AND (c1 <= 20002)) +(6 rows) + +--the plan before reindex and after reindex should be same +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + c1 | c2 | c3 +-------+-------+------- + 19998 | 19998 | 19998 + 19999 | 19999 | 19999 + 20000 | 20000 | 20000 + 20001 | 20001 | 20001 + 20002 | 20002 | 20002 +(5 rows) + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + relname | relpagesgtzero | reltuplesgtzero | indisunique | indisvalid | indcheckxmin | indisready +-------------------------------------+----------------+-----------------+-------------+------------+--------------+------------ + global_partition_reindex_table_ind1 | t | t | t | t | f | t +(1 row) + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + relname | reltuples | parttype +-------------------------------------+-----------+---------- + global_partition_reindex_table_ind1 | 40000 | n +(1 row) + +drop index global_partition_reindex_table_ind1; +--succeed +reindex index global_partition_reindex_table_ind1; +ERROR: relation "global_partition_reindex_table_ind1" does not exist +alter index global_partition_reindex_table_ind1 rebuild; +ERROR: relation "global_partition_reindex_table_ind1" does not exist +reindex index global_partition_reindex_table_ind1 partition p0_partition_reindex_table; +ERROR: relation "global_partition_reindex_table_ind1" does not exist +alter index global_partition_reindex_table_ind1 rebuild partition p1_partition_reindex_table; +ERROR: relation "global_partition_reindex_table_ind1" does not exist +create unique index global_partition_reindex_table_ind1 on partition_reindex_table(c1) global; +--succeed +reindex index global_partition_reindex_table_ind1 partition p0_partition_reindex_table; +ERROR: cannot reindex global index with partition name +alter index global_partition_reindex_table_ind1 rebuild partition p1_partition_reindex_table; +ERROR: cannot reindex global index with partition name +create index global_partition_reindex_table_ind1 on partition_reindex_table using btree(c1) global; +ERROR: relation "global_partition_reindex_table_ind1" already exists +create index global_partition_reindex_table_ind2 on partition_reindex_table using btree(c1) global; +--succeed +create index global_partition_reindex_table_ind3 on partition_reindex_table using btree(c1) global; +--succeed +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1 partition p0_partition_reindex_table; +ERROR: cannot reindex global index with partition name +reindex table partition_reindex_table; +reindex internal table partition_reindex_table; +ERROR: The table "partition_reindex_table" doesn't support the operation of 'REINDEX INTERNAL TABLE'. There is no Desc table on it. +reindex table partition_reindex_table partition p0_partition_reindex_table; +reindex internal table partition_reindex_table partition p0_partition_reindex_table; +ERROR: The table "partition_reindex_table" doesn't support the operation of 'REINDEX INTERNAL TABLE'. There is no Desc table on it. +alter index global_partition_reindex_table_ind1 rebuild; +create table partition_reindex_internal_table( + c_id varchar, + c_w_id integer, + c_date date, + partial cluster key(c_id,c_w_id) +) with (orientation = column, max_batchrow = 30700, compression = middle) +partition by range (c_date, c_w_id) +( + PARTITION p0_partition_reindex_internal_table values less than ('20170331',5), + PARTITION p1_partition_reindex_internal_table values less than ('20170731',450), + PARTITION p2_partition_reindex_internal_table values less than ('20170930',1062), + PARTITION p3_partition_reindex_internal_table values less than ('20171231',1765), + PARTITION p4_partition_reindex_internal_table values less than ('20180331',2024), + PARTITION p5_partition_reindex_internal_table values less than ('20180731',2384), + PARTITION p6_partition_reindex_internal_table values less than ('20180930',2786), + PARTITION p7_partition_reindex_internal_table values less than (maxvalue,maxvalue) +); +insert into partition_reindex_internal_table values('gauss1',4,'20170301'); +insert into partition_reindex_internal_table values('gauss2',400,'20170625'); +insert into partition_reindex_internal_table values('gauss3',480,'20170920'); +insert into partition_reindex_internal_table values('gauss4',1065,'20170920'); +insert into partition_reindex_internal_table values('gauss5',1800,'20170920'); +insert into partition_reindex_internal_table values('gauss6',2030,'20170920'); +insert into partition_reindex_internal_table values('gauss7',2385,'20170920'); +insert into partition_reindex_internal_table values('gauss8',2789,'20191020'); +insert into partition_reindex_internal_table values('gauss9',2789,'20171020'); +create index partition_reindex_internal_table_ind1 on partition_reindex_internal_table using btree(c_w_id) LOCAL; +create index partition_reindex_internal_table_ind2 on partition_reindex_internal_table using btree(c_w_id) LOCAL ( + PARTITION p0_partition_reindex_internal_table, + PARTITION p1_partition_reindex_internal_table, + PARTITION p2_partition_reindex_internal_table, + PARTITION p3_partition_reindex_internal_table, + PARTITION p4_partition_reindex_internal_table, + PARTITION p5_partition_reindex_internal_table, + PARTITION p6_partition_reindex_internal_table, + PARTITION p7_partition_reindex_internal_table +); +create index global_partition_reindex_internal_table_ind1 on partition_reindex_internal_table using btree(c_id) global; +ERROR: Global partition index does not support column store. +reindex index global_partition_reindex_internal_table_ind1; +ERROR: relation "global_partition_reindex_internal_table_ind1" does not exist +reindex index global_partition_reindex_internal_table_ind1 partition p0_partition_reindex_internal_table; +ERROR: relation "global_partition_reindex_internal_table_ind1" does not exist +reindex table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex table partition_reindex_internal_table partition p0_partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table partition p0_partition_reindex_internal_table; +alter index global_partition_reindex_internal_table_ind1 rebuild; +ERROR: relation "global_partition_reindex_internal_table_ind1" does not exist +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +\parallel off +\parallel on +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +\parallel off +\parallel on +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +\parallel off +\parallel on +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +\parallel off +\parallel on +alter index global_partition_reindex_table_ind1 rebuild; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +\parallel off +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +reindex index global_partition_reindex_table_ind1; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +\parallel off +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind2; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind3; +\parallel off +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind2; +reindex index global_partition_reindex_table_ind3; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +\parallel off +--clean +drop index if exists global_partition_reindex_table_ind1; +drop index if exists global_partition_reindex_table_ind2; +drop index if exists global_partition_reindex_table_ind3; +drop index if exists partition_reindex_table_ind1; +drop index if exists partition_reindex_table_ind2; +drop table if exists partition_reindex_table; +drop index if exists partition_reindex_internal_table_ind1; +drop index if exists partition_reindex_internal_table_ind2; +drop index if exists global_partition_reindex_internal_table_ind1; +NOTICE: index "global_partition_reindex_internal_table_ind1" does not exist, skipping +drop table if exists partition_reindex_internal_table; diff --git a/src/test/regress/expected/gpi_set_index_unusable.out b/src/test/regress/expected/gpi_set_index_unusable.out new file mode 100644 index 0000000000000000000000000000000000000000..f8056b87f23067b8317cdd60308c8cef632beb68 --- /dev/null +++ b/src/test/regress/expected/gpi_set_index_unusable.out @@ -0,0 +1,533 @@ +-- MERGE PARTITIONS +set client_min_messages=error; +drop table if exists test_merge_llt; +create table test_merge_llt (a int, b int) +partition by range (a) +( +partition test_merge_llt_p1 values less than (10), +partition test_merge_llt_p2 values less than (20), +partition test_merge_llt_p3 values less than (30), +partition test_merge_llt_p4 values less than (maxvalue) +); +create index test_merge_llt_idx on test_merge_llt(a) global; +create index test_merge_llt_idx_local on test_merge_llt(b) local; +insert into test_merge_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_merge_llt') order by 2; + relname | boundaries +-------------------+------------ + test_merge_llt_p1 | {10} + test_merge_llt_p2 | {20} + test_merge_llt_p3 | {30} + test_merge_llt_p4 | {NULL} + test_merge_llt | +(5 rows) + +vacuum analyze test_merge_llt; +-- indexscan +explain (costs false) select * from test_merge_llt where a=40; + QUERY PLAN +------------------------------------------------------- + [Bypass] + Index Scan using test_merge_llt_idx on test_merge_llt + Index Cond: (a = 40) +(3 rows) + +-- 1 rows +select * from test_merge_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +alter table test_merge_llt merge partitions test_merge_llt_p1, test_merge_llt_p2 into partition test_merge_llt_px; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_merge_llt') order by 2; + relname | boundaries +-------------------+------------ + test_merge_llt_px | {20} + test_merge_llt_p3 | {30} + test_merge_llt_p4 | {NULL} + test_merge_llt | +(4 rows) + +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_merge_llt'::regclass ORDER BY c.relname; + relname | indisusable +--------------------------+------------- + test_merge_llt_idx | f + test_merge_llt_idx_local | t +(2 rows) + +set enable_bitmapscan=off; +set enable_seqscan=off; +-- seqscan +explain (costs false) select * from test_merge_llt where a=40; + QUERY PLAN +---------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Seq Scan on test_merge_llt + Filter: (a = 40) + Selected Partitions: 3 +(5 rows) + +select * from test_merge_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +reindex index test_merge_llt_idx; +-- indexscan +explain (costs false) select * from test_merge_llt where a=40; + QUERY PLAN +------------------------------------------------------- + [Bypass] + Index Scan using test_merge_llt_idx on test_merge_llt + Index Cond: (a = 40) +(3 rows) + +select * from test_merge_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table if exists test_merge_llt; +-- End. Clean up +---truncate partition table with index +set client_min_messages=error; +drop table if exists test_truncate_llt; +create table test_truncate_llt (a int, b int) +partition by range (a) +( +partition test_truncate_llt_p1 values less than (10), +partition test_truncate_llt_p2 values less than (20), +partition test_truncate_llt_p3 values less than (30), +partition test_truncate_llt_p4 values less than (maxvalue) +); +create index test_truncate_llt_idx on test_truncate_llt(a) global; +insert into test_truncate_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_truncate_llt') order by 2; + relname | boundaries +----------------------+------------ + test_truncate_llt_p1 | {10} + test_truncate_llt_p2 | {20} + test_truncate_llt_p3 | {30} + test_truncate_llt_p4 | {NULL} + test_truncate_llt | +(5 rows) + +vacuum analyze test_truncate_llt; +-- indexscan +explain (costs false) select * from test_truncate_llt where a=40; + QUERY PLAN +------------------------------------------------------------- + [Bypass] + Index Scan using test_truncate_llt_idx on test_truncate_llt + Index Cond: (a = 40) +(3 rows) + +select * from test_truncate_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +alter table test_truncate_llt truncate partition test_truncate_llt_p3; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_truncate_llt') order by 2; + relname | boundaries +----------------------+------------ + test_truncate_llt_p1 | {10} + test_truncate_llt_p2 | {20} + test_truncate_llt_p3 | {30} + test_truncate_llt_p4 | {NULL} + test_truncate_llt | +(5 rows) + +-- test_truncate_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_truncate_llt'::regclass ORDER BY c.relname; + relname | indisusable +-----------------------+------------- + test_truncate_llt_idx | f +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +-- seqscan +explain (costs false) select * from test_truncate_llt where a = 40; + QUERY PLAN +------------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Seq Scan on test_truncate_llt + Filter: (a = 40) + Selected Partitions: 4 +(5 rows) + +select * from test_truncate_llt where a = 40; + a | b +----+---- + 40 | 40 +(1 row) + +alter index test_truncate_llt_idx rebuild; +-- test_truncate_llt_idx usable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_truncate_llt'::regclass ORDER BY c.relname; + relname | indisusable +-----------------------+------------- + test_truncate_llt_idx | t +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +-- indexscan +explain (costs false) select * from test_truncate_llt where a=40; + QUERY PLAN +------------------------------------------------------------- + [Bypass] + Index Scan using test_truncate_llt_idx on test_truncate_llt + Index Cond: (a = 40) +(3 rows) + +select * from test_truncate_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +drop table if exists test_truncate_llt; +-- End. Clean up +--exchange partition +set client_min_messages=error; +drop table if exists test_exchange_llt; +drop table if exists test_ord; +create table test_exchange_llt (a int, b int) +partition by range (a) +( +partition test_exchange_llt_p1 values less than (10), +partition test_exchange_llt_p2 values less than (20), +partition test_exchange_llt_p3 values less than (30), +partition test_exchange_llt_p4 values less than (maxvalue) +); +create index test_exchange_llt_idx on test_exchange_llt(a) global; +insert into test_exchange_llt select generate_series(0,1000), 100; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_exchange_llt') order by 2; + relname | boundaries +----------------------+------------ + test_exchange_llt_p1 | {10} + test_exchange_llt_p2 | {20} + test_exchange_llt_p3 | {30} + test_exchange_llt_p4 | {NULL} + test_exchange_llt | +(5 rows) + +create table test_ord (a int, b int); +insert into test_ord select 13, generate_series(0,1000); +vacuum analyze test_exchange_llt; +-- indexscan +explain (costs false) select * from test_exchange_llt where a=40; + QUERY PLAN +------------------------------------------------------------- + [Bypass] + Index Scan using test_exchange_llt_idx on test_exchange_llt + Index Cond: (a = 40) +(3 rows) + +select * from test_exchange_llt where a=40; + a | b +----+----- + 40 | 100 +(1 row) + +-- exchange +alter table test_exchange_llt exchange partition (test_exchange_llt_p2) with table test_ord with validation; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_exchange_llt') order by 2; + relname | boundaries +----------------------+------------ + test_exchange_llt_p1 | {10} + test_exchange_llt_p2 | {20} + test_exchange_llt_p3 | {30} + test_exchange_llt_p4 | {NULL} + test_exchange_llt | +(5 rows) + +-- test_exchange_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_exchange_llt'::regclass ORDER BY c.relname; + relname | indisusable +-----------------------+------------- + test_exchange_llt_idx | f +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +--seqcan +explain (costs false) select * from test_exchange_llt where a=40; + QUERY PLAN +------------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Seq Scan on test_exchange_llt + Filter: (a = 40) + Selected Partitions: 4 +(5 rows) + +select * from test_exchange_llt where a=40; + a | b +----+----- + 40 | 100 +(1 row) + +-- rebuild +reindex table test_exchange_llt; +-- test_exchange_llt_idx usable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_exchange_llt'::regclass ORDER BY c.relname; + relname | indisusable +-----------------------+------------- + test_exchange_llt_idx | t +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +-- indexscan +explain (costs false) select * from test_exchange_llt where a=40; + QUERY PLAN +------------------------------------------------------------- + [Bypass] + Index Scan using test_exchange_llt_idx on test_exchange_llt + Index Cond: (a = 40) +(3 rows) + +select * from test_exchange_llt where a=40; + a | b +----+----- + 40 | 100 +(1 row) + +drop table if exists test_exchange_llt; +drop table if exists test_ord; +-- End. Clean up +--split partition +set client_min_messages=error; +drop table if exists test_split_llt; +create table if not exists test_split_llt (a int, b int) +partition by range(a) +( +partition test_split_llt_p1 values less than(10), +partition test_split_llt_p2 values less than(20), +partition test_split_llt_p3 values less than(30), +partition test_split_llt_p4 values less than(maxvalue) +); +create index test_split_llt_idx1 on test_split_llt(a) global; +insert into test_split_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_split_llt') order by 2; + relname | boundaries +-------------------+------------ + test_split_llt_p1 | {10} + test_split_llt_p2 | {20} + test_split_llt_p3 | {30} + test_split_llt_p4 | {NULL} + test_split_llt | +(5 rows) + +vacuum analyze test_split_llt; +-- indexscan +explain (costs false) select * from test_split_llt where a=40; + QUERY PLAN +-------------------------------------------------------- + [Bypass] + Index Scan using test_split_llt_idx1 on test_split_llt + Index Cond: (a = 40) +(3 rows) + +select * from test_split_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +alter table test_split_llt + merge partitions test_split_llt_p1, test_split_llt_p2 + into partition test_split_llt_p1_2; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_split_llt') order by 2; + relname | boundaries +---------------------+------------ + test_split_llt_p1_2 | {20} + test_split_llt_p3 | {30} + test_split_llt_p4 | {NULL} + test_split_llt | +(4 rows) + +alter table test_split_llt + split partition test_split_llt_p1_2 + into (partition test_split_llt_p1 values less than (10), partition test_split_llt_p2 values less than (20)); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_split_llt') order by 2; + relname | boundaries +-------------------+------------ + test_split_llt_p1 | {10} + test_split_llt_p2 | {20} + test_split_llt_p3 | {30} + test_split_llt_p4 | {NULL} + test_split_llt | +(5 rows) + +-- test_split_llt_idx1 unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_split_llt'::regclass ORDER BY c.relname; + relname | indisusable +---------------------+------------- + test_split_llt_idx1 | f +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +--seqscan +explain (costs false) select * from test_split_llt where a=40; + QUERY PLAN +---------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Seq Scan on test_split_llt + Filter: (a = 40) + Selected Partitions: 4 +(5 rows) + +select * from test_split_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +reindex database postgres; +ERROR: can only reindex the currently open database +-- test_split_llt_idx1 unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_split_llt'::regclass ORDER BY c.relname; + relname | indisusable +---------------------+------------- + test_split_llt_idx1 | f +(1 row) + +--seqscan +explain (costs false) select * from test_split_llt where a=40; + QUERY PLAN +---------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Seq Scan on test_split_llt + Filter: (a = 40) + Selected Partitions: 4 +(5 rows) + +select * from test_split_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +drop table if exists test_split_llt; +-- End. Clean up +--drop partition +set client_min_messages=error; +drop table if exists test_drop_llt; +create table test_drop_llt (a int, b int) +partition by range (a) +( +partition test_drop_llt_p1 values less than (10), +partition test_drop_llt_p2 values less than (20), +partition test_drop_llt_p3 values less than (30), +partition test_drop_llt_p4 values less than (maxvalue) +); +create index test_drop_llt_idx on test_drop_llt(a) global; +insert into test_drop_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_drop_llt') order by 2; + relname | boundaries +------------------+------------ + test_drop_llt_p1 | {10} + test_drop_llt_p2 | {20} + test_drop_llt_p3 | {30} + test_drop_llt_p4 | {NULL} + test_drop_llt | +(5 rows) + +vacuum analyze test_drop_llt; +--indexscan +explain (costs false) select * from test_drop_llt where a=40; + QUERY PLAN +----------------------------------------------------- + [Bypass] + Index Scan using test_drop_llt_idx on test_drop_llt + Index Cond: (a = 40) +(3 rows) + +select * from test_drop_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +alter table test_drop_llt drop partition test_drop_llt_p1; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_drop_llt') order by 2; + relname | boundaries +------------------+------------ + test_drop_llt_p2 | {20} + test_drop_llt_p3 | {30} + test_drop_llt_p4 | {NULL} + test_drop_llt | +(4 rows) + +-- test_drop_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_drop_llt'::regclass ORDER BY c.relname; + relname | indisusable +-------------------+------------- + test_drop_llt_idx | f +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +--seqscan +explain (costs false) select * from test_drop_llt where a=40; + QUERY PLAN +--------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Seq Scan on test_drop_llt + Filter: (a = 40) + Selected Partitions: 3 +(5 rows) + +select * from test_drop_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +vacuum full test_drop_llt; +-- test_drop_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_drop_llt'::regclass ORDER BY c.relname; + relname | indisusable +-------------------+------------- + test_drop_llt_idx | f +(1 row) + +--seqscan +explain (costs false) select * from test_drop_llt where a=40; + QUERY PLAN +--------------------------------------------- + Partition Iterator + Iterations: 1 + -> Partitioned Seq Scan on test_drop_llt + Filter: (a = 40) + Selected Partitions: 3 +(5 rows) + +select * from test_drop_llt where a=40; + a | b +----+---- + 40 | 40 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table if exists test_drop_llt; +-- End. Clean up diff --git a/src/test/regress/expected/gpi_unique_check.out b/src/test/regress/expected/gpi_unique_check.out new file mode 100644 index 0000000000000000000000000000000000000000..293f8153f5c949000f2a441a7b5fce6e1db56ba5 --- /dev/null +++ b/src/test/regress/expected/gpi_unique_check.out @@ -0,0 +1,31 @@ +DROP TABLE if exists gpi_uniquecheck_table1; +NOTICE: table "gpi_uniquecheck_table1" does not exist, skipping +CREATE TABLE gpi_uniquecheck_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_uniquecheck_table1 VALUES less than (10000), + partition p1_gpi_uniquecheck_table1 VALUES less than (20000), + partition p2_gpi_uniquecheck_table1 VALUES less than (30000), + partition p3_gpi_uniquecheck_table1 VALUES less than (maxvalue) +); +INSERT INTO gpi_uniquecheck_table1 SELECT r, r, r FROM generate_series(0,40000) AS r; +CREATE UNIQUE INDEX idx1_gpi_uniquecheck_table1 ON gpi_uniquecheck_table1 (c2) GLOBAL; +INSERT INTO gpi_uniquecheck_table1 VALUES (9999,1,1); +ERROR: duplicate key value violates unique constraint "idx1_gpi_uniquecheck_table1" +DETAIL: Key (c2)=(1) already exists. +--error +INSERT INTO gpi_uniquecheck_table1 VALUES (10000,1,1); +ERROR: duplicate key value violates unique constraint "idx1_gpi_uniquecheck_table1" +DETAIL: Key (c2)=(1) already exists. +--error +INSERT INTO gpi_uniquecheck_table1 VALUES (10001,1,1); +ERROR: duplicate key value violates unique constraint "idx1_gpi_uniquecheck_table1" +DETAIL: Key (c2)=(1) already exists. +--error +INSERT INTO gpi_uniquecheck_table1 VALUES (10001,40001,1); +--ok \ No newline at end of file diff --git a/src/test/regress/expected/gpi_vacuum_lazy.out b/src/test/regress/expected/gpi_vacuum_lazy.out new file mode 100644 index 0000000000000000000000000000000000000000..854ccabbcd53fd26e683141b863a6ecaaf88d4c2 --- /dev/null +++ b/src/test/regress/expected/gpi_vacuum_lazy.out @@ -0,0 +1,472 @@ +--drop table and index +drop index if exists global_index_test_b; +NOTICE: index "global_index_test_b" does not exist, skipping +drop index if exists local_index_test_c; +NOTICE: index "local_index_test_c" does not exist, skipping +drop index if exists global_index_test_d; +NOTICE: index "global_index_test_d" does not exist, skipping +drop table if exists test_vacuum_lazy; +NOTICE: table "test_vacuum_lazy" does not exist, skipping +--prepare table and index +create table test_vacuum_lazy(a int, b int, c int, d int) partition by range(a) (partition p1 values less than (10000), partition p2 values less than (20000), partition p3 values less than (30001)); +create unique index global_index_test_a on test_vacuum_lazy (a) global; +create index global_index_test_b on test_vacuum_lazy (b) global; +create index local_index_test_c on test_vacuum_lazy (c) local (partition c_index1, partition c_index2, partition c_index3); +create index global_index_test_d on test_vacuum_lazy(d) global; +--one thread +insert into test_vacuum_lazy select r,r,r,10000 from generate_series(0,9999) as r; +insert into test_vacuum_lazy select r,r,r,20000 from generate_series(10000,19999) as r; +insert into test_vacuum_lazy select r,r,r,30000 from generate_series(20000,29999) as r; +select count(*) from test_vacuum_lazy; + count +------- + 30000 +(1 row) + +vacuum analyze test_vacuum_lazy; +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; + QUERY PLAN +--------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_test_d on test_vacuum_lazy + Index Cond: (d = 20000) +(3 rows) + +select count(*) from test_vacuum_lazy where d = 20000; + count +------- + 10000 +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where c < 20000; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Partition Iterator + Iterations: 3 + -> Partitioned Index Only Scan using local_index_test_c on test_vacuum_lazy + Index Cond: (c < 20000) + Selected Partitions: 1..3 +(6 rows) + +select count(*) from test_vacuum_lazy where c < 20000; + count +------- + 20000 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +delete from test_vacuum_lazy where d = 10000; +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; + QUERY PLAN +--------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_test_d on test_vacuum_lazy + Index Cond: (d = 20000) +(3 rows) + +select count(*) from test_vacuum_lazy where d = 20000; + count +------- + 10000 +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where c < 20000; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Partition Iterator + Iterations: 3 + -> Partitioned Index Only Scan using local_index_test_c on test_vacuum_lazy + Index Cond: (c < 20000) + Selected Partitions: 1..3 +(6 rows) + +select count(*) from test_vacuum_lazy where c < 20000; + count +------- + 10000 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +start transaction; +alter table test_vacuum_lazy add partition p6 values less than (40001); +insert into test_vacuum_lazy select r,r,r,100 from generate_series(30000,39999) as r; +abort; +vacuum analyze test_vacuum_lazy; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; + QUERY PLAN +--------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_test_d on test_vacuum_lazy + Index Cond: (d = 20000) +(3 rows) + +select count(*) from test_vacuum_lazy where d = 20000; + count +------- + 10000 +(1 row) + +explain (costs off) select count(*) from test_vacuum_lazy where c < 20000; + QUERY PLAN +-------------------------------------------------------------------------------------- + Aggregate + -> Partition Iterator + Iterations: 3 + -> Partitioned Index Only Scan using local_index_test_c on test_vacuum_lazy + Index Cond: (c < 20000) + Selected Partitions: 1..3 +(6 rows) + +select count(*) from test_vacuum_lazy where c < 20000; + count +------- + 10000 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +select count(*) from test_vacuum_lazy; + count +------- + 20000 +(1 row) + +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 10000; +vacuum test_vacuum_lazy; +delete from test_vacuum_lazy where d = 30000; +analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy; + count +------- + 10000 +(1 row) + +--multi thread +\parallel on +insert into test_vacuum_lazy select r,r,r,5000 from generate_series(0,4999) as r; +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,10000 from generate_series(5000,9999) as r; +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,15000 from generate_series(10000,14999) as r; +analyze test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,20000 from generate_series(15000,19999) as r; +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,25000 from generate_series(20000,24999) as r; +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,30000 from generate_series(25000,29999) as r; +analyze test_vacuum_lazy; +\parallel off +ERROR: duplicate key value violates unique constraint "global_index_test_a" +--?.* +ERROR: duplicate key value violates unique constraint "global_index_test_a" +--?.* +select count(*) from test_vacuum_lazy; + count +------- + 30000 +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; + QUERY PLAN +--------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_test_d on test_vacuum_lazy + Index Cond: (d = 20000) +(3 rows) + +select count(*) from test_vacuum_lazy where d = 20000; + count +------- + 10000 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +\parallel on +delete from test_vacuum_lazy where d = 5000; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 10000; +vacuum test_vacuum_lazy; +delete from test_vacuum_lazy where d = 15000; +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 20000; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 25000; +vacuum test_vacuum_lazy; +delete from test_vacuum_lazy where d = 30000; +analyze test_vacuum_lazy; +\parallel off +select * from test_vacuum_lazy; + a | b | c | d +---+---+---+--- +(0 rows) + +\parallel on +insert into test_vacuum_lazy values (0, 0, 0, 999); +insert into test_vacuum_lazy select r,r,r,5000 from generate_series(1,4998) as r; +insert into test_vacuum_lazy values (4999, 4999, 4999, 999); +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy values (5000, 5000, 5000, 999); +insert into test_vacuum_lazy select r,r,r,10000 from generate_series(5001,9998) as r; +insert into test_vacuum_lazy values (9999, 9999, 9999, 999); +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy values (10000, 10000, 10000, 999); +insert into test_vacuum_lazy select r,r,r,15000 from generate_series(10001,14998) as r; +insert into test_vacuum_lazy values (14999, 14999, 14999, 999); +analyze test_vacuum_lazy; +insert into test_vacuum_lazy values (15000, 15000, 15000, 999); +insert into test_vacuum_lazy select r,r,r,20000 from generate_series(15001,19998) as r; +insert into test_vacuum_lazy values (19999, 19999, 19999, 999); +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy values (20000, 20000, 20000, 999); +insert into test_vacuum_lazy select r,r,r,25000 from generate_series(20001,24998) as r; +insert into test_vacuum_lazy values (24999, 24999, 24999, 999); +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy values (25000, 25000, 25000, 999); +insert into test_vacuum_lazy select r,r,r,30000 from generate_series(25001,29998) as r; +insert into test_vacuum_lazy values (29999, 29999, 29999, 999); +analyze test_vacuum_lazy; +\parallel off +select count(*) from test_vacuum_lazy; + count +------- + 30000 +(1 row) + +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where b > 10 and b < 20000; + QUERY PLAN +--------------------------------------------------------------------- + Aggregate + -> Index Only Scan using global_index_test_b on test_vacuum_lazy + Index Cond: ((b > 10) AND (b < 20000)) +(3 rows) + +select count(*) from test_vacuum_lazy where b > 10 and b < 20000; + count +------- + 19989 +(1 row) + +explain (costs off) select count(*) from test_vacuum_lazy where d = 999 order by 1; + QUERY PLAN +--------------------------------------------------------------------------- + Sort + Sort Key: (count(*)) + -> Aggregate + -> Index Only Scan using global_index_test_d on test_vacuum_lazy + Index Cond: (d = 999) +(5 rows) + +select count(*) from test_vacuum_lazy where d = 999 order by 1; + count +------- + 12 +(1 row) + +set enable_bitmapscan=on; +set enable_seqscan=on; +\parallel on +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +analyze test_vacuum_lazy; +\parallel off + count +------- + 12 +(1 row) + + count +------- + 12 +(1 row) + + count +------- + 12 +(1 row) + + count +------- + 12 +(1 row) + + count +------- + 12 +(1 row) + + count +------- + 12 +(1 row) + +alter table test_vacuum_lazy disable row movement; +\parallel on +delete from test_vacuum_lazy where b % 7 = 0 and d <> 999; +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 5 = 0 and d <> 999; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 3 = 0 and d <> 999; +vacuum test_vacuum_lazy; +\parallel off +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; + QUERY PLAN +---------------------------------------------------------------- + Sort + Sort Key: a + -> Index Scan using global_index_test_d on test_vacuum_lazy + Index Cond: (d = 999) +(4 rows) + +select * from test_vacuum_lazy where d = 999 order by 1; + a | b | c | d +-------+-------+-------+----- + 0 | 0 | 0 | 999 + 4999 | 4999 | 4999 | 999 + 5000 | 5000 | 5000 | 999 + 9999 | 9999 | 9999 | 999 + 10000 | 10000 | 10000 | 999 + 14999 | 14999 | 14999 | 999 + 15000 | 15000 | 15000 | 999 + 19999 | 19999 | 19999 | 999 + 20000 | 20000 | 20000 | 999 + 24999 | 24999 | 24999 | 999 + 25000 | 25000 | 25000 | 999 + 29999 | 29999 | 29999 | 999 +(12 rows) + +set enable_bitmapscan=on; +set enable_seqscan=on; +\parallel on +delete from test_vacuum_lazy where b % 19 = 0 and d <> 999; +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 17 = 0 and d <> 999; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 13 = 0 and d <> 999; +vacuum test_vacuum_lazy; +\parallel off +alter table test_vacuum_lazy enable row movement; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; + QUERY PLAN +---------------------------------------------------------------- + Sort + Sort Key: a + -> Index Scan using global_index_test_d on test_vacuum_lazy + Index Cond: (d = 999) +(4 rows) + +select * from test_vacuum_lazy where d = 999 order by 1; + a | b | c | d +-------+-------+-------+----- + 0 | 0 | 0 | 999 + 4999 | 4999 | 4999 | 999 + 5000 | 5000 | 5000 | 999 + 9999 | 9999 | 9999 | 999 + 10000 | 10000 | 10000 | 999 + 14999 | 14999 | 14999 | 999 + 15000 | 15000 | 15000 | 999 + 19999 | 19999 | 19999 | 999 + 20000 | 20000 | 20000 | 999 + 24999 | 24999 | 24999 | 999 + 25000 | 25000 | 25000 | 999 + 29999 | 29999 | 29999 | 999 +(12 rows) + +set enable_bitmapscan=on; +set enable_seqscan=on; +set enable_indexscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; + QUERY PLAN +------------------------------------------------------ + Sort + Sort Key: a + -> Bitmap Heap Scan on test_vacuum_lazy + Recheck Cond: (d = 999) + -> Bitmap Index Scan on global_index_test_d + Index Cond: (d = 999) +(6 rows) + +select * from test_vacuum_lazy where d = 999 order by 1; + a | b | c | d +-------+-------+-------+----- + 0 | 0 | 0 | 999 + 4999 | 4999 | 4999 | 999 + 5000 | 5000 | 5000 | 999 + 9999 | 9999 | 9999 | 999 + 10000 | 10000 | 10000 | 999 + 14999 | 14999 | 14999 | 999 + 15000 | 15000 | 15000 | 999 + 19999 | 19999 | 19999 | 999 + 20000 | 20000 | 20000 | 999 + 24999 | 24999 | 24999 | 999 + 25000 | 25000 | 25000 | 999 + 29999 | 29999 | 29999 | 999 +(12 rows) + +set enable_indexscan=on; +set enable_bitmapscan=off; +set enable_indexscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; + QUERY PLAN +------------------------------------------------------ + Sort + Sort Key: a + -> Partition Iterator + Iterations: 3 + -> Partitioned Seq Scan on test_vacuum_lazy + Filter: (d = 999) + Selected Partitions: 1..3 +(7 rows) + +select * from test_vacuum_lazy where d = 999 order by 1; + a | b | c | d +-------+-------+-------+----- + 0 | 0 | 0 | 999 + 4999 | 4999 | 4999 | 999 + 5000 | 5000 | 5000 | 999 + 9999 | 9999 | 9999 | 999 + 10000 | 10000 | 10000 | 999 + 14999 | 14999 | 14999 | 999 + 15000 | 15000 | 15000 | 999 + 19999 | 19999 | 19999 | 999 + 20000 | 20000 | 20000 | 999 + 24999 | 24999 | 24999 | 999 + 25000 | 25000 | 25000 | 999 + 29999 | 29999 | 29999 | 999 +(12 rows) + +set enable_indexscan=on; +set enable_bitmapscan=on; +--clean table and index +drop index if exists global_index_test_b; +drop index if exists local_index_test_c; +drop index if exists global_index_test_d; +drop table if exists test_vacuum_lazy; diff --git a/src/test/regress/expected/hw_partition_index.out b/src/test/regress/expected/hw_partition_index.out index e18f8d0b528d5c3227ba06126f9a741fbea53f0f..563e475b4f15410cd5c3132dc677f3451fc1843d 100644 --- a/src/test/regress/expected/hw_partition_index.out +++ b/src/test/regress/expected/hw_partition_index.out @@ -125,8 +125,7 @@ create unique index CONCURRENTLY on hw_partition_index_rp (c1) local partition srp2_index_local tablespace PG_DEFAULT, partition srp3_index_local tablespace PG_DEFAULT ); -ERROR: PGXC does not support concurrent INDEX yet -DETAIL: The feature is not currently supported +ERROR: cannot create concurrent partitioned indexes --fail create unique index on hw_partition_index_rp (c1) local ( @@ -170,9 +169,9 @@ ERROR: syntax error at or near "srp1_index_local" LINE 3: srp1_index_local tablespace PG_DEFAULT, ^ --fail wrong syntax -create unique index on hw_partition_index_rp (c1); -ERROR: partitioned table does not support global index ---fail wrong syntax +create unique index global_index on hw_partition_index_rp (c1); +ERROR: Global and local partition index should not be on same column +--fail drop table hw_partition_index_rp; --unique index , index para must contain partition key create table hw_partition_index_rp diff --git a/src/test/regress/expected/hw_partition_interval_index.out b/src/test/regress/expected/hw_partition_interval_index.out index e3541a561aead60e1b54c7341b1d32ac96a80996..12c47834a14047b042da2390313a7ccb5b7a5972 100644 --- a/src/test/regress/expected/hw_partition_interval_index.out +++ b/src/test/regress/expected/hw_partition_interval_index.out @@ -95,8 +95,7 @@ create unique index CONCURRENTLY on hw_partition_index_ip (logdate) local partition sip2_index_local tablespace PG_DEFAULT, partition sip3_index_local tablespace PG_DEFAULT ); -ERROR: PGXC does not support concurrent INDEX yet -DETAIL: The feature is not currently supported +ERROR: cannot create concurrent partitioned indexes -- Not enough index partition defined create unique index on hw_partition_index_ip (logdate) local ( @@ -113,9 +112,9 @@ create unique index on hw_partition_index_ip (logdate) local partition sip4_index_local tablespace PG_DEFAULT ); ERROR: number of partitions of LOCAL index must equal that of the underlying table -create unique index on hw_partition_index_ip (logdate); -ERROR: partitioned table does not support global index ---fail wrong syntax +create unique index global_internal_index on hw_partition_index_ip (logdate); +ERROR: Global and local partition index should not be on same column +--fail drop table hw_partition_index_ip; --unique index , index para must contain partition key create table hw_partition_index_ip diff --git a/src/test/regress/parallel_schedule18 b/src/test/regress/parallel_schedule18 index af63e928b97068aabe63c319ce5284ac27a3bd0b..899b412d42ab63ac75ba55a2f39f86d8b5f02991 100644 --- a/src/test/regress/parallel_schedule18 +++ b/src/test/regress/parallel_schedule18 @@ -26,3 +26,42 @@ test: hw_partition_interval_check_syntax #test: hw_partition_vacuum test: hw_partition_interval_split test: hw_partition_interval_merge + +# Global Partition index feature testcase +# gpi create +test: gpi_build_index +test: gpi_build_index_01 gpi_build_index_02 +# gpi check +test: gpi_create_constraint +test: gpi_unique_check +# gpi index scan +test: gpi_index +# gpi index only scan +test: gpi_index_only + +# gpi bitmap +test: gpi_bitmapscan + +# gpi pwj +test: gpi_pwj + +# gpi set unusable +test: gpi_set_index_unusable + +# gpi rebuild +test: gpi_rebuild_index + +# gpi cluster +test: gpi_cluster_01 gpi_cluster_02 gpi_cluster_03 + +# gpi interval partition +#test: gpi_interval + +# gpi invliad part +test: gpi_invalid_part +test: gpi_clean_wait + +# gpi vacuum +test: gpi_vacuum_lazy +test: gpi_hw_partition_vacuum_full +test: gpi_hw_partition_vacuum_full_01 diff --git a/src/test/regress/sql/gpi_bitmapscan.sql b/src/test/regress/sql/gpi_bitmapscan.sql new file mode 100644 index 0000000000000000000000000000000000000000..92b47a347e2df765edb693674b4fdb86706195b1 --- /dev/null +++ b/src/test/regress/sql/gpi_bitmapscan.sql @@ -0,0 +1,81 @@ + +DROP TABLE if exists gpi_bitmap_table1; +DROP TABLE if exists gpi_bitmap_table2; + +SET enable_seqscan=off; +SET enable_indexscan=off; + +CREATE TABLE gpi_bitmap_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_bitmap_table1 VALUES less than (10000), + partition p1_gpi_bitmap_table1 VALUES less than (20000), + partition p2_gpi_bitmap_table1 VALUES less than (30000), + partition p3_gpi_bitmap_table1 VALUES less than (maxvalue) +); + + +INSERT INTO gpi_bitmap_table1 + SELECT (r%53), (r%59), (r%56) + FROM generate_series(1,70000) r; +--ok + +CREATE INDEX idx1_gpi_bitmap_table1 ON gpi_bitmap_table1 (c1) GLOBAL; +--ok +CREATE INDEX idx2_gpi_bitmap_table1 ON gpi_bitmap_table1 (c2) GLOBAL; +--ok + +-- test bitmap-and +SELECT count(*) FROM gpi_bitmap_table1 WHERE c1 = 1 AND c2 = 1; +SELECT * FROM gpi_bitmap_table1 WHERE c1 = 1 AND c2 = 1; +SELECT * FROM gpi_bitmap_table1 WHERE c1 = 1 AND c2 = 1 ORDER BY c3; + +-- test bitmap-or +SELECT count(*) FROM gpi_bitmap_table1 WHERE c1 = 1 OR c2 = 1; + +CREATE TABLE gpi_bitmap_table2 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_bitmap_table2 VALUES less than (10000), + partition p1_gpi_bitmap_table2 VALUES less than (20000), + partition p2_gpi_bitmap_table2 VALUES less than (30000), + partition p3_gpi_bitmap_table2 VALUES less than (maxvalue) +); +--ok + +INSERT INTO gpi_bitmap_table2 + SELECT r, (r+1), 500 + FROM generate_series(1,70000) r; +--ok + +CREATE INDEX idx1_gpi_bitmap_table2 ON gpi_bitmap_table2 (c1) GLOBAL; +--ok +CREATE INDEX idx2_gpi_bitmap_table2 ON gpi_bitmap_table2 (c2) GLOBAL; +--ok +EXPLAIN (COSTS OFF) SELECT count(*) FROM gpi_bitmap_table2 WHERE c1 > 9990 AND c1 < 10010; +SELECT count(*) FROM gpi_bitmap_table2 WHERE c1 > 9990 AND c1 < 10010; +SELECT * FROM gpi_bitmap_table2 WHERE c1 > 9990 AND c1 < 10010; + +EXPLAIN (COSTS OFF) SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 AND c2 = 10000; +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 AND c2 = 10000; +EXPLAIN (COSTS OFF) SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 10000; +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 10000; +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 9999 OR c2 = 10001; +EXPLAIN (COSTS OFF) SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 30000; +SELECT * FROM gpi_bitmap_table2 WHERE c1 = 10000 OR c2 = 30000; + + +DROP TABLE idx1_gpi_bitmap_table1; +DROP TABLE idx1_gpi_bitmap_table2; +SET enable_seqscan=on; +SET enable_indexscan=on; \ No newline at end of file diff --git a/src/test/regress/sql/gpi_build_index.sql b/src/test/regress/sql/gpi_build_index.sql new file mode 100644 index 0000000000000000000000000000000000000000..d3f8ee7a4cae2c477ea52ec76c78344b2af062d1 --- /dev/null +++ b/src/test/regress/sql/gpi_build_index.sql @@ -0,0 +1,240 @@ +-- +---- test global index +-- + +drop table if exists hw_global_index_rp; +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (50), + partition hw_global_index_rp_p1 values less than (100), + partition hw_global_index_rp_p2 values less than (150) +); +--succeed + +insert into hw_global_index_rp values(1, 1); +insert into hw_global_index_rp values(49, 2); +insert into hw_global_index_rp values(50, 3); +insert into hw_global_index_rp values(100, 4); +insert into hw_global_index_rp values(149, 5); + +create index rp_index_global1 on hw_global_index_rp (c1) global; +--succeed + +create index on hw_global_index_rp (c1) global; +--fail + +create unique index rp_index_global2 on hw_global_index_rp (c1) global; +--succeed + +create unique index on hw_global_index_rp (c1) global; +--fail + +--expression +create index rp_index_global3 on hw_global_index_rp ((c1+c2)) global; +--fail + +create index rp_index_global4 on hw_global_index_rp ((c1-c2)) global; +--succeed + +create index rp_index_global15 on hw_global_index_rp ((c1-c2),c1,c2) global; +--succeed + +create index rp_index_global16 on hw_global_index_rp using hash (c1) global; +--fail ERROR: access method "HASH" does not support global indexes + +create index rp_index_global17 on hw_global_index_rp using gin ((c1-c2),c1,c2) global; +--fail ERROR: data type INTEGER has no default operator class for access method "GIN" ??? + +create unique index CONCURRENTLY rp_index_global18 on hw_global_index_rp (c1) global; +--fail ERROR: not support CONCURRENTLY + +create unique index rp_index_global19 on hw_global_index_rp (c1); + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'rp_index_global1' and ind.indexrelid = class.oid; + +drop index rp_index_global1; + +drop table hw_global_index_rp; + +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (50), + partition hw_global_index_rp_p1 values less than (100), + partition hw_global_index_rp_p2 values less than (150) +); + +insert into hw_global_index_rp values(1, 1); +insert into hw_global_index_rp values(1, 1); +insert into hw_global_index_rp values(2, 1); + +create unique index rp_index_global21 on hw_global_index_rp (c1) global; +-- fail + +delete from hw_global_index_rp where c1 = '1'; +create unique index rp_index_global21 on hw_global_index_rp (c1) global; +--succeed + +create index rp_index_global22 on hw_global_index_rp(c1 DESC NULLS LAST); +-- succeed + +create index rp_index_globa23 on hw_global_index_rp(c1) where c2 > 0; +--fail + +alter index rp_index_global22 rename to rp_index_global24; +-- succeed + +create index rp_index_globa25 on hw_global_index_rp(c1); +alter index if exists rp_index_global24 unusable; +-- succeed + +create index rp_index_globa26 on hw_global_index_rp(c1) tablespace pg_default; +-- succeed + +drop table hw_global_index_rp; + +Create table hw_global_index_t1 +( + C1 int, + C2 int +); + +Create unique index t1_unique_index_1 on hw_global_index_t1 (c1) global; +--fail non-partitioned table does not support global global index + +drop table hw_global_index_t1; + +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); + +Create index rp_global_index on hw_global_index_rp (c1) global; + +COMMENT ON INDEX rp_global_index IS 'global index comment'; + +\di+ rp_global_index; +\d rp_global_index; +\d hw_global_index_rp; + +select pg_get_indexdef('rp_global_index'::regclass); + +COMMENT ON INDEX rp_global_index IS NULL; + +\di+ rp_global_index; + +drop table hw_global_index_rp; + +create table hw_global_index_rp +( + c1 int, + name text +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); + +Create index rp_global_index on hw_global_index_rp (name) global; + +drop table hw_global_index_rp; + +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); + +insert into hw_global_index_rp values(generate_series(0,9999), 1); + +\parallel on +create index rp_index_global1 on hw_global_index_rp (c1) global; +select count(*) from hw_global_index_rp; +\parallel off + +drop table hw_global_index_rp; + +create table hw_global_index_rp +( + c1 int, + c2 int +) +partition by range (c1) +( + partition hw_global_index_rp_p0 values less than (2000), + partition hw_global_index_rp_p1 values less than (4000), + partition hw_global_index_rp_p2 values less than (6000), + partition hw_global_index_rp_p3 values less than (8000), + partition hw_global_index_rp_p4 values less than (10000) +); + +insert into hw_global_index_rp values(generate_series(0,9999), 1); + +create index on hw_global_index_rp(c1); + +create index on hw_global_index_rp(c1); + +create index on hw_global_index_rp(c1, c2); + +create index index_for_create_like on hw_global_index_rp(c2) global; + +create table test_gpi_create_like (like hw_global_index_rp including all); + +\d test_gpi_create_like; + +insert into test_gpi_create_like values(generate_series(0,9999), 1); + +explain (costs off) select * from test_gpi_create_like where c1 = 0; +select * from test_gpi_create_like where c1 = 0; + +drop table test_gpi_create_like; + +drop table if exists gpi_multi_cols; +create table gpi_multi_cols(a0 int,a1 int,a2 int,a3 int,a4 int,a5 int,a6 int,a7 int,a8 int,a9 int,a10 int,a11 int,a12 int,a13 int,a14 int,a15 int,a16 int,a17 int,a18 int,a19 int,a20 int,a21 int,a22 int,a23 int,a24 int,a25 int,a26 int,a27 int,a28 int,a29 int,a30 int,a31 int,a32 int, a33 int) partition by range(a0) (partition p1 values less than (1001), partition p2 values less than (2001), partition p3 values less than (3001)); + +-- success +create index gpi_multi_cols_index on gpi_multi_cols(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30) global; +--failed +create index gpi_multi_cols_index on gpi_multi_cols(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30, a31) global; +--success +create index gpi_multi_cols_index_local on gpi_multi_cols(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30, a31) local; + +insert into gpi_multi_cols select r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r from generate_series(1,3000) as r; + +vacuum analyze gpi_multi_cols; +set enable_seqscan = off; +select * from gpi_multi_cols where a1 = 200; +reset enable_seqscan; + +--clean data +drop table gpi_multi_cols; diff --git a/src/test/regress/sql/gpi_build_index_01.sql b/src/test/regress/sql/gpi_build_index_01.sql new file mode 100644 index 0000000000000000000000000000000000000000..cb454a93b242f0678a9bf661155371db01c3f2ce --- /dev/null +++ b/src/test/regress/sql/gpi_build_index_01.sql @@ -0,0 +1,4 @@ +-- +---- test global index +-- +create index rp_index_global1 on hw_global_index_rp (c1) global; diff --git a/src/test/regress/sql/gpi_build_index_02.sql b/src/test/regress/sql/gpi_build_index_02.sql new file mode 100644 index 0000000000000000000000000000000000000000..a94153d5678c72e870c2d0a825efd3d4e2aed0b6 --- /dev/null +++ b/src/test/regress/sql/gpi_build_index_02.sql @@ -0,0 +1,4 @@ +-- +---- test global index +-- +select count(*) from hw_global_index_rp; diff --git a/src/test/regress/sql/gpi_clean_wait.sql b/src/test/regress/sql/gpi_clean_wait.sql new file mode 100644 index 0000000000000000000000000000000000000000..2b854eacd49b51262e2b0e0fcf80ed517032921a --- /dev/null +++ b/src/test/regress/sql/gpi_clean_wait.sql @@ -0,0 +1,250 @@ +drop table if exists test_gpi_more_invalid; +create table test_gpi_more_invalid(a int, b int, c int) partition by range(a) (partition p1 values less than (1001), partition p2 values less than (2001), partition p3 values less than (3001)); +insert into test_gpi_more_invalid select r,r,100 from generate_series(1,1000) as r; +insert into test_gpi_more_invalid select r,r,200 from generate_series(1001,2000) as r; +insert into test_gpi_more_invalid select r,r,300 from generate_series(2001,3000) as r; + +create index global_index_gpi_more_invalid_b on test_gpi_more_invalid (a) global; +create index global_index_gpi_more_invalid_c on test_gpi_more_invalid (c) global; +create unique index global_index_gpi_more_invalid_a on test_gpi_more_invalid (b) global; +create index local_index_gpi_more_invalid_a_b on test_gpi_more_invalid (a,b) local; +create index global_index_gpi_more_invalid_a_b_c on test_gpi_more_invalid(a,b,c) global; +vacuum analyze test_gpi_more_invalid; + +start transaction; +alter table test_gpi_more_invalid add partition p6 values less than (4001); +update test_gpi_more_invalid set a = 3000 + a where a <= 100; +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; +explain (costs off) select * from test_gpi_more_invalid where a >= 3000; +--100 rows +select count(*) from test_gpi_more_invalid where a > 3000; +abort; + +-- delete 100 +delete test_gpi_more_invalid where b % 10 = 0 and c = 100; +-- delete 100 +delete test_gpi_more_invalid where b % 10 = 0 and c = 200; +-- delete 100 +delete test_gpi_more_invalid where b % 10 = 0 and c = 300; + +-- after select's where condition +insert into test_gpi_more_invalid values(10, 100000, 100); +insert into test_gpi_more_invalid values(1020, 200000, 200); +insert into test_gpi_more_invalid values(2030, 300000, 300); + +explain (costs off) select count(*) from test_gpi_more_invalid where a <= 100 and b = 100000; +explain (costs off) select count(*) from test_gpi_more_invalid where a > 3000; +-- 1 rows +select count(*) from test_gpi_more_invalid where a <= 100 and b = 100000; +-- 0 rows +select count(*) from test_gpi_more_invalid where a >= 3000; +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; +vacuum analyze test_gpi_more_invalid; +-- test_gpi_more_invalid have wait_clean_gpi=n +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + +explain (costs off) select count(*) from test_gpi_more_invalid where a <= 100; +explain (costs off) select count(*) from test_gpi_more_invalid where a >= 4000; +-- 91 rows +select count(*) from test_gpi_more_invalid where a <= 100; +-- 0 rows +select count(*) from test_gpi_more_invalid where a >= 4000; + +explain (costs off) select count(*) from test_gpi_more_invalid where c = 100; +-- 900 rows +select count(*) from test_gpi_more_invalid where c = 100; +explain (costs off) select count(*) from test_gpi_more_invalid where c = 200; +-- 900 rows +select count(*) from test_gpi_more_invalid where c = 200; +explain (costs off) select count(*) from test_gpi_more_invalid where c = 300; +-- 900 rows +select count(*) from test_gpi_more_invalid where c = 300; + +explain (costs off) delete test_gpi_more_invalid where b%5 != 0 and (c = 100 or c = 200 or c = 300); +delete test_gpi_more_invalid where b%5 != 0 and (c = 100 or c = 200 or c = 300); + +start transaction; +alter table test_gpi_more_invalid add partition p6 values less than (4001); +insert into test_gpi_more_invalid select r,r,300 from generate_series(3001,4000) as r; +--failed +update test_gpi_more_invalid set b = 1001 where c = 200; +commit; + +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + +set enable_seqscan = off; +-- rows 100 +explain (costs off) select count(*) from test_gpi_more_invalid where c = 200; +select count(*) from test_gpi_more_invalid where c = 200; + +explain (costs off) select count(*) from test_gpi_more_invalid where c = 300; +select count(*) from test_gpi_more_invalid where c = 300; + +explain (costs off) update test_gpi_more_invalid set a = a - 500 where a < 1500 and a >= 1000; +update test_gpi_more_invalid set a = a - 500 where a < 1500 and a >= 1000; +explain (costs off) update test_gpi_more_invalid set a = a + 500 where a < 1000 and a >= 500; +update test_gpi_more_invalid set a = a + 500 where a < 1000 and a >= 500; + +explain (costs off) select count(*) from test_gpi_more_invalid where a < 1500 and a >= 1000; +select count(*) from test_gpi_more_invalid where a < 1500 and a >= 1000; +explain (costs off) select count(*) from test_gpi_more_invalid where a <= 1000; +select count(*) from test_gpi_more_invalid where a <= 1000; + +set force_bitmapand = on; +-- partition 1 +explain (costs off) select * from test_gpi_more_invalid where b = 100000 and c = 100; +select * from test_gpi_more_invalid where b = 100000 and c = 100; + +--partition 2 +explain (costs off) select * from test_gpi_more_invalid where b = 200000 and c = 200; +select * from test_gpi_more_invalid where b = 200000 and c = 200; +reset force_bitmapand; + +alter table test_gpi_more_invalid DISABLE ROW MOVEMENT; +update test_gpi_more_invalid set a = a - 500 where a < 2000 and a > 1500; +-- failed +update test_gpi_more_invalid set a = a - 500 where a <= 1500; + +alter table test_gpi_more_invalid ENABLE ROW MOVEMENT; + +set force_bitmapand = on; +--partition 2 +explain (costs off) select * from test_gpi_more_invalid where b = 200000 and c = 200; +select * from test_gpi_more_invalid where b = 200000 and c = 200; +reset force_bitmapand; + +start transaction; +alter table test_gpi_more_invalid add partition p5 values less than (4001); +select * from test_gpi_more_invalid where b = 300000 and c = 700 for update; +update test_gpi_more_invalid set a = a + 1000 where a > 1000 or a < 500; +select count(*) from test_gpi_more_invalid where c = 100 and b = 2000; +-- test_gpi_more_invalid/p5 have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; +abort; + +--failed +vacuum full pg_partition; + +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; +vacuum analyze test_gpi_more_invalid; +-- test_gpi_more_invalid have wait_clean_gpi=n +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + +-- success +vacuum full pg_partition; + +set force_bitmapand = on; +-- partition 1 +explain (costs off) select * from test_gpi_more_invalid where b = 100000 and c = 100; +select * from test_gpi_more_invalid where b = 100000 and c = 100; + +--partition 2 +explain (costs off) select * from test_gpi_more_invalid where b = 200000 and c = 200; +select * from test_gpi_more_invalid where b = 200000 and c = 200; + +--partition 3 +explain (costs off) select * from test_gpi_more_invalid where b = 300000 and c = 300; +select * from test_gpi_more_invalid where b = 300000 and c = 300; +reset force_bitmapand; + +start transaction; +alter table test_gpi_more_invalid add partition p5 values less than (4001); +update test_gpi_more_invalid set a = a + 1000 where a > 1000 or a < 500; +alter table test_gpi_more_invalid drop partition p5; +commit; + +-- all global index unusuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + +vacuum full test_gpi_more_invalid; + +-- all global index unusuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; +-- test_gpi_more_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'test_gpi_more_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + +alter index local_index_gpi_more_invalid_a_b unusable; + +reindex table test_gpi_more_invalid; +-- all global index unusuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; +alter index global_index_gpi_more_invalid_a rebuild; +alter index global_index_gpi_more_invalid_a_b_c rebuild; +alter index global_index_gpi_more_invalid_b rebuild; +alter index global_index_gpi_more_invalid_c rebuild; +-- all global index usuable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_gpi_more_invalid'::regclass ORDER BY c.relname; + +reset enable_seqscanï¼› +drop table test_gpi_more_invalid; + +drop table if exists interval_normal_date; +CREATE TABLE interval_normal_date (logdate date not null, b int, c int) +PARTITION BY RANGE (logdate) +INTERVAL ('1 day') +( + PARTITION interval_normal_date_p1 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_normal_date_p2 VALUES LESS THAN ('2020-05-01'), + PARTITION interval_normal_date_p3 VALUES LESS THAN ('2020-06-01') +); + +create index global_interval_index on interval_normal_date(b) global; +insert into interval_normal_date select '2020-6-01', 1000,r from generate_series(0, 10000) as r; +vacuum interval_normal_date; + +start transaction; +insert into interval_normal_date select '2020-6-02', r,r from generate_series(0,500) as r; +explain (costs off) select count(*) from interval_normal_date where b <= 500; +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_normal_date' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; +alter table interval_normal_date drop partition sys_p2; +commit; +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_normal_date' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; +vacuum analyze interval_normal_date; +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_normal_date' and a.reloptions[3] like '%wait_clean_gpi=y%' order by 1,2,3; + +drop table if exists interval_partition_table_vacuum; +create table interval_partition_table_vacuum +( + c1 int, + c2 int, + logdate date not null, + PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 day') +( + PARTITION interval_partition_table_004_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_004_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_004_p2 VALUES LESS THAN ('2020-05-01') +); + +create index global_index_interval_partition_table_vacuum_a on interval_partition_table_vacuum(c1) global; + +\parallel on +insert into interval_partition_table_vacuum values (generate_series(1,10), generate_series(1,10), generate_series(TO_DATE('1990-01-01', 'YYYY-MM-DD'),TO_DATE('2020-12-01', 'YYYY-MM-DD'),'1 day')); +vacuum interval_partition_table_vacuum; +vacuum interval_partition_table_vacuum; +vacuum analyze interval_partition_table_vacuum; +\parallel off + +set enable_bitmapscan = off; +explain (costs off) select count(*) from interval_partition_table_vacuum where c1 = 1; +-- 11293 rows +select count(*) from interval_partition_table_vacuum where c1 = 1; + +-- all new add partition have wait_clean_gpi=y return true +select true from (select count(*) as count from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_partition_table_vacuum' and a.reloptions[3] like '%wait_clean_gpi=y%') wait_clean_gpi where wait_clean_gpi.count = 0 or wait_clean_gpi.count = 216; +vacuum analyze interval_partition_table_vacuum; +-- 0 rows +select count(*) from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'interval_partition_table_vacuum' and a.reloptions[3] like '%wait_clean_gpi=y%'; +reset enable_bitmapscan; + +-- clean table +drop table interval_partition_table_vacuum; +drop table interval_normal_date; +drop table test_gpi_more_invalid; diff --git a/src/test/regress/sql/gpi_cluster_01.sql b/src/test/regress/sql/gpi_cluster_01.sql new file mode 100644 index 0000000000000000000000000000000000000000..0de6170786d1ffa188ceb4de992881fa3141a857 --- /dev/null +++ b/src/test/regress/sql/gpi_cluster_01.sql @@ -0,0 +1,71 @@ +-- +---- test cluster on global index +-- + +--drop table and index +drop index if exists global_inventory_table_01_index1; +drop index if exists global_inventory_table_01_index2; +drop table if exists inventory_table_01; + +create table inventory_table_01 +( + inv_date_sk integer not null, + inv_item_sk numeric not null, + inv_warehouse_sk integer not null, + inv_quantity_on_hand integer +) +partition by range(inv_date_sk) +( + partition p1 values less than(10000), + partition p2 values less than(20000), + partition p3 values less than(30000), + partition p4 values less than(40000), + partition p5 values less than(50000), + partition p6 values less than(60000), + partition p7 values less than(maxvalue) +); +--succeed + +insert into inventory_table_01 values ( 1, 0.205546309705824, 1 ); +insert into inventory_table_01 values ( 2, 0.635608241427690, 2 ); +insert into inventory_table_01 values ( 3, 0.868086945265532, 3 ); +insert into inventory_table_01 values ( 4, 0.513698554132134, 4 ); +insert into inventory_table_01 values ( 5, 0.172576570883393, 5 ); +insert into inventory_table_01 values ( 6, 0.537533693015575, 6 ); +insert into inventory_table_01 values ( 7, 0.967260550707579, 7 ); +insert into inventory_table_01 values ( 8, 0.515049927402288, 8 ); +insert into inventory_table_01 values ( 9, 0.583286581560969, 9 ); +insert into inventory_table_01 values (10, 0.313009374774992, 10 ); + +vacuum analyze inventory_table_01; + +select ctid,* from inventory_table_01; + +create index global_inventory_table_01_index1 on inventory_table_01(inv_date_sk) global; + +create index global_inventory_table_01_index2 on inventory_table_01(inv_item_sk) global; + +cluster inventory_table_01 using global_inventory_table_01_index2; + +vacuum analyze inventory_table_01; + +select ctid,* from inventory_table_01; + +cluster inventory_table_01 using global_inventory_table_01_index1; + +vacuum analyze inventory_table_01; + +select ctid,* from inventory_table_01; + +cluster inventory_table_01 using global_inventory_table_01_index2; + +\dS+ inventory_table_01; + +vacuum analyze inventory_table_01; + +select ctid,* from inventory_table_01; + +--clean +drop index if exists global_inventory_table_01_index1; +drop index if exists global_inventory_table_01_index2; +drop table if exists inventory_table_01; diff --git a/src/test/regress/sql/gpi_cluster_02.sql b/src/test/regress/sql/gpi_cluster_02.sql new file mode 100644 index 0000000000000000000000000000000000000000..0ea06a9c68e740bc88bc22decd7de4b9c69f7a8c --- /dev/null +++ b/src/test/regress/sql/gpi_cluster_02.sql @@ -0,0 +1,79 @@ +-- +---- test cluster on global index +-- + +--drop table and index +drop index if exists global_inventory_table_02_index1; +drop index if exists global_inventory_table_02_index2; +drop table if exists inventory_table_02; + +create table inventory_table_02 +( + inv_date_sk integer not null, + inv_item_sk numeric not null, + inv_warehouse_sk integer not null, + inv_quantity_on_hand integer +) +partition by range(inv_date_sk) +( + partition p1 values less than(10000), + partition p2 values less than(20000), + partition p3 values less than(30000), + partition p4 values less than(40000), + partition p5 values less than(50000), + partition p6 values less than(60000), + partition p7 values less than(maxvalue) +); +--succeed + +insert into inventory_table_02 values (generate_series(1,100000), random(), generate_series(1,100000)); +--succeed + +vacuum analyze inventory_table_02; + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_item_sk=(select min(inv_item_sk) from inventory_table_02)) where ctid = '(0,1)'; +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_date_sk=(select min(inv_date_sk) from inventory_table_02)) where ctid = '(0,1)'; + +create index global_inventory_table_02_index1 on inventory_table_02(inv_date_sk) global; + +create index global_inventory_table_02_index2 on inventory_table_02(inv_item_sk) global; + +cluster inventory_table_02 using global_inventory_table_02_index2; + +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_item_sk=(select min(inv_item_sk) from inventory_table_02)) where ctid = '(0,1)'; +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_date_sk=(select min(inv_date_sk) from inventory_table_02)) where ctid = '(0,1)'; + +vacuum analyze inventory_table_02; + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + +cluster inventory_table_02 using global_inventory_table_02_index1; + +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_item_sk=(select min(inv_item_sk) from inventory_table_02)) where ctid = '(0,1)'; +select true from (select ctid,inv_date_sk,inv_item_sk from inventory_table_02 where inv_date_sk=(select min(inv_date_sk) from inventory_table_02)) where ctid = '(0,1)'; + +vacuum analyze inventory_table_02; + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.partkey, part.interval, part.boundaries, part.reltuples +from pg_class class, pg_partition part, pg_index ind where class.relname = 'inventory_table_02' and ind.indrelid = class.oid and part.parentid = ind.indexrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.partkey, part.interval, part.boundaries, part.reltuples +from pg_class class, pg_partition part, pg_index ind where class.relname = 'inventory_table_02' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + +cluster inventory_table_02 using global_inventory_table_02_index2; + +\dS+ inventory_table_02; + +--clean +drop index if exists global_inventory_table_02_index1; +drop index if exists global_inventory_table_02_index2; +drop table if exists inventory_table_02; diff --git a/src/test/regress/sql/gpi_cluster_03.sql b/src/test/regress/sql/gpi_cluster_03.sql new file mode 100644 index 0000000000000000000000000000000000000000..38c69d7397d1ed5464643d7e23074c15bd27f96d --- /dev/null +++ b/src/test/regress/sql/gpi_cluster_03.sql @@ -0,0 +1,155 @@ +-- +---- test cluster on global index +-- + +--drop table and index +drop index if exists global_inventory_table_index1; +drop index if exists global_inventory_table_index2; +drop index if exists inventory_table_index1; +drop index if exists inventory_table_index2; +drop table if exists inventory_table; + +CREATE TABLE inventory_table +( + INV_DATE_SK INTEGER NOT NULL, + INV_ITEM_SK INTEGER NOT NULL, + INV_WAREHOUSE_SK INTEGER NOT NULL, + INV_QUANTITY_ON_HAND INTEGER +) +PARTITION BY RANGE(INV_DATE_SK) +( + PARTITION P1 VALUES LESS THAN(10000), + PARTITION P2 VALUES LESS THAN(20000), + PARTITION P3 VALUES LESS THAN(30000), + PARTITION P4 VALUES LESS THAN(40000), + PARTITION P5 VALUES LESS THAN(50000), + PARTITION P6 VALUES LESS THAN(60000), + PARTITION P7 VALUES LESS THAN(MAXVALUE) +); +--succeed + +insert into inventory_table values (generate_series(1,40000), generate_series(1,40000), generate_series(1,40000)); +--succeed + +create index inventory_table_index1 on inventory_table(INV_DATE_SK) local; +--succeed + +create index inventory_table_index2 on inventory_table(INV_DATE_SK) local; +--succeed + +create index global_inventory_table_index1 on inventory_table(INV_ITEM_SK) global; +--succeed + +create index global_inventory_table_index2 on inventory_table(INV_ITEM_SK) global; +--succeed + +\dS+ inventory_table; + +cluster inventory_table using global_inventory_table_index1; + +\dS+ inventory_table; + +cluster inventory_table PARTITION(P3) USING inventory_table_index1; + +\dS+ inventory_table; + +cluster inventory_table using global_inventory_table_index1; + +\dS+ inventory_table; + +alter table inventory_table cluster on inventory_table_index1; + +\dS+ inventory_table; + +alter table inventory_table cluster on global_inventory_table_index1; + +\dS+ inventory_table; + +alter table inventory_table cluster on global_inventory_table_index2; + +\dS+ inventory_table; + +cluster inventory_table; + +\dS+ inventory_table; + +cluster inventory_table using global_inventory_table_index1; + +\dS+ inventory_table; + +cluster inventory_table; + +\dS+ inventory_table; + +SELECT conname FROM pg_constraint WHERE conrelid = 'inventory_table'::regclass +ORDER BY 1; + +SELECT relname, relkind, + EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast +FROM pg_class c WHERE relname = 'inventory_table' ORDER BY relname; + +-- Verify that indisclustered is correctly set +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'inventory_table' + AND indisclustered; + +-- Try changing indisclustered +ALTER TABLE inventory_table CLUSTER ON global_inventory_table_index2; +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'inventory_table' + AND indisclustered; + +-- Try turning off all clustering +ALTER TABLE inventory_table SET WITHOUT cluster; +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'inventory_table' + AND indisclustered; + +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index1; +\parallel off + +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index2; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index2; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table using global_inventory_table_index2; +\parallel off + +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +\parallel off + +\parallel on +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +cluster inventory_table using global_inventory_table_index1; +cluster inventory_table; +\parallel off + +--clean +drop index if exists global_inventory_table_index1; +drop index if exists global_inventory_table_index2; +drop index if exists inventory_table_index1; +drop index if exists inventory_table_index2; +drop table if exists inventory_table; diff --git a/src/test/regress/sql/gpi_create_constraint.sql b/src/test/regress/sql/gpi_create_constraint.sql new file mode 100644 index 0000000000000000000000000000000000000000..ca3362350c71c40087e84ebb7ca9aa60a7103d44 --- /dev/null +++ b/src/test/regress/sql/gpi_create_constraint.sql @@ -0,0 +1,99 @@ + +DROP TABLE if exists gpi_table1; +DROP TABLE if exists gpi_table2; +CREATE TABLE gpi_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_table1 VALUES less than (10000), + partition p1_gpi_table1 VALUES less than (20000), + partition p2_gpi_table1 VALUES less than (30000), + partition p3_gpi_table1 VALUES less than (maxvalue) +); +--ok +INSERT INTO gpi_table1 SELECT r, r, r FROM generate_series(0,10000) AS r; +--ok + +CREATE INDEX ON gpi_table1 (c1) GLOBAL; +--error +CREATE INDEX idx1_gpi_table1 ON gpi_table1 (c1) GLOBAL; +--ok +CREATE INDEX idx2_gpi_table1 ON gpi_table1 (c1) LOCAL; +--error +CREATE INDEX idx3_gpi_table1 ON gpi_table1 (c2) LOCAL; +--ok +CREATE INDEX idx4_gpi_table1 ON gpi_table1 ((c1+10)) LOCAL; +--ok +CREATE INDEX idx5_gpi_table1 ON gpi_table1 (c1, c2) LOCAL; +--ok +CREATE INDEX idx6_gpi_table1 ON gpi_table1 (c1, c2) GLOBAL; +--error +CREATE INDEX idx7_gpi_table1 ON gpi_table1 ((c1+10)) GLOBAL; +--error +CREATE INDEX idx8_gpi_table1 ON gpi_table1 ((c1+10), c2) GLOBAL; +--error +CREATE INDEX idx9_gpi_table1 ON gpi_table1 USING hash (c1, c2) GLOBAL; +--error +CREATE INDEX idx10_gpi_table1 ON gpi_table1 (c1); +--ok +\d gpi_table1; + + +CREATE TABLE gpi_table2 +( + c1 int, + c2 int, + c3 int +) WITH (orientation = column) +partition by range (c1) +( + partition p0_gpi_table2 VALUES less than (10000), + partition p1_gpi_table2 VALUES less than (20000), + partition p2_gpi_table2 VALUES less than (30000), + partition p3_gpi_table2 VALUES less than (maxvalue) +); +--ok +CREATE INDEX idx1_gpi_table2 ON gpi_table2 USING btree (c1) GLOBAL; +--error +CREATE INDEX idx1_gpi_table2 ON gpi_table2 (c1) GLOBAL; +--error +CREATE INDEX idx1_gpi_table2 ON gpi_table2 (c2) GLOBAL; + +drop table gpi_table2; + +CREATE TABLE gpi_table2 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_table2 VALUES less than (10000), + partition p1_gpi_table2 VALUES less than (20000), + partition p2_gpi_table2 VALUES less than (30000), + partition p3_gpi_table2 VALUES less than (maxvalue) +); +--ok +CREATE INDEX idx1_gpi_table2 ON gpi_table2 USING btree (c1) GLOBAL; +--error +CREATE INDEX idx1_gpi_table2 ON gpi_table2 (c1) GLOBAL; +--error +CREATE INDEX idx2_gpi_table2_local_c1 ON gpi_table2 (c1) LOCAL; +--ok +CREATE INDEX idx1_gpi_table2_global_c2 ON gpi_table2 (c2) GLOBAL; + +\d gpi_table2 +alter table gpi_table2 drop column c2; +\d gpi_table2 + +create table gpi_table2_inherits (c4 int) INHERITS (gpi_table2); + +-- error +alter table gpi_table2 set (wait_clean_gpi=y); + +drop table gpi_table2; \ No newline at end of file diff --git a/src/test/regress/sql/gpi_hw_partition_vacuum_full.sql b/src/test/regress/sql/gpi_hw_partition_vacuum_full.sql new file mode 100644 index 0000000000000000000000000000000000000000..d9604d2a47c2082e6fdcc027bc7571529673a26b --- /dev/null +++ b/src/test/regress/sql/gpi_hw_partition_vacuum_full.sql @@ -0,0 +1,63 @@ +-- +-- test vacuum full partition +-- + +--i1. create table +create table hw_partition_vacuum_full_partition_table(id int,name text, city text) +partition by range(id) +( + partition hw_partition_vacuum_full_partition_table_p1 values less than(1000), + partition hw_partition_vacuum_full_partition_table_p2 values less than(2000) +); + +--i2. create btree index +create index inx_part0_id on hw_partition_vacuum_full_partition_table(id) global; + +--i3. insert data +create or replace function insert_part0_data() returns void as $$ +declare + times integer :=1; +begin + loop + insert into hw_partition_vacuum_full_partition_table values(times, 'xian', 'beijing'); + times = times + 1; + if times > 1998 then + exit; + end if; + end loop; +end; +$$ language plpgsql; + +select insert_part0_data(); + +--i4. delete data +delete from hw_partition_vacuum_full_partition_table where id%3=0; +select pg_sleep(2); +select count(*) from hw_partition_vacuum_full_partition_table where id%3=2; + +--i5. vacuum full hw_partition_vacuum_full_partition_table_p1 +analyze hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p1); +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p1'; +vacuum full hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p1); +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p1'; + +--i6. vacuum full hw_partition_vacuum_full_partition_table_p2 +analyze hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p2); +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p2'; +vacuum full hw_partition_vacuum_full_partition_table partition(hw_partition_vacuum_full_partition_table_p2); +select pg_sleep(2); +select count(*) from hw_partition_vacuum_full_partition_table where id%3=2; +select relpages, reltuples from pg_partition where relname='hw_partition_vacuum_full_partition_table_p2'; + +--i7. delete all the data +delete from hw_partition_vacuum_full_partition_table where id%3=1; + +--i8. vacuum full hw_partition_vacuum_full_partition_table +vacuum full hw_partition_vacuum_full_partition_table; +select pg_sleep(2); +select count(*) from hw_partition_vacuum_full_partition_table where id%3=2; +select relpages > 0 as relpagesgtzero, reltuples > 0 as reltuplesgtzero from pg_class where relname='hw_partition_vacuum_full_partition_table'; + +--i9. drop table +drop table hw_partition_vacuum_full_partition_table; + diff --git a/src/test/regress/sql/gpi_hw_partition_vacuum_full_01.sql b/src/test/regress/sql/gpi_hw_partition_vacuum_full_01.sql new file mode 100644 index 0000000000000000000000000000000000000000..61503e8d234557bfca4a1dfa8425d4c33cd58e6d --- /dev/null +++ b/src/test/regress/sql/gpi_hw_partition_vacuum_full_01.sql @@ -0,0 +1,48 @@ +-- +-- test vacuum full partition +-- + +--i1. create table +create table hw_partition_vacuum_full_partition_table(id int,name text, city text) +partition by range(id) +( + partition hw_partition_vacuum_full_partition_table_p1 values less than(1000), + partition hw_partition_vacuum_full_partition_table_p2 values less than(2000) +); + +--i2. create btree index +create index inx_part0_id on hw_partition_vacuum_full_partition_table(id) global; + +--i3. insert data +create or replace function insert_part0_data() returns void as $$ +declare + times integer :=1; +begin + loop + insert into hw_partition_vacuum_full_partition_table values(times, 'xian', 'beijing'); + times = times + 1; + if times > 1998 then + exit; + end if; + end loop; +end; +$$ language plpgsql; + +select insert_part0_data(); + +--i4. delete data + +delete from hw_partition_vacuum_full_partition_table where id%10=1; +\parallel on +vacuum full hw_partition_vacuum_full_partition_table; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +select count(*) from hw_partition_vacuum_full_partition_table where id%10=0; +\parallel off +drop table hw_partition_vacuum_full_partition_table; diff --git a/src/test/regress/sql/gpi_index.sql b/src/test/regress/sql/gpi_index.sql new file mode 100644 index 0000000000000000000000000000000000000000..cd8a6dc4945d7bfdddfec961efea876cf27379ff --- /dev/null +++ b/src/test/regress/sql/gpi_index.sql @@ -0,0 +1,135 @@ +--clean and create data +drop table if exists gpi_index_test; +create table gpi_index_test(a int, b int, c text) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500), partition p4 values less than (maxvalue)); +insert into gpi_index_test select r,r,'gpi_index_test' from generate_series(0,1000) as r; +create index gpi_index_test_global_a on gpi_index_test(a) global; +vacuum analyze gpi_index_test; + +-- select in parition key and with global index +-- use global index +set enable_bitmapscan = off; +explain (costs off) select * from gpi_index_test where a < 200; +explain (costs off) select * from gpi_index_test where a < 200 order by a; +select count(*) from gpi_index_test where a < 200; + +explain (costs off) select avg(b), a from gpi_index_test where a < 10 group by (a) having avg(b) < 100 order by a; +select avg(b), a from gpi_index_test where a < 10 group by (a) having avg(b) < 100 order by a; + +-- multi index with global index +create index gpi_index_test_global_b on gpi_index_test(b) global; +explain (costs off) select * from gpi_index_test where a < 200 and b > 100; +explain (costs off) select * from gpi_index_test where a < 200 and b > 100 order by a; +select * from gpi_index_test where a < 200 and b > 190 order by a; + +explain (costs off) select * from gpi_index_test where a < 200 or b > 100; +explain (costs off) select * from gpi_index_test where a < 200 or b > 100 order by a; +select * from gpi_index_test where a < 200 and b > 190 order by a; + +-- multi column index with global index +create index gpi_index_test_global_a_b on gpi_index_test(a,b) global; +explain (costs off) select * from gpi_index_test where a = 200 and b = 100; +select * from gpi_index_test where a = 100 and b = 100 order by a; + +-- select with local index and global index +drop index gpi_index_test_global_a_b; +drop index gpi_index_test_global_a; +drop index gpi_index_test_global_b; + +create index gpi_index_test_local_a on gpi_index_test(a) local; +create index gpi_index_test_global_b on gpi_index_test(b) global; +vacuum analyze gpi_index_test; + +explain (costs off) select * from gpi_index_test where (a < 200 or a < 300) and b > 100; + +select * from gpi_index_test where a < 200 and b > 190 order by b; + +-- use bitmapscan +reset enable_bitmapscan; +set force_bitmapand = on; +set enable_indexscan = off; +-- use bitmapand local index and global index and use Partition Iterator +explain (costs off) select * from gpi_index_test where a = 800 and b%4 <> 0 and b = 50; +select * from gpi_index_test where a < 800 and b%4 <> 0 and b <= 50; + +-- must use seqscan +explain (costs off) select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); +select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); + +drop index gpi_index_test_global_b; +create index gpi_index_test_local_b on gpi_index_test(b) local; +-- use bitmapor +explain (costs off) select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); +select * from gpi_index_test where a = 200 or (b%4 = 0 and b <= 50); +drop index gpi_index_test_local_b; +reset enable_indexscan; +reset force_bitmapand; + +--two table join use global index scan +drop table if exists gpi_index_test1; +create table gpi_index_test1(a int, b int, c text) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500), partition p4 values less than (maxvalue)); +insert into gpi_index_test1 select r,r,'gpi_index_test' from generate_series(0,1000) as r; +create index gpi_index_test1_global_a on gpi_index_test1(a) global; +vacuum analyze gpi_index_test1; + + +-- single global index +--hash join +explain (costs off) select * from gpi_index_test1 a, gpi_index_test b where a.a = b.a; +select count(*) from gpi_index_test1 a, gpi_index_test b where a.a = b.a; + +-- merge join +explain (costs off) select * from gpi_index_test1 a, gpi_index_test b where a.a = b.a and (a.b < 10 or b.b > 9990) order by a.a; +select * from gpi_index_test1 a, gpi_index_test b where a.a = b.a and (a.b < 10 or b.b > 9990) order by a.a; +select count(*) from gpi_index_test1 a, gpi_index_test b where a.a = b.a and (a.b < 10 or b.b > 10); + +-- nestloop +explain (costs off) select * from gpi_index_test1 a, gpi_index_test b where a.a < b.a and (a.b < 10 or b.b > 9990) order by a.a; +select count(*) from gpi_index_test1 a, gpi_index_test b where a.a < b.a and (a.b < 10 or b.b > 9990); + +create index gpi_index_test_global_b on gpi_index_test(b) global; +explain (costs off) select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a; +select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a; + +explain (costs off) select count(*) from gpi_index_test as t1 NATURAL inner join gpi_index_test1 t2 where t1.b < 200 and t2.a = 50; +select count(*) from gpi_index_test as t1 NATURAL inner join gpi_index_test1 t2 where t1.b < 200 and t2.a = 50; + +explain (costs off) select * from gpi_index_test t1 full join gpi_index_test1 t2 on t1.b >= t2.a where t1.b < 200 and t2.a = 50 and t1.a = 100; +select * from gpi_index_test t1 full join gpi_index_test1 t2 on t1.b >= t2.a where t1.b < 200 and t2.a = 50 and t1.a = 100; + +explain (costs off) select * from gpi_index_test t1 left join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a != 60 or t1.b != 30)) where t1.b < 200 and t2.a < 1000; +select count(*) from gpi_index_test t1 left join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a != 60 or t1.b != 30)) where t1.b < 200 and t2.a < 1000; + +explain (costs off) select * from gpi_index_test t1 right join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a < 60 or t2.b != 30)) where t1.b < 200 and t2.a < 100; +select count(*) from gpi_index_test t1 left join gpi_index_test1 t2 on (t1.b >= t2.a and (t1.a != 60 or t1.b != 30)) where t1.b < 200 and t2.a < 100; + + +--three table join use global index scan +drop table if exists gpi_index_test2; +create table gpi_index_test2(a int, b int, c text) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500), partition p4 values less than (maxvalue)); +insert into gpi_index_test2 select r,r,'gpi_index_test' from generate_series(0,1000) as r; +create index gpi_index_test2_global_b on gpi_index_test2(b) global; +create index gpi_index_test2_global_a on gpi_index_test2(a) global; +vacuum analyze gpi_index_test2; + +explain (costs off) select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b <= 10; +select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b <= 10; + +create index gpi_index_test1_global_b on gpi_index_test1(b) global; +explain (costs off) select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.a <= 100); +select count(*) from (select a from gpi_index_test where gpi_index_test.b < 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.a <= 100); + +insert into gpi_index_test1 values(100, 100, 'test_my'); +insert into gpi_index_test2 values(100, 100, 'test_my'); +insert into gpi_index_test values(100, 100, 'test_my'); +explain (costs off) select * from (select a from gpi_index_test where gpi_index_test.b <= 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.b <= 100 and t3.c = 'test_my'); +select * from (select a from gpi_index_test where gpi_index_test.b <= 100) as t1 inner join gpi_index_test1 t2 on t1.a = t2.a left join gpi_index_test2 t3 on (t1.a = t3.a) where t3.b in (select b from gpi_index_test1 where gpi_index_test1.b <= 100 and t3.c = 'test_my'); + +insert into gpi_index_test1 values(50, 40, 'test_my'); +insert into gpi_index_test values(60, 60, 'test_my'); +explain (costs off) select * from gpi_index_test t1 cross join gpi_index_test1 t2 where t1.a > t2.b and (t1.a + t2.b = 100) and t1.c = 'test_my' order by t1.b; +select * from gpi_index_test t1 cross join gpi_index_test1 t2 where t1.a > t2.b and (t1.a + t2.b = 100) and t1.c = 'test_my' order by t1.b; + +-- clean data +drop table gpi_index_test; +drop table gpi_index_tes1; +drop table gpi_index_test2; diff --git a/src/test/regress/sql/gpi_index_only.sql b/src/test/regress/sql/gpi_index_only.sql new file mode 100644 index 0000000000000000000000000000000000000000..83e5be13294353cef4a66f485399e73e991547e6 --- /dev/null +++ b/src/test/regress/sql/gpi_index_only.sql @@ -0,0 +1,239 @@ +-- +---- test partitioned index +-- +set client_min_messages=error; + +drop table if exists global_part_indexonly_table; + +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(0,50), generate_series(0,50); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table order by c2,c3; +select distinct(c2),c3 from global_part_indexonly_table order by c2,c3; +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; + +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(-50,50), generate_series(-20,30); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; +select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; + +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(-50,50), generate_series(-20,30); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +update global_part_indexonly_table set c1=c1+5; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; +select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; + +create table global_part_indexonly_table +( + c1 int , + c2 int , + c3 int +) +partition by range (c1) +( + partition global_part_indexonly_table_p0 values less than (10), + partition global_part_indexonly_table_p1 values less than (30), + partition global_part_indexonly_table_p2 values less than (maxvalue) +); +create index on global_part_indexonly_table(c1) local; +insert into global_part_indexonly_table select generate_series(0,50), generate_series(-50,50), generate_series(-20,30); +create index test_global_nonpartition_index on global_part_indexonly_table(c2,c3) global; +update global_part_indexonly_table set c2=c2+35; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs false) select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; +select distinct(c2),c3 from global_part_indexonly_table where c2+c3<-10 and c2+c3>-30 and c2=-19 order by c2,c3; +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table global_part_indexonly_table; + +drop table if exists gpi_J1_TBL; +drop table if exists gpi_J2_TBL; +CREATE TABLE gpi_J1_TBL ( + i integer, + j integer, + t integer +) +partition by range (i) +( + partition gpi_J1_TBL_p0 values less than (10), + partition gpi_J1_TBL_p1 values less than (30), + partition gpi_J1_TBL_p2 values less than (maxvalue) +); + +CREATE TABLE gpi_J2_TBL ( + i integer, + k integer, + t integer +) +partition by range (i) +( + partition gpi_J2_TBL_p0 values less than (10000), + partition gpi_J2_TBL_p1 values less than (20000), + partition gpi_J2_TBL_p2 values less than (30000), + partition gpi_J2_TBL_p3 values less than (40000), + partition gpi_J2_TBL_p4 values less than (50000), + partition gpi_J2_TBL_p5 values less than (60000), + partition gpi_J2_TBL_p6 values less than (maxvalue) +); + +create index gpi_J1_TBL_index on gpi_J1_TBL(i) local; +create index gpi_J2_TBL_index on gpi_J2_TBL(i) local; + +INSERT INTO gpi_J1_TBL select generate_series(0,50),generate_series(-10,30),generate_series(-50,40); +INSERT INTO gpi_J2_TBL select r,r,r from generate_series(0,90000) as r; + +create index gpi_J1_TBL_nonp_j_index on gpi_J1_TBL(j) global; +create index gpi_J1_TBL_nonp_t_index on gpi_J1_TBL(t) global; +create index gpi_J2_TBL_nonp_k_index on gpi_J2_TBL(k) global; +create index gpi_J2_TBL_nonp_t_index on gpi_J2_TBL(t) global; + +set enable_bitmapscan=off; +set enable_seqscan=off; + +vacuum analyze gpi_J1_TBL; +vacuum analyze gpi_J2_TBL; + +explain (costs false) SELECT distinct(t1.b), t2.e + FROM gpi_J1_TBL t1 (a, b, c), gpi_J2_TBL t2 (d, e) + WHERE t1.b > t2.e and t1.b = 5 + ORDER BY b, e; +SELECT distinct(t1.b), t2.e + FROM gpi_J1_TBL t1 (a, b, c), gpi_J2_TBL t2 (d, e) + WHERE t1.b > t2.e and t1.b = 5 + ORDER BY b, e; + +explain (costs false) SELECT distinct(j), k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL + WHERE j > k and j = 5 + ORDER BY j, k; +SELECT distinct(j), k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL + WHERE j > k and j = 5 + ORDER BY j, k; + +explain (costs false) SELECT distinct(jj), kk + FROM (gpi_J1_TBL CROSS JOIN gpi_J2_TBL) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; +SELECT distinct(jj), kk + FROM (gpi_J1_TBL CROSS JOIN gpi_J2_TBL) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; + +explain (costs false) SELECT distinct(jj), kk + FROM (gpi_J1_TBL t1 (a, b, c) CROSS JOIN gpi_J2_TBL t2 (d, e)) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; +SELECT distinct(jj), kk + FROM (gpi_J1_TBL t1 (a, b, c) CROSS JOIN gpi_J2_TBL t2 (d, e)) + AS tx (ii, jj, tt, ii2, kk) + WHERE jj > kk and jj = 5 + ORDER BY jj, kk; + +explain (costs false) SELECT distinct(j),a.k,b.k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL a CROSS JOIN gpi_J2_TBL b + WHERE j > a.k and j = 5 and a.k>b.k + ORDER BY j,a.k,b.k; +SELECT distinct(j),a.k,b.k + FROM gpi_J1_TBL CROSS JOIN gpi_J2_TBL a CROSS JOIN gpi_J2_TBL b + WHERE j > a.k and j = 5 and a.k>b.k + ORDER BY j,a.k,b.k; + +explain (costs false) SELECT distinct(t) + FROM gpi_J1_TBL INNER JOIN gpi_J2_TBL USING (t) + WHERE t>0 and t<5 + ORDER BY t; +SELECT distinct(t) + FROM gpi_J1_TBL INNER JOIN gpi_J2_TBL USING (t) + WHERE t>0 and t<5 + ORDER BY t; + +explain (costs false) SELECT distinct(b) + FROM gpi_J1_TBL t1 (a, b, c) NATURAL JOIN gpi_J2_TBL t2 (e, b, d) + ORDER BY b; +SELECT distinct(b) + FROM gpi_J1_TBL t1 (a, b, c) NATURAL JOIN gpi_J2_TBL t2 (e, b, d) + ORDER BY b; + +explain (costs false) SELECT distinct(j),k + FROM gpi_J1_TBL JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; +SELECT distinct(j),k + FROM gpi_J1_TBL JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; + +explain (costs false) SELECT distinct(j),k + FROM gpi_J1_TBL LEFT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; +SELECT distinct(j),k + FROM gpi_J1_TBL LEFT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + ORDER BY j,k; + +explain (costs false) SELECT distinct(j),k + FROM gpi_J1_TBL RIGHT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + WHERE k>25 and k<35 + ORDER BY j,k; +SELECT distinct(j),k + FROM gpi_J1_TBL RIGHT OUTER JOIN gpi_J2_TBL ON (gpi_J1_TBL.j = gpi_J2_TBL.k) + WHERE k>25 and k<35 + ORDER BY j,k; + +drop table if exists gpi_J1_TBL; +drop table if exists gpi_J2_TBL; +set client_min_messages=notice; diff --git a/src/test/regress/sql/gpi_interval.sql b/src/test/regress/sql/gpi_interval.sql new file mode 100644 index 0000000000000000000000000000000000000000..382be75ea599ac9546f7fa328eaed0f52408bf72 --- /dev/null +++ b/src/test/regress/sql/gpi_interval.sql @@ -0,0 +1,288 @@ +-- +---- test interval partitioned global index +-- + +--drop table and index +drop index if exists ip_index_global; +drop index if exists ip_index_local; +drop table if exists gpi_partition_global_index_interval; +drop index if exists index_interval_partition_table_001; +drop table if exists interval_partition_table_001; +drop table if exists interval_partition_table_002; +drop table if exists interval_partition_table_003; +drop table if exists interval_partition_table_004; + +create table gpi_partition_global_index_interval +( + c1 int, + c2 int, + logdate date not null +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION gpi_partition_global_index_interval_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION gpi_partition_global_index_interval_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION gpi_partition_global_index_interval_p2 VALUES LESS THAN ('2020-05-01') +); +--succeed + +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + +create index ip_index_global on gpi_partition_global_index_interval(c1) global; +--succeed + +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + +create index ip_index_local on gpi_partition_global_index_interval(c2) local; +--succeed + +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + +--insert into table +insert into gpi_partition_global_index_interval values(7,2,'2020-03-01'); +insert into gpi_partition_global_index_interval values(3,1,'2020-04-01'); +insert into gpi_partition_global_index_interval values(5,3,'2020-05-01'); +insert into gpi_partition_global_index_interval values(7,5,'2020-06-01'); +insert into gpi_partition_global_index_interval values(1,4,'2020-07-01'); +--succeed + +explain (costs off, nodes off) select * from gpi_partition_global_index_interval where c1 = 7 order by 1, 2; +select * from gpi_partition_global_index_interval where c1 = 7 order by 1, 2; + +--to select all index object +select part.relname, part.parttype, part.rangenum, part.intervalnum, part.partstrategy, part.relallvisible, part.reltoastrelid, part.partkey, part.interval, part.boundaries +from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid +order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; + +select count(*) from pg_class class, pg_partition part, pg_index ind where class.relname = 'gpi_partition_global_index_interval' and ind.indrelid = class.oid and part.parentid = ind.indrelid; + +-- +---- test input for unique index +-- +create table interval_partition_table_001 +( + c1 int, + logdate date not null +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_001_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_001_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_001_p2 VALUES LESS THAN ('2020-05-01') +); + +create unique index index_interval_partition_table_001 on interval_partition_table_001(logdate) global; +--fail: unique index +insert into interval_partition_table_001 values(10, '2020-06-01'); +insert into interval_partition_table_001 values(10, '2020-06-01'); + +-- +---- test with primary key +-- +create table interval_partition_table_002 +( + c1 int, + c2 int, + logdate date not null, + CONSTRAINT interval_partition_table_CONSTRAINT PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_002_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_002_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_002_p2 VALUES LESS THAN ('2020-05-01') +); + +insert into interval_partition_table_002 values(10, 10, '2020-06-01'); +insert into interval_partition_table_002 values(10, 10, '2020-06-01'); +analyze interval_partition_table_002; + +-- +---- test with btree index +-- +create table interval_partition_table_003 +( + c1 int, + c2 int, + logdate date not null, + PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_003_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_003_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_003_p2 VALUES LESS THAN ('2020-05-01') +); + +create index interval_partition_table_003_1 ON interval_partition_table_003 USING BTREE (logdate) global; +create index interval_partition_table_003_2 ON interval_partition_table_003 USING BTREE (c2) global; +create index interval_partition_table_003_3 ON interval_partition_table_003 USING BTREE (c1) global; + +select relname from pg_class where relname like '%interval_partition_table_003%' order by 1; + +select relname, parttype, partstrategy, boundaries from pg_partition +where parentid = (select oid from pg_class where relname = 'interval_partition_table_003') +order by relname; + +insert into interval_partition_table_003 values(1,2,'2020-03-01'); +insert into interval_partition_table_003 values(1,2,'2020-04-01'); +insert into interval_partition_table_003 values(1,2,'2020-05-01'); +insert into interval_partition_table_003 values(1,2,'2020-06-01'); +insert into interval_partition_table_003 values(1,2,'2020-07-01'); + +alter table interval_partition_table_003 drop column C2; +insert into interval_partition_table_003 values(1,2,'2020-07-01'); +insert into interval_partition_table_003 values(1,'2020-07-01'); + +select relname from pg_class where relname like '%interval_partition_table_003%' order by 1; + +select relname, parttype, partstrategy, boundaries from pg_partition +where parentid = (select oid from pg_class where relname = 'interval_partition_table_003') +order by relname; + +-- +---- test partition BTREE index +-- +create table interval_partition_table_004 +( + c1 int, + c2 int, + logdate date not null, + PRIMARY KEY(c2,logdate) +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION interval_partition_table_004_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_partition_table_004_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_partition_table_004_p2 VALUES LESS THAN ('2020-05-01') +); + +-- expression index +CREATE INDEX interval_partition_table_004_index_01 on interval_partition_table_004 using btree(c2) global; +CREATE INDEX interval_partition_table_004_index_02 on interval_partition_table_004 using btree(logdate) global; +CREATE INDEX interval_partition_table_004_index_03 on interval_partition_table_004 using btree(c1) global; + +cluster interval_partition_table_004 using interval_partition_table_004_index_01; + +insert into interval_partition_table_004 values(7,1,'2020-03-01'); +insert into interval_partition_table_004 values(3,2,'2020-04-01'); +insert into interval_partition_table_004 values(5,3,'2020-05-01'); +insert into interval_partition_table_004 values(7,4,'2020-06-01'); +insert into interval_partition_table_004 values(1,5,'2020-07-01'); +insert into interval_partition_table_004 values(7,2,'2020-03-01'); +insert into interval_partition_table_004 values(3,3,'2020-04-01'); +insert into interval_partition_table_004 values(5,4,'2020-05-01'); +insert into interval_partition_table_004 values(7,5,'2020-06-01'); +insert into interval_partition_table_004 values(1,1,'2020-07-01'); +insert into interval_partition_table_004 values(7,3,'2020-03-01'); +insert into interval_partition_table_004 values(3,4,'2020-04-01'); +insert into interval_partition_table_004 values(5,5,'2020-05-01'); +insert into interval_partition_table_004 values(7,1,'2020-06-01'); +insert into interval_partition_table_004 values(1,2,'2020-07-01'); +insert into interval_partition_table_004 values(7,4,'2020-03-01'); +insert into interval_partition_table_004 values(3,5,'2020-04-01'); +insert into interval_partition_table_004 values(5,1,'2020-05-01'); +insert into interval_partition_table_004 values(7,2,'2020-06-01'); +insert into interval_partition_table_004 values(1,3,'2020-07-01'); + +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +delete from interval_partition_table_004 where c1 = 1; + +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +update interval_partition_table_004 set c1 = 100 where c1 = 7; + +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 100; + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 100; + +update interval_partition_table_004 set c1 = 7 where c1 = 100; + +reindex table interval_partition_table_004; + +cluster; + +reindex table interval_partition_table_004; + +cluster; + +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +start transaction; +insert into interval_partition_table_004 values (generate_series(1,10), generate_series(1,10), generate_series(TO_DATE('2000-01-01', 'YYYY-MM-DD'),TO_DATE('2019-12-01', 'YYYY-MM-DD'),'1 day')); +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; +commit; +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +delete from interval_partition_table_004; + +\parallel on +insert into interval_partition_table_004 values (generate_series(1,10), generate_series(1,10), generate_series(TO_DATE('1990-01-01', 'YYYY-MM-DD'),TO_DATE('2020-12-01', 'YYYY-MM-DD'),'1 day')); +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +select true from (SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7) where count = 0 or count = 11293; +\parallel off + +SELECT COUNT(*) FROM interval_partition_table_004 where c1 = 7; + +\parallel on +DELETE from interval_partition_table_004 where c1 = 1; +VACUUM analyze interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 2; +VACUUM interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 3; +ANALYZE interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 4; +VACUUM analyze interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 5; +VACUUM interval_partition_table_004; +DELETE from interval_partition_table_004 where c1 = 6; +ANALYZE interval_partition_table_004; +\parallel off + +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; +SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; +VACUUM full interval_partition_table_004; +set enable_seqscan = off; +explain (costs off, nodes off) SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; +SELECT COUNT(*) FROM interval_partition_table_004 where c1 <= 7; +reset enable_seqscan; + +--drop table and index +drop index if exists ip_index_global; +drop index if exists ip_index_local; +drop table if exists gpi_partition_global_index_interval; +drop index if exists index_interval_partition_table_001; +drop table if exists interval_partition_table_001; +drop table if exists interval_partition_table_002; +drop table if exists interval_partition_table_003; +drop table if exists interval_partition_table_004; diff --git a/src/test/regress/sql/gpi_invalid_part.sql b/src/test/regress/sql/gpi_invalid_part.sql new file mode 100644 index 0000000000000000000000000000000000000000..16389b5be59f9fb5a082e2a522bce223580c8ffb --- /dev/null +++ b/src/test/regress/sql/gpi_invalid_part.sql @@ -0,0 +1,174 @@ +drop table if exists gpi_test_invalid_part; +create table gpi_test_invalid_part(a int, b int,c int) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500)); +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' order by b.relname; +insert into gpi_test_invalid_part select r,r,100 from generate_series(0,400) as r; +create index global_index_gpi_test_invalid_part_b on gpi_test_invalid_part (b) global; +create index local_index_gpi_test_invalid_part_c on gpi_test_invalid_part (c) local; +vacuum analyze gpi_test_invalid_part; +--Scenario 1: abort one create partition +start transaction; +alter table gpi_test_invalid_part add partition p6 values less than (900); +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; +insert into gpi_test_invalid_part select r,r,100 from generate_series(500,800) as r; +--p6/gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; +abort; + +--gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + +start transaction read only; +set enable_show_any_tuples = on; +set enable_indexscan = off; +set enable_bitmapscan = off; +--p6 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p6' and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; +commit; + +reset enable_indexscan; +reset enable_bitmapscan; +reset enable_show_any_tuples; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; +--skip vacuum full +vacuum full pg_partition; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + +--Scenario 2: abort one create partition in sub transaction +start transaction; +alter table gpi_test_invalid_part add partition p7 values less than (900); +savepoint s1; +alter table gpi_test_invalid_part add partition p8 values less than (1000); +insert into gpi_test_invalid_part select r,r,100 from generate_series(500,950) as r; +-- gpi_test_invalid_part and p7/p8 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; +rollback to s1; +commit; + +--gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_invalid_part' order by b.relname; + +start transaction read only; +set enable_show_any_tuples = on; +set enable_indexscan = off; +set enable_bitmapscan = off; +--p7/p8 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and (a.relname = 'p7' or a.relname = 'p8') and b.relname = 'gpi_test_invalid_part' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; +commit; + +reset enable_indexscan; +reset enable_bitmapscan; +reset enable_show_any_tuples; + +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; +--skip vacuum full +vacuum full pg_partition; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + + +--Scenario 3: drop one create partition +start transaction; +alter table gpi_test_invalid_part add partition p9 values less than (1000); +insert into gpi_test_invalid_part select r,r,100 from generate_series(950,990) as r; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; +alter table gpi_test_invalid_part drop partition p9; +--p9 not exists +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; +commit; + +-- gpi_test_invalid_part reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + +start transaction read only; +set enable_show_any_tuples = on; +set enable_indexscan = off; +set enable_bitmapscan = off; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; +commit; + +reset enable_indexscan; +reset enable_bitmapscan; +reset enable_show_any_tuples; +--use seqscan +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--drop partition set global index unusable +reindex index global_index_gpi_test_invalid_part_b; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; +--skip vacuum full +vacuum full pg_partition; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select nothing +select count(*) from gpi_test_invalid_part where b > 700; + +--Scenario 4: create one partition, sub transaction drop and rollback +start transaction; +alter table gpi_test_invalid_part add partition p9 values less than (1000); +insert into gpi_test_invalid_part select r,r,100 from generate_series(950,990) as r; +savepoint s1; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; +alter table gpi_test_invalid_part drop partition p9; +--p9 not exists +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; +rollback to s1; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p9' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; +commit; + +-- gpi_test_invalid_part and p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_invalid_part' order by b.relname; + +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select insert 41 rows +select count(*) from gpi_test_invalid_part where b > 700; +--skip vacuum full +vacuum full pg_partition; +explain (costs off) select count(*) from gpi_test_invalid_part where b > 700; +--select insert 41 rows +select count(*) from gpi_test_invalid_part where b > 700; + + +--Scenario 5: create partition, sub transaction create one partition, rollback; +drop table if exists gpi_test_create_invalid; +start transaction; +create table gpi_test_create_invalid(a int, b int,c int) partition by range(a) (partition p1 values less than (100), partition p2 values less than (200), partition p3 values less than (500)); +insert into gpi_test_create_invalid select r,r,100 from generate_series(0,400) as r; +--gpi_test_create_invalid have wait_clean_gpi=n +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_create_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; +create index global_gpi_test_create_invalid_b on gpi_test_create_invalid (b) global; +create index local_gpi_test_create_invalid_c on gpi_test_create_invalid (c) local; +savepoint s1; +alter table gpi_test_create_invalid add partition p6 values less than (800); +insert into gpi_test_create_invalid select r,r,100 from generate_series(500,790) as r; +--p9 reloptions have "wait_clean_gpi=y" +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parttype = 'p' and a.relname = 'p6' and a.reloptions[3] like '%wait_clean_gpi=y%' and b.relname = 'gpi_test_create_invalid' order by b.relname; +rollback to s1; +commit; + +--gpi_test_create_invalid have wait_clean_gpi=y +select a.relname,a.parttype,a.reloptions from pg_partition a, pg_class b where a.parentid = b.oid and b.relname = 'gpi_test_create_invalid' and a.reloptions[3] like '%wait_clean_gpi=y%' order by b.relname; + +vacuum analyze gpi_test_create_invalid; +explain (costs off) select count(*) from gpi_test_create_invalid where b > 700; +--select nothing +select count(*) from gpi_test_create_invalid where b > 700; +----skip vacuum full +vacuum full pg_partition; +explain (costs off) select count(*) from gpi_test_create_invalid where b > 700; +--select nothing +select count(*) from gpi_test_create_invalid where b > 700; + +--clean data +drop table if exists gpi_test_invalid_part; +drop table if exists gpi_test_create_invalid; diff --git a/src/test/regress/sql/gpi_pwj.sql b/src/test/regress/sql/gpi_pwj.sql new file mode 100644 index 0000000000000000000000000000000000000000..6108b9a171ea7e96e2265609514a746b0fedcc94 --- /dev/null +++ b/src/test/regress/sql/gpi_pwj.sql @@ -0,0 +1,45 @@ + +DROP TABLE if exists gpi_pwj_table1; +DROP TABLE if exists gpi_pwj_table2; +CREATE TABLE gpi_pwj_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_pwj_table1 VALUES less than (10000), + partition p1_gpi_pwj_table1 VALUES less than (20000), + partition p2_gpi_pwj_table1 VALUES less than (30000), + partition p3_gpi_pwj_table1 VALUES less than (maxvalue) +); +CREATE TABLE gpi_pwj_table2 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_pwj_table2 VALUES less than (10000), + partition p1_gpi_pwj_table2 VALUES less than (20000), + partition p2_gpi_pwj_table2 VALUES less than (30000), + partition p3_gpi_pwj_table2 VALUES less than (maxvalue) +); + +INSERT INTO gpi_pwj_table1 SELECT r, r, r FROM generate_series(0,40000) AS r; +INSERT INTO gpi_pwj_table2 SELECT r, r, r FROM generate_series(0,40000) AS r; + +CREATE INDEX idx1_gpi_pwj_table1 ON gpi_pwj_table1 (c1) GLOBAL; +CREATE INDEX idx1_gpi_pwj_table2 ON gpi_pwj_table2 (c2) GLOBAL; + +explain (costs off, nodes off) +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; +VACUUM analyze; +SET enable_partitionwise=ON; +explain (costs off, nodes off) +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; +SELECT count(*) FROM gpi_pwj_table1, gpi_pwj_table2 WHERE gpi_pwj_table1.c1 = gpi_pwj_table2.c1 AND gpi_pwj_table1.c2 < 15000; +SET enable_partitionwise=OFF; \ No newline at end of file diff --git a/src/test/regress/sql/gpi_rebuild_index.sql b/src/test/regress/sql/gpi_rebuild_index.sql new file mode 100644 index 0000000000000000000000000000000000000000..952e2e0c6bc99164ace55b904060b30f5b8211d6 --- /dev/null +++ b/src/test/regress/sql/gpi_rebuild_index.sql @@ -0,0 +1,276 @@ +-- +---- test reindex global index +-- + +--drop table and index +drop index if exists global_partition_reindex_table_ind1; +drop index if exists global_partition_reindex_table_ind2; +drop index if exists global_partition_reindex_table_ind3; +drop index if exists partition_reindex_table_ind1; +drop index if exists partition_reindex_table_ind2; +drop table if exists partition_reindex_table; + +drop index if exists partition_reindex_internal_table_ind1; +drop index if exists partition_reindex_internal_table_ind2; +drop index if exists global_partition_reindex_internal_table_ind1; +drop table if exists partition_reindex_internal_table; + +--prepare table and index +create table partition_reindex_table +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_partition_reindex_table values less than (10000), + partition p1_partition_reindex_table values less than (20000), + partition p2_partition_reindex_table values less than (30000), + partition p3_partition_reindex_table values less than (40000), + partition p4_partition_reindex_table values less than (50000), + partition p5_partition_reindex_table values less than (MAXVALUE) +); +--succeed + +create index partition_reindex_table_ind1 on partition_reindex_table(c1) local; +--succeed + +insert into partition_reindex_table values (generate_series(1,40000), generate_series(1,40000), generate_series(1,40000)); +--succeed + +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + +create unique index global_partition_reindex_table_ind1 on partition_reindex_table(c1) global; + +drop index partition_reindex_table_ind1; + +create index partition_reindex_table_ind1 on partition_reindex_table(c2) local; +--succeed + +create index partition_reindex_table_ind2 on partition_reindex_table(c2) local ( + PARTITION p0_partition_reindex_table, + PARTITION p1_partition_reindex_table, + PARTITION p2_partition_reindex_table, + PARTITION p3_partition_reindex_table, + PARTITION p4_partition_reindex_table, + PARTITION p5_partition_reindex_table +); +--succeed + +create unique index global_partition_reindex_table_ind1 on partition_reindex_table(c1) global; + +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + +reindex index global_partition_reindex_table_ind1; +--succeed + +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; +--the plan before reindex and after reindex should be same + +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + +alter index global_partition_reindex_table_ind1 rebuild; +--succeed + +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; +--the plan before reindex and after reindex should be same + +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + +reindex table partition_reindex_table; +--succeed + +explain (costs off, nodes off) select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; +--the plan before reindex and after reindex should be same + +select * from partition_reindex_table where c1 >= 19998 and c1 <= 20002 order by 1; + +select c.relname,c.relpages > 0 as relpagesgtzero, c.reltuples > 0 as reltuplesgtzero,i.indisunique, i.indisvalid, i.indcheckxmin, i.indisready from pg_index i, pg_class c where c.relname = 'global_partition_reindex_table_ind1' and c.oid = i.indexrelid; + +select class.relname, class.reltuples, class.parttype from pg_class class, pg_index ind where class.relname = 'global_partition_reindex_table_ind1' and ind.indexrelid = class.oid; + +drop index global_partition_reindex_table_ind1; +--succeed + +reindex index global_partition_reindex_table_ind1; + +alter index global_partition_reindex_table_ind1 rebuild; + +reindex index global_partition_reindex_table_ind1 partition p0_partition_reindex_table; + +alter index global_partition_reindex_table_ind1 rebuild partition p1_partition_reindex_table; + +create unique index global_partition_reindex_table_ind1 on partition_reindex_table(c1) global; +--succeed + +reindex index global_partition_reindex_table_ind1 partition p0_partition_reindex_table; + +alter index global_partition_reindex_table_ind1 rebuild partition p1_partition_reindex_table; + +create index global_partition_reindex_table_ind1 on partition_reindex_table using btree(c1) global; + +create index global_partition_reindex_table_ind2 on partition_reindex_table using btree(c1) global; +--succeed + +create index global_partition_reindex_table_ind3 on partition_reindex_table using btree(c1) global; +--succeed + +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1 partition p0_partition_reindex_table; +reindex table partition_reindex_table; +reindex internal table partition_reindex_table; +reindex table partition_reindex_table partition p0_partition_reindex_table; +reindex internal table partition_reindex_table partition p0_partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; + +create table partition_reindex_internal_table( + c_id varchar, + c_w_id integer, + c_date date, + partial cluster key(c_id,c_w_id) +) with (orientation = column, max_batchrow = 30700, compression = middle) +partition by range (c_date, c_w_id) +( + PARTITION p0_partition_reindex_internal_table values less than ('20170331',5), + PARTITION p1_partition_reindex_internal_table values less than ('20170731',450), + PARTITION p2_partition_reindex_internal_table values less than ('20170930',1062), + PARTITION p3_partition_reindex_internal_table values less than ('20171231',1765), + PARTITION p4_partition_reindex_internal_table values less than ('20180331',2024), + PARTITION p5_partition_reindex_internal_table values less than ('20180731',2384), + PARTITION p6_partition_reindex_internal_table values less than ('20180930',2786), + PARTITION p7_partition_reindex_internal_table values less than (maxvalue,maxvalue) +); + +insert into partition_reindex_internal_table values('gauss1',4,'20170301'); +insert into partition_reindex_internal_table values('gauss2',400,'20170625'); +insert into partition_reindex_internal_table values('gauss3',480,'20170920'); +insert into partition_reindex_internal_table values('gauss4',1065,'20170920'); +insert into partition_reindex_internal_table values('gauss5',1800,'20170920'); +insert into partition_reindex_internal_table values('gauss6',2030,'20170920'); +insert into partition_reindex_internal_table values('gauss7',2385,'20170920'); +insert into partition_reindex_internal_table values('gauss8',2789,'20191020'); +insert into partition_reindex_internal_table values('gauss9',2789,'20171020'); + +create index partition_reindex_internal_table_ind1 on partition_reindex_internal_table using btree(c_w_id) LOCAL; +create index partition_reindex_internal_table_ind2 on partition_reindex_internal_table using btree(c_w_id) LOCAL ( + PARTITION p0_partition_reindex_internal_table, + PARTITION p1_partition_reindex_internal_table, + PARTITION p2_partition_reindex_internal_table, + PARTITION p3_partition_reindex_internal_table, + PARTITION p4_partition_reindex_internal_table, + PARTITION p5_partition_reindex_internal_table, + PARTITION p6_partition_reindex_internal_table, + PARTITION p7_partition_reindex_internal_table +); + +create index global_partition_reindex_internal_table_ind1 on partition_reindex_internal_table using btree(c_id) global; + +reindex index global_partition_reindex_internal_table_ind1; +reindex index global_partition_reindex_internal_table_ind1 partition p0_partition_reindex_internal_table; +reindex table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex table partition_reindex_internal_table partition p0_partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table partition p0_partition_reindex_internal_table; +alter index global_partition_reindex_internal_table_ind1 rebuild; + +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +\parallel off + +\parallel on +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +\parallel off + +\parallel on +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +reindex internal table partition_reindex_internal_table; +\parallel off + +\parallel on +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +\parallel off + +\parallel on +alter index global_partition_reindex_table_ind1 rebuild; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +alter index global_partition_reindex_table_ind1 rebuild; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +\parallel off + +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +reindex index global_partition_reindex_table_ind1; +reindex table partition_reindex_table; +alter index global_partition_reindex_table_ind1 rebuild; +\parallel off + +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind2; +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind3; +\parallel off + +\parallel on +reindex index global_partition_reindex_table_ind1; +reindex index global_partition_reindex_table_ind2; +reindex index global_partition_reindex_table_ind3; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +reindex table partition_reindex_table; +\parallel off + +--clean +drop index if exists global_partition_reindex_table_ind1; +drop index if exists global_partition_reindex_table_ind2; +drop index if exists global_partition_reindex_table_ind3; +drop index if exists partition_reindex_table_ind1; +drop index if exists partition_reindex_table_ind2; +drop table if exists partition_reindex_table; + +drop index if exists partition_reindex_internal_table_ind1; +drop index if exists partition_reindex_internal_table_ind2; +drop index if exists global_partition_reindex_internal_table_ind1; +drop table if exists partition_reindex_internal_table; diff --git a/src/test/regress/sql/gpi_set_index_unusable.sql b/src/test/regress/sql/gpi_set_index_unusable.sql new file mode 100644 index 0000000000000000000000000000000000000000..feb94b8e8d3fae0da6838f85d2fbcca8156064fe --- /dev/null +++ b/src/test/regress/sql/gpi_set_index_unusable.sql @@ -0,0 +1,226 @@ +-- MERGE PARTITIONS +set client_min_messages=error; +drop table if exists test_merge_llt; +create table test_merge_llt (a int, b int) +partition by range (a) +( +partition test_merge_llt_p1 values less than (10), +partition test_merge_llt_p2 values less than (20), +partition test_merge_llt_p3 values less than (30), +partition test_merge_llt_p4 values less than (maxvalue) +); +create index test_merge_llt_idx on test_merge_llt(a) global; +create index test_merge_llt_idx_local on test_merge_llt(b) local; +insert into test_merge_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_merge_llt') order by 2; +vacuum analyze test_merge_llt; +-- indexscan +explain (costs false) select * from test_merge_llt where a=40; +-- 1 rows +select * from test_merge_llt where a=40; + +alter table test_merge_llt merge partitions test_merge_llt_p1, test_merge_llt_p2 into partition test_merge_llt_px; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_merge_llt') order by 2; +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_merge_llt'::regclass ORDER BY c.relname; +set enable_bitmapscan=off; +set enable_seqscan=off; +-- seqscan +explain (costs false) select * from test_merge_llt where a=40; +select * from test_merge_llt where a=40; + +reindex index test_merge_llt_idx; +-- indexscan +explain (costs false) select * from test_merge_llt where a=40; +select * from test_merge_llt where a=40; +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table if exists test_merge_llt; + +-- End. Clean up + +---truncate partition table with index +set client_min_messages=error; +drop table if exists test_truncate_llt; +create table test_truncate_llt (a int, b int) +partition by range (a) +( +partition test_truncate_llt_p1 values less than (10), +partition test_truncate_llt_p2 values less than (20), +partition test_truncate_llt_p3 values less than (30), +partition test_truncate_llt_p4 values less than (maxvalue) +); + +create index test_truncate_llt_idx on test_truncate_llt(a) global; +insert into test_truncate_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_truncate_llt') order by 2; +vacuum analyze test_truncate_llt; +-- indexscan +explain (costs false) select * from test_truncate_llt where a=40; +select * from test_truncate_llt where a=40; + +alter table test_truncate_llt truncate partition test_truncate_llt_p3; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_truncate_llt') order by 2; +-- test_truncate_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_truncate_llt'::regclass ORDER BY c.relname; +set enable_bitmapscan=off; +set enable_seqscan=off; +-- seqscan +explain (costs false) select * from test_truncate_llt where a = 40; +select * from test_truncate_llt where a = 40; + +alter index test_truncate_llt_idx rebuild; +-- test_truncate_llt_idx usable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_truncate_llt'::regclass ORDER BY c.relname; + +set enable_bitmapscan=on; +set enable_seqscan=on; +-- indexscan +explain (costs false) select * from test_truncate_llt where a=40; +select * from test_truncate_llt where a=40; +drop table if exists test_truncate_llt; + +-- End. Clean up + +--exchange partition +set client_min_messages=error; +drop table if exists test_exchange_llt; +drop table if exists test_ord; +create table test_exchange_llt (a int, b int) +partition by range (a) +( +partition test_exchange_llt_p1 values less than (10), +partition test_exchange_llt_p2 values less than (20), +partition test_exchange_llt_p3 values less than (30), +partition test_exchange_llt_p4 values less than (maxvalue) +); +create index test_exchange_llt_idx on test_exchange_llt(a) global; +insert into test_exchange_llt select generate_series(0,1000), 100; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_exchange_llt') order by 2; + +create table test_ord (a int, b int); +insert into test_ord select 13, generate_series(0,1000); +vacuum analyze test_exchange_llt; +-- indexscan +explain (costs false) select * from test_exchange_llt where a=40; +select * from test_exchange_llt where a=40; + +-- exchange +alter table test_exchange_llt exchange partition (test_exchange_llt_p2) with table test_ord with validation; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_exchange_llt') order by 2; +-- test_exchange_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_exchange_llt'::regclass ORDER BY c.relname; + +set enable_bitmapscan=off; +set enable_seqscan=off; +--seqcan +explain (costs false) select * from test_exchange_llt where a=40; +select * from test_exchange_llt where a=40; + +-- rebuild +reindex table test_exchange_llt; +-- test_exchange_llt_idx usable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_exchange_llt'::regclass ORDER BY c.relname; + +set enable_bitmapscan=on; +set enable_seqscan=on; +-- indexscan +explain (costs false) select * from test_exchange_llt where a=40; +select * from test_exchange_llt where a=40; + +drop table if exists test_exchange_llt; +drop table if exists test_ord; + +-- End. Clean up + +--split partition +set client_min_messages=error; +drop table if exists test_split_llt; +create table if not exists test_split_llt (a int, b int) +partition by range(a) +( +partition test_split_llt_p1 values less than(10), +partition test_split_llt_p2 values less than(20), +partition test_split_llt_p3 values less than(30), +partition test_split_llt_p4 values less than(maxvalue) +); +create index test_split_llt_idx1 on test_split_llt(a) global; +insert into test_split_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_split_llt') order by 2; +vacuum analyze test_split_llt; +-- indexscan +explain (costs false) select * from test_split_llt where a=40; +select * from test_split_llt where a=40; + +alter table test_split_llt + merge partitions test_split_llt_p1, test_split_llt_p2 + into partition test_split_llt_p1_2; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_split_llt') order by 2; + +alter table test_split_llt + split partition test_split_llt_p1_2 + into (partition test_split_llt_p1 values less than (10), partition test_split_llt_p2 values less than (20)); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_split_llt') order by 2; +-- test_split_llt_idx1 unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_split_llt'::regclass ORDER BY c.relname; + +set enable_bitmapscan=off; +set enable_seqscan=off; +--seqscan +explain (costs false) select * from test_split_llt where a=40; +select * from test_split_llt where a=40; +set enable_bitmapscan=on; +set enable_seqscan=on; + +reindex database postgres; +-- test_split_llt_idx1 unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_split_llt'::regclass ORDER BY c.relname; +--seqscan +explain (costs false) select * from test_split_llt where a=40; +select * from test_split_llt where a=40; +drop table if exists test_split_llt; + +-- End. Clean up + +--drop partition +set client_min_messages=error; +drop table if exists test_drop_llt; +create table test_drop_llt (a int, b int) +partition by range (a) +( +partition test_drop_llt_p1 values less than (10), +partition test_drop_llt_p2 values less than (20), +partition test_drop_llt_p3 values less than (30), +partition test_drop_llt_p4 values less than (maxvalue) +); +create index test_drop_llt_idx on test_drop_llt(a) global; +insert into test_drop_llt select generate_series(0,1000), generate_series(0,1000); +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_drop_llt') order by 2; +vacuum analyze test_drop_llt; +--indexscan +explain (costs false) select * from test_drop_llt where a=40; +select * from test_drop_llt where a=40; + +alter table test_drop_llt drop partition test_drop_llt_p1; +select relname, boundaries from pg_partition where parentid in (select oid from pg_class where relname = 'test_drop_llt') order by 2; +-- test_drop_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_drop_llt'::regclass ORDER BY c.relname; + +set enable_bitmapscan=off; +set enable_seqscan=off; +--seqscan +explain (costs false) select * from test_drop_llt where a=40; +select * from test_drop_llt where a=40; + +vacuum full test_drop_llt; +-- test_drop_llt_idx unusable +select c.relname, i.indisusable from pg_index i join pg_class c on i.indexrelid = c.oid where i.indrelid = 'test_drop_llt'::regclass ORDER BY c.relname; + +--seqscan +explain (costs false) select * from test_drop_llt where a=40; +select * from test_drop_llt where a=40; + +set enable_bitmapscan=on; +set enable_seqscan=on; +drop table if exists test_drop_llt; + +-- End. Clean up diff --git a/src/test/regress/sql/gpi_unique_check.sql b/src/test/regress/sql/gpi_unique_check.sql new file mode 100644 index 0000000000000000000000000000000000000000..1e3304ec4b3190a807789604aca8c99fb881f15e --- /dev/null +++ b/src/test/regress/sql/gpi_unique_check.sql @@ -0,0 +1,27 @@ +DROP TABLE if exists gpi_uniquecheck_table1; +CREATE TABLE gpi_uniquecheck_table1 +( + c1 int, + c2 int, + c3 int +) +partition by range (c1) +( + partition p0_gpi_uniquecheck_table1 VALUES less than (10000), + partition p1_gpi_uniquecheck_table1 VALUES less than (20000), + partition p2_gpi_uniquecheck_table1 VALUES less than (30000), + partition p3_gpi_uniquecheck_table1 VALUES less than (maxvalue) +); + +INSERT INTO gpi_uniquecheck_table1 SELECT r, r, r FROM generate_series(0,40000) AS r; + +CREATE UNIQUE INDEX idx1_gpi_uniquecheck_table1 ON gpi_uniquecheck_table1 (c2) GLOBAL; + +INSERT INTO gpi_uniquecheck_table1 VALUES (9999,1,1); +--error +INSERT INTO gpi_uniquecheck_table1 VALUES (10000,1,1); +--error +INSERT INTO gpi_uniquecheck_table1 VALUES (10001,1,1); +--error +INSERT INTO gpi_uniquecheck_table1 VALUES (10001,40001,1); +--ok diff --git a/src/test/regress/sql/gpi_vacuum_lazy.sql b/src/test/regress/sql/gpi_vacuum_lazy.sql new file mode 100644 index 0000000000000000000000000000000000000000..80f60eac847716f0fbe5a47edd4f802246a874a6 --- /dev/null +++ b/src/test/regress/sql/gpi_vacuum_lazy.sql @@ -0,0 +1,201 @@ +--drop table and index +drop index if exists global_index_test_b; +drop index if exists local_index_test_c; +drop index if exists global_index_test_d; +drop table if exists test_vacuum_lazy; + +--prepare table and index +create table test_vacuum_lazy(a int, b int, c int, d int) partition by range(a) (partition p1 values less than (10000), partition p2 values less than (20000), partition p3 values less than (30001)); +create unique index global_index_test_a on test_vacuum_lazy (a) global; +create index global_index_test_b on test_vacuum_lazy (b) global; +create index local_index_test_c on test_vacuum_lazy (c) local (partition c_index1, partition c_index2, partition c_index3); +create index global_index_test_d on test_vacuum_lazy(d) global; + +--one thread +insert into test_vacuum_lazy select r,r,r,10000 from generate_series(0,9999) as r; +insert into test_vacuum_lazy select r,r,r,20000 from generate_series(10000,19999) as r; +insert into test_vacuum_lazy select r,r,r,30000 from generate_series(20000,29999) as r; +select count(*) from test_vacuum_lazy; +vacuum analyze test_vacuum_lazy; +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; +select count(*) from test_vacuum_lazy where d = 20000; + +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where c < 20000; +select count(*) from test_vacuum_lazy where c < 20000; +set enable_bitmapscan=on; +set enable_seqscan=on; + +delete from test_vacuum_lazy where d = 10000; + +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; +select count(*) from test_vacuum_lazy where d = 20000; + +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where c < 20000; +select count(*) from test_vacuum_lazy where c < 20000; +set enable_bitmapscan=on; +set enable_seqscan=on; + +start transaction; +alter table test_vacuum_lazy add partition p6 values less than (40001); +insert into test_vacuum_lazy select r,r,r,100 from generate_series(30000,39999) as r; +abort; +vacuum analyze test_vacuum_lazy; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; +select count(*) from test_vacuum_lazy where d = 20000; +explain (costs off) select count(*) from test_vacuum_lazy where c < 20000; +select count(*) from test_vacuum_lazy where c < 20000; +set enable_bitmapscan=on; +set enable_seqscan=on; + +select count(*) from test_vacuum_lazy; +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 10000; +vacuum test_vacuum_lazy; +delete from test_vacuum_lazy where d = 30000; +analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy; + +--multi thread +\parallel on +insert into test_vacuum_lazy select r,r,r,5000 from generate_series(0,4999) as r; +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,10000 from generate_series(5000,9999) as r; +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,15000 from generate_series(10000,14999) as r; +analyze test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,20000 from generate_series(15000,19999) as r; +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,25000 from generate_series(20000,24999) as r; +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy select r,r,r,30000 from generate_series(25000,29999) as r; +analyze test_vacuum_lazy; +\parallel off +select count(*) from test_vacuum_lazy; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where d = 20000; +select count(*) from test_vacuum_lazy where d = 20000; +set enable_bitmapscan=on; +set enable_seqscan=on; + +\parallel on +delete from test_vacuum_lazy where d = 5000; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 10000; +vacuum test_vacuum_lazy; +delete from test_vacuum_lazy where d = 15000; +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 20000; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where d = 25000; +vacuum test_vacuum_lazy; +delete from test_vacuum_lazy where d = 30000; +analyze test_vacuum_lazy; +\parallel off +select * from test_vacuum_lazy; + +\parallel on +insert into test_vacuum_lazy values (0, 0, 0, 999); +insert into test_vacuum_lazy select r,r,r,5000 from generate_series(1,4998) as r; +insert into test_vacuum_lazy values (4999, 4999, 4999, 999); +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy values (5000, 5000, 5000, 999); +insert into test_vacuum_lazy select r,r,r,10000 from generate_series(5001,9998) as r; +insert into test_vacuum_lazy values (9999, 9999, 9999, 999); +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy values (10000, 10000, 10000, 999); +insert into test_vacuum_lazy select r,r,r,15000 from generate_series(10001,14998) as r; +insert into test_vacuum_lazy values (14999, 14999, 14999, 999); +analyze test_vacuum_lazy; +insert into test_vacuum_lazy values (15000, 15000, 15000, 999); +insert into test_vacuum_lazy select r,r,r,20000 from generate_series(15001,19998) as r; +insert into test_vacuum_lazy values (19999, 19999, 19999, 999); +vacuum analyze test_vacuum_lazy; +insert into test_vacuum_lazy values (20000, 20000, 20000, 999); +insert into test_vacuum_lazy select r,r,r,25000 from generate_series(20001,24998) as r; +insert into test_vacuum_lazy values (24999, 24999, 24999, 999); +vacuum test_vacuum_lazy; +insert into test_vacuum_lazy values (25000, 25000, 25000, 999); +insert into test_vacuum_lazy select r,r,r,30000 from generate_series(25001,29998) as r; +insert into test_vacuum_lazy values (29999, 29999, 29999, 999); +analyze test_vacuum_lazy; +\parallel off +select count(*) from test_vacuum_lazy; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select count(*) from test_vacuum_lazy where b > 10 and b < 20000; +select count(*) from test_vacuum_lazy where b > 10 and b < 20000; +explain (costs off) select count(*) from test_vacuum_lazy where d = 999 order by 1; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +set enable_bitmapscan=on; +set enable_seqscan=on; + +\parallel on +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum analyze test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +vacuum test_vacuum_lazy; +select count(*) from test_vacuum_lazy where d = 999 order by 1; +analyze test_vacuum_lazy; +\parallel off + +alter table test_vacuum_lazy disable row movement; +\parallel on +delete from test_vacuum_lazy where b % 7 = 0 and d <> 999; +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 5 = 0 and d <> 999; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 3 = 0 and d <> 999; +vacuum test_vacuum_lazy; +\parallel off +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; +select * from test_vacuum_lazy where d = 999 order by 1; +set enable_bitmapscan=on; +set enable_seqscan=on; + +\parallel on +delete from test_vacuum_lazy where b % 19 = 0 and d <> 999; +analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 17 = 0 and d <> 999; +vacuum analyze test_vacuum_lazy; +delete from test_vacuum_lazy where b % 13 = 0 and d <> 999; +vacuum test_vacuum_lazy; +\parallel off +alter table test_vacuum_lazy enable row movement; +set enable_bitmapscan=off; +set enable_seqscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; +select * from test_vacuum_lazy where d = 999 order by 1; +set enable_bitmapscan=on; +set enable_seqscan=on; +set enable_indexscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; +select * from test_vacuum_lazy where d = 999 order by 1; +set enable_indexscan=on; +set enable_bitmapscan=off; +set enable_indexscan=off; +explain (costs off) select * from test_vacuum_lazy where d = 999 order by 1; +select * from test_vacuum_lazy where d = 999 order by 1; +set enable_indexscan=on; +set enable_bitmapscan=on; + +--clean table and index +drop index if exists global_index_test_b; +drop index if exists local_index_test_c; +drop index if exists global_index_test_d; +drop table if exists test_vacuum_lazy; + diff --git a/src/test/regress/sql/hw_partition_index.sql b/src/test/regress/sql/hw_partition_index.sql index 46d53fc8c0ba4eb6f98f93be3f05710a67cef1a9..e1a18d964da70e8275e718699301a84e671e96ae 100644 --- a/src/test/regress/sql/hw_partition_index.sql +++ b/src/test/regress/sql/hw_partition_index.sql @@ -173,9 +173,8 @@ create unique index on hw_partition_index_rp (c1) local ); --fail wrong syntax -create unique index on hw_partition_index_rp (c1); - ---fail wrong syntax +create unique index global_index on hw_partition_index_rp (c1); +--fail drop table hw_partition_index_rp; --unique index , index para must contain partition key diff --git a/src/test/regress/sql/hw_partition_interval_index.sql b/src/test/regress/sql/hw_partition_interval_index.sql index 7941bfbd6c390fcfff47de45c5488d016363a17a..074d944d16ea07fea4dc285a5d481ff7683ed535 100644 --- a/src/test/regress/sql/hw_partition_interval_index.sql +++ b/src/test/regress/sql/hw_partition_interval_index.sql @@ -117,9 +117,8 @@ create unique index on hw_partition_index_ip (logdate) local partition sip4_index_local tablespace PG_DEFAULT ); -create unique index on hw_partition_index_ip (logdate); ---fail wrong syntax - +create unique index global_internal_index on hw_partition_index_ip (logdate); +--fail drop table hw_partition_index_ip; --unique index , index para must contain partition key