diff --git a/src/bin/pg_dump/pg_dump.cpp b/src/bin/pg_dump/pg_dump.cpp index 4dbd1b5e758bd3dc52277a9d87f68ac3f69873d7..aa7efceffe7327f596e7e756ce06da0b8c1be105 100644 --- a/src/bin/pg_dump/pg_dump.cpp +++ b/src/bin/pg_dump/pg_dump.cpp @@ -2526,6 +2526,10 @@ static void makeTableDataInfo(TableInfo* tbinfo, bool boids) if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && no_unlogged_table_data) return; + /* Don't dump data in global temp table/sequence */ + if (tbinfo->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) + return; + /* Check that the data is not explicitly excluded */ if (simple_oid_list_member(&tabledata_exclude_oids, tbinfo->dobj.catId.oid)) return; @@ -16035,9 +16039,17 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo) if (tbinfo->parttype == PARTTYPE_PARTITIONED_RELATION) { appendPQExpBuffer(q, "CREATE %s %s", reltypename, fmtId(tbinfo->dobj.name)); } else { + const char *tableType = nullptr; + if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED) { + tableType = "UNLOGGED "; + } else if (tbinfo->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { + tableType = "GLOBAL TEMPORARY "; + } else { + tableType = ""; + } appendPQExpBuffer(q, "CREATE %s%s %s", - tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ? "UNLOGGED " : "", + tableType, reltypename, fmtId(tbinfo->dobj.name)); } @@ -16715,7 +16727,9 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo) * attislocal correctly, plus fix up any inherited CHECK constraints. * Analogously, we set up typed tables using ALTER TABLE / OF here. */ - if (binary_upgrade && ((tbinfo->relkind == RELKIND_RELATION) || (tbinfo->relkind == RELKIND_FOREIGN_TABLE))) { + if (binary_upgrade && + ((tbinfo->relkind == RELKIND_RELATION) || (tbinfo->relkind == RELKIND_FOREIGN_TABLE)) && + tbinfo->relpersistence != RELPERSISTENCE_GLOBAL_TEMP) { for (j = 0; j < tbinfo->numatts; j++) { if (tbinfo->attisdropped[j]) { appendPQExpBuffer(q, "\n-- For binary upgrade, recreate dropped column.\n"); diff --git a/src/common/backend/catalog/Makefile b/src/common/backend/catalog/Makefile index 3a106b5cf050853cf52c66de90e8b6ffd5d801b4..c8fd21ccc1bbaf8400ca0e9f77bcffadd32e51df 100644 --- a/src/common/backend/catalog/Makefile +++ b/src/common/backend/catalog/Makefile @@ -21,7 +21,8 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \ pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o pg_object.o\ pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o pg_synonym.o\ - pg_type.o pgxc_class.o storage.o toasting.o pg_job.o pg_partition.o pg_hashbucket.o cstore_ctlg.o dfsstore_ctlg.o pg_builtin_proc.o + pg_type.o pgxc_class.o storage_gtt.o storage.o toasting.o pg_job.o pg_partition.o \ + pg_hashbucket.o cstore_ctlg.o dfsstore_ctlg.o pg_builtin_proc.o BKIFILES = postgres.bki postgres.description postgres.shdescription diff --git a/src/common/backend/catalog/builtin_funcs.ini b/src/common/backend/catalog/builtin_funcs.ini index a256028a7f56a501990651de9cf8c122a48501c2..26f59aefde8caf6fc55da23d1f1b37a1b01084ba 100644 --- a/src/common/backend/catalog/builtin_funcs.ini +++ b/src/common/backend/catalog/builtin_funcs.ini @@ -6358,6 +6358,14 @@ "pg_get_functiondef", 1, AddBuiltinFunc(_0(2098), _1("pg_get_functiondef"), _2(1), _3(true), _4(false), _5(pg_get_functiondef), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('s'), _18(0), _19(1, 26), _20(3, 26, 23, 25), _21(3, 'i', 'o', 'o'), _22(3, "funcid", "headerlines", "definition"), _23(NULL), _24("pg_get_functiondef"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) ), + AddFuncGroup( + "pg_get_gtt_relstats", 1, + AddBuiltinFunc(_0(3596), _1("pg_get_gtt_relstats"), _2(1), _3(true), _4(true), _5(pg_get_gtt_relstats), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(1, 26), _20(7, 26, 26, 23, 700, 23, 28, 28), _21(7, 'i', 'o', 'o', 'o', 'o', 'o', 'o'), _22(7, "relid", "relfilenode", "relpages", "reltuples", "relallvisible", "relfrozenxid", "relminmxid"), _23(NULL), _24("pg_get_gtt_relstats"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) + ), + AddFuncGroup( + "pg_get_gtt_statistics", 1, + AddBuiltinFunc(_0(3597), _1("pg_get_gtt_statistics"), _2(3), _3(false), _4(true), _5(pg_get_gtt_statistics), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(3, 26, 23, 2283), _20(32, 26, 23, 2283, 26, 18, 21, 16, 700, 23, 700, 21, 21, 21, 21, 21, 26, 26, 26, 26, 26, 1021, 1021, 1021, 1021, 1021, 2277, 2277, 2277, 2277, 2277, 700, 25), _21(32, 'i', 'i', 'i', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _22(32, "relid", "att", "x", "starelid", "starelkind", "staattnum", "stainherit", "stanullfrac", "stawidth", "stadistinct", "stakind1", "stakind2", "stakind3", "stakind4", "stakind5", "staop1", "staop2", "staop3", "staop4", "staop5", "stanumbers1", "stanumbers2", "stanumbers3", "stanumbers4", "stanumbers5", "stavalues1", "stavalues2", "stavalues3", "stavalues4", "stavalues5", "stadndistinct", "staextinfo"), _23(NULL), _24("pg_get_gtt_statistics"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) + ), AddFuncGroup( "pg_get_indexdef", 2, AddBuiltinFunc(_0(1643), _1("pg_get_indexdef"), _2(1), _3(true), _4(false), _5(pg_get_indexdef), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('s'), _18(0), _19(1, 26), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("pg_get_indexdef"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)), @@ -6417,6 +6425,10 @@ "pg_get_xidlimit", 1, AddBuiltinFunc(_0(2000), _1("pg_get_xidlimit"), _2(0), _3(true), _4(true), _5(pg_get_xidlimit), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1000), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(0), _20(7, 28, 28, 28, 28, 28, 28, 26), _21(7, 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _22(7, "nextXid", "oldestXid", "xidVacLimit", "xidWarnLimit", "xidStopLimit", "xidWrapLimit", "oldestXidDB"), _23(NULL), _24("pg_get_xidlimit"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) ), + AddFuncGroup( + "pg_gtt_attached_pid", 1, + AddBuiltinFunc(_0(3598), _1("pg_gtt_attached_pid"), _2(1), _3(true), _4(true), _5(pg_gtt_attached_pid), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(1, 26), _20(3, 26, 26, 20), _21(3, 'i', 'o', 'o'), _22(3, "relid", "relid", "pid"), _23(NULL), _24("pg_gtt_attached_pid"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) + ), AddFuncGroup( "pg_has_role", 6, AddBuiltinFunc(_0(2705), _1("pg_has_role"), _2(3), _3(true), _4(false), _5(pg_has_role_name_name), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('s'), _18(0), _19(3, 19, 19, 25), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("pg_has_role_name_name"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)), @@ -6454,6 +6466,10 @@ "pg_last_xlog_replay_location", 1, AddBuiltinFunc(_0(3821), _1("pg_last_xlog_replay_location"), _2(0), _3(true), _4(false), _5(pg_last_xlog_replay_location), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(0), _20(2, 25, 25), _21(2, 'o', 'o'), _22(2, "term", "lsn"), _23(NULL), _24("pg_last_xlog_replay_location"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) ), + AddFuncGroup( + "pg_list_gtt_relfrozenxids", 1, + AddBuiltinFunc(_0(3599), _1("pg_list_gtt_relfrozenxids"), _2(0), _3(true), _4(true), _5(pg_list_gtt_relfrozenxids), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(0), _20(2, 20, 28), _21(2, 'o', 'o'), _22(2, "pid", "relfrozenxid"), _23(NULL), _24("pg_list_gtt_relfrozenxids"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) + ), AddFuncGroup( "pg_listening_channels", 1, AddBuiltinFunc(_0(3035), _1("pg_listening_channels"), _2(0), _3(true), _4(true), _5(pg_listening_channels), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('s'), _18(0), _19(0), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("pg_listening_channels"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) diff --git a/src/common/backend/catalog/catalog.cpp b/src/common/backend/catalog/catalog.cpp index 60f8b04a35a3eafbdb98ea5e5fd05276513b77ab..635d7cc408d72779e5c4f3edfcbed31ffb44d324 100755 --- a/src/common/backend/catalog/catalog.cpp +++ b/src/common/backend/catalog/catalog.cpp @@ -1049,9 +1049,22 @@ Oid GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence) char* rpath = NULL; int fd; bool collides = false; + BackendId backend; + + switch (relpersistence) { + case RELPERSISTENCE_GLOBAL_TEMP: + backend = t_thrd.proc_cxt.MyBackendId; + break; + case RELPERSISTENCE_TEMP: + case RELPERSISTENCE_UNLOGGED: + case RELPERSISTENCE_PERMANENT: + backend = InvalidBackendId; + break; + default: + elog(ERROR, "invalid relpersistence: %c", relpersistence); + return InvalidOid; /* placate compiler */ + } - //@Temp Table. we now use same storage as unlogged table for temp table, - // so backendID is no need. /* This logic should match relation_init_physical_addr */ rnode.node.spcNode = ConvertToRelfilenodeTblspcOid(reltablespace); rnode.node.dbNode = (rnode.node.spcNode == GLOBALTABLESPACE_OID) ? InvalidOid : u_sess->proc_cxt.MyDatabaseId; @@ -1061,7 +1074,7 @@ Oid GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence) * that properly here to make sure that any collisions based on filename * are properly detected. */ - rnode.backend = InvalidBackendId; + rnode.backend = backend; do { CHECK_FOR_INTERRUPTS(); diff --git a/src/common/backend/catalog/heap.cpp b/src/common/backend/catalog/heap.cpp index 0e7bcaca3825a14419c03696023691f781f0db8d..6db766d9352baee0437c9354dd8e72a5439d90ba 100755 --- a/src/common/backend/catalog/heap.cpp +++ b/src/common/backend/catalog/heap.cpp @@ -63,6 +63,7 @@ #include "catalog/pg_type_fn.h" #include "catalog/storage.h" #include "catalog/storage_xlog.h" +#include "catalog/storage_gtt.h" #include "commands/tablecmds.h" #include "commands/tablespace.h" #include "commands/typecmds.h" @@ -110,7 +111,8 @@ #endif static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, Oid new_type_oid, - Oid reloftype, Oid relowner, char relkind, Datum relacl, Datum reloptions, int2vector* bucketcol, bool ispartrel); + Oid reloftype, Oid relowner, char relkind, char relpersistence, Datum relacl, Datum reloptions, + int2vector* bucketcol, bool ispartrel); static oidvector* BuildIntervalTablespace(const IntervalPartitionDefState* intervalPartDef); static void deletePartitionTuple(Oid part_id); static void addNewPartitionTuplesForPartition(Relation pg_partition_rel, Oid relid, List *filenodelist, Oid reltablespace, @@ -406,7 +408,8 @@ Form_pg_attribute SystemAttributeByName(const char* attname, bool relhasoids) */ Relation heap_create(const char* relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid relfilenode, Oid bucketOid, TupleDesc tupDesc, char relkind, char relpersistence, bool partitioned_relation, bool rowMovement, - bool shared_relation, bool mapped_relation, bool allow_system_table_mods, int8 row_compress, Oid ownerid) + bool shared_relation, bool mapped_relation, bool allow_system_table_mods, int8 row_compress, Oid ownerid, + bool skip_create_storage) { bool create_storage = false; Relation rel; @@ -512,6 +515,9 @@ Relation heap_create(const char* relname, Oid relnamespace, Oid reltablespace, O if (u_sess->attr.attr_common.IsInplaceUpgrade && !u_sess->upg_cxt.new_catalog_need_storage) create_storage = false; + if (skip_create_storage) { + create_storage = false; + } /* * Have the storage manager create the relation's disk file, if needed. * @@ -520,7 +526,7 @@ Relation heap_create(const char* relname, Oid relnamespace, Oid reltablespace, O */ if (create_storage) { RelationOpenSmgr(rel); - RelationCreateStorage(rel->rd_node, relpersistence, ownerid, bucketOid); + RelationCreateStorage(rel->rd_node, relpersistence, ownerid, bucketOid, rel); } if (RelationUsesSpaceType(rel->rd_rel->relpersistence) == SP_TEMP) { @@ -1008,7 +1014,8 @@ void InsertPgClassTuple( * -------------------------------- */ static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, Oid new_type_oid, - Oid reloftype, Oid relowner, char relkind, Datum relacl, Datum reloptions, int2vector* bucketcol, bool ispartrel) + Oid reloftype, Oid relowner, char relkind, char relpersistence, Datum relacl, Datum reloptions, + int2vector* bucketcol, bool ispartrel) { Form_pg_class new_rel_reltup; @@ -1040,6 +1047,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, O new_rel_reltup->relallvisible = 0; break; } + /* Initialize relfrozenxid */ if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE) { /* @@ -1057,6 +1065,12 @@ static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, O */ new_rel_reltup->relfrozenxid = (ShortTransactionId)InvalidTransactionId; } + + /* global temp table not remember transaction info in catalog */ + if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { + new_rel_reltup->relfrozenxid = (ShortTransactionId)InvalidTransactionId; + } + new_rel_reltup->relowner = relowner; new_rel_reltup->reltype = new_type_oid; new_rel_reltup->reloftype = reloftype; @@ -1928,7 +1942,8 @@ Oid heap_create_with_catalog(const char* relname, Oid relnamespace, Oid reltable mapped_relation, allow_system_table_mods, row_compress, - ownerid); + ownerid, + false); /* Recode the table or other object in pg_class create time. */ PgObjectType objectType = GetPgObjectTypePgClass(relkind); @@ -2068,6 +2083,7 @@ Oid heap_create_with_catalog(const char* relname, Oid relnamespace, Oid reltable reloftypeid, ownerid, relkind, + relpersistence, PointerGetDatum(relacl), reloptions, bucketcol, @@ -2701,6 +2717,15 @@ void heap_drop_with_catalog(Oid relid) heapDropPartitionTable(rel); } + /* We allow to drop global temp table only this session use it */ + if (RELATION_IS_GLOBAL_TEMP(rel)) { + if (is_other_backend_use_gtt(RelationGetRelid(rel))) + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop global temporary table %s when other backend attached it.", + RelationGetRelationName(rel)))); + } + /* * When dropping temp objects, we need further check whether current datanode is * suffered from an unclean shutdown without gsql reconnect, as in this case the @@ -3603,7 +3628,7 @@ void RemoveStatistics(Oid relid, AttrNumber attnum) * The routine will truncate and then reconstruct the indexes on * the specified relation. Caller must hold exclusive lock on rel. */ -static void RelationTruncateIndexes(Relation heapRelation) +static void RelationTruncateIndexes(Relation heapRelation, LOCKMODE lockmode) { ListCell* indlist = NULL; @@ -3614,7 +3639,7 @@ static void RelationTruncateIndexes(Relation heapRelation) IndexInfo* indexInfo = NULL; /* Open the index relation; use exclusive lock, just to be sure */ - currentIndex = index_open(indexId, AccessExclusiveLock); + currentIndex = index_open(indexId, lockmode); /* Fetch info needed for index_build */ indexInfo = BuildIndexInfo(currentIndex); @@ -3629,7 +3654,7 @@ static void RelationTruncateIndexes(Relation heapRelation) } /* truncate psort relation */ if (unlikely(currentIndex->rd_rel->relam == PSORT_AM_OID)) { - Relation psort_rel = heap_open(currentIndex->rd_rel->relcudescrelid, AccessExclusiveLock); + Relation psort_rel = heap_open(currentIndex->rd_rel->relcudescrelid, lockmode); heap_truncate_one_rel(psort_rel); heap_close(psort_rel, NoLock); } @@ -3661,8 +3686,14 @@ void heap_truncate(List* relids) foreach (cell, relids) { Oid rid = lfirst_oid(cell); Relation rel; + LOCKMODE lockmode = AccessExclusiveLock; + + /* truncate global temp table only need RowExclusiveLock */ + if (get_rel_persistence(rid) == RELPERSISTENCE_GLOBAL_TEMP) { + lockmode = RowExclusiveLock; + } - rel = heap_open(rid, AccessExclusiveLock); + rel = heap_open(rid, lockmode); relations = lappend(relations, rel); } @@ -3725,7 +3756,7 @@ static void heap_truncate_one_rel_for_bucket(Relation rel, Partition part) if (OidIsValid(toastOid)) { toastBucketRel = bucketGetRelation(rel, NULL, bucketlist->values[i]); RelationTruncate(toastBucketRel, 0); - RelationTruncateIndexes(toastBucketRel); + RelationTruncateIndexes(toastBucketRel, AccessExclusiveLock); bucketCloseRelation(toastBucketRel); } } @@ -3747,6 +3778,17 @@ static void heap_truncate_one_rel_for_bucket(Relation rel, Partition part) void heap_truncate_one_rel(Relation rel) { Oid toastrelid; + LOCKMODE lockmode = AccessExclusiveLock; + + if (RELATION_IS_GLOBAL_TEMP(rel)) { + if (!gtt_storage_attached(RelationGetRelid(rel))) + return; + + /* + * Truncate global temp table only need RowExclusiveLock + */ + lockmode = RowExclusiveLock; + } if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(RelationGetRelid(rel))) { FdwRoutine* fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(rel)); @@ -3767,12 +3809,12 @@ void heap_truncate_one_rel(Relation rel) /* If the relation is a cloumn store */ if (RelationIsColStore(rel)) { /* cudesc */ - Relation cudesc_rel = heap_open(rel->rd_rel->relcudescrelid, AccessExclusiveLock); + Relation cudesc_rel = heap_open(rel->rd_rel->relcudescrelid, lockmode); heap_truncate_one_rel(cudesc_rel); heap_close(cudesc_rel, NoLock); /* delta */ - Relation delta_rel = heap_open(rel->rd_rel->reldeltarelid, AccessExclusiveLock); + Relation delta_rel = heap_open(rel->rd_rel->reldeltarelid, lockmode); heap_truncate_one_rel(delta_rel); heap_close(delta_rel, NoLock); @@ -3783,16 +3825,16 @@ void heap_truncate_one_rel(Relation rel) /* If there is a toast table, truncate that too */ toastrelid = rel->rd_rel->reltoastrelid; if (OidIsValid(toastrelid)) { - Relation toastrel = heap_open(toastrelid, AccessExclusiveLock); + Relation toastrel = heap_open(toastrelid, lockmode); RelationTruncate(toastrel, 0); - RelationTruncateIndexes(toastrel); + RelationTruncateIndexes(toastrel, lockmode); /* keep the lock... */ heap_close(toastrel, NoLock); } } /* If the relation has indexes, truncate the indexes too */ - RelationTruncateIndexes(rel); + RelationTruncateIndexes(rel, lockmode); } else /* partitioned table */ { List* partOidList = NIL; @@ -3806,7 +3848,7 @@ void heap_truncate_one_rel(Relation rel) /* truncate each partition */ partOidList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_PARTITION, rel->rd_id); foreach (partCell, partOidList) { - Partition p = partitionOpen(rel, HeapTupleGetOid((HeapTuple)lfirst(partCell)), AccessExclusiveLock); + Partition p = partitionOpen(rel, HeapTupleGetOid((HeapTuple)lfirst(partCell)), lockmode); /* * two levels and in dn @@ -3831,7 +3873,7 @@ void heap_truncate_one_rel(Relation rel) ListCell* cell1 = NULL; IndexInfo* indexInfo = NULL; Oid indexId = lfirst_oid(indCell); - currentIndex = index_open(indexId, AccessExclusiveLock); + currentIndex = index_open(indexId, lockmode); indexInfo = BuildIndexInfo(currentIndex); @@ -3839,7 +3881,7 @@ void heap_truncate_one_rel(Relation rel) foreach (cell1, currentParttiionIndexList) { Partition indexPart = - partitionOpen(currentIndex, HeapTupleGetOid((HeapTuple)lfirst(cell1)), AccessExclusiveLock); + partitionOpen(currentIndex, HeapTupleGetOid((HeapTuple)lfirst(cell1)), lockmode); Partition p; if (RELATION_OWN_BUCKET(currentIndex)) { @@ -3849,7 +3891,7 @@ void heap_truncate_one_rel(Relation rel) } /* truncate psort relation */ if (unlikely(currentIndex->rd_rel->relam == PSORT_AM_OID)) { - Relation psort_rel = heap_open(currentIndex->rd_rel->relcudescrelid, AccessExclusiveLock); + Relation psort_rel = heap_open(currentIndex->rd_rel->relcudescrelid, lockmode); heap_truncate_one_rel(psort_rel); heap_close(psort_rel, NoLock); } @@ -3873,10 +3915,10 @@ void heap_truncate_one_rel(Relation rel) Form_pg_partition partForm = (Form_pg_partition)GETSTRUCT(tup); if (partForm->reltoastrelid != InvalidOid) { - Relation toastrel = heap_open(partForm->reltoastrelid, AccessExclusiveLock); + Relation toastrel = heap_open(partForm->reltoastrelid, lockmode); RelationTruncate(toastrel, 0); - RelationTruncateIndexes(toastrel); + RelationTruncateIndexes(toastrel, lockmode); /* keep the lock... */ heap_close(toastrel, NoLock); } @@ -3885,6 +3927,11 @@ void heap_truncate_one_rel(Relation rel) freePartList(partOidList); } + + // for GTT + if (RELATION_IS_GLOBAL_TEMP(rel)) { + up_gtt_relstats(rel, 0, 0, 0, u_sess->utils_cxt.RecentXmin); + } } /* diff --git a/src/common/backend/catalog/index.cpp b/src/common/backend/catalog/index.cpp index b410dca9ad5b3ce80619e7a551f8ccad316c7fa6..d642a72b19c9dddcd5c7036c1198d4dce061c96d 100755 --- a/src/common/backend/catalog/index.cpp +++ b/src/common/backend/catalog/index.cpp @@ -21,6 +21,7 @@ */ #include "postgres.h" #include "knl/knl_variable.h" +#include "access/multixact.h" #include "access/reloptions.h" #include "access/relscan.h" #include "access/sysattr.h" @@ -43,6 +44,7 @@ #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" #include "catalog/storage.h" +#include "catalog/storage_gtt.h" #include "commands/tablecmds.h" #include "commands/trigger.h" #include "commands/vacuum.h" @@ -689,6 +691,11 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index int i; char relpersistence; Oid psortRelationId = InvalidOid; + bool skip_create_storage = false; + + if (RELATION_IS_GLOBAL_TEMP(heapRelation) && !gtt_storage_attached(RelationGetRelid(heapRelation))) { + skip_create_storage = true; + } is_exclusion = (indexInfo->ii_ExclusionOps != NULL); @@ -766,6 +773,20 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index if (get_relname_relid(indexRelationName, namespaceId)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("relation \"%s\" already exists", indexRelationName))); + + if (RELATION_IS_GLOBAL_TEMP(heapRelation)) + { + /* No support create index on global temp table use concurrent mode yet */ + if (concurrent) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot reindex global temporary tables concurrently"))); + + /* if global temp table not init storage, then skip build index */ + if (!gtt_storage_attached(RelationGetRelid(heapRelation))) { + skip_build = true; + } + } /* * construct tuple descriptor for index tuples @@ -828,7 +849,8 @@ Oid index_create(Relation heapRelation, const char* indexRelationName, Oid index mapped_relation, allow_system_table_mods, REL_CMPRS_NOT_SUPPORT, - heapRelation->rd_rel->relowner); + heapRelation->rd_rel->relowner, + skip_create_storage); Assert(indexRelationId == RelationGetRelid(indexRelation)); @@ -1413,6 +1435,15 @@ void index_drop(Oid indexId, bool concurrent) VirtualTransactionId* old_lockholders = NULL; List* partIndexlist = NIL; + /* + * A temporary relation uses a non-concurrent DROP. Other backends can't + * access a temporary relation, so there's no harm in grabbing a stronger + * lock (see comments in RemoveRelations), and a non-concurrent DROP is + * more efficient. + */ + Assert(!(get_rel_persistence(indexId) == RELPERSISTENCE_TEMP || + get_rel_persistence(indexId) == RELPERSISTENCE_GLOBAL_TEMP) || + (!concurrent)); /* * To drop an index safely, we must grab exclusive lock on its parent @@ -1443,6 +1474,14 @@ void index_drop(Oid indexId, bool concurrent) */ CheckTableNotInUse(userIndexRelation, "DROP INDEX"); + /* We allow to drop index on global temp table only this session use it */ + if (RELATION_IS_GLOBAL_TEMP(userHeapRelation)) { + if (is_other_backend_use_gtt(RelationGetRelid(userHeapRelation))) { + elog(ERROR, + "can not drop index %s when other backend attached this global temp table.", + RelationGetRelationName(userHeapRelation)); + } + } /* * Drop Index Concurrently is more or less the reverse process of Create * Index Concurrently. @@ -1760,36 +1799,69 @@ IndexInfo* BuildIndexInfo(Relation index) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("invalid indnatts %d for index %u", numKeys, RelationGetRelid(index)))); - ii->ii_NumIndexAttrs = numKeys; - for (i = 0; i < numKeys; i++) - ii->ii_KeyAttrNumbers[i] = indexStruct->indkey.values[i]; - - /* fetch any expressions needed for expressional indexes */ - ii->ii_Expressions = RelationGetIndexExpressions(index); - ii->ii_ExpressionsState = NIL; - /* fetch index predicate if any */ - ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii = makeIndexInfo(indexStruct->indnatts, + RelationGetIndexExpressions(index), + RelationGetIndexPredicate(index), + indexStruct->indisunique, + IndexIsReady(indexStruct), + false); + /* fill in attribute numbers */ + for (i = 0; i < numKeys; i++) { + ii->ii_KeyAttrNumbers[i] = indexStruct->indkey.values[i]; + } /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) { RelationGetExclusionInfo(index, &ii->ii_ExclusionOps, &ii->ii_ExclusionProcs, &ii->ii_ExclusionStrats); - } else { - ii->ii_ExclusionOps = NULL; - ii->ii_ExclusionProcs = NULL; - ii->ii_ExclusionStrats = NULL; + } + return ii; +} + +/* ---------------- + * BuildDummyIndexInfo + * Construct a dummy IndexInfo record for an open index + * + * This differs from the real BuildIndexInfo in that it will never run any + * user-defined code that might exist in index expressions or predicates. + * Instead of the real index expressions, we return null constants that have + * the right types/typmods/collations. Predicates and exclusion clauses are + * just ignored. This is sufficient for the purpose of truncating an index, + * since we will not need to actually evaluate the expressions or predicates; + * the only thing that's likely to be done with the data is construction of + * a tupdesc describing the index's rowtype. + * ---------------- + */ +IndexInfo* BuildDummyIndexInfo(Relation index) +{ + IndexInfo* ii; + Form_pg_index indexStruct = index->rd_index; + int i; + int numAtts; + + /* check the number of keys, and copy attr numbers into the IndexInfo */ + numAtts = indexStruct->indnatts; + if (numAtts < 1 || numAtts > INDEX_MAX_KEYS) { + elog(ERROR, "invalid indnatts %d for index %u", numAtts, RelationGetRelid(index)); } - /* other info */ - ii->ii_Unique = indexStruct->indisunique; - ii->ii_ReadyForInserts = IndexIsReady(indexStruct); + /* + * Create the node, using dummy index expressions, and pretending there is + * no predicate. + */ + ii = makeIndexInfo(indexStruct->indnatts, + RelationGetDummyIndexExpressions(index), + NIL, + indexStruct->indisunique, + indexStruct->indisready, + false); - /* initialize index-build state to default */ - ii->ii_Concurrent = false; - ii->ii_BrokenHotChain = false; - ii->ii_PgClassAttrId = 0; + /* fill in attribute numbers */ + for (i = 0; i < numAtts; i++) { + ii->ii_KeyAttrNumbers[i] = indexStruct->indkey.values[i]; + } + /* We ignore the exclusion constraint if any */ return ii; } @@ -1889,7 +1961,12 @@ void index_update_stats( HeapTuple tuple; Form_pg_class rd_rel; bool dirty = false; + bool is_gtt = false; + /* update index stats into localhash and rel_rd_rel for global temp table */ + if (RELATION_IS_GLOBAL_TEMP(rel)) { + is_gtt = true; + } /* * We always update the pg_class row using a non-transactional, * overwrite-in-place update. There are several reasons for this: @@ -1996,18 +2073,30 @@ void index_update_stats( else /* don't bother for indexes */ relallvisible = 0; - if (rd_rel->relpages != (float8)relpages) { + if (is_gtt) { + rel->rd_rel->relpages = static_cast(relpages); + } else if (rd_rel->relpages != (float8)relpages) { rd_rel->relpages = (float8)relpages; dirty = true; } - if (rd_rel->reltuples != (float8)reltuples) { + + if (is_gtt) { + rel->rd_rel->reltuples = (float4) reltuples; + } else if (rd_rel->reltuples != (float8)reltuples) { rd_rel->reltuples = (float8)reltuples; dirty = true; } - if (rd_rel->relallvisible != (int32)relallvisible) { + + if (is_gtt) { + rel->rd_rel->relallvisible = (int32) relallvisible; + } else if (rd_rel->relallvisible != (int32)relallvisible) { rd_rel->relallvisible = (int32)relallvisible; dirty = true; } + + if (is_gtt) { + up_gtt_relstats(rel, relpages, reltuples, relallvisible, InvalidTransactionId); + } } #ifdef PGXC } @@ -2311,12 +2400,25 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR relation_close(psortRel, NoLock); } + if (RELATION_IS_GLOBAL_TEMP(indexRelation)) { + if (indexRelation->rd_smgr == NULL) { + /* Open it at the smgr level if not already done */ + RelationOpenSmgr(indexRelation); + } + if (!gtt_storage_attached(RelationGetRelid(indexRelation)) || + !smgrexists(indexRelation->rd_smgr, MAIN_FORKNUM)) { + gtt_force_enable_index(indexRelation); + RelationCreateStorage(indexRelation->rd_node, RELPERSISTENCE_GLOBAL_TEMP, indexRelation->rd_rel->relowner, + indexRelation->rd_bucketoid, indexRelation); + } + } + /* * Call the access method's build procedure */ hasbucket = (!isPartition && RELATION_CREATE_BUCKET(heapRelation)) || (isPartition && RELATION_OWN_BUCKETKEY(heapRelation)); - if (hasbucket == true) { + if (hasbucket) { index_build_storage_for_bucket(heapRelation, indexRelation, heapPartition, @@ -3527,7 +3629,8 @@ void reindex_indexpart_internal(Relation heapRelation, Relation iRel, IndexInfo* /* * reindex_index - This routine is used to recreate a single index */ -void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks, AdaptMem* mem_info, bool db_wide) +void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks, + AdaptMem *memInfo, bool dbWide, char persistence) { Relation iRel, heapRelation; Oid heapId; @@ -3615,19 +3718,19 @@ void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks, Ad /* workload client manager */ if (IS_PGXC_COORDINATOR && ENABLE_WORKLOAD_CONTROL) { /* if operatorMem is already set, the mem check is already done */ - if (mem_info != NULL && mem_info->work_mem == 0) { + if (memInfo != NULL && memInfo->work_mem == 0) { EstIdxMemInfo(heapRelation, NULL, &indexInfo->ii_desc, indexInfo, iRel->rd_am->amname.data); - if (db_wide) { + if (dbWide) { indexInfo->ii_desc.cost = g_instance.cost_cxt.disable_cost; indexInfo->ii_desc.query_mem[0] = Max(STATEMENT_MIN_MEM * 1024, indexInfo->ii_desc.query_mem[0]); } WLMInitQueryPlan((QueryDesc*)&indexInfo->ii_desc, false); dywlm_client_manager((QueryDesc*)&indexInfo->ii_desc, false); - AdjustIdxMemInfo(mem_info, &indexInfo->ii_desc); + AdjustIdxMemInfo(memInfo, &indexInfo->ii_desc); } - } else if (IS_PGXC_DATANODE && mem_info != NULL && mem_info->work_mem > 0) { - indexInfo->ii_desc.query_mem[0] = mem_info->work_mem; - indexInfo->ii_desc.query_mem[1] = mem_info->max_mem; + } else if (IS_PGXC_DATANODE && memInfo != NULL && memInfo->work_mem > 0) { + indexInfo->ii_desc.query_mem[0] = memInfo->work_mem; + indexInfo->ii_desc.query_mem[1] = memInfo->max_mem; } if (!RELATION_IS_PARTITIONED(heapRelation)) /* for non partitioned table */ @@ -3862,8 +3965,9 @@ bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo, Oid indexOid = lfirst_oid(indexId); Relation indexRel = index_open(indexOid, AccessShareLock); - if (is_pg_class) + if (is_pg_class) { RelationSetIndexList(rel, doneIndexes, InvalidOid); + } if ((((uint32)reindexType) & REINDEX_ALL_INDEX) || ((((uint32)reindexType) & REINDEX_BTREE_INDEX) && (indexRel->rd_rel->relam == BTREE_AM_OID)) || @@ -3873,17 +3977,19 @@ bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo, ((((uint32)reindexType) & REINDEX_GIST_INDEX) && (indexRel->rd_rel->relam == GIST_AM_OID))) { index_close(indexRel, AccessShareLock); reindex_index( - indexOid, InvalidOid, !(((uint32)flags) & REINDEX_REL_CHECK_CONSTRAINTS), memInfo, dbWide); - + indexOid, InvalidOid, !((static_cast(flags)) & REINDEX_REL_CHECK_CONSTRAINTS), memInfo, + dbWide, rel->rd_rel->relpersistence); CommandCounterIncrement(); - } else + } else { index_close(indexRel, AccessShareLock); + } /* Index should no longer be in the pending list */ Assert(!ReindexIsProcessingIndex(indexOid)); - if (is_pg_class) + if (is_pg_class) { doneIndexes = lappend_oid(doneIndexes, indexOid); + } } } PG_CATCH(); @@ -3895,8 +4001,9 @@ bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo, PG_END_TRY(); ResetReindexPending(); - if (is_pg_class) + if (is_pg_class) { RelationSetIndexList(rel, indexIds, ClassOidIndexId); + } /* * Close rel, but continue to hold the lock. @@ -3904,8 +4011,7 @@ bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo, heap_close(rel, NoLock); // reset all local indexes on partition usable if needed - if (RELATION_IS_PARTITIONED(rel)) /* for partitioned table */ - { + if (RELATION_IS_PARTITIONED(rel)) { /* for partitioned table */ Oid partOid; ListCell* cell = NULL; List* partOidList = relationGetPartitionOidList(rel); @@ -3918,16 +4024,14 @@ bool reindex_relation(Oid relid, int flags, int reindexType, AdaptMem* memInfo, result = (indexIds != NIL); - if (!isPartitioned) /* for non partitioned table */ - { + if (!isPartitioned) { /* for non partitioned table */ /* * If the relation has a secondary toast rel, reindex that too while we * still hold the lock on the master table. */ if ((((uint32)flags) & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid)) result = reindex_relation(toast_relid, flags, REINDEX_BTREE_INDEX) || result; - } else /* for partitioned table */ - { + } else { /* for partitioned table */ List* partTupleList = NULL; ListCell* partCell = NULL; diff --git a/src/common/backend/catalog/namespace.cpp b/src/common/backend/catalog/namespace.cpp index 1872b4f0761aed75cdda3a79f6db2d64979a6994..911aa45094dfd8bddc715e28f3433b376af3f2be 100755 --- a/src/common/backend/catalog/namespace.cpp +++ b/src/common/backend/catalog/namespace.cpp @@ -593,6 +593,12 @@ void RangeVarAdjustRelationPersistence(RangeVar* newRelation, Oid nspid) errmsg("cannot create temporary relation in non-temporary schema"))); } break; + case RELPERSISTENCE_GLOBAL_TEMP: /* global temp table */ + if (isAnyTempNamespace(nspid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot create global temp relations in temporary schemas"))); + break; case RELPERSISTENCE_PERMANENT: if (isTempOrToastNamespace(nspid)) newRelation->relpersistence = RELPERSISTENCE_TEMP; diff --git a/src/common/backend/catalog/storage.cpp b/src/common/backend/catalog/storage.cpp index 9be26cab0dd6067289212a63009385da469beb2b..a68b693d3237ffd5968ebd6e739e16356edf7ab7 100644 --- a/src/common/backend/catalog/storage.cpp +++ b/src/common/backend/catalog/storage.cpp @@ -31,6 +31,7 @@ #include "catalog/catalog.h" #include "catalog/dfsstore_ctlg.h" #include "catalog/storage.h" +#include "catalog/storage_gtt.h" #include "catalog/storage_xlog.h" #include "catalog/pg_hashbucket_fn.h" #include "commands/tablespace.h" @@ -67,6 +68,7 @@ typedef struct PendingRelDelete { RelFileNode relnode; /* relation that may need to be deleted */ ForkNumber forknum; /* MAIN_FORKNUM for row table; or valid column ForkNum */ BackendId backend; /* InvalidBackendId if not a temp rel */ + Oid relOid; /* InvalidOid if not a global temp rel */ Oid ownerid; /* owner id for user space statistics */ bool atCommit; /* T=delete at commit; F=delete at abort */ int nestLevel; /* xact nesting level of request */ @@ -117,6 +119,10 @@ static void StorageSetBackendAndLogged(_in_ char relpersistence, _out_ BackendId *needs_wal = false; } break; + case RELPERSISTENCE_GLOBAL_TEMP: + *backend = t_thrd.proc_cxt.MyBackendId; + *needs_wal = false; + break; case RELPERSISTENCE_UNLOGGED: *backend = InvalidBackendId; *needs_wal = false; @@ -136,8 +142,8 @@ static void StorageSetBackendAndLogged(_in_ char relpersistence, _out_ BackendId * if it's a row-storage table, *whichAttr* must is *AllTheAttrs*. * if it's a column-storage table, *whichAttr* >= *AllTheAttrs*. */ -static void InsertStorageIntoPendingList(_in_ RelFileNode* rnode, _in_ AttrNumber attrnum, _in_ BackendId backend, - _in_ Oid ownerid, _in_ bool atCommit, _in_ bool isDfsTruncate = false) +static void InsertStorageIntoPendingList(_in_ const RelFileNode* rnode, _in_ AttrNumber attrnum, _in_ BackendId backend, + _in_ Oid ownerid, _in_ bool atCommit, _in_ bool isDfsTruncate = false, Relation rel = NULL) { PendingRelDelete* pending = (PendingRelDelete*)MemoryContextAlloc(u_sess->top_mem_cxt, sizeof(PendingRelDelete)); pending->relnode = *rnode; @@ -165,17 +171,22 @@ static void InsertStorageIntoPendingList(_in_ RelFileNode* rnode, _in_ AttrNumbe } } pending->backend = backend; + pending->relOid = InvalidOid; pending->ownerid = ownerid; pending->atCommit = atCommit; /* false: delete if abort; true: delete if commit */ pending->nestLevel = GetCurrentTransactionNestLevel(); pending->next = u_sess->catalog_cxt.pendingDeletes; u_sess->catalog_cxt.pendingDeletes = pending; - /* Lock RelFileNode to control concurrent with Catchup Thread */ - LockRelFileNode(*rnode, AccessExclusiveLock); + if (RELATION_IS_GLOBAL_TEMP(rel)) { + pending->relOid = RelationGetRelid(rel); + } else { + /* Lock RelFileNode to control concurrent with Catchup Thread */ + LockRelFileNode(*rnode, AccessExclusiveLock); + } } -void RelationCreateStorageInternal(RelFileNode rnode, char relpersistence, Oid ownerid) +static void RelationCreateStorageInternal(RelFileNode rnode, char relpersistence, Oid ownerid, Relation rel = NULL) { SMgrRelation srel; BackendId backend; @@ -190,7 +201,12 @@ void RelationCreateStorageInternal(RelFileNode rnode, char relpersistence, Oid o log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM); /* Add the relation to the list of stuff to delete at abort */ - InsertStorageIntoPendingList(&rnode, InvalidAttrNumber, backend, ownerid, false); + InsertStorageIntoPendingList(&rnode, InvalidAttrNumber, backend, ownerid, false, false, rel); + + /* remember global temp table storage info to localhash */ + if (rel && relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { + remember_gtt_storage_info(rnode, rel); + } } /* @@ -204,12 +220,12 @@ void RelationCreateStorageInternal(RelFileNode rnode, char relpersistence, Oid o * This function is transactional. The creation is WAL-logged, and if the * transaction aborts later on, the storage will be destroyed. */ -void RelationCreateStorage(RelFileNode rnode, char relpersistence, Oid ownerid, Oid bucketOid) +void RelationCreateStorage(RelFileNode rnode, char relpersistence, Oid ownerid, Oid bucketOid, Relation rel) { if (OidIsValid(bucketOid) && (bucketOid != VirtualBktOid)) { BucketCreateStorage(rnode, bucketOid, ownerid); } else { - RelationCreateStorageInternal(rnode, relpersistence, ownerid); + RelationCreateStorageInternal(rnode, relpersistence, ownerid, rel); } } @@ -328,6 +344,17 @@ void CStoreRelDropColumn(Relation rel, AttrNumber attrnum, Oid ownerid) */ void RelationDropStorage(Relation rel, bool isDfsTruncate) { + // global temp table files may not exist + if (RELATION_IS_GLOBAL_TEMP(rel)) { + if (rel->rd_smgr == NULL) { + /* Open it at the smgr level if not already done */ + RelationOpenSmgr(rel); + } + if (!smgrexists(rel->rd_smgr, MAIN_FORKNUM)) { + return; + } + } + /* * First we must push the column file, column bcm file to the pendingDeletes and * then push the logical table file to pendingDeletes. @@ -369,7 +396,7 @@ void RelationDropStorage(Relation rel, bool isDfsTruncate) } else { /* Add the relation to the list of stuff to delete at commit */ InsertStorageIntoPendingList( - &rel->rd_node, InvalidAttrNumber, rel->rd_backend, rel->rd_rel->relowner, true, isDfsTruncate); + &rel->rd_node, InvalidAttrNumber, rel->rd_backend, rel->rd_rel->relowner, true, isDfsTruncate, rel); } /* @@ -536,6 +563,11 @@ void RelationTruncate(Relation rel, BlockNumber nblocks) if (bcm) BCM_truncate(rel); + /* skip truncating if global temp table index does not exist */ + if (RELATION_IS_GLOBAL_TEMP(rel) && !smgrexists(rel->rd_smgr, MAIN_FORKNUM)) { + return; + } + /* * We WAL-log the truncation before actually truncating, which means * trouble if the truncation fails. If we then crash, the WAL replay @@ -570,9 +602,10 @@ void RelationTruncate(Relation rel, BlockNumber nblocks) if (fsm || vm) XLogFlush(lsn); } - - /* Lock RelFileNode to control concurrent with Catchup Thread */ - LockRelFileNode(rel->rd_node, AccessExclusiveLock); + if (!RELATION_IS_GLOBAL_TEMP(rel)) { + /* Lock RelFileNode to control concurrent with Catchup Thread */ + LockRelFileNode(rel->rd_node, AccessExclusiveLock); + } /* Do the real work */ smgrtruncate(rel->rd_smgr, MAIN_FORKNUM, nblocks); @@ -686,7 +719,8 @@ void smgrDoPendingDeletes(bool isCommit) /* do deletion if called for */ if (pending->atCommit == isCommit) { if (!IsValidColForkNum(pending->forknum)) { - RowRelationDoDeleteFiles(pending->relnode, pending->backend, pending->ownerid); + RowRelationDoDeleteFiles( + pending->relnode, pending->backend, pending->ownerid, pending->relOid, isCommit); /* * "CREATE/DROP hdfs table" will use Two-Phrases Commit Transaction, @@ -1900,7 +1934,7 @@ void ColumnRelationDoDeleteFiles(RelFileNode* rnode, ForkNumber forknum, Backend } /* Delete all the physical files for row relation. */ -void RowRelationDoDeleteFiles(RelFileNode rnode, BackendId backend, Oid ownerid) +void RowRelationDoDeleteFiles(RelFileNode rnode, BackendId backend, Oid ownerid, Oid relOid, bool isCommit) { /* decrease the permanent space on users' record */ uint64 size = GetSMgrRelSize(&rnode, backend, InvalidForkNumber); @@ -1912,6 +1946,11 @@ void RowRelationDoDeleteFiles(RelFileNode rnode, BackendId backend, Oid ownerid) smgrdounlink(srel, false); smgrclose(srel); + /* clean global temp table flags when transaction commit or rollback */ + if (SmgrIsTemp(srel) && relOid != InvalidOid && gtt_storage_attached(relOid)) { + forget_gtt_storage_info(relOid, rnode, isCommit); + } + /* * After files are deleted, append this filenode into BCM file list, * so that we know all the BCM shared buffers of column relation has been diff --git a/src/common/backend/catalog/storage_gtt.cpp b/src/common/backend/catalog/storage_gtt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dec09a707ea953b20704b64db1b48509f759781b --- /dev/null +++ b/src/common/backend/catalog/storage_gtt.cpp @@ -0,0 +1,1357 @@ +/* ------------------------------------------------------------------------- + * + * storage_gtt.c + * code to create and destroy physical storage for global temparary table + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2012 Postgres-XC Development Group + * + * IDENTIFICATION + * src/backend/catalog/storage_gtt.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/genam.h" +#include "access/multixact.h" +#include "access/transam.h" +#include "access/visibilitymap.h" +#include "access/xact.h" +#include "access/xlog.h" +#include "access/xloginsert.h" +#include "access/xlogutils.h" +#include "catalog/heap.h" +#include "catalog/index.h" +#include "catalog/namespace.h" +#include "catalog/pg_statistic.h" +#include "catalog/pg_type.h" +#include "catalog/storage.h" +#include "catalog/storage_gtt.h" +#include "catalog/storage_xlog.h" +#include "commands/sequence.h" +#include "commands/tablecmds.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/execnodes.h" +#include "nodes/pg_list.h" +#include "nodes/primnodes.h" +#include "storage/freespace.h" +#include "storage/ipc.h" +#include "storage/lwlock.h" +#include "storage/proc.h" +#include "storage/procarray.h" +#include "storage/shmem.h" +#include "storage/sinvaladt.h" +#include "storage/smgr.h" +#include "utils/catcache.h" +#include "gs_threadlocal.h" +#include "utils/guc.h" +#include "utils/hsearch.h" +#include "utils/inval.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/rel.h" +#include + +/* Copy from bitmapset.c, because gtt used the function in bitmapset.c */ +#define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD) + +#define BITMAPSET_SIZE(nwords) (offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword)) + +THR_LOCAL bool gtt_cleaner_exit_registered = false; +THR_LOCAL HTAB* gtt_storage_local_hash = NULL; +THR_LOCAL MemoryContext gtt_relstats_context = NULL; + +/* relfrozenxid of all gtts in the current session */ +THR_LOCAL List* gtt_session_relfrozenxid_list = NIL; +THR_LOCAL TransactionId gtt_session_frozenxid = InvalidTransactionId; + +struct gtt_ctl_data { + LWLock lock; + int max_entry; + Size entry_size; +}; + +struct gtt_fnode { + Oid dbNode; + Oid relNode; +}; + +struct gtt_shared_hash_entry { + gtt_fnode rnode; + Bitmapset* map; + /* bitmap data */ +}; + +struct gtt_relfilenode { + Oid relfilenode; + Oid spcnode; + + /* pg_class stat */ + int32 relpages; + float4 reltuples; + int32 relallvisible; + TransactionId relfrozenxid; +}; + +struct gtt_local_hash_entry { + Oid relid; + + List* relfilenode_list; + + char relkind; + bool on_commit_delete; + + /* pg_statistic */ + int natts; + int* attnum; + HeapTuple* att_stat_tups; + + Oid oldrelid; /* remember the source of relid, before the switch relfilenode. + */ +}; + +static Size action_gtt_shared_hash_entry_size(void); +static void gtt_storage_checkin(Oid relid); +static void gtt_storage_checkout(Oid relid, bool skiplock, bool isCommit); +static void gtt_storage_removeall(int code, Datum arg); +static void insert_gtt_relfrozenxid_to_ordered_list(Oid relfrozenxid); +static void remove_gtt_relfrozenxid_from_ordered_list(Oid relfrozenxid); +static void set_gtt_session_relfrozenxid(void); +static void gtt_reset_statistics(gtt_local_hash_entry* entry); +static void gtt_free_statistics(gtt_local_hash_entry* entry); +static gtt_relfilenode* gtt_search_relfilenode(const gtt_local_hash_entry* entry, Oid relfilenode, bool missingOk); +static gtt_local_hash_entry* gtt_search_by_relid(Oid relid, bool missingOk); + +Datum pg_get_gtt_statistics(PG_FUNCTION_ARGS); +Datum pg_get_gtt_relstats(PG_FUNCTION_ARGS); +Datum pg_gtt_attached_pid(PG_FUNCTION_ARGS); +Datum pg_list_gtt_relfrozenxids(PG_FUNCTION_ARGS); + +static Size action_gtt_shared_hash_entry_size(void) +{ + int wordnum; + Size hashEntrySize = 0; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return 0; + + wordnum = WORDNUM(g_instance.shmem_cxt.MaxBackends + 1); + hashEntrySize += MAXALIGN(sizeof(gtt_shared_hash_entry)); + hashEntrySize += (size_t)MAXALIGN(BITMAPSET_SIZE(wordnum + 1)); + + return hashEntrySize; +} + +Size active_gtt_shared_hash_size(void) +{ + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return 0; + + Size size = MAXALIGN(sizeof(gtt_ctl_data)); + Size hashEntrySize = action_gtt_shared_hash_entry_size(); + size += hash_estimate_size(u_sess->attr.attr_storage.max_active_gtt, hashEntrySize); + + return size; +} + +void active_gtt_shared_hash_init(void) +{ + HASHCTL info; + bool found; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return; + + t_thrd.shemem_ptr_cxt.gtt_shared_ctl = + reinterpret_cast(ShmemInitStruct("gtt_shared_ctl", sizeof(gtt_ctl_data), &found)); + + if (!found) { + LWLockRegisterTranche((int)LWTRANCHE_GTT_CTL, "gtt_shared_ctl"); + LWLockInitialize(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock, (int)LWTRANCHE_GTT_CTL); + t_thrd.shemem_ptr_cxt.gtt_shared_ctl->max_entry = u_sess->attr.attr_storage.max_active_gtt; + t_thrd.shemem_ptr_cxt.gtt_shared_ctl->entry_size = action_gtt_shared_hash_entry_size(); + } + + errno_t rc = memset_s(&info, sizeof(info), 0, sizeof(info)); + securec_check(rc, "", ""); + info.keysize = sizeof(gtt_fnode); + info.entrysize = action_gtt_shared_hash_entry_size(); + t_thrd.shemem_ptr_cxt.active_gtt_shared_hash = ShmemInitHash("active gtt shared hash", + t_thrd.shemem_ptr_cxt.gtt_shared_ctl->max_entry, + t_thrd.shemem_ptr_cxt.gtt_shared_ctl->max_entry, + &info, + HASH_ELEM | HASH_BLOBS | HASH_FIXED_SIZE); +} + +static void gtt_storage_checkin(Oid relid) +{ + gtt_shared_hash_entry* entry; + bool found; + gtt_fnode fnode; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return; + + fnode.dbNode = u_sess->proc_cxt.MyDatabaseId; + fnode.relNode = relid; + (void)LWLockAcquire(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock, LW_EXCLUSIVE); + entry = reinterpret_cast(hash_search( + t_thrd.shemem_ptr_cxt.active_gtt_shared_hash, reinterpret_cast(&(fnode)), HASH_ENTER_NULL, &found)); + + if (entry == NULL) { + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of shared memory"), + errhint("You might need to increase max_active_global_temporary_table."))); + } + + if (found == false) { + int wordnum; + + entry->map = reinterpret_cast((char*)entry + MAXALIGN(sizeof(gtt_shared_hash_entry))); + wordnum = WORDNUM(g_instance.shmem_cxt.MaxBackends + 1); + errno_t rc = memset_s(entry->map, (size_t)BITMAPSET_SIZE(wordnum + 1), 0, (size_t)BITMAPSET_SIZE(wordnum + 1)); + securec_check(rc, "", ""); + entry->map->nwords = wordnum + 1; + } + + (void)bms_add_member(entry->map, t_thrd.proc_cxt.MyBackendId); + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); +} + +static void gtt_storage_checkout(Oid relid, bool skiplock, bool isCommit) +{ + gtt_shared_hash_entry* entry; + gtt_fnode fnode; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return; + + fnode.dbNode = u_sess->proc_cxt.MyDatabaseId; + fnode.relNode = relid; + if (!skiplock) { + (void)LWLockAcquire(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock, LW_EXCLUSIVE); + } + + entry = reinterpret_cast(hash_search( + t_thrd.shemem_ptr_cxt.active_gtt_shared_hash, reinterpret_cast(&(fnode)), HASH_FIND, NULL)); + + if (entry == NULL) { + if (!skiplock) { + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + } + if (isCommit) { + elog(WARNING, "relid %u not exist in gtt shared hash when forget", relid); + } + return; + } + + Assert(t_thrd.proc_cxt.MyBackendId >= 1 && t_thrd.proc_cxt.MyBackendId <= g_instance.shmem_cxt.MaxBackends); + (void)bms_del_member(entry->map, t_thrd.proc_cxt.MyBackendId); + + if (bms_is_empty(entry->map)) { + if (!hash_search(t_thrd.shemem_ptr_cxt.active_gtt_shared_hash, &fnode, HASH_REMOVE, NULL)) { + elog(PANIC, "gtt shared hash table corrupted"); + } + } + + if (!skiplock) + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + + return; +} + +Bitmapset* copy_active_gtt_bitmap(Oid relid) +{ + gtt_shared_hash_entry* entry; + Bitmapset* mapCopy = NULL; + gtt_fnode fnode; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return NULL; + + fnode.dbNode = u_sess->proc_cxt.MyDatabaseId; + fnode.relNode = relid; + (void)LWLockAcquire(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock, LW_SHARED); + entry = reinterpret_cast(hash_search( + t_thrd.shemem_ptr_cxt.active_gtt_shared_hash, reinterpret_cast(&(fnode)), HASH_FIND, NULL)); + + if (entry == NULL) { + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + return NULL; + } + + Assert(entry->map); + if (!bms_is_empty(entry->map)) + mapCopy = bms_copy(entry->map); + + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + + return mapCopy; +} + +bool is_other_backend_use_gtt(Oid relid) +{ + gtt_shared_hash_entry* entry; + bool inUse = false; + gtt_fnode fnode; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return false; + + fnode.dbNode = u_sess->proc_cxt.MyDatabaseId; + fnode.relNode = relid; + (void)LWLockAcquire(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock, LW_SHARED); + entry = reinterpret_cast(hash_search( + t_thrd.shemem_ptr_cxt.active_gtt_shared_hash, reinterpret_cast(&(fnode)), HASH_FIND, NULL)); + + if (entry == NULL) { + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + return false; + } + + Assert(entry->map); + Assert(t_thrd.proc_cxt.MyBackendId >= 1 && t_thrd.proc_cxt.MyBackendId <= g_instance.shmem_cxt.MaxBackends); + + int numUse = bms_num_members(entry->map); + if (numUse == 0) { + inUse = false; + } else if (numUse == 1) { + if (bms_is_member(t_thrd.proc_cxt.MyBackendId, entry->map)) { + inUse = false; + } else { + inUse = true; + } + } else { + inUse = true; + } + + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + + return inUse; +} + +void remember_gtt_storage_info(const RelFileNode rnode, Relation rel) +{ + gtt_local_hash_entry* entry; + MemoryContext oldcontext; + gtt_relfilenode* newNode = NULL; + Oid relid = RelationGetRelid(rel); + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Global temporary table feature is disable"), + errhint("You might need to increase max_active_global_temporary_table " + "to enable this feature."))); + } + + if (RecoveryInProgress()) { + elog(ERROR, "readonly mode not support access global temporary table"); + } + if (rel->rd_rel->relkind == RELKIND_INDEX && rel->rd_index && + (!rel->rd_index->indisvalid || !rel->rd_index->indisready)) { + elog(ERROR, "invalid gtt index %s not allow to create storage", RelationGetRelationName(rel)); + } + if (gtt_storage_local_hash == NULL) { +#define GTT_LOCAL_HASH_SIZE 1024 + /* First time through: initialize the hash table */ + HASHCTL ctl; + errno_t rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl)); + securec_check(rc, "", ""); + ctl.keysize = sizeof(Oid); + ctl.entrysize = sizeof(gtt_local_hash_entry); + gtt_storage_local_hash = + hash_create("global temporary table info", GTT_LOCAL_HASH_SIZE, &ctl, HASH_ELEM | HASH_BLOBS); + + if (!CacheMemoryContext) + CreateCacheMemoryContext(); + + gtt_relstats_context = + AllocSetContextCreate(CacheMemoryContext, "gtt relstats context", ALLOCSET_DEFAULT_SIZES); + } + + oldcontext = MemoryContextSwitchTo(gtt_relstats_context); + + entry = gtt_search_by_relid(relid, true); + if (!entry) { + bool found = false; + int natts = 0; + + /* Look up or create an entry */ + entry = reinterpret_cast(hash_search(gtt_storage_local_hash, + reinterpret_cast(&relid), HASH_ENTER, &found)); + + if (found) { + (void)MemoryContextSwitchTo(oldcontext); + elog(ERROR, "backend %d relid %u already exists in gtt local hash", t_thrd.proc_cxt.MyBackendId, relid); + } + + entry->relfilenode_list = NIL; + entry->relkind = rel->rd_rel->relkind; + entry->on_commit_delete = false; + entry->natts = 0; + entry->attnum = NULL; + entry->att_stat_tups = NULL; + entry->oldrelid = InvalidOid; + + natts = RelationGetNumberOfAttributes(rel); + entry->attnum = reinterpret_cast(palloc0(sizeof(int) * (unsigned long)natts)); + entry->att_stat_tups = reinterpret_cast(palloc0(sizeof(HeapTuple) * (unsigned long)natts)); + entry->natts = natts; + + if (entry->relkind == RELKIND_RELATION) { + if (RELATION_GTT_ON_COMMIT_DELETE(rel)) { + entry->on_commit_delete = true; + register_on_commit_action(RelationGetRelid(rel), ONCOMMIT_DELETE_ROWS); + } + } + + if (entry->relkind == RELKIND_RELATION || entry->relkind == RELKIND_SEQUENCE) { + gtt_storage_checkin(relid); + } + } + + newNode = reinterpret_cast(palloc0(sizeof(gtt_relfilenode))); + newNode->relfilenode = rnode.relNode; + newNode->spcnode = rnode.spcNode; + newNode->relpages = 0; + newNode->reltuples = 0; + newNode->relallvisible = 0; + newNode->relfrozenxid = InvalidTransactionId; + entry->relfilenode_list = lappend(entry->relfilenode_list, newNode); + + /* only heap contain transaction information */ + if (entry->relkind == RELKIND_RELATION) { + newNode->relfrozenxid = u_sess->utils_cxt.RecentXmin; + insert_gtt_relfrozenxid_to_ordered_list((Oid)newNode->relfrozenxid); + set_gtt_session_relfrozenxid(); + } + + gtt_reset_statistics(entry); + + (void)MemoryContextSwitchTo(oldcontext); + + if (!gtt_cleaner_exit_registered) { + on_shmem_exit(gtt_storage_removeall, 0); + gtt_cleaner_exit_registered = true; + } + + return; +} + +void forget_gtt_storage_info(Oid relid, const RelFileNode rnode, bool isCommit) +{ + gtt_local_hash_entry* entry = NULL; + gtt_relfilenode* dRnode = NULL; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) { + return; + } + entry = gtt_search_by_relid(relid, true); + if (entry == NULL) { + if (isCommit) { + elog(ERROR, "gtt rel %u not found in local hash", relid); + } + return; + } + + dRnode = gtt_search_relfilenode(entry, rnode.relNode, true); + if (dRnode == NULL) { + if (isCommit) { + elog(ERROR, "gtt relfilenode %u not found in rel %u", rnode.relNode, relid); + } else if (entry->oldrelid != InvalidOid) { + gtt_local_hash_entry* entry2 = NULL; + gtt_relfilenode* gttnode2 = NULL; + + entry2 = gtt_search_by_relid(entry->oldrelid, false); + gttnode2 = gtt_search_relfilenode(entry2, rnode.relNode, false); + Assert(gttnode2->relfilenode == rnode.relNode); + Assert(list_length(entry->relfilenode_list) == 1); + /* rollback switch relfilenode */ + gtt_switch_rel_relfilenode( + entry2->relid, gttnode2->relfilenode, entry->relid, gtt_fetch_current_relfilenode(entry->relid), false); + /* clean up footprint */ + entry2->oldrelid = InvalidOid; + dRnode = gtt_search_relfilenode(entry, rnode.relNode, false); + Assert(dRnode); + } else { + if (entry->relfilenode_list == NIL) { + if (entry->relkind == RELKIND_RELATION || entry->relkind == RELKIND_SEQUENCE) + gtt_storage_checkout(relid, false, isCommit); + + gtt_free_statistics(entry); + (void*)hash_search(gtt_storage_local_hash, reinterpret_cast(&(relid)), HASH_REMOVE, NULL); + } + return; + } + } + + if (entry->relkind == RELKIND_RELATION) { + Assert(TransactionIdIsNormal(dRnode->relfrozenxid) || !isCommit); + if (TransactionIdIsValid(dRnode->relfrozenxid)) { + remove_gtt_relfrozenxid_from_ordered_list((Oid)dRnode->relfrozenxid); + set_gtt_session_relfrozenxid(); + } + } + + entry->relfilenode_list = list_delete_ptr(entry->relfilenode_list, dRnode); + pfree(dRnode); + if (entry->relfilenode_list == NIL) { + if (entry->relkind == RELKIND_RELATION || entry->relkind == RELKIND_SEQUENCE) + gtt_storage_checkout(relid, false, isCommit); + + if (isCommit && entry->oldrelid != InvalidOid) { + gtt_local_hash_entry* entry2 = NULL; + + entry2 = gtt_search_by_relid(entry->oldrelid, false); + /* clean up footprint */ + entry2->oldrelid = InvalidOid; + } + + gtt_free_statistics(entry); + (void*)hash_search(gtt_storage_local_hash, reinterpret_cast(&(relid)), HASH_REMOVE, NULL); + } else + gtt_reset_statistics(entry); + + return; +} + +/* is the storage file was created in this backend */ +bool gtt_storage_attached(Oid relid) +{ + bool found = false; + gtt_local_hash_entry* entry = NULL; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) { + return false; + } + + entry = gtt_search_by_relid(relid, true); + if (entry) { + found = true; + } + return found; +} + +#define INIT_ALLOC_NUMS 32 +static void gtt_storage_removeall(int code, Datum arg) +{ + HASH_SEQ_STATUS status; + gtt_local_hash_entry* entry; + SMgrRelation* srels = NULL; + Oid* relids = NULL; + char* relkinds = NULL; + unsigned long nrels = 0; + unsigned long nfiles = 0; + unsigned long maxrels = 0; + unsigned long maxfiles = 0; + unsigned long i = 0; + + if (gtt_storage_local_hash == NULL) + return; + + hash_seq_init(&status, gtt_storage_local_hash); + while ((entry = reinterpret_cast(hash_seq_search(&status))) != NULL) { + ListCell* lc; + + foreach (lc, entry->relfilenode_list) { + SMgrRelation srel; + RelFileNode rnode; + gtt_relfilenode* gtt_rnode = reinterpret_cast(lfirst(lc)); + + rnode.spcNode = gtt_rnode->spcnode; + rnode.dbNode = u_sess->proc_cxt.MyDatabaseId; + rnode.relNode = gtt_rnode->relfilenode; + rnode.bucketNode = InvalidBktId; + srel = smgropen(rnode, t_thrd.proc_cxt.MyBackendId); + + if (maxfiles == 0) { + maxfiles = INIT_ALLOC_NUMS; + srels = reinterpret_cast(palloc(sizeof(SMgrRelation) * maxfiles)); + } else if (maxfiles <= nfiles) { + maxfiles *= 2; + srels = reinterpret_cast(repalloc(srels, sizeof(SMgrRelation) * maxfiles)); + } + + srels[nfiles++] = srel; + } + + if (maxrels == 0) { + maxrels = INIT_ALLOC_NUMS; + relids = reinterpret_cast(palloc(sizeof(Oid) * maxrels)); + relkinds = reinterpret_cast(palloc(sizeof(char) * maxrels)); + } else if (maxrels <= nrels) { + maxrels *= 2; + relids = reinterpret_cast(repalloc(relids, sizeof(Oid) * maxrels)); + relkinds = reinterpret_cast(repalloc(relkinds, sizeof(char) * maxrels)); + } + + relkinds[nrels] = entry->relkind; + relids[nrels] = entry->relid; + nrels++; + } + + if (nfiles > 0) { + for (i = 0; i < nfiles; i++) { + smgrdounlink(srels[i], false); + smgrclose(srels[i]); + } + pfree(srels); + } + + if (nrels) { + (void)LWLockAcquire(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock, LW_EXCLUSIVE); + for (i = 0; i < nrels; i++) { + if (relkinds[i] == RELKIND_RELATION || relkinds[i] == RELKIND_SEQUENCE) + gtt_storage_checkout(relids[i], true, false); + } + LWLockRelease(&t_thrd.shemem_ptr_cxt.gtt_shared_ctl->lock); + + pfree(relids); + pfree(relkinds); + } + + t_thrd.proc->session_gtt_frozenxid = InvalidTransactionId; + + return; +} + +/* + * Update global temp table relstats(relpage/reltuple/relallvisible) + * to local hashtable + */ +void up_gtt_relstats(const Relation relation, BlockNumber numPages, double numTuples, BlockNumber numAllVisiblePages, + TransactionId relfrozenxid) +{ + Oid relid = RelationGetRelid(relation); + gtt_local_hash_entry* entry; + gtt_relfilenode* gttRnode = NULL; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return; + + entry = gtt_search_by_relid(relid, true); + if (entry == NULL) + return; + + gttRnode = reinterpret_cast(lfirst(list_tail(entry->relfilenode_list))); + if (gttRnode == NULL) + return; + + if (gttRnode->relpages != static_cast(numPages)) { + gttRnode->relpages = static_cast(numPages); + } + + if (numTuples >= 0 && gttRnode->reltuples != (float4)numTuples) + gttRnode->reltuples = (float4)numTuples; + + /* only heap contain transaction information and relallvisible */ + if (entry->relkind == RELKIND_RELATION) { + if (gttRnode->relallvisible >= 0 && gttRnode->relallvisible != static_cast(numAllVisiblePages)) { + gttRnode->relallvisible = static_cast(numAllVisiblePages); + } + + if (TransactionIdIsNormal(relfrozenxid) && gttRnode->relfrozenxid != relfrozenxid && + (TransactionIdPrecedes(gttRnode->relfrozenxid, relfrozenxid) || + TransactionIdPrecedes(ReadNewTransactionId(), gttRnode->relfrozenxid))) { + remove_gtt_relfrozenxid_from_ordered_list((Oid)(gttRnode->relfrozenxid)); + gttRnode->relfrozenxid = relfrozenxid; + insert_gtt_relfrozenxid_to_ordered_list((Oid)relfrozenxid); + set_gtt_session_relfrozenxid(); + } + } + + return; +} + +/* + * Search global temp table relstats(relpage/reltuple/relallvisible) + * from local hashtable. + */ +bool get_gtt_relstats(Oid relid, BlockNumber* relpages, double* reltuples, BlockNumber* relallvisible, + TransactionId* relfrozenxid) +{ + gtt_local_hash_entry* entry; + gtt_relfilenode* gttRnode = NULL; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return false; + + entry = gtt_search_by_relid(relid, true); + if (entry == NULL) + return false; + + Assert(entry->relid == relid); + + gttRnode = reinterpret_cast(lfirst(list_tail(entry->relfilenode_list))); + if (gttRnode == NULL) + return false; + + if (relpages) + *relpages = (unsigned int)(gttRnode->relpages); + + if (reltuples) + *reltuples = gttRnode->reltuples; + + if (relallvisible) + *relallvisible = (unsigned int)(gttRnode->relallvisible); + + if (relfrozenxid) + *relfrozenxid = gttRnode->relfrozenxid; + + return true; +} + +/* + * Update global temp table statistic info(definition is same as pg_statistic) + * to local hashtable where ananyze global temp table + */ +void up_gtt_att_statistic( + Oid reloid, int attnum, int natts, TupleDesc tupleDescriptor, Datum* values, bool* isnull) +{ + gtt_local_hash_entry* entry; + MemoryContext oldcontext; + int i = 0; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return; + + entry = gtt_search_by_relid(reloid, true); + if (entry == NULL) + return; + + if (entry->natts < natts) { + elog(WARNING, "reloid %u not support update attstat after add colunm", reloid); + return; + } + + oldcontext = MemoryContextSwitchTo(gtt_relstats_context); + Assert(entry->relid == reloid); + for (i = 0; i < entry->natts; i++) { + if (entry->attnum[i] == 0) { + entry->attnum[i] = attnum; + break; + } else if (entry->attnum[i] == attnum) { + Assert(entry->att_stat_tups[i]); + heap_freetuple(entry->att_stat_tups[i]); + entry->att_stat_tups[i] = NULL; + break; + } + } + + Assert(i < entry->natts); + Assert(entry->att_stat_tups[i] == NULL); + entry->att_stat_tups[i] = heap_form_tuple(tupleDescriptor, values, isnull); + (void)MemoryContextSwitchTo(oldcontext); + + return; +} + +/* + * Search global temp table statistic info(definition is same as pg_statistic) + * from local hashtable. + */ +HeapTuple get_gtt_att_statistic(Oid reloid, int attnum) +{ + gtt_local_hash_entry* entry; + int i = 0; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return NULL; + + entry = gtt_search_by_relid(reloid, true); + if (entry == NULL) + return NULL; + + for (i = 0; i < entry->natts; i++) { + if (entry->attnum[i] == attnum) { + Assert(entry->att_stat_tups[i]); + return entry->att_stat_tups[i]; + } + } + + return NULL; +} + +void release_gtt_statistic_cache(HeapTuple tup) +{ + /* do nothing */ + return; +} + +static void insert_gtt_relfrozenxid_to_ordered_list(Oid relfrozenxid) +{ + MemoryContext oldcontext; + ListCell* cell; + int i; + + Assert(TransactionIdIsNormal(relfrozenxid)); + + oldcontext = MemoryContextSwitchTo(gtt_relstats_context); + /* Does the datum belong at the front? */ + if (gtt_session_relfrozenxid_list == NIL || + TransactionIdFollowsOrEquals(relfrozenxid, linitial_oid(gtt_session_relfrozenxid_list))) { + gtt_session_relfrozenxid_list = lcons_oid(relfrozenxid, gtt_session_relfrozenxid_list); + (void)MemoryContextSwitchTo(oldcontext); + + return; + } + + /* No, so find the entry it belongs after */ + i = 0; + foreach (cell, gtt_session_relfrozenxid_list) { + if (TransactionIdFollowsOrEquals(relfrozenxid, lfirst_oid(cell))) + break; + + i++; + } + gtt_session_relfrozenxid_list = + list_insert_nth_oid(gtt_session_relfrozenxid_list, i, relfrozenxid); + (void)MemoryContextSwitchTo(oldcontext); + + return; +} + +static void remove_gtt_relfrozenxid_from_ordered_list(Oid relfrozenxid) +{ + gtt_session_relfrozenxid_list = list_delete_oid(gtt_session_relfrozenxid_list, relfrozenxid); +} + +static void set_gtt_session_relfrozenxid(void) +{ + TransactionId gtt_frozenxid = InvalidTransactionId; + + if (gtt_session_relfrozenxid_list) + gtt_frozenxid = llast_oid(gtt_session_relfrozenxid_list); + + gtt_session_frozenxid = gtt_frozenxid; + if (t_thrd.proc->session_gtt_frozenxid != gtt_frozenxid) + t_thrd.proc->session_gtt_frozenxid = gtt_frozenxid; +} + +Datum pg_get_gtt_statistics(PG_FUNCTION_ARGS) +{ + HeapTuple tuple; + int attnum = PG_GETARG_INT32(1); + Oid reloid = PG_GETARG_OID(0); + char relPersistence; + ReturnSetInfo* rsinfo = reinterpret_cast(fcinfo->resultinfo); + TupleDesc tupdesc; + MemoryContext oldcontext; + Tuplestorestate* tupstore; + TupleDesc sd; + + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { + elog(ERROR, "return type must be a row type"); + } + + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot " + "accept a set"))); + } + + if (!(rsinfo->allowedModes & SFRM_Materialize)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " + "allowed in this context"))); + } + + oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); + + tupstore = tuplestore_begin_heap(true, false, u_sess->attr.attr_memory.work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + (void)MemoryContextSwitchTo(oldcontext); + + Relation rel = relation_open(reloid, AccessShareLock); + relPersistence = get_rel_persistence(reloid); + if (relPersistence != RELPERSISTENCE_GLOBAL_TEMP) { + elog(WARNING, "relation OID %u is not a global temporary table", reloid); + relation_close(rel, NoLock); + return (Datum)0; + } + + Relation pgStatistic = relation_open(StatisticRelationId, AccessShareLock); + sd = RelationGetDescr(pgStatistic); + + tuple = get_gtt_att_statistic(reloid, attnum); + if (tuple) { + Datum values[Natts_pg_statistic]; + bool isnull[Natts_pg_statistic]; + HeapTuple res = NULL; + + errno_t rc = memset_s(&values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "", ""); + rc = memset_s(isnull, sizeof(isnull), 0, sizeof(isnull)); + securec_check(rc, "", ""); + heap_deform_tuple(tuple, sd, values, isnull); + res = heap_form_tuple(tupdesc, values, isnull); + tuplestore_puttuple(tupstore, res); + } + + relation_close(rel, NoLock); + relation_close(pgStatistic, AccessShareLock); + tuplestore_donestoring(tupstore); + + return (Datum)0; +} + +Datum pg_get_gtt_relstats(PG_FUNCTION_ARGS) +{ + ReturnSetInfo* rsinfo = reinterpret_cast(fcinfo->resultinfo); + TupleDesc tupdesc; + Tuplestorestate* tupstore; + MemoryContext oldcontext; + HeapTuple tuple; + Oid reloid = PG_GETARG_OID(0); + char relPersistence; + BlockNumber relpages = 0; + double reltuples = 0; + BlockNumber relallvisible = 0; + TransactionId relfrozenxid = 0; + + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { + elog(ERROR, "return type must be a row type"); + } + + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot " + "accept a set"))); + } + + if (!(rsinfo->allowedModes & SFRM_Materialize)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not allowed " + "in this context"))); + } + + oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); + tupstore = tuplestore_begin_heap(true, false, u_sess->attr.attr_memory.work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + (void)MemoryContextSwitchTo(oldcontext); + + Relation rel = relation_open(reloid, AccessShareLock); + relPersistence = get_rel_persistence(reloid); + if (relPersistence != RELPERSISTENCE_GLOBAL_TEMP) { + elog(WARNING, "relation OID %u is not a global temporary table", reloid); + relation_close(rel, NoLock); + return (Datum)0; + } + + (void)get_gtt_relstats(reloid, &relpages, &reltuples, &relallvisible, &relfrozenxid); + Oid relnode = gtt_fetch_current_relfilenode(reloid); + if (relnode != InvalidOid) { + // output attribute: relfilenode | relpages | reltuples | relallvisible | relfrozenxid | relminmxid + Datum values[6]; + bool isnull[6]; + + errno_t rc = memset_s(isnull, sizeof(isnull), 0, sizeof(isnull)); + securec_check(rc, "", ""); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "", ""); + values[0] = UInt32GetDatum(relnode); + values[1] = Int32GetDatum(relpages); + values[2] = Float4GetDatum((float4)reltuples); + values[3] = Int32GetDatum(relallvisible); + values[4] = UInt32GetDatum(relfrozenxid); + tuple = heap_form_tuple(tupdesc, values, isnull); + tuplestore_puttuple(tupstore, tuple); + } + + tuplestore_donestoring(tupstore); + relation_close(rel, NoLock); + + return (Datum)0; +} + +Datum pg_gtt_attached_pid(PG_FUNCTION_ARGS) +{ + ReturnSetInfo* rsinfo = reinterpret_cast(fcinfo->resultinfo); + TupleDesc tupdesc; + Tuplestorestate* tupstore; + MemoryContext oldcontext; + HeapTuple tuple; + Oid reloid = PG_GETARG_OID(0); + char relPersistence; + PGPROC* proc = NULL; + Bitmapset* map = NULL; + ThreadId pid = 0; + int backendid = 0; + + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot " + "accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not allowed " + "in this context"))); + + oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); + tupstore = tuplestore_begin_heap(true, false, u_sess->attr.attr_memory.work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + (void)MemoryContextSwitchTo(oldcontext); + + Relation rel = relation_open(reloid, AccessShareLock); + relPersistence = get_rel_persistence(reloid); + if (relPersistence != RELPERSISTENCE_GLOBAL_TEMP) { + elog(WARNING, "relation OID %u is not a global temporary table", reloid); + relation_close(rel, NoLock); + return (Datum)0; + } + + map = copy_active_gtt_bitmap(reloid); + if (map) { + backendid = bms_first_member(map); + + do { + proc = BackendIdGetProc(backendid); + pid = proc->pid; + // output attribute: relid | pid + Datum values[2]; + bool isnull[2]; + errno_t rc = memset_s(isnull, sizeof(isnull), 0, sizeof(isnull)); + securec_check(rc, "", ""); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "", ""); + values[0] = UInt32GetDatum(reloid); + values[1] = UInt64GetDatum(pid); + tuple = heap_form_tuple(tupdesc, values, isnull); + tuplestore_puttuple(tupstore, tuple); + backendid = bms_next_member(map, backendid); + } while (backendid > 0); + + pfree(map); + } + + tuplestore_donestoring(tupstore); + relation_close(rel, NoLock); + + return (Datum)0; +} + +Datum pg_list_gtt_relfrozenxids(PG_FUNCTION_ARGS) +{ + ReturnSetInfo* rsinfo = reinterpret_cast(fcinfo->resultinfo); + TupleDesc tupdesc; + Tuplestorestate* tupstore; + MemoryContext oldcontext; + HeapTuple tuple; + int numXid = g_instance.shmem_cxt.MaxBackends + 1; + ThreadId* pids = NULL; + TransactionId* xids = NULL; + int i = 0; + int j = 0; + + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { + elog(ERROR, "return type must be a row type"); + } + + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot " + "accept a set"))); + } + if (!(rsinfo->allowedModes & SFRM_Materialize)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not allowed " + "in this context"))); + } + + oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); + tupstore = tuplestore_begin_heap(true, false, u_sess->attr.attr_memory.work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + (void)MemoryContextSwitchTo(oldcontext); + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) { + return (Datum)0; + } + + if (RecoveryInProgress()) { + return (Datum)0; + } + + pids = (ThreadId*)palloc0(sizeof(ThreadId) * numXid); + xids = reinterpret_cast(palloc0(sizeof(TransactionId) * numXid)); + TransactionId oldest = list_all_session_gtt_frozenxids(numXid, pids, xids, &i); + if (i > 0) { + pids[i] = 0; + xids[i] = oldest; + i++; + + for (j = 0; j < i; j++) { + // output attribute: pid | relfrozenxid + Datum values[2]; + bool isnull[2]; + + errno_t rc = memset_s(isnull, sizeof(isnull), 0, sizeof(isnull)); + securec_check(rc, "", ""); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "", ""); + values[0] = UInt64GetDatum(pids[j]); + values[1] = UInt64GetDatum(xids[j]); + tuple = heap_form_tuple(tupdesc, values, isnull); + tuplestore_puttuple(tupstore, tuple); + } + } + tuplestore_donestoring(tupstore); + pfree(pids); + pfree(xids); + + return (Datum)0; +} + +void gtt_force_enable_index(Relation index) +{ + if (!RELATION_IS_GLOBAL_TEMP(index)) + return; + + Assert(index->rd_rel->relkind == RELKIND_INDEX); + Assert(OidIsValid(RelationGetRelid(index))); + + index->rd_index->indisvalid = true; + index->rd_index->indisready = true; +} + +void gtt_fix_index_state(Relation index) +{ + Oid indexOid = RelationGetRelid(index); + Oid relOid = index->rd_index->indrelid; + + if (!RELATION_IS_GLOBAL_TEMP(index)) + return; + + if (!index->rd_index->indisvalid) + return; + + if (gtt_storage_attached(relOid) && !gtt_storage_attached(indexOid)) { + index->rd_index->indisvalid = false; + index->rd_index->indisready = false; + } + + return; +} + +void init_gtt_storage(CmdType operation, ResultRelInfo* resultRelInfo) +{ + Relation relation = resultRelInfo->ri_RelationDesc; + int i; + Oid toastrelid; + + if (!RELATION_IS_GLOBAL_TEMP(relation)) { + return; + } + + if (!(operation == CMD_UTILITY || operation == CMD_INSERT)) { + return; + } + + if (!RELKIND_HAS_STORAGE(relation->rd_rel->relkind)) { + return; + } + + if (relation->rd_smgr == NULL) { + /* Open it at the smgr level if not already done */ + RelationOpenSmgr(relation); + } + if (smgrexists(relation->rd_smgr, MAIN_FORKNUM) && gtt_storage_attached(RelationGetRelid(relation))) { + return; + } + + RelationCreateStorage( + relation->rd_node, RELPERSISTENCE_GLOBAL_TEMP, relation->rd_rel->relowner, InvalidOid, relation); + for (i = 0; i < resultRelInfo->ri_NumIndices; i++) { + Relation index = resultRelInfo->ri_IndexRelationDescs[i]; + 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); + } + + toastrelid = relation->rd_rel->reltoastrelid; + if (OidIsValid(toastrelid)) { + Relation toastrel; + ListCell* indlist; + + toastrel = relation_open(toastrelid, RowExclusiveLock); + RelationCreateStorage( + toastrel->rd_node, RELPERSISTENCE_GLOBAL_TEMP, toastrel->rd_rel->relowner, InvalidOid, toastrel); + + foreach (indlist, RelationGetIndexList(toastrel)) { + Oid indexId = lfirst_oid(indlist); + Relation currentIndex; + IndexInfo* indexInfo = NULL; + + currentIndex = index_open(indexId, RowExclusiveLock); + + indexInfo = BuildDummyIndexInfo(currentIndex); + index_build( + toastrel, NULL, currentIndex, NULL, indexInfo, currentIndex->rd_index->indisprimary, false, false); + index_close(currentIndex, NoLock); + } + + relation_close(toastrel, NoLock); + } + + return; +} + +static void gtt_reset_statistics(gtt_local_hash_entry* entry) +{ + int i; + + for (i = 0; i < entry->natts; i++) { + if (entry->att_stat_tups[i]) { + heap_freetuple(entry->att_stat_tups[i]); + entry->att_stat_tups[i] = NULL; + } + + entry->attnum[i] = 0; + } + + return; +} + +static void gtt_free_statistics(gtt_local_hash_entry* entry) +{ + int i; + + for (i = 0; i < entry->natts; i++) { + if (entry->att_stat_tups[i]) { + heap_freetuple(entry->att_stat_tups[i]); + entry->att_stat_tups[i] = NULL; + } + } + + if (entry->attnum) + pfree(entry->attnum); + + if (entry->att_stat_tups) + pfree(entry->att_stat_tups); + + return; +} + +Oid gtt_fetch_current_relfilenode(Oid relid) +{ + gtt_local_hash_entry* entry; + gtt_relfilenode* gttRnode = NULL; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return InvalidOid; + + entry = gtt_search_by_relid(relid, true); + if (entry == NULL) + return InvalidOid; + + Assert(entry->relid == relid); + + gttRnode = reinterpret_cast(lfirst(list_tail(entry->relfilenode_list))); + if (gttRnode == NULL) + return InvalidOid; + + return gttRnode->relfilenode; +} + +void gtt_switch_rel_relfilenode(Oid rel1, Oid relfilenode1, Oid rel2, Oid relfilenode2, bool footprint) +{ + gtt_local_hash_entry* entry1; + gtt_local_hash_entry* entry2; + gtt_relfilenode* gttRnode1 = NULL; + gtt_relfilenode* gttRnode2 = NULL; + MemoryContext oldcontext; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return; + + if (gtt_storage_local_hash == NULL) + return; + + entry1 = gtt_search_by_relid(rel1, false); + gttRnode1 = gtt_search_relfilenode(entry1, relfilenode1, false); + + entry2 = gtt_search_by_relid(rel2, false); + gttRnode2 = gtt_search_relfilenode(entry2, relfilenode2, false); + + oldcontext = MemoryContextSwitchTo(gtt_relstats_context); + entry1->relfilenode_list = list_delete_ptr(entry1->relfilenode_list, gttRnode1); + entry2->relfilenode_list = lappend(entry2->relfilenode_list, gttRnode1); + + entry2->relfilenode_list = list_delete_ptr(entry2->relfilenode_list, gttRnode2); + entry1->relfilenode_list = lappend(entry1->relfilenode_list, gttRnode2); + (void)MemoryContextSwitchTo(oldcontext); + + if (footprint) { + entry1->oldrelid = rel2; + entry2->oldrelid = rel1; + } + + return; +} + +static gtt_relfilenode* gtt_search_relfilenode(const gtt_local_hash_entry* entry, Oid relfilenode, bool missingOk) +{ + gtt_relfilenode* rnode = NULL; + ListCell* lc; + + Assert(entry); + + foreach (lc, entry->relfilenode_list) { + gtt_relfilenode* gtt_rnode = reinterpret_cast(lfirst(lc)); + if (gtt_rnode->relfilenode == relfilenode) { + rnode = gtt_rnode; + break; + } + } + + if (!missingOk && rnode == NULL) { + elog(ERROR, "find relfilenode %u relfilenodelist from relid %u fail", relfilenode, entry->relid); + } + + return rnode; +} + +static gtt_local_hash_entry* gtt_search_by_relid(Oid relid, bool missingOk) +{ + gtt_local_hash_entry* entry = NULL; + + if (gtt_storage_local_hash == NULL) { + return NULL; + } + + entry = reinterpret_cast(hash_search(gtt_storage_local_hash, + reinterpret_cast(&(relid)), HASH_FIND, NULL)); + + if (entry == NULL && !missingOk) { + elog(ERROR, "relid %u not found in local hash", relid); + } + + return entry; +} + diff --git a/src/common/backend/catalog/system_views.sql b/src/common/backend/catalog/system_views.sql index 979474336fba4dc5dd9f6f4b8e494e6b0d8c7124..760df349f7f1bd221b3a40fbd1e61bc47f55f7f2 100644 --- a/src/common/backend/catalog/system_views.sql +++ b/src/common/backend/catalog/system_views.sql @@ -182,6 +182,117 @@ CREATE VIEW pg_indexes AS LEFT JOIN pg_tablespace T ON (T.oid = I.reltablespace) WHERE C.relkind = 'r' AND I.relkind = 'i'; +-- For global temporary table +CREATE VIEW pg_gtt_relstats WITH (security_barrier) AS + SELECT n.nspname AS schemaname, + c.relname AS tablename, + (select relfilenode from pg_get_gtt_relstats(c.oid)), + (select relpages from pg_get_gtt_relstats(c.oid)), + (select reltuples from pg_get_gtt_relstats(c.oid)), + (select relallvisible from pg_get_gtt_relstats(c.oid)), + (select relfrozenxid from pg_get_gtt_relstats(c.oid)), + (select relminmxid from pg_get_gtt_relstats(c.oid)) + FROM + pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relpersistence='g' AND c.relkind in('r','p','i','t'); + +CREATE VIEW pg_gtt_attached_pids WITH (security_barrier) AS + SELECT n.nspname AS schemaname, + c.relname AS tablename, + c.oid AS relid, + array(select pid from pg_gtt_attached_pid(c.oid)) AS pids + FROM + pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relpersistence='g' AND c.relkind in('r','S'); + +CREATE VIEW pg_gtt_stats WITH (security_barrier) AS +SELECT s.nspname AS schemaname, + s.relname AS tablename, + s.attname, + s.stainherit AS inherited, + s.stanullfrac AS null_frac, + s.stawidth AS avg_width, + s.stadistinct AS n_distinct, + CASE + WHEN s.stakind1 = 1 THEN s.stavalues1 + WHEN s.stakind2 = 1 THEN s.stavalues2 + WHEN s.stakind3 = 1 THEN s.stavalues3 + WHEN s.stakind4 = 1 THEN s.stavalues4 + WHEN s.stakind5 = 1 THEN s.stavalues5 + END AS most_common_vals, + CASE + WHEN s.stakind1 = 1 THEN s.stanumbers1 + WHEN s.stakind2 = 1 THEN s.stanumbers2 + WHEN s.stakind3 = 1 THEN s.stanumbers3 + WHEN s.stakind4 = 1 THEN s.stanumbers4 + WHEN s.stakind5 = 1 THEN s.stanumbers5 + END AS most_common_freqs, + CASE + WHEN s.stakind1 = 2 THEN s.stavalues1 + WHEN s.stakind2 = 2 THEN s.stavalues2 + WHEN s.stakind3 = 2 THEN s.stavalues3 + WHEN s.stakind4 = 2 THEN s.stavalues4 + WHEN s.stakind5 = 2 THEN s.stavalues5 + END AS histogram_bounds, + CASE + WHEN s.stakind1 = 3 THEN s.stanumbers1[1] + WHEN s.stakind2 = 3 THEN s.stanumbers2[1] + WHEN s.stakind3 = 3 THEN s.stanumbers3[1] + WHEN s.stakind4 = 3 THEN s.stanumbers4[1] + WHEN s.stakind5 = 3 THEN s.stanumbers5[1] + END AS correlation, + CASE + WHEN s.stakind1 = 4 THEN s.stavalues1 + WHEN s.stakind2 = 4 THEN s.stavalues2 + WHEN s.stakind3 = 4 THEN s.stavalues3 + WHEN s.stakind4 = 4 THEN s.stavalues4 + WHEN s.stakind5 = 4 THEN s.stavalues5 + END AS most_common_elems, + CASE + WHEN s.stakind1 = 4 THEN s.stanumbers1 + WHEN s.stakind2 = 4 THEN s.stanumbers2 + WHEN s.stakind3 = 4 THEN s.stanumbers3 + WHEN s.stakind4 = 4 THEN s.stanumbers4 + WHEN s.stakind5 = 4 THEN s.stanumbers5 + END AS most_common_elem_freqs, + CASE + WHEN s.stakind1 = 5 THEN s.stanumbers1 + WHEN s.stakind2 = 5 THEN s.stanumbers2 + WHEN s.stakind3 = 5 THEN s.stanumbers3 + WHEN s.stakind4 = 5 THEN s.stanumbers4 + WHEN s.stakind5 = 5 THEN s.stanumbers5 + END AS elem_count_histogram + FROM + (SELECT n.nspname, + c.relname, + a.attname, + (select stainherit from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stainherit, + (select stanullfrac from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stanullfrac, + (select stawidth from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stawidth, + (select stadistinct from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stadistinct, + (select stakind1 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stakind1, + (select stakind2 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stakind2, + (select stakind3 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stakind3, + (select stakind4 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stakind4, + (select stakind5 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stakind5, + (select stanumbers1 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stanumbers1, + (select stanumbers2 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stanumbers2, + (select stanumbers3 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stanumbers3, + (select stanumbers4 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stanumbers4, + (select stanumbers5 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stanumbers5, + (select stavalues1 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stavalues1, + (select stavalues2 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stavalues2, + (select stavalues3 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stavalues3, + (select stavalues4 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stavalues4, + (select stavalues5 from pg_get_gtt_statistics(c.oid, a.attnum, ''::text)) as stavalues5 + FROM + pg_class c + JOIN pg_attribute a ON c.oid = a.attrelid + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relpersistence='g' AND c.relkind in('r','p','i','t') and a.attnum > 0 and NOT a.attisdropped AND has_column_privilege(c.oid, a.attnum, 'select'::text)) s; + CREATE VIEW pg_stats AS SELECT nspname AS schemaname, diff --git a/src/common/backend/nodes/list.cpp b/src/common/backend/nodes/list.cpp index 94d965dab8535d0a529599a7068c2da05767f700..76de79048850aa8e832af7b5d78bacfc2ca6182b 100755 --- a/src/common/backend/nodes/list.cpp +++ b/src/common/backend/nodes/list.cpp @@ -1750,3 +1750,22 @@ List* list_merge_int(List* list1, List* list2) return list_dst; } + +List* list_insert_nth_oid(List* list, int pos, Oid datum) +{ + if (list == NIL) { + Assert(pos == 0); + return list_make1_oid(datum); + } + Assert(IsOidList(list)); + + if (pos == 0) { // add at first pos + list = lcons_oid(datum, list); + } else { // find cell at pos - 1, then add new cell after it; + ListCell* prevCell = list_nth_cell(list, pos - 1); + ListCell* newCell = add_new_cell(list, prevCell); + lfirst_oid(newCell) = datum; + } + check_list_invariants(list); + return list; +} diff --git a/src/common/backend/nodes/makefuncs.cpp b/src/common/backend/nodes/makefuncs.cpp index 5eaf948c98785fc4e9f96afb56c6affdf205601a..a14815dbb44c54a4d180ad02a1e6664afedf51bb 100755 --- a/src/common/backend/nodes/makefuncs.cpp +++ b/src/common/backend/nodes/makefuncs.cpp @@ -615,3 +615,36 @@ Param* makeParam(ParamKind paramkind, int paramid, Oid paramtype, int32 paramtyp return argp; } + +/* + * makeIndexInfo + * create an IndexInfo node + */ +IndexInfo* makeIndexInfo(int numattrs, List* expressions, List* predicates, bool unique, bool isready, bool concurrent) +{ + IndexInfo* n = makeNode(IndexInfo); + + n->ii_NumIndexAttrs = numattrs; + n->ii_Unique = unique; + n->ii_ReadyForInserts = isready; + n->ii_Concurrent = concurrent; + + /* expressions */ + n->ii_Expressions = expressions; + n->ii_ExpressionsState = NIL; + + /* predicates */ + n->ii_Predicate = predicates; + n->ii_PredicateState = NULL; + + /* exclusion constraints */ + n->ii_ExclusionOps = NULL; + n->ii_ExclusionProcs = NULL; + n->ii_ExclusionStrats = NULL; + + /* initialize index-build state to default */ + n->ii_BrokenHotChain = false; + n->ii_PgClassAttrId = 0; + + return n; +} diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index e1f7bff5175aefb22105dbcd4d5442334d88d9bf..e94c320e71eaa65cb9dfe756de2873f7df4e8001 100755 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -4184,20 +4184,8 @@ OptTemp: TEMPORARY { $$ = RELPERSISTENCE_TEMP; } | TEMP { $$ = RELPERSISTENCE_TEMP; } | LOCAL TEMPORARY { $$ = RELPERSISTENCE_TEMP; } | LOCAL TEMP { $$ = RELPERSISTENCE_TEMP; } - | GLOBAL TEMPORARY - { - ereport(WARNING, - (errmsg("GLOBAL is deprecated in temporary table creation"), - parser_errposition(@1))); - $$ = RELPERSISTENCE_TEMP; - } - | GLOBAL TEMP - { - ereport(WARNING, - (errmsg("GLOBAL is deprecated in temporary table creation"), - parser_errposition(@1))); - $$ = RELPERSISTENCE_TEMP; - } + | GLOBAL TEMPORARY { $$ = RELPERSISTENCE_GLOBAL_TEMP; } + | GLOBAL TEMP { $$ = RELPERSISTENCE_GLOBAL_TEMP; } | UNLOGGED { $$ = RELPERSISTENCE_UNLOGGED; } | /*EMPTY*/ { $$ = RELPERSISTENCE_PERMANENT; } ; @@ -13646,19 +13634,13 @@ OptTempTableName: } | GLOBAL TEMPORARY opt_table qualified_name { - ereport(WARNING, - (errmsg("GLOBAL is deprecated in temporary table creation"), - parser_errposition(@1))); $$ = $4; - $$->relpersistence = RELPERSISTENCE_TEMP; + $$->relpersistence = RELPERSISTENCE_GLOBAL_TEMP; } | GLOBAL TEMP opt_table qualified_name { - ereport(WARNING, - (errmsg("GLOBAL is deprecated in temporary table creation"), - parser_errposition(@1))); $$ = $4; - $$->relpersistence = RELPERSISTENCE_TEMP; + $$->relpersistence = RELPERSISTENCE_GLOBAL_TEMP; } | UNLOGGED opt_table qualified_name { diff --git a/src/common/backend/parser/parse_utilcmd.cpp b/src/common/backend/parser/parse_utilcmd.cpp index 7744e21325d8d6f3dc43208755ed5e718e0eea18..b9cae1919f76b3336f13cbb361cd162dba585467 100644 --- a/src/common/backend/parser/parse_utilcmd.cpp +++ b/src/common/backend/parser/parse_utilcmd.cpp @@ -3508,6 +3508,7 @@ List* transformAlterTableStmt(Oid relid, AlterTableStmt* stmt, const char* query cxt.stmtType = ALTER_TABLE; } cxt.relation = stmt->relation; + cxt.relation->relpersistence = RelationGetRelPersistence(rel); cxt.rel = rel; cxt.inhRelations = NIL; cxt.isalter = true; diff --git a/src/common/backend/utils/adt/dbsize.cpp b/src/common/backend/utils/adt/dbsize.cpp index 2f4e3f3d2b495144ea9c4ee69462f38e0ac72052..0b1bf5f39eb54b7eaf0623bcb43cff73d927a282 100755 --- a/src/common/backend/utils/adt/dbsize.cpp +++ b/src/common/backend/utils/adt/dbsize.cpp @@ -1822,10 +1822,13 @@ Datum pg_relation_filepath(PG_FUNCTION_ARGS) switch (relform->relpersistence) { case RELPERSISTENCE_UNLOGGED: case RELPERSISTENCE_PERMANENT: - case RELPERSISTENCE_TEMP: // @Temp Table. temp table is the same as unlogged table here. + case RELPERSISTENCE_TEMP: backend = InvalidBackendId; break; + case RELPERSISTENCE_GLOBAL_TEMP: + backend = t_thrd.proc_cxt.MyBackendId; + break; default: ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), diff --git a/src/common/backend/utils/adt/selfuncs.cpp b/src/common/backend/utils/adt/selfuncs.cpp index 9a75abd6584a845ebcc9182d855f5a1918c3c113..a007f26548f4e960eb693852dcec04f3a8147a83 100644 --- a/src/common/backend/utils/adt/selfuncs.cpp +++ b/src/common/backend/utils/adt/selfuncs.cpp @@ -112,6 +112,7 @@ #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "catalog/pg_proc.h" +#include "catalog/storage_gtt.h" #include "executor/executor.h" #include "foreign/foreign.h" #include "mb/pg_wchar.h" @@ -4754,18 +4755,23 @@ void examine_variable(PlannerInfo* root, Node* node, int var_relid, VariableStat */ char stakind = STARELKIND_CLASS; Oid indexid = index->indexoid; - + char relPersistence = get_rel_persistence(index->indexoid); + if (u_sess->attr.attr_common.upgrade_mode != 0) { var_data->statsTuple = NULL; + var_data->freefunc = ReleaseSysCache; + } else if (relPersistence == RELPERSISTENCE_GLOBAL_TEMP) { + var_data->statsTuple = get_gtt_att_statistic(index->indexoid, + Int16GetDatum(pos + 1)); + var_data->freefunc = release_gtt_statistic_cache; } else { var_data->statsTuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(indexid), CharGetDatum(stakind), Int16GetDatum(pos + 1), BoolGetDatum(false)); + var_data->freefunc = ReleaseSysCache; } - - var_data->freefunc = ReleaseSysCache; if (HeapTupleIsValid(var_data->statsTuple)) { /* Get index's table for permission check */ RangeTblEntry *rte; @@ -4853,17 +4859,22 @@ static void examine_simple_variable(PlannerInfo* root, Var* var, VariableStatDat * * We do not search system cache in upgrading */ + char relPersistence = get_rel_persistence(rte->relid); if (u_sess->attr.attr_common.upgrade_mode != 0) { var_data->statsTuple = NULL; + var_data->freefunc = ReleaseSysCache; + } else if (relPersistence == RELPERSISTENCE_GLOBAL_TEMP) { + var_data->statsTuple = get_gtt_att_statistic(rte->relid, var->varattno); + var_data->freefunc = release_gtt_statistic_cache; } else { var_data->statsTuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(sta_relid), CharGetDatum(sta_kind), Int16GetDatum(var->varattno), BoolGetDatum(rte->inh)); + var_data->freefunc = ReleaseSysCache; } - var_data->freefunc = ReleaseSysCache; - + if (HeapTupleIsValid(var_data->statsTuple)) { /* check if user has permission to read this column */ var_data->aclOk = (pg_class_aclcheck(rte->relid, GetUserId(), ACL_SELECT) == ACLCHECK_OK) || @@ -5800,7 +5811,6 @@ static Pattern_Prefix_Status regex_fixed_prefix( /* Use the regexp machinery to extract the prefix, if any */ prefix = regexp_fixed_prefix(DatumGetTextPP(patt_const->constvalue), case_insensitive, collation, &exact); - if (prefix == NULL) { *prefix_const = NULL; @@ -6851,7 +6861,7 @@ Datum btcostestimate(PG_FUNCTION_ARGS) if (index->indexkeys[0] != 0) { /* Simple variable --- look to stats for the underlying table */ RangeTblEntry* rte = planner_rt_fetch(index->rel->relid, root); - + char relPersistence = get_rel_persistence(rte->relid); Assert(rte->rtekind == RTE_RELATION); relid = rte->relid; Assert(relid != InvalidOid); @@ -6868,16 +6878,22 @@ Datum btcostestimate(PG_FUNCTION_ARGS) if (u_sess->attr.attr_common.upgrade_mode != 0) { var_data.statsTuple = NULL; + var_data.freefunc = ReleaseSysCache; + } else if (relPersistence == RELPERSISTENCE_GLOBAL_TEMP) { + var_data.statsTuple = get_gtt_att_statistic(rte->relid, col_num); + var_data.freefunc = release_gtt_statistic_cache; } else { var_data.statsTuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(staoid), CharGetDatum(stakind), Int16GetDatum(col_num), BoolGetDatum(rte->inh)); + var_data.freefunc = ReleaseSysCache; } - var_data.freefunc = ReleaseSysCache; + } else { /* Expression --- maybe there are stats for the index itself */ + char relPersistence = get_rel_persistence(index->indexoid); relid = index->indexoid; col_num = 1; @@ -6892,14 +6908,19 @@ Datum btcostestimate(PG_FUNCTION_ARGS) if (u_sess->attr.attr_common.upgrade_mode != 0) { var_data.statsTuple = NULL; + var_data.freefunc = ReleaseSysCache; + } else if (relPersistence == RELPERSISTENCE_GLOBAL_TEMP) { + var_data.statsTuple = get_gtt_att_statistic(relid, col_num); + var_data.freefunc = release_gtt_statistic_cache; } else { var_data.statsTuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(staoid), CharGetDatum(stakind), Int16GetDatum(col_num), BoolGetDatum(false)); + var_data.freefunc = ReleaseSysCache; } - var_data.freefunc = ReleaseSysCache; + } if (HeapTupleIsValid(var_data.statsTuple)) { @@ -8420,7 +8441,6 @@ void set_noanalyze_rellist(Oid relid, AttrNumber attid) * so only check the statistics of foreign table. do not check column statistic. */ Relation rel = relation_open(relid, AccessShareLock); - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isSpecifiedSrvTypeFromRelId(relid, OBS_SERVER)) { is_obs_ft = true; } diff --git a/src/common/backend/utils/cache/catcache.cpp b/src/common/backend/utils/cache/catcache.cpp index 3cd55144bb9c90ccb2ec23de8ffebb3c3c71e8ab..db6c26219f9d908b8c31da9b6d4c96e7da4fccb2 100644 --- a/src/common/backend/utils/cache/catcache.cpp +++ b/src/common/backend/utils/cache/catcache.cpp @@ -697,6 +697,25 @@ void AtEOXact_CatCache(bool is_commit) #endif } +/* + * Standard routine for creating cache context if it doesn't exist yet + * + * There are a lot of places (probably far more than necessary) that check + * whether CacheMemoryContext exists yet and want to create it if not. + * We centralize knowledge of exactly how to create it here. + */ +void CreateCacheMemoryContext(void) +{ + /* + * Purely for paranoia, check that context doesn't exist; caller probably + * did so already. + */ + if (!CacheMemoryContext) + CacheMemoryContext = AllocSetContextCreate(TopMemoryContext, + "CacheMemoryContext", + ALLOCSET_DEFAULT_SIZES); +} + /* * reset_catalog_cache * diff --git a/src/common/backend/utils/cache/lsyscache.cpp b/src/common/backend/utils/cache/lsyscache.cpp index 18bae47682d64c8b95dc747c981879099b8ef4d6..da9bdde0264b5ceef549a1edb532e5b91bb1f2cb 100644 --- a/src/common/backend/utils/cache/lsyscache.cpp +++ b/src/common/backend/utils/cache/lsyscache.cpp @@ -47,6 +47,7 @@ #include "catalog/pg_app_workloadgroup_mapping.h" #include "catalog/namespace.h" #endif +#include "catalog/storage_gtt.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -1912,6 +1913,27 @@ Oid get_rel_tablespace(Oid relid) } } +/* + * get_rel_persistence + * + * Returns the relpersistence associated with a given relation. + */ +char get_rel_persistence(Oid relid) +{ + HeapTuple tp; + Form_pg_class reltup; + char result; + + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for relation %u", relid); + reltup = (Form_pg_class) GETSTRUCT(tp); + result = reltup->relpersistence; + ReleaseSysCache(tp); + + return result; +} + /* * get_typisdefined * @@ -4073,6 +4095,18 @@ int32 get_attavgwidth(Oid relid, AttrNumber attnum, bool ispartition) if (u_sess->attr.attr_common.upgrade_mode != 0) { return 0; } + if (!ispartition && get_rel_persistence(relid) == RELPERSISTENCE_GLOBAL_TEMP) { + tp = get_gtt_att_statistic(relid, attnum); + if (!HeapTupleIsValid(tp)) { + return 0; + } + stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth; + if (stawidth > 0) { + return stawidth; + } else { + return 0; + } + } tp = SearchSysCache4( STATRELKINDATTINH, ObjectIdGetDatum(relid), CharGetDatum(stakind), Int16GetDatum(attnum), BoolGetDatum(false)); if (HeapTupleIsValid(tp)) { diff --git a/src/common/backend/utils/cache/relcache.cpp b/src/common/backend/utils/cache/relcache.cpp index 464abb73c1894aaf463cc9464b3e73cb8b260058..0d16b6627b598c563a4c52d746e3c958a84412bb 100644 --- a/src/common/backend/utils/cache/relcache.cpp +++ b/src/common/backend/utils/cache/relcache.cpp @@ -112,6 +112,7 @@ #include "catalog/schemapg.h" #include "catalog/storage.h" #include "catalog/pg_extension_data_source.h" +#include "catalog/storage_gtt.h" #include "commands/sec_rls_cmds.h" #include "commands/tablespace.h" #include "commands/trigger.h" @@ -148,6 +149,8 @@ #include "utils/partitionmap_gs.h" #include "utils/resowner.h" #include "access/cstore_am.h" +#include "nodes/nodeFuncs.h" +#include "nodes/makefuncs.h" /* * name of relcache init file(s), used to speed up backend startup @@ -1693,10 +1696,27 @@ static Relation relation_build_desc(Oid targetRelId, bool insertIt, bool buildke switch (relation->rd_rel->relpersistence) { case RELPERSISTENCE_UNLOGGED: case RELPERSISTENCE_PERMANENT: - case RELPERSISTENCE_TEMP: // @Temp Table. Temp table here is just like unlogged table. relation->rd_backend = InvalidBackendId; relation->rd_islocaltemp = false; break; + case RELPERSISTENCE_TEMP: // @Temp Table. Temp table here is just like unlogged table. + relation->rd_backend = InvalidBackendId; + relation->rd_islocaltemp = true; + break; + case RELPERSISTENCE_GLOBAL_TEMP: // global temp table + { + BlockNumber relpages = 0; + double reltuples = 0; + BlockNumber relallvisible = 0; + + relation->rd_backend = t_thrd.proc_cxt.MyBackendId; + relation->rd_islocaltemp = false; + get_gtt_relstats(RelationGetRelid(relation), &relpages, &reltuples, &relallvisible, NULL); + relation->rd_rel->relpages = static_cast(relpages); + relation->rd_rel->reltuples = static_cast(reltuples); + relation->rd_rel->relallvisible = static_cast(relallvisible); + } + break; default: ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1961,7 +1981,16 @@ static void relation_init_physical_addr(Relation relation) heap_freetuple_ext(phys_tuple); } - relation->rd_node.relNode = relation->rd_rel->relfilenode; + if (RELATION_IS_GLOBAL_TEMP(relation)) { + Oid newrelnode = gtt_fetch_current_relfilenode(RelationGetRelid(relation)); + if (newrelnode != InvalidOid && newrelnode != relation->rd_rel->relfilenode) { + relation->rd_node.relNode = newrelnode; + } else { + relation->rd_node.relNode = relation->rd_rel->relfilenode; + } + } else { + relation->rd_node.relNode = relation->rd_rel->relfilenode; + } } else { /* Consult the relation mapper */ relation->rd_node.relNode = RelationMapOidToFilenode(relation->rd_id, relation->rd_rel->relisshared); @@ -2735,6 +2764,8 @@ static void relation_reload_index_info(Relation relation) HeapTupleSetXmin(relation->rd_indextuple, HeapTupleGetRawXmin(tuple)); ReleaseSysCache(tuple); + + gtt_fix_index_state(relation); } /* Okay, now it's valid again */ @@ -3672,10 +3703,16 @@ Relation RelationBuildLocalRelation(const char* relname, Oid relnamespace, Tuple switch (relpersistence) { case RELPERSISTENCE_UNLOGGED: case RELPERSISTENCE_PERMANENT: + rel->rd_backend = InvalidBackendId; + rel->rd_islocaltemp = false; + break; case RELPERSISTENCE_TEMP: // @Temp Table. Temp table here is just like unlogged table. rel->rd_backend = InvalidBackendId; + rel->rd_islocaltemp = true; + break; + case RELPERSISTENCE_GLOBAL_TEMP: // global temp table + rel->rd_backend = t_thrd.proc_cxt.MyBackendId; rel->rd_islocaltemp = false; - break; default: ereport(ERROR, @@ -3815,8 +3852,8 @@ void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, bool Datum values[Natts_pg_class]; bool nulls[Natts_pg_class]; bool replaces[Natts_pg_class]; - errno_t rc; - + 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) ? freezeXid == InvalidTransactionId : TransactionIdIsNormal(freezeXid)) || @@ -3850,17 +3887,22 @@ void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, bool } } - /* - * Get a writable copy of the pg_class tuple for the given relation. - */ - pg_class = heap_open(RelationRelationId, RowExclusiveLock); - - tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(RelationGetRelid(relation))); - if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not find tuple for relation %u", RelationGetRelid(relation)))); - classform = (Form_pg_class)GETSTRUCT(tuple); + if (modifyPgClass) { + /* + * Get a writable copy of the pg_class tuple for the given relation. + */ + pg_class = heap_open(RelationRelationId, RowExclusiveLock); + tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(RelationGetRelid(relation))); + if (!HeapTupleIsValid(tuple)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not find tuple for relation %u", RelationGetRelid(relation)))); + } + classform = (Form_pg_class) GETSTRUCT(tuple); + } else { + memset_s(&classform, sizeof(classform), 0, sizeof(classform)); + securec_check(rc, "\0", "\0"); + } ereport(LOG, (errmsg("Relation %s(%u) set newfilenode %u oldfilenode %u xid %lu", @@ -3880,52 +3922,58 @@ void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, bool newrnode.node.relNode = newrelfilenode; newrnode.backend = relation->rd_backend; RelationCreateStorage( - newrnode.node, relation->rd_rel->relpersistence, relation->rd_rel->relowner, relation->rd_bucketoid); + newrnode.node, relation->rd_rel->relpersistence, relation->rd_rel->relowner, relation->rd_bucketoid, relation); smgrclosenode(newrnode); /* * Schedule unlinking of the old storage at transaction commit. */ RelationDropStorage(relation, isDfsTruncate); + if (!modifyPgClass) { + Oid relnode = gtt_fetch_current_relfilenode(RelationGetRelid(relation)); + Assert(RELATION_IS_GLOBAL_TEMP(relation)); + Assert(!RelationIsMapped(relation)); + relation->rd_node.relNode = relnode; + CacheInvalidateRelcache(relation); + } else { + /* + * Now update the pg_class row. However, if we're dealing with a mapped + * index, pg_class.relfilenode doesn't change; instead we have to send the + * update to the relation mapper. + */ + if (RelationIsMapped(relation)) + RelationMapUpdateMap(RelationGetRelid(relation), newrelfilenode, relation->rd_rel->relisshared, false); + else + classform->relfilenode = newrelfilenode; - /* - * Now update the pg_class row. However, if we're dealing with a mapped - * index, pg_class.relfilenode doesn't change; instead we have to send the - * update to the relation mapper. - */ - if (RelationIsMapped(relation)) - RelationMapUpdateMap(RelationGetRelid(relation), newrelfilenode, relation->rd_rel->relisshared, false); - else - classform->relfilenode = newrelfilenode; - - /* These changes are safe even for a mapped relation */ - if (relation->rd_rel->relkind != RELKIND_SEQUENCE) { - classform->relpages = 0; /* it's empty until further notice */ - classform->reltuples = 0; - classform->relallvisible = 0; - } - /* set classform's relfrozenxid and relfrozenxid64 */ - classform->relfrozenxid = (ShortTransactionId)InvalidTransactionId; - - rc = memset_s(values, sizeof(values), 0, sizeof(values)); - securec_check(rc, "\0", "\0"); - rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); - securec_check(rc, "\0", "\0"); - rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); - securec_check(rc, "\0", "\0"); + /* These changes are safe even for a mapped relation */ + if (relation->rd_rel->relkind != RELKIND_SEQUENCE) { + classform->relpages = 0; /* it's empty until further notice */ + classform->reltuples = 0; + classform->relallvisible = 0; + } + /* set classform's relfrozenxid and relfrozenxid64 */ + classform->relfrozenxid = (ShortTransactionId)InvalidTransactionId; - replaces[Anum_pg_class_relfrozenxid64 - 1] = true; - values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(freezeXid); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); - nctup = heap_modify_tuple(tuple, RelationGetDescr(pg_class), values, nulls, replaces); + replaces[Anum_pg_class_relfrozenxid64 - 1] = true; + values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(freezeXid); - simple_heap_update(pg_class, &nctup->t_self, nctup); - CatalogUpdateIndexes(pg_class, nctup); + nctup = heap_modify_tuple(tuple, RelationGetDescr(pg_class), values, nulls, replaces); - heap_freetuple_ext(nctup); - heap_freetuple_ext(tuple); + simple_heap_update(pg_class, &nctup->t_self, nctup); + CatalogUpdateIndexes(pg_class, nctup); - heap_close(pg_class, RowExclusiveLock); + heap_freetuple_ext(nctup); + heap_freetuple_ext(tuple); + heap_close(pg_class, RowExclusiveLock); + } /* * Make the pg_class row change visible, as well as the relation map @@ -5067,6 +5115,45 @@ List* RelationGetIndexExpressions(Relation relation) return result; } +/* + * RelationGetDummyIndexExpressions -- get dummy expressions for an index + * + * Return a list of dummy expressions (just Const nodes) with the same + * types/typmods/collations as the index's real expressions. This is + * useful in situations where we don't want to run any user-defined code. + */ +List* RelationGetDummyIndexExpressions(Relation relation) +{ + List* result; + Datum exprsDatum; + bool isnull; + char* exprsString; + List* rawExprs; + ListCell* lc; + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL)) { + return NIL; + } + + /* Extract raw node tree(s) from index tuple. */ + exprsDatum = heap_getattr(relation->rd_indextuple, Anum_pg_index_indexprs, get_pg_index_descriptor(), &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + rawExprs = (List*)stringToNode(exprsString); + pfree(exprsString); + + /* Construct null Consts; the typlen and typbyval are arbitrary. */ + result = NIL; + foreach (lc, rawExprs) { + Node* rawExpr = (Node*)lfirst(lc); + result = lappend( + result, makeConst(exprType(rawExpr), exprTypmod(rawExpr), exprCollation(rawExpr), 1, (Datum)0, true, true)); + } + + return result; +} + /* * RelationGetIndexPredicate -- get the index predicate for an index * diff --git a/src/common/backend/utils/misc/guc.cpp b/src/common/backend/utils/misc/guc.cpp index cfa110e6a7031820b2ab9c186985aa3e0d565c54..a841961f9033888b24186549e362eca03088f5e5 100644 --- a/src/common/backend/utils/misc/guc.cpp +++ b/src/common/backend/utils/misc/guc.cpp @@ -40,6 +40,7 @@ #include "access/dfs/dfs_insert.h" #include "catalog/namespace.h" #include "catalog/pgxc_group.h" +#include "catalog/storage_gtt.h" #include "commands/async.h" #include "commands/prepare.h" #include "commands/vacuum.h" @@ -4597,6 +4598,38 @@ void set_qunit_case_number_hook(int newval, void* extra) static void init_configure_names_int() { struct config_int local_configure_names_int[] = { + { + { + "max_active_global_temporary_table", + PGC_USERSET, + UNGROUPED, + gettext_noop("max active global temporary table."), + NULL + }, + &u_sess->attr.attr_storage.max_active_gtt, + 1000, + 0, + 1000000, + NULL, + NULL, + NULL + }, + { + { + "vacuum_gtt_defer_check_age", + PGC_USERSET, + CLIENT_CONN_STATEMENT, + gettext_noop("The defer check age of GTT, used to check expired data after vacuum."), + NULL + }, + &u_sess->attr.attr_storage.vacuum_gtt_defer_check_age, + 10000, + 0, + 1000000, + NULL, + NULL, + NULL + }, { { "archive_timeout", diff --git a/src/common/backend/utils/mmgr/mcxt.cpp b/src/common/backend/utils/mmgr/mcxt.cpp index 2812326d963c10adf8f800fa6f158bdf8b390ed2..bd01afdd81d641b6158c1b398c17b72f56321db3 100755 --- a/src/common/backend/utils/mmgr/mcxt.cpp +++ b/src/common/backend/utils/mmgr/mcxt.cpp @@ -57,6 +57,7 @@ MemoryContext StreamInfoContext = NULL; * of these contexts, refer to src/backend/utils/mmgr/README */ THR_LOCAL MemoryContext ErrorContext = NULL; +THR_LOCAL MemoryContext CacheMemoryContext = NULL; THR_LOCAL MemoryContext SelfMemoryContext = NULL; THR_LOCAL MemoryContext TopMemoryContext = NULL; THR_LOCAL MemoryContext AlignMemoryContext = NULL; diff --git a/src/gausskernel/bootstrap/bootparse.y b/src/gausskernel/bootstrap/bootparse.y index 3ea1cb58c2b20ff60b30253cc01ba35644dfe32c..7aab250bbece9f420718a3630b6f5d59f7d1986f 100755 --- a/src/gausskernel/bootstrap/bootparse.y +++ b/src/gausskernel/bootstrap/bootparse.y @@ -233,7 +233,8 @@ Boot_CreateStmt: mapped_relation, true, REL_CMPRS_NOT_SUPPORT, - BOOTSTRAP_SUPERUSERID); + BOOTSTRAP_SUPERUSERID, + false); ereport(DEBUG4, (errmsg("bootstrap relation created"))); /* diff --git a/src/gausskernel/optimizer/commands/analyze.cpp b/src/gausskernel/optimizer/commands/analyze.cpp index 4b6943187745af62afe38fb4d0ab892c5923496d..cef10598dc6a09e95b538553b54e97f8f50f3f35 100755 --- a/src/gausskernel/optimizer/commands/analyze.cpp +++ b/src/gausskernel/optimizer/commands/analyze.cpp @@ -34,6 +34,7 @@ #include "catalog/pg_statistic_ext.h" #include "catalog/pg_hashbucket_fn.h" #include "catalog/namespace.h" +#include "catalog/storage_gtt.h" #include "commands/dbcommands.h" #include "commands/tablecmds.h" #include "commands/tablespace.h" @@ -234,7 +235,8 @@ static void get_sample_rows_for_query( MemoryContext speccontext, Relation rel, VacuumStmt* vacstmt, int64* num_sample_rows, HeapTuple** samplerows); template static void update_stats_catalog( - Relation pgstat, MemoryContext oldcontext, Oid relid, char relkind, bool inh, VacAttrStats* stats); + Relation pgstat, MemoryContext oldcontext, Oid relid, char relkind, bool inh, VacAttrStats* stats, int natts, + char relpersistence); /* The sample info of special attribute for compute statistic for index or type of tsvector. */ typedef struct { @@ -494,6 +496,11 @@ static void analyze_rel_internal(Relation onerel, VacuumStmt* vacstmt, BufferAcc return; } + if (RELATION_IS_GLOBAL_TEMP(onerel) && !gtt_storage_attached(RelationGetRelid(onerel))) { + relation_close(onerel, ShareUpdateExclusiveLock); + return; + } + /* * We can ANALYZE any table except pg_statistic. See update_attstats */ @@ -720,7 +727,7 @@ static int get_total_width( total_width1 = 0; for (i = 0; i < thisdata->attr_cnt; i++) { - for (unsigned int j = 0; j < vacattrstats[i]->num_attrs; ++j) { + for (unsigned int j = 0; j < thisdata->vacattrstats[i]->num_attrs; ++j) { attr = thisdata->vacattrstats[i]->attrs[j]; /* This should match set_rel_width() in costsize.c */ @@ -1578,7 +1585,8 @@ static void do_analyze_rel(Relation onerel, VacuumStmt* vacstmt, BlockNumber rel VacAttrStats* stats = vacattrstats[i]; if (stats->num_attrs > 1) { stats->stats_valid = true; - update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, inh, 1, &stats); + update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, inh, 1, &stats, + RelationGetRelPersistence(onerel)); } } } @@ -4188,7 +4196,7 @@ void releaseSourceAfterDelteOrUpdateAttStats( * function */ // Added parameter - char relkind by data partition -void update_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats** vacattrstats) +void update_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats** vacattrstats, char relpersistence) { Relation pgstat = NULL; Relation pgstat_ext = NULL; @@ -4226,7 +4234,7 @@ void update_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats* } /* do multi column stats update */ - update_stats_catalog(pgstat_ext, oldcontext, relid, relkind, inh, stats); + update_stats_catalog(pgstat_ext, oldcontext, relid, relkind, inh, stats, natts, relpersistence); } else { /* Open rel handler for pg_statistic to process single-column statistic */ if (!pgstat) { @@ -4234,7 +4242,7 @@ void update_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats* } /* do signle column stats update */ - update_stats_catalog(pgstat, oldcontext, relid, relkind, inh, stats); + update_stats_catalog(pgstat, oldcontext, relid, relkind, inh, stats, natts, relpersistence); } } @@ -4261,7 +4269,8 @@ void update_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats* */ template static void update_stats_catalog( - Relation pgstat, MemoryContext oldcontext, Oid relid, char relkind, bool inh, VacAttrStats* stats) + Relation pgstat, MemoryContext oldcontext, Oid relid, char relkind, bool inh, VacAttrStats* stats, int natts, + char relpersistence) { HeapTuple stup, oldtup; int i, k, n; @@ -4402,6 +4411,17 @@ static void update_stats_catalog( values[Anum_pg_statistic_ext_stakey - 1] = PointerGetDatum(stakey); } + /* Update column statistic to localhash, not catalog */ + if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { + up_gtt_att_statistic(relid, + attnum, + natts, + RelationGetDescr(pgstat), + values, + nulls); + return; + } + /* store tuple to pg_statistic(_ext) */ PG_TRY(); { @@ -6546,11 +6566,13 @@ static bool do_analyze_samplerows(Relation onerel, VacuumStmt* vacstmt, int attr * previous statistics for the target columns. (If there are stats in * pg_statistic for columns we didn't process, we leave them alone.) */ - update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, inh, attr_cnt, vacattrstats); + update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, inh, attr_cnt, vacattrstats, + RelationGetRelPersistence(onerel)); for (i = 0; i < nindexes; i++) { AnlIndexData* thisdata = &indexdata[i]; - update_attstats(RelationGetRelid(Irel[i]), STARELKIND_CLASS, false, thisdata->attr_cnt, thisdata->vacattrstats); + update_attstats(RelationGetRelid(Irel[i]), STARELKIND_CLASS, false, thisdata->attr_cnt, thisdata->vacattrstats, + RelationGetRelPersistence(Irel[i])); } return true; @@ -6685,7 +6707,8 @@ static void do_analyze_sampletable(Relation onerel, VacuumStmt* vacstmt, int att * previous statistics for the target columns. (If there are stats in * pg_statistic for columns we didn't process, we leave them alone.) */ - update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, inh, attr_cnt, vacattrstats); + update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, inh, attr_cnt, vacattrstats, + RelationGetRelPersistence(onerel)); /* * Update stats of dfs table or delta table using statistic of complex table @@ -6693,10 +6716,12 @@ static void do_analyze_sampletable(Relation onerel, VacuumStmt* vacstmt, int att */ if (analyzemode == ANALYZECOMPLEX) { if (!vacstmt->pstGlobalStatEx[ANALYZEMAIN - 1].exec_query) - update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, false, attr_cnt, vacattrstats); + update_attstats(RelationGetRelid(onerel), STARELKIND_CLASS, false, + attr_cnt, vacattrstats, RelationGetRelPersistence(onerel)); if (!vacstmt->pstGlobalStatEx[ANALYZEDELTA - 1].exec_query) - update_attstats(onerel->rd_rel->reldeltarelid, STARELKIND_CLASS, false, attr_cnt, vacattrstats); + update_attstats(onerel->rd_rel->reldeltarelid, STARELKIND_CLASS, false, + attr_cnt, vacattrstats, RelationGetRelPersistence(onerel)); } /* @@ -6712,7 +6737,8 @@ static void do_analyze_sampletable(Relation onerel, VacuumStmt* vacstmt, int att AnlIndexData* thisdata = &indexdata[i]; update_attstats( - RelationGetRelid(Irel[i]), STARELKIND_CLASS, false, thisdata->attr_cnt, thisdata->vacattrstats); + RelationGetRelid(Irel[i]), STARELKIND_CLASS, false, thisdata->attr_cnt, thisdata->vacattrstats, + RelationGetRelPersistence(Irel[i])); } } diff --git a/src/gausskernel/optimizer/commands/cluster.cpp b/src/gausskernel/optimizer/commands/cluster.cpp index f1001cb9cfe96964052f366cfb3d9dc9321b2de1..202d2e740243a1620df48892560e869171e5479d 100644 --- a/src/gausskernel/optimizer/commands/cluster.cpp +++ b/src/gausskernel/optimizer/commands/cluster.cpp @@ -33,6 +33,7 @@ #include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/toasting.h" +#include "catalog/storage_gtt.h" #include "commands/cluster.h" #include "commands/tablecmds.h" #include "commands/vacuum.h" @@ -141,6 +142,9 @@ static Datum pgxc_parallel_execution(const char* query, ExecNodes* exec_nodes); static int switch_relfilenode_execnode(Oid relOid1, Oid relOid2, bool isbucket, RedisSwitchNode* rsn); #endif static void swapRelationIndicesRelfileNode(Relation rel1, Relation rel2, bool swapBucket); +static void GttSwapRelationFiles(Oid r1, Oid r2, bool targetIsPgClass, bool swapToastByContent, + TransactionId frozenXid, Oid *mappedTables); + /* --------------------------------------------------------------------------- * This cluster code allows for clustering multiple tables at once. Because * of this, we cannot just run everything on a single transaction, or we @@ -453,6 +457,12 @@ void cluster_rel(Oid tableOid, Oid partitionOid, Oid indexOid, bool recheck, boo (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot vacuum temporary tables of other sessions"))); } + if (RELATION_IS_GLOBAL_TEMP(OldHeap) && !gtt_storage_attached(RelationGetRelid(OldHeap))) { + relation_close(OldHeap, lockMode); + gstrace_exit(GS_TRC_ID_cluster_rel); + return; + } + /* * Also check for active uses of the relation in the current transaction, * including open scans and pending AFTER trigger events. @@ -688,6 +698,7 @@ static void rebuild_relation( Oid tableOid = RelationGetRelid(OldHeap); Oid tableSpace = OldHeap->rd_rel->reltablespace; Oid OIDNewHeap; + char relpersistence; bool is_system_catalog = false; bool swap_toast_by_content = false; TransactionId frozenXid; @@ -699,6 +710,7 @@ static void rebuild_relation( mark_index_clustered(OldHeap, indexOid); /* Remember if it's a system catalog */ + relpersistence = OldHeap->rd_rel->relpersistence; is_system_catalog = IsSystemRelation(OldHeap); /* Close relcache entry, but keep lock until transaction commit */ @@ -732,7 +744,8 @@ static void rebuild_relation( * Swap the physical files of the target and transient tables, then * rebuild the target's indexes and throw away the transient table. */ - finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog, swap_toast_by_content, false, frozenXid, memUsage); + finish_heap_swap( + tableOid, OIDNewHeap, is_system_catalog, swap_toast_by_content, false, frozenXid, memUsage, relpersistence); /* report vacuum full stat to PgStatCollector */ pgstat_report_vacuum(tableOid, InvalidOid, is_shared, deleteTupleNum); @@ -1783,6 +1796,8 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int TransactionId FreezeXid; bool use_sort = false; double tups_vacuumed = 0; + bool isGtt = false; + TransactionId gttRelfrozenxid = 0; /* * Open the relations we need. @@ -1794,6 +1809,10 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int else OldIndex = NULL; + if (RELATION_IS_GLOBAL_TEMP(OldHeap)) { + isGtt = true; + } + /* * If the OldHeap has a toast table, get lock on the toast table to keep * it from being vacuumed. This is needed because autovacuum processes @@ -1851,32 +1870,38 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int * FreezeXid will become the table's new relfrozenxid, and that mustn't go * backwards, so take the max. */ - bool isNull = false; - TransactionId relfrozenxid; - Relation rel = heap_open(RelationRelationId, AccessShareLock); - HeapTuple tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap)); - if (!HeapTupleIsValid(tuple)) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("cache lookup failed for relation %u", RelationGetRelid(OldHeap)))); - } - Datum xid64datum = heap_getattr(tuple, Anum_pg_class_relfrozenxid64, RelationGetDescr(rel), &isNull); - heap_close(rel, AccessShareLock); - heap_freetuple(tuple); + if (isGtt) { + (void)get_gtt_relstats(OIDOldHeap, NULL, NULL, NULL, >tRelfrozenxid); + if (TransactionIdIsValid(gttRelfrozenxid) && TransactionIdPrecedes(FreezeXid, gttRelfrozenxid)) + FreezeXid = gttRelfrozenxid; + } else { + bool isNull = false; + TransactionId relfrozenxid; + Relation rel = heap_open(RelationRelationId, AccessShareLock); + HeapTuple tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap)); + if (!HeapTupleIsValid(tuple)) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("cache lookup failed for relation %u", RelationGetRelid(OldHeap)))); + } + Datum xid64datum = heap_getattr(tuple, Anum_pg_class_relfrozenxid64, RelationGetDescr(rel), &isNull); + heap_close(rel, AccessShareLock); + heap_freetuple(tuple); - if (isNull) { - relfrozenxid = OldHeap->rd_rel->relfrozenxid; + if (isNull) { + relfrozenxid = OldHeap->rd_rel->relfrozenxid; - if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, relfrozenxid) || - !TransactionIdIsNormal(relfrozenxid)) { - relfrozenxid = FirstNormalTransactionId; - } - } else { - relfrozenxid = DatumGetTransactionId(xid64datum); - } + if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, relfrozenxid) || + !TransactionIdIsNormal(relfrozenxid)) { + relfrozenxid = FirstNormalTransactionId; + } + } else { + relfrozenxid = DatumGetTransactionId(xid64datum); + } - if (TransactionIdPrecedes(FreezeXid, relfrozenxid)) { - FreezeXid = relfrozenxid; + if (TransactionIdPrecedes(FreezeXid, relfrozenxid)) { + FreezeXid = relfrozenxid; + } } /* return selected value to caller */ *pFreezeXid = FreezeXid; @@ -2765,7 +2790,7 @@ static void SwapCStoreTables(Oid relId1, Oid relId2, Oid parentOid, Oid tempTabl * cleaning up (including rebuilding all indexes on the old heap). */ void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, - bool check_constraints, TransactionId frozenXid, AdaptMem* memInfo) + bool checkConstraints, TransactionId frozenXid, AdaptMem* memInfo, char newrelpersistence) { ObjectAddress object; Oid mapped_tables[4]; @@ -2781,8 +2806,15 @@ void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bo * Swap the contents of the heap relations (including any toast tables). * Also set old heap's relfrozenxid to frozenXid. */ - swap_relation_files( - OIDOldHeap, OIDNewHeap, (OIDOldHeap == RelationRelationId), swap_toast_by_content, frozenXid, mapped_tables); + if (newrelpersistence == RELPERSISTENCE_GLOBAL_TEMP) { + Assert(!is_system_catalog); + GttSwapRelationFiles(OIDOldHeap, OIDNewHeap, (OIDOldHeap == RelationRelationId), + swap_toast_by_content, frozenXid, mapped_tables); + } else { + swap_relation_files(OIDOldHeap, OIDNewHeap, (OIDOldHeap == RelationRelationId), + swap_toast_by_content, frozenXid, mapped_tables); + } + /* * If it's a system catalog, queue an sinval message to flush all * catcaches on the catalog when we reach CommandCounterIncrement. @@ -2806,7 +2838,7 @@ void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bo * broken ones, so it can't be necessary to set indcheckxmin. */ reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE; - if (check_constraints) + if (checkConstraints) reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS; reindex_relation(OIDOldHeap, reindex_flags, REINDEX_ALL_INDEX, memInfo); @@ -2965,6 +2997,111 @@ static List* get_tables_to_cluster(MemoryContext cluster_context) return rvs; } +static void GttSwapRelationFiles(Oid r1, Oid r2, bool targetIsPgClass, bool swapToastByContent, + TransactionId frozenXid, Oid *mappedTables) +{ + Relation relRelation; + Oid relfilenode1, + relfilenode2; + Relation rel1; + Relation rel2; + + relRelation = relation_open(RelationRelationId, RowExclusiveLock); + + rel1 = relation_open(r1, AccessExclusiveLock); + rel2 = relation_open(r2, AccessExclusiveLock); + + relfilenode1 = gtt_fetch_current_relfilenode(r1); + relfilenode2 = gtt_fetch_current_relfilenode(r2); + + Assert(OidIsValid(relfilenode1) && OidIsValid(relfilenode2)); + gtt_switch_rel_relfilenode(r1, relfilenode1, r2, relfilenode2, true); + + CacheInvalidateRelcache(rel1); + CacheInvalidateRelcache(rel2); + + if (rel1->rd_rel->reltoastrelid || rel2->rd_rel->reltoastrelid) { + if (swapToastByContent) { + if (rel1->rd_rel->reltoastrelid && rel2->rd_rel->reltoastrelid) { + GttSwapRelationFiles(rel1->rd_rel->reltoastrelid, + rel2->rd_rel->reltoastrelid, + targetIsPgClass, + swapToastByContent, + frozenXid, + mappedTables); + } else { + elog(ERROR, "cannot swap toast files by content when there's only one"); + } + } else { + ObjectAddress baseobject, + toastobject; + long count; + + if (IsSystemRelation(rel1)) { + elog(ERROR, "cannot swap toast files by links for system catalogs"); + } + + if (rel1->rd_rel->reltoastrelid) { + count = deleteDependencyRecordsFor(RelationRelationId, + rel1->rd_rel->reltoastrelid, + false); + if (count != 1) { + elog(ERROR, "expected one dependency record for TOAST table, found %ld", + count); + } + } + if (rel2->rd_rel->reltoastrelid) { + count = deleteDependencyRecordsFor(RelationRelationId, + rel2->rd_rel->reltoastrelid, + false); + if (count != 1) { + elog(ERROR, "expected one dependency record for TOAST table, found %ld", + count); + } + } + + /* Register new dependencies */ + baseobject.classId = RelationRelationId; + baseobject.objectSubId = 0; + toastobject.classId = RelationRelationId; + toastobject.objectSubId = 0; + + if (rel1->rd_rel->reltoastrelid) { + baseobject.objectId = r1; + toastobject.objectId = rel1->rd_rel->reltoastrelid; + recordDependencyOn(&toastobject, &baseobject, + DEPENDENCY_INTERNAL); + } + + if (rel2->rd_rel->reltoastrelid) { + baseobject.objectId = r2; + toastobject.objectId = rel2->rd_rel->reltoastrelid; + recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); + } + } + } + + if (swapToastByContent && rel1->rd_rel->relkind == RELKIND_TOASTVALUE && + rel2->rd_rel->relkind == RELKIND_TOASTVALUE) { + GttSwapRelationFiles(rel1->rd_rel->reltoastidxid, + rel2->rd_rel->reltoastidxid, + targetIsPgClass, + swapToastByContent, + InvalidTransactionId, + mappedTables); + } + + relation_close(rel1, NoLock); + relation_close(rel2, NoLock); + + relation_close(relRelation, RowExclusiveLock); + + RelationCloseSmgrByOid(r1); + RelationCloseSmgrByOid(r2); + + CommandCounterIncrement(); +} + /* * Reconstruct and rewrite the given tuple * diff --git a/src/gausskernel/optimizer/commands/copy.cpp b/src/gausskernel/optimizer/commands/copy.cpp index 1b6e08b317ceb007a8e90c701ab3ea67637ce026..261a4bff53972d8fd0fb69637f2d8049556ca21e 100644 --- a/src/gausskernel/optimizer/commands/copy.cpp +++ b/src/gausskernel/optimizer/commands/copy.cpp @@ -34,6 +34,7 @@ #ifdef PGXC #include "catalog/pg_trigger.h" #endif +#include "catalog/storage_gtt.h" #include "commands/copy.h" #include "commands/defrem.h" #include "commands/trigger.h" @@ -982,7 +983,7 @@ uint64 DoCopy(CopyStmt* stmt, const char* queryString) Assert(rel); /* check read-only transaction */ - if (u_sess->attr.attr_common.XactReadOnly && !RelationIsLocalTemp(rel)) + if (u_sess->attr.attr_common.XactReadOnly && !RELATION_IS_TEMP(rel)) PreventCommandIfReadOnly("COPY FROM"); /* set write for backend status for the thread, we will use it to check default transaction readOnly */ @@ -3479,6 +3480,7 @@ static uint64 CopyFrom(CopyState cstate) 0); ExecOpenIndices(resultRelInfo); + init_gtt_storage(CMD_INSERT, resultRelInfo); resultRelationDesc = resultRelInfo->ri_RelationDesc; isPartitionRel = RELATION_IS_PARTITIONED(resultRelationDesc); diff --git a/src/gausskernel/optimizer/commands/indexcmds.cpp b/src/gausskernel/optimizer/commands/indexcmds.cpp index 3f931fdf61558c10af505abc6a372ce092f9f1fa..7ef2a711869733b4711be904fd6820a8ed312fe2 100755 --- a/src/gausskernel/optimizer/commands/indexcmds.cpp +++ b/src/gausskernel/optimizer/commands/indexcmds.cpp @@ -332,6 +332,23 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al List* partitionTableList = NIL; List* partitionIndexdef = NIL; List* partitiontspList = NIL; + char relPersistence; + bool concurrent; + + /* + * Force non-concurrent build on temporary relations, even if CONCURRENTLY + * was requested. Other backends can't access a temporary relation, so + * there's no harm in grabbing a stronger lock, and a non-concurrent DROP + * is more efficient. Do this before any use of the concurrent option is + * done. + */ + relPersistence = get_rel_persistence(relationId); + if (stmt->concurrent && !(relPersistence == RELPERSISTENCE_TEMP || + relPersistence == RELPERSISTENCE_GLOBAL_TEMP)) { + concurrent = true; + } else { + concurrent = false; + } /* * count attributes in index @@ -356,7 +373,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al * the relation. To avoid lock upgrade hazards, that lock should be at * least as strong as the one we take here. */ - lockmode = stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock; + lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock; rel = heap_open(relationId, lockmode); relationId = RelationGetRelid(rel); @@ -393,7 +410,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al /* * partitioned index not is not support concurrent index */ - if (stmt->isPartitioned && stmt->concurrent) { + if (stmt->isPartitioned && concurrent) { ereport( ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot create concurrent partitioned indexes "))); } @@ -655,8 +672,8 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al indexInfo->ii_ExclusionStrats = NULL; indexInfo->ii_Unique = stmt->unique; /* In a concurrent build, mark it not-ready-for-inserts */ - indexInfo->ii_ReadyForInserts = !stmt->concurrent; - indexInfo->ii_Concurrent = stmt->concurrent; + indexInfo->ii_ReadyForInserts = !concurrent; + indexInfo->ii_Concurrent = concurrent; indexInfo->ii_BrokenHotChain = false; indexInfo->ii_PgClassAttrId = 0; @@ -765,7 +782,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al * A valid stmt->oldPSortOid implies that we already have a built form * of the psort index. */ - if ((OidIsValid(stmt->oldNode) && !(skip_build && !stmt->concurrent) && + if ((OidIsValid(stmt->oldNode) && !(skip_build && !concurrent) && !u_sess->attr.attr_sql.enable_cluster_resize) || (OidIsValid(stmt->oldPSortOid) && !OidIsValid(stmt->oldNode))) { ereport(defence_errlevel(), @@ -803,7 +820,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al stmt->initdeferred, g_instance.attr.attr_common.allowSystemTableMods, true, - stmt->concurrent, + concurrent, &extra); heap_close(rel, NoLock); return indexRelationId; @@ -855,8 +872,8 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al stmt->deferrable, stmt->initdeferred, (g_instance.attr.attr_common.allowSystemTableMods || u_sess->attr.attr_common.IsInplaceUpgrade), - skip_build || stmt->concurrent, - stmt->concurrent, + skip_build || concurrent, + concurrent, &extra); /* Add any requested comment */ if (stmt->idxcomment != NULL) @@ -984,7 +1001,7 @@ Oid DefineIndex(Oid relationId, IndexStmt* stmt, Oid indexRelationId, bool is_al return indexRelationId; } - if (!stmt->concurrent) { + if (!concurrent) { /* Close the heap and we're done, in the non-concurrent case */ heap_close(rel, NoLock); return indexRelationId; @@ -2105,6 +2122,8 @@ void ReindexIndex(RangeVar* indexRelation, const char* partition_name, AdaptMem* Oid heapOid = InvalidOid; Oid heapPartOid = InvalidOid; LOCKMODE lockmode; + Relation irel; + char persistence; /* lock level used here should match index lock reindex_index() */ if (partition_name != NULL) @@ -2120,6 +2139,13 @@ void ReindexIndex(RangeVar* indexRelation, const char* partition_name, AdaptMem* false, RangeVarCallbackForReindexIndex, (void*)&heapOid); + /* + * Obtain the current persistence of the existing index. We already hold + * lock on the index. + */ + irel = index_open(indOid, NoLock); + persistence = irel->rd_rel->relpersistence; + index_close(irel, NoLock); if (partition_name != NULL) indPartOid = partitionNameGetPartitionOid(indOid, @@ -2131,7 +2157,7 @@ void ReindexIndex(RangeVar* indexRelation, const char* partition_name, AdaptMem* PartitionNameCallbackForIndexPartition, (void*)&heapPartOid, ShareLock); // lock on heap partition - reindex_index(indOid, indPartOid, false, mem_info, false); + reindex_index(indOid, indPartOid, false, mem_info, false, persistence); } void PartitionNameCallbackForIndexPartition(Oid partitionedRelationOid, const char* partitionName, Oid partId, diff --git a/src/gausskernel/optimizer/commands/sequence.cpp b/src/gausskernel/optimizer/commands/sequence.cpp index 06be9c70b6f387536f165b594912c4b4e90f7e15..b77028dce5bdb31d5d816ce7523b3e5325c28642 100755 --- a/src/gausskernel/optimizer/commands/sequence.cpp +++ b/src/gausskernel/optimizer/commands/sequence.cpp @@ -408,7 +408,8 @@ void DefineSequence(CreateSeqStmt* seq) isUseLocalSeq = IS_SINGLE_NODE || isTempNamespace(namespaceOid); bool notSupportTmpSeq = false; - if (seq->sequence->relpersistence == RELPERSISTENCE_TEMP) { + if (seq->sequence->relpersistence == RELPERSISTENCE_TEMP || + seq->sequence->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { notSupportTmpSeq = true; } else if (IS_MAIN_COORDINATOR || IS_SINGLE_NODE) { if (seq->canCreateTempSeq) { diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index a285b4b0898c4080a7f9427dda7892a7f4e1d470..241966692ec46ab23d2037dfd37c26057494b7ef 100644 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -61,6 +61,7 @@ #include "catalog/storage_xlog.h" #include "catalog/toasting.h" #include "catalog/cstore_ctlg.h" +#include "catalog/storage_gtt.h" #include "commands/cluster.h" #include "commands/comment.h" #include "commands/defrem.h" @@ -608,6 +609,8 @@ static void ResetRelRedisCtidRelOptions( Relation rel, Oid part_oid, int cat_id, int att_num, int att_inx, Oid pgcat_oid); static bool WLMRelationCanTruncate(Relation rel); static void alter_partition_policy_if_needed(Relation rel, List* defList); +static OnCommitAction GttOncommitOption(const List *options); + /* get all partitions oid */ static List* get_all_part_oid(Oid relid) @@ -1429,12 +1432,16 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId) stmt->relation->relpersistence = RELPERSISTENCE_PERMANENT; /* Check consistency of arguments */ - if (stmt->oncommit != ONCOMMIT_NOOP && stmt->relation->relpersistence != RELPERSISTENCE_TEMP) + if (stmt->oncommit != ONCOMMIT_NOOP && + !(stmt->relation->relpersistence == RELPERSISTENCE_TEMP || + stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT can only be used on temporary tables"))); /* @Temp Table. We do not support on commit drop right now. */ - if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP && stmt->oncommit == ONCOMMIT_DROP) + if ((stmt->relation->relpersistence == RELPERSISTENCE_TEMP || + stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) && + stmt->oncommit == ONCOMMIT_DROP) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT only support PRESERVE ROWS or DELETE ROWS option"))); @@ -1494,7 +1501,9 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId) * code. This is needed because calling code might not expect untrusted * tables to appear in pg_temp at the front of its search path. */ - if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP && InSecurityRestrictedOperation()) + if ((stmt->relation->relpersistence == RELPERSISTENCE_TEMP || + stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) && + InSecurityRestrictedOperation()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("cannot create temporary table within security-restricted operation"))); @@ -1567,6 +1576,40 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId) /* * Parse and validate reloptions, if any. */ + /* global temp table */ + OnCommitAction oncommitAction = GttOncommitOption(stmt->options); + if (stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP && + relkind == RELKIND_RELATION) { + if (oncommitAction != ONCOMMIT_NOOP) { + if (stmt->oncommit != ONCOMMIT_NOOP) { + elog(ERROR, "could not create global temporary table with on commit and with clause at same time"); + } + stmt->oncommit = oncommitAction; + } else { + DefElem *opt = makeNode(DefElem); + + opt->type = T_DefElem; + opt->defnamespace = NULL; + opt->defname = "on_commit_delete_rows"; + opt->defaction = DEFELEM_UNSPEC; + + /* use reloptions to remember on commit clause */ + if (stmt->oncommit == ONCOMMIT_DELETE_ROWS) { + opt->arg = reinterpret_cast(makeString("true")); + } else if (stmt->oncommit == ONCOMMIT_PRESERVE_ROWS) { + opt->arg = reinterpret_cast(makeString("false")); + } else if (stmt->oncommit == ONCOMMIT_NOOP) { + opt->arg = reinterpret_cast(makeString("false")); + } else { + elog(ERROR, "global temp table not support on commit drop clause"); + } + stmt->options = lappend(stmt->options, opt); + } + } else if (oncommitAction != ONCOMMIT_NOOP) { + elog(ERROR, "The parameter on_commit_delete_rows is exclusive to the global temp table, which cannot be " + "specified by a regular table"); + } + reloptions = transformRelOptions((Datum)0, stmt->options, NULL, validnsps, true, false); orientedFrom = (Node*)makeString(ORIENTATION_ROW); /* default is ORIENTATION_ROW */ @@ -1850,7 +1893,7 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId) Assert((createbucket == true && bucketinfo->bucketlist != NULL && bucketinfo->bucketcol != NULL) || (createbucket == false && bucketinfo->bucketlist == NULL && bucketinfo->bucketcol != NULL)); } - } else { + } else { /* here is normal mode */ /* check if the table can be hash partition */ if (!IS_SINGLE_NODE && !IsInitdb && (relkind == RELKIND_RELATION) && !IsSystemNamespace(namespaceId) && @@ -2345,10 +2388,10 @@ void RemoveRelations(DropStmt* drop, StringInfo tmp_queryString, RemoteQueryExec LOCKMODE lockmode = AccessExclusiveLock; bool cn_miss_relation = false; StringInfo relation_namelist = makeStringInfo(); + char relPersistence; /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */ if (drop->concurrent) { - flags |= PERFORM_DELETION_CONCURRENTLY; lockmode = ShareUpdateExclusiveLock; Assert(drop->removeType == OBJECT_INDEX); if (list_length(drop->objects) != 1) @@ -2467,7 +2510,13 @@ void RemoveRelations(DropStmt* drop, StringInfo tmp_queryString, RemoteQueryExec if (delrel != NULL) { relation_close(delrel, NoLock); } - + + relPersistence = get_rel_persistence(relOid); + if (drop->concurrent && + !(relPersistence == RELPERSISTENCE_TEMP || relPersistence == RELPERSISTENCE_GLOBAL_TEMP)) { + Assert(list_length(drop->objects) == 1 && drop->removeType == OBJECT_INDEX); + flags |= PERFORM_DELETION_CONCURRENTLY; + } /* OK, we're ready to delete this one */ obj.classId = RelationRelationId; obj.objectId = relOid; @@ -3054,6 +3103,10 @@ void ExecuteTruncate(TruncateStmt* stmt) */ CheckTableForSerializableConflictIn(rel); + if (RELATION_IS_GLOBAL_TEMP(rel) && !gtt_storage_attached(RelationGetRelid(rel))) { + continue; + } + if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(RelationGetRelid(rel))) { FdwRoutine* fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(rel)); if (fdwroutine->TruncateForeignTable != NULL) { @@ -4554,7 +4607,6 @@ void RenameRelationInternal(Oid myrelid, const char* newrelname) */ if (targetrelation->rd_rel->relkind == RELKIND_INDEX) { Oid constraintId = get_index_constraint(myrelid); - if (OidIsValid(constraintId)) RenameConstraintById(constraintId, newrelname); } @@ -5275,6 +5327,17 @@ void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt* stmt) /* Caller is required to provide an adequate lock. */ rel = relation_open(relid, lockmode); + /* We allow to alter global temp table only this session use it */ + if (RELATION_IS_GLOBAL_TEMP(rel)) { + if (is_other_backend_use_gtt(RelationGetRelid(rel))) { + ereport( + ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("can not alter table %s when other backend attached this global temp table", + RelationGetRelationName(rel)))); + } + } + CheckTableNotInUse(rel, "ALTER TABLE"); /* @@ -6598,6 +6661,28 @@ static void ATRewriteTables(List** wqueue, LOCKMODE lockmode) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot rewrite temporary tables of other sessions"))); + if (RELATION_IS_GLOBAL_TEMP(OldHeap)) { + /* gtt may not attached, create it */ + if (!gtt_storage_attached(tab->relid)) { + ResultRelInfo *resultRelInfo; + MemoryContext oldcontext; + MemoryContext ctx_alter_gtt; + ctx_alter_gtt = + AllocSetContextCreate(CurrentMemoryContext, "gtt alter table", ALLOCSET_DEFAULT_SIZES); + oldcontext = MemoryContextSwitchTo(ctx_alter_gtt); + resultRelInfo = makeNode(ResultRelInfo); + InitResultRelInfo(resultRelInfo, OldHeap, 1, 0); + if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex && + resultRelInfo->ri_IndexRelationDescs == NULL) + ExecOpenIndices(resultRelInfo); + + init_gtt_storage(CMD_UTILITY, resultRelInfo); + ExecCloseIndices(resultRelInfo); + (void)MemoryContextSwitchTo(oldcontext); + MemoryContextDelete(ctx_alter_gtt); + } + } + /* * Select destination tablespace (same as original unless user * requested a change) @@ -8248,15 +8333,16 @@ static void ATExecAddStatistics(Relation rel, Node* def, LOCKMODE lockmode) VacAttrStats** vacattrstats_array = es_build_vacattrstats_array(rel, (List*)def, true, &array_length, inh); if (array_length > 0) { - update_attstats(relid, relkind, false, array_length, vacattrstats_array); + update_attstats(relid, relkind, false, array_length, vacattrstats_array, RelationGetRelPersistence(rel)); if (RelationIsDfsStore(rel)) { /* HDFS complex table */ - update_attstats(relid, relkind, true, array_length, vacattrstats_array); + update_attstats(relid, relkind, true, array_length, vacattrstats_array, RelationGetRelPersistence(rel)); /* HDFS delta table */ Oid delta_relid = rel->rd_rel->reldeltarelid; Assert(OidIsValid(delta_relid)); - update_attstats(delta_relid, relkind, false, array_length, vacattrstats_array); + update_attstats( + delta_relid, relkind, false, array_length, vacattrstats_array, RelationGetRelPersistence(rel)); } } } @@ -9120,6 +9206,13 @@ static void ATAddForeignKeyConstraint(AlteredTableInfo* tab, Relation rel, Const (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("constraints on temporary tables must involve temporary tables of this session"))); break; + case RELPERSISTENCE_GLOBAL_TEMP: + if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_GLOBAL_TEMP) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("constraints on global temporary tables may reference only global temporary tables"))); + } + break; default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), @@ -12198,6 +12291,12 @@ static void ATExecSetRelOptions(Relation rel, List* defList, AlterTableType oper if (defList == NIL && operation != AT_ReplaceRelOptions) return; /* nothing to do */ + if (GttOncommitOption(defList) != ONCOMMIT_NOOP) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("table cannot add or modify on commit parameter by ALTER TABLE command."))); + } + /* forbid user to set or change inner options */ ForbidOutUsersToSetInnerOptions(defList); @@ -12617,7 +12716,7 @@ static void atexecset_table_space_internal(Relation rel, Oid newTableSpace, Oid * NOTE: any conflict in relfilenode value will be caught in * RelationCreateStorage function. */ - RelationCreateStorage(newrnode, rel->rd_rel->relpersistence, rel->rd_rel->relowner, rel->rd_bucketoid); + RelationCreateStorage(newrnode, rel->rd_rel->relpersistence, rel->rd_rel->relowner, rel->rd_bucketoid, rel); /* copy main fork */ copy_relation_data(rel, &dstrel, MAIN_FORKNUM, rel->rd_rel->relpersistence); @@ -12730,6 +12829,11 @@ static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmo */ rel = relation_open(tableOid, lockmode); + if (RELATION_IS_GLOBAL_TEMP(rel)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("not support alter table set tablespace on global temp table."))); + } /* * No work if no change in tablespace. */ @@ -15134,14 +15238,7 @@ void PreCommit_on_commit_actions(void) /* Do nothing (there shouldn't be such entries, actually) */ break; case ONCOMMIT_DELETE_ROWS: - - /* - * If this transaction hasn't accessed any temporary - * relations, we can skip truncating ON COMMIT DELETE ROWS - * tables, as they must still be empty. - */ - if (t_thrd.xact_cxt.MyXactAccessedTempRel) - oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); + oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); break; case ONCOMMIT_DROP: { ObjectAddress object; @@ -21182,3 +21279,32 @@ static void at_timeseries_check(Relation rel, AlterTableCmd* cmd) } } +static OnCommitAction GttOncommitOption(const List *options) +{ + ListCell *listptr; + OnCommitAction action = ONCOMMIT_NOOP; + + foreach(listptr, options) { + DefElem *def = reinterpret_cast(lfirst(listptr)); + if (strcmp(def->defname, "on_commit_delete_rows") == 0) { + bool res = false; + char *sval = defGetString(def); + + if (!parse_bool(sval, &res)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"on_commit_delete_rows\" requires a Boolean value"))); + } + + if (res) { + action = ONCOMMIT_DELETE_ROWS; + } else { + action = ONCOMMIT_PRESERVE_ROWS; + } + break; + } + } + return action; +} + + diff --git a/src/gausskernel/optimizer/commands/vacuum.cpp b/src/gausskernel/optimizer/commands/vacuum.cpp index a88eee3f4a8051d595565788ff8d4034f5fc3aad..d0a1949f0b5d990d8a447ce5c0d3a1d0294158fa 100755 --- a/src/gausskernel/optimizer/commands/vacuum.cpp +++ b/src/gausskernel/optimizer/commands/vacuum.cpp @@ -39,6 +39,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pgxc_class.h" #include "catalog/storage.h" +#include "catalog/storage_gtt.h" #include "commands/cluster.h" #include "commands/tablespace.h" #include "commands/vacuum.h" @@ -975,6 +976,16 @@ void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_p bool isNull = false; TransactionId relfrozenxid; Datum xid64datum; + bool isGtt = false; + + /* global temp table remember relstats to localhash and rel->rd_rel, not catalog */ + if (RELATION_IS_GLOBAL_TEMP(relation)) { + isGtt = true; + up_gtt_relstats(relation, + static_cast(num_pages), num_tuples, + num_all_visible_pages, + frozenxid); + } /* Fetch a copy of the tuple to scribble on */ ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid)); @@ -989,17 +1000,23 @@ void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_p // frozenxid == BootstrapTransactionId means it was invoked by execRemote.cpp:ReceivePageAndTuple() if (IS_PGXC_DATANODE || (frozenxid == BootstrapTransactionId) || IsSystemRelation(relation)) { #endif - if (pgcform->relpages - num_pages != 0) { - pgcform->relpages = num_pages; - dirty = true; - } - if (pgcform->reltuples - num_tuples != 0) { - pgcform->reltuples = num_tuples; - dirty = true; - } - if (pgcform->relallvisible != (int32)num_all_visible_pages) { - pgcform->relallvisible = (int32)num_all_visible_pages; - dirty = true; + if (isGtt) { + relation->rd_rel->relpages = (int32) num_pages; + relation->rd_rel->reltuples = (float4) num_tuples; + relation->rd_rel->relallvisible = (int32) num_all_visible_pages; + } else { + if (pgcform->relpages - num_pages != 0) { + pgcform->relpages = num_pages; + dirty = true; + } + if (pgcform->reltuples - num_tuples != 0) { + pgcform->reltuples = num_tuples; + dirty = true; + } + if (pgcform->relallvisible != (int32)num_all_visible_pages) { + pgcform->relallvisible = (int32)num_all_visible_pages; + dirty = true; + } } #ifdef PGXC } @@ -1144,6 +1161,11 @@ void vac_update_datfrozenxid(void) if (classForm->relkind != RELKIND_RELATION && classForm->relkind != RELKIND_TOASTVALUE) continue; + /* global temp table relstats not in pg_class */ + if (classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { + continue; + } + xid64datum = heap_getattr(classTup, Anum_pg_class_relfrozenxid64, RelationGetDescr(relation), &isNull); if (isNull) { @@ -1198,6 +1220,40 @@ void vac_update_datfrozenxid(void) } Assert(TransactionIdIsNormal(newFrozenXid)); + /* + * Global temp table get frozenxid from MyProc + * to avoid the vacuum truncate clog that gtt need. + */ + if (u_sess->attr.attr_storage.max_active_gtt > 0) { + TransactionId safeAge; + TransactionId oldestGttFrozenxid = list_all_session_gtt_frozenxids(0, NULL, NULL, NULL); + + if (TransactionIdIsNormal(oldestGttFrozenxid)) { + safeAge = + oldestGttFrozenxid + static_cast(u_sess->attr.attr_storage.vacuum_gtt_defer_check_age); + if (safeAge < FirstNormalTransactionId) { + safeAge += FirstNormalTransactionId; + } + + /* + * We tolerate that the minimum age of gtt is less than + * the minimum age of conventional tables, otherwise it will + * throw warning message. + */ + if (TransactionIdIsNormal(safeAge) && + TransactionIdPrecedes(safeAge, newFrozenXid)) { + ereport(WARNING, + (errmsg( + "global temp table oldest relfrozenxid %lu is the oldest in the entire db", oldestGttFrozenxid), + errdetail("The oldest relfrozenxid in pg_class is %lu", newFrozenXid), + errhint("If they differ greatly, please consider cleaning up the data in global temp table."))); + } + + if (TransactionIdPrecedes(oldestGttFrozenxid, newFrozenXid)) { + newFrozenXid = oldestGttFrozenxid; + } + } + } /* Now fetch the pg_database tuple we need to update. */ relation = heap_open(DatabaseRelationId, RowExclusiveLock); @@ -1791,6 +1847,13 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast) return false; } + if (RELATION_IS_GLOBAL_TEMP(onerel) && + !gtt_storage_attached(RelationGetRelid(onerel))) { + CloseAllRelationsBeforeReturnFalse(); + proc_snapshot_and_transaction(); + return false; + } + /* * Get a session-level lock too. This will protect our access to the * relation across multiple transactions, so that we can vacuum the @@ -2263,9 +2326,7 @@ void vac_open_part_indexes( 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)); diff --git a/src/gausskernel/optimizer/commands/vacuumlazy.cpp b/src/gausskernel/optimizer/commands/vacuumlazy.cpp index a662dd19e940fbb50a6dd95dff7e8e3ecddf6a35..90ad2d8eef7ddb6ec444b90eea3e49fdd304a735 100644 --- a/src/gausskernel/optimizer/commands/vacuumlazy.cpp +++ b/src/gausskernel/optimizer/commands/vacuumlazy.cpp @@ -49,6 +49,7 @@ #include "catalog/catalog.h" #include "catalog/storage.h" #include "catalog/pg_hashbucket_fn.h" +#include "catalog/storage_gtt.h" #include "commands/dbcommands.h" #include "commands/vacuum.h" #include "miscadmin.h" diff --git a/src/gausskernel/optimizer/commands/view.cpp b/src/gausskernel/optimizer/commands/view.cpp index 277c7731fce730bdef91a805c9248c50d1ec9f57..b81f440711cf905e9b9d96a5d0f434c970e58844 100755 --- a/src/gausskernel/optimizer/commands/view.cpp +++ b/src/gausskernel/optimizer/commands/view.cpp @@ -475,6 +475,12 @@ void DefineView(ViewStmt* stmt, const char* queryString, bool isFirstNode) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("views cannot be unlogged because they do not have storage"))); + if (stmt->view->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("views cannot be global temp because they do not have storage"))); + } + /* * If the user didn't explicitly ask for a temporary view, check whether * we need one implicitly. We allow TEMP to be inserted automatically as diff --git a/src/gausskernel/optimizer/util/plancat.cpp b/src/gausskernel/optimizer/util/plancat.cpp index 3a0ae0f8b38aaee9606f24564c50dae5d2a72226..34b6b26ed922833e60fcb94a975acbeeba8116d5 100644 --- a/src/gausskernel/optimizer/util/plancat.cpp +++ b/src/gausskernel/optimizer/util/plancat.cpp @@ -27,6 +27,7 @@ #include "catalog/pg_partition_fn.h" #include "catalog/pg_statistic.h" #include "catalog/heap.h" +#include "catalog/storage_gtt.h" #include "commands/dbcommands.h" #include "executor/nodeModifyTable.h" #include "foreign/fdwapi.h" @@ -232,6 +233,12 @@ void get_relation_info(PlannerInfo* root, Oid relationObjectId, bool inhparent, continue; } + /* Ignore empty index for global temp table */ + if (RELATION_IS_GLOBAL_TEMP(indexRelation) && + !gtt_storage_attached(RelationGetRelid(indexRelation))) { + index_close(indexRelation, NoLock); + continue; + } /* * If the index is valid, but cannot yet be used, ignore it; but * mark the plan we are generating as transient. See diff --git a/src/gausskernel/process/postmaster/autovacuum.cpp b/src/gausskernel/process/postmaster/autovacuum.cpp index 7d5bfb5b5944f154588d924fdf07cbf5347f2a3c..d7e4a5ef848636e21b625d710053384e1665e5bc 100755 --- a/src/gausskernel/process/postmaster/autovacuum.cpp +++ b/src/gausskernel/process/postmaster/autovacuum.cpp @@ -2170,8 +2170,10 @@ static void do_autovacuum(void) bool enable_vacuum = false; /* We cannot safely process other backends' temp tables, so skip 'em. */ - if (RELPERSISTENCE_TEMP == classForm->relpersistence) + if (classForm->relpersistence == RELPERSISTENCE_TEMP || + classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { continue; + } /* Fetch reloptions for this table */ relopts = extract_autovac_opts(tuple, pg_class_desc); @@ -2414,7 +2416,8 @@ static void do_autovacuum(void) av_toastid_mainid* at_entry = NULL; /* We cannot safely process other backends' temp tables, so skip 'em. */ - if (classForm->relpersistence == RELPERSISTENCE_TEMP) + if (classForm->relpersistence == RELPERSISTENCE_TEMP || + classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) continue; at_entry = (av_toastid_mainid*)hash_search(toast_table_map, &(relid), HASH_FIND, &found); diff --git a/src/gausskernel/runtime/executor/execMain.cpp b/src/gausskernel/runtime/executor/execMain.cpp index 102d84c63cc98f38a86c99a852ff809bae169252..d6b41deca2beb9c219b3f7b69f9db36247352ac6 100644 --- a/src/gausskernel/runtime/executor/execMain.cpp +++ b/src/gausskernel/runtime/executor/execMain.cpp @@ -1045,6 +1045,11 @@ void ExecCheckXactReadOnly(PlannedStmt *plannedstmt) if (isTempNamespace(get_rel_namespace(rte->relid))) { continue; } + + if (get_rel_persistence(rte->relid) == RELPERSISTENCE_GLOBAL_TEMP) { + continue; + } + if (rte->relid == PgxcNodeRelationId && g_instance.attr.attr_storage.IsRoachStandbyCluster && u_sess->attr.attr_common.xc_maintenance_mode) { continue; diff --git a/src/gausskernel/runtime/executor/nodeModifyTable.cpp b/src/gausskernel/runtime/executor/nodeModifyTable.cpp index 5dea81324789c98d10ea38c81ca809f1550cf804..decd04027af50d89569d1fb92eef020b996a7a25 100644 --- a/src/gausskernel/runtime/executor/nodeModifyTable.cpp +++ b/src/gausskernel/runtime/executor/nodeModifyTable.cpp @@ -43,6 +43,7 @@ #include "catalog/heap.h" #include "catalog/pg_namespace.h" #include "catalog/pg_partition_fn.h" +#include "catalog/storage_gtt.h" #include "commands/defrem.h" #include "commands/tablecmds.h" #ifdef PGXC @@ -2161,7 +2162,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl result_rel_info->ri_FdwRoutine->GetFdwType() != MOT_ORC) ExecOpenIndices(result_rel_info); } - + init_gtt_storage(operation, result_rel_info); /* Now init the plan for this result rel */ estate->es_result_relation_info = result_rel_info; mt_state->mt_plans[i] = ExecInitNode(sub_plan, estate, eflags); diff --git a/src/gausskernel/runtime/executor/opfusion.cpp b/src/gausskernel/runtime/executor/opfusion.cpp index 18eb18bf2509675faa74449bf95297946fec8c49..db67067220317dd84b2f4478c5dd04a8a6da744e 100644 --- a/src/gausskernel/runtime/executor/opfusion.cpp +++ b/src/gausskernel/runtime/executor/opfusion.cpp @@ -29,6 +29,7 @@ #include "access/printtup.h" #include "access/transam.h" #include "catalog/pg_aggregate.h" +#include "catalog/storage_gtt.h" #include "commands/copy.h" #include "executor/nodeIndexscan.h" #include "gstrace/executer_gstrace.h" @@ -1099,7 +1100,7 @@ bool InsertFusion::execute(long max_rows, char* completionTag) CommandId mycid = GetCurrentCommandId(true); refreshParameterIfNecessary(); - + init_gtt_storage(CMD_INSERT, result_rel_info); /************************ * step 2: begin insert * ************************/ diff --git a/src/gausskernel/storage/access/common/reloptions.cpp b/src/gausskernel/storage/access/common/reloptions.cpp index 0e38871cb0f2a8d6c72a886368a15af6868440a9..d0c4734aadada738a669118b4134d513be6e4b8a 100644 --- a/src/gausskernel/storage/access/common/reloptions.cpp +++ b/src/gausskernel/storage/access/common/reloptions.cpp @@ -85,6 +85,7 @@ static relopt_bool boolRelOpts[] = { {{"multi_zall", "segmente all word from long words in zhparser text search praser", RELOPT_KIND_ZHPARSER}, false}, {{"ignore_enable_hadoop_env", "ignore enable_hadoop_env option", RELOPT_KIND_HEAP}, false}, {{"hashbucket", "Enables hashbucket in this relation", RELOPT_KIND_HEAP}, false}, + {{"on_commit_delete_rows", "global temp table on commit options", RELOPT_KIND_HEAP}, true}, /* list terminator */ {{NULL}}}; @@ -1490,7 +1491,8 @@ bytea* default_reloptions(Datum reloptions, bool validate, relopt_kind kind) {"start_ctid_internal", RELOPT_TYPE_STRING, offsetof(StdRdOptions, start_ctid_internal)}, {"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)}}; + {"hashbucket", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, hashbucket)}, + {"on_commit_delete_rows", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, on_commit_delete_rows)}}; options = parseRelOptions(reloptions, validate, kind, &numoptions); diff --git a/src/gausskernel/storage/access/nbtree/nbtree.cpp b/src/gausskernel/storage/access/nbtree/nbtree.cpp index 329d8a757e0b8b068a8bf1635aa7154ecd4cdc1c..03f0ef8a3abee8ea4676614d28db1df34469f5d3 100755 --- a/src/gausskernel/storage/access/nbtree/nbtree.cpp +++ b/src/gausskernel/storage/access/nbtree/nbtree.cpp @@ -228,6 +228,17 @@ Datum btinsert(PG_FUNCTION_ARGS) bool result = false; IndexTuple itup; + /* skip inserting if global temp table index does not exist */ + if (RELATION_IS_GLOBAL_TEMP(rel)) { + if (rel->rd_smgr == NULL) { + /* Open it at the smgr level if not already done */ + RelationOpenSmgr(rel); + } + if (!smgrexists(rel->rd_smgr, MAIN_FORKNUM)) { + PG_RETURN_BOOL(result); + } + } + /* generate an index tuple */ itup = index_form_tuple(RelationGetDescr(rel), values, isnull); itup->t_tid = *ht_ctid; diff --git a/src/gausskernel/storage/access/transam/multixact.cpp b/src/gausskernel/storage/access/transam/multixact.cpp index 415df027dcd6f816264442820458840d0a048e64..e36b5dfca625ab09badb93f64ac4e519499064f8 100644 --- a/src/gausskernel/storage/access/transam/multixact.cpp +++ b/src/gausskernel/storage/access/transam/multixact.cpp @@ -503,6 +503,25 @@ static void MultiXactIdSetOldestVisible(void) } } +/* + * ReadNextMultiXactId + * Return the next MultiXactId to be assigned, but don't allocate it + */ +MultiXactId ReadNextMultiXactId(void) +{ + MultiXactId mxid; + + /* XXX we could presumably do this without a lock. */ + LWLockAcquire(MultiXactGenLock, LW_SHARED); + mxid = t_thrd.shemem_ptr_cxt.MultiXactState->nextMXact; + LWLockRelease(MultiXactGenLock); + + if (mxid < FirstMultiXactId) + mxid = FirstMultiXactId; + + return mxid; +} + /* * MultiXactIdWait * Sleep on a MultiXactId. diff --git a/src/gausskernel/storage/access/transam/xlog.cpp b/src/gausskernel/storage/access/transam/xlog.cpp index a308a0cd575466ed29d99838e5c560a914b60a68..f8d5415c5da14a315537d3eb3f21365904f70d30 100755 --- a/src/gausskernel/storage/access/transam/xlog.cpp +++ b/src/gausskernel/storage/access/transam/xlog.cpp @@ -7958,6 +7958,11 @@ void StartupXLOG(void) */ t_thrd.xlog_cxt.recoveryTargetTLI = t_thrd.shemem_ptr_cxt.ControlFile->checkPointCopy.ThisTimeLineID; + /* clean temp relation files */ + if (u_sess->attr.attr_storage.max_active_gtt > 0) { + RemovePgTempFiles(); + } + // Check for recovery control file, and if so set up state for offline recovery readRecoveryCommandFile(); diff --git a/src/gausskernel/storage/buffer/bufmgr.cpp b/src/gausskernel/storage/buffer/bufmgr.cpp index 95de0effe74114d42739d79df1f306131a907d9a..91221cba0febefc03532e148157c0f3463d68609 100644 --- a/src/gausskernel/storage/buffer/bufmgr.cpp +++ b/src/gausskernel/storage/buffer/bufmgr.cpp @@ -44,6 +44,7 @@ #include "catalog/catalog.h" #include "catalog/dfsstore_ctlg.h" #include "catalog/pg_hashbucket_fn.h" +#include "catalog/storage_gtt.h" #include "commands/tablespace.h" #include "executor/instrument.h" #include "lib/binaryheap.h" @@ -4197,6 +4198,14 @@ void FlushBuffer(void* buf, SMgrRelation reln, ReadBufferMethod flushmethod) BlockNumber RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber fork_num) { BlockNumber result = 0; + /* + * When this backend not init gtt storage + * return 0 + */ + if (RELATION_IS_GLOBAL_TEMP(relation) && + !gtt_storage_attached(RelationGetRelid(relation))) { + return result; + } // Just return the relpages in pg_class for column-store relation. // Future: new interface should be implemented to calculate the number of blocks. diff --git a/src/gausskernel/storage/file/fd.cpp b/src/gausskernel/storage/file/fd.cpp index b6eefd03981e3177345d91b6e0b70ab8b31f21de..ac2f9178367a85cac96e3005456e51a88986d05f 100755 --- a/src/gausskernel/storage/file/fd.cpp +++ b/src/gausskernel/storage/file/fd.cpp @@ -3135,13 +3135,20 @@ void RemovePgTempFiles(void) if (strcmp(spc_de->d_name, ".") == 0 || strcmp(spc_de->d_name, "..") == 0) continue; + /* + * subDir returned by ReadDir will be overwritten by the next invoking. + * therefore, the result needs to be saved. + */ + char curSubDir[MAXPGPATH] = {0}; + strncpy_s(curSubDir, MAXPGPATH, spc_de->d_name, strlen(spc_de->d_name)); + securec_check_ss(rc, "", ""); #ifdef PGXC /* Postgres-XC tablespaces include node name in path */ rc = snprintf_s(temp_path, sizeof(temp_path), sizeof(temp_path) - 1, "pg_tblspc/%s/%s_%s/%s", - spc_de->d_name, + curSubDir, TABLESPACE_VERSION_DIRECTORY, g_instance.attr.attr_common.PGXCNodeName, PG_TEMP_FILES_DIR); @@ -3151,7 +3158,7 @@ void RemovePgTempFiles(void) sizeof(temp_path), sizeof(temp_path) - 1, "pg_tblspc/%s/%s/%s", - spc_de->d_name, + curSubDir, TABLESPACE_VERSION_DIRECTORY, PG_TEMP_FILES_DIR); securec_check_ss(rc, "", ""); @@ -3164,7 +3171,7 @@ void RemovePgTempFiles(void) sizeof(temp_path), sizeof(temp_path) - 1, "pg_tblspc/%s/%s_%s", - spc_de->d_name, + curSubDir, TABLESPACE_VERSION_DIRECTORY, g_instance.attr.attr_common.PGXCNodeName); securec_check_ss(rc, "", ""); @@ -3173,7 +3180,7 @@ void RemovePgTempFiles(void) sizeof(temp_path), sizeof(temp_path) - 1, "pg_tblspc/%s/%s", - spc_de->d_name, + curSubDir, TABLESPACE_VERSION_DIRECTORY); securec_check_ss(rc, "", ""); #endif diff --git a/src/gausskernel/storage/ipc/ipc.cpp b/src/gausskernel/storage/ipc/ipc.cpp index 9e684ea05e2b2754c9031ed95016fb42e29320fe..4fd99a3df7484263540aef411f981f4ada009f1d 100644 --- a/src/gausskernel/storage/ipc/ipc.cpp +++ b/src/gausskernel/storage/ipc/ipc.cpp @@ -481,6 +481,23 @@ void shmem_exit(int code) t_thrd.storage_cxt.on_shmem_exit_index = 0; } +/* ---------------------------------------------------------------- + * atexit_callback + * + * Backstop to ensure that direct calls of exit() don't mess us up. + * + * Somebody who was being really uncooperative could call _exit(), + * but for that case we have a "dead man switch" that will make the + * postmaster treat it as a crash --- see pmsignal.c. + * ---------------------------------------------------------------- + */ +static void atexit_callback(void) +{ + /* Clean up everything that must be cleaned up */ + /* ... too bad we don't know the real exit code ... */ + proc_exit_prepare(-1); +} + /* ---------------------------------------------------------------- * on_proc_exit * @@ -503,6 +520,32 @@ void on_proc_exit(pg_on_exit_callback function, Datum arg) } } +/* ---------------------------------------------------------------- + * before_shmem_exit + * + * Register early callback to perform user-level cleanup, + * e.g. transaction abort, before we begin shutting down + * low-level subsystems. + * ---------------------------------------------------------------- + */ +void before_shmem_exit(pg_on_exit_callback function, Datum arg) +{ + if (t_thrd.storage_cxt.before_shmem_exit_index >= MAX_ON_EXITS) + ereport(FATAL, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg_internal("out of before_shmem_exit slots"))); + + t_thrd.storage_cxt.before_shmem_exit_list[t_thrd.storage_cxt.before_shmem_exit_index].function = function; + t_thrd.storage_cxt.before_shmem_exit_list[t_thrd.storage_cxt.before_shmem_exit_index].arg = arg; + + ++t_thrd.storage_cxt.before_shmem_exit_index; + + if (!t_thrd.storage_cxt.atexit_callback_setup) { + (void)atexit(atexit_callback); + t_thrd.storage_cxt.atexit_callback_setup = true; + } +} + /* ---------------------------------------------------------------- * on_shmem_exit * diff --git a/src/gausskernel/storage/ipc/ipci.cpp b/src/gausskernel/storage/ipc/ipci.cpp index eff9b4a8e405588db34650fd1253ff0f6c28426f..508a9abcbd688be6334a06ed68cfe5b8d305f053 100755 --- a/src/gausskernel/storage/ipc/ipci.cpp +++ b/src/gausskernel/storage/ipc/ipci.cpp @@ -26,6 +26,7 @@ #include "access/subtrans.h" #include "access/twophase.h" #include "access/double_write.h" +#include "catalog/storage_gtt.h" #include "commands/tablespace.h" #include "commands/async.h" #include "foreign/dummyserver.h" @@ -161,6 +162,7 @@ void CreateSharedMemoryAndSemaphores(bool makePrivate, int port) size = add_size(size, BTreeShmemSize()); size = add_size(size, SyncScanShmemSize()); size = add_size(size, AsyncShmemSize()); + size = add_size(size, active_gtt_shared_hash_size()); #ifdef PGXC size = add_size(size, NodeTablesShmemSize()); #endif @@ -243,6 +245,8 @@ void CreateSharedMemoryAndSemaphores(bool makePrivate, int port) CSNLOGShmemInit(); MultiXactShmemInit(); InitBufferPool(); + /* global temporay table */ + active_gtt_shared_hash_init(); /* * Set up lock manager */ diff --git a/src/gausskernel/storage/ipc/procarray.cpp b/src/gausskernel/storage/ipc/procarray.cpp index fc103060984ca59cb8451a0f2b0ba60aae4ecb6e..8308789aa4d0c5d5c63062011d525d9a833117d4 100644 --- a/src/gausskernel/storage/ipc/procarray.cpp +++ b/src/gausskernel/storage/ipc/procarray.cpp @@ -4523,3 +4523,70 @@ char* transfer_snapshot_type(SnapshotType snap_type) } return "UnKnown"; } + +/* + * search all active backend to get oldest frozenxid + * for global temp table. + */ +TransactionId list_all_session_gtt_frozenxids(int max_size, ThreadId *pids, TransactionId *xids, int *n) +{ + ProcArrayStruct *arrayP = g_instance.proc_array_idx; + TransactionId result = InvalidTransactionId; + int index; + int flags = 0; + int i = 0; + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return 0; + + if (max_size > 0) { + Assert(pids); + Assert(xids); + Assert(n); + *n = 0; + } + + if (u_sess->attr.attr_storage.max_active_gtt <= 0) + return InvalidTransactionId; + + if (RecoveryInProgress()) + return InvalidTransactionId; + + flags |= PROC_IS_AUTOVACUUM; + flags |= PROC_IN_LOGICAL_DECODING; + + LWLockAcquire(ProcArrayLock, LW_SHARED); + if (max_size > 0 && max_size < arrayP->numProcs) { + LWLockRelease(ProcArrayLock); + elog(ERROR, "list_all_gtt_frozenxids require more array"); + } + + for (index = 0; index < arrayP->numProcs; index++) { + int pgprocno = arrayP->pgprocnos[index]; + volatile PGPROC *proc = g_instance.proc_base_all_procs[pgprocno]; + volatile PGXACT *pgxact = &g_instance.proc_base_all_xacts[pgprocno]; + + if (pgxact->vacuumFlags & flags) + continue; + + if (proc->databaseId == u_sess->proc_cxt.MyDatabaseId && + TransactionIdIsNormal(proc->session_gtt_frozenxid)) { + if (result == InvalidTransactionId) + result = proc->session_gtt_frozenxid; + else if (TransactionIdPrecedes(proc->session_gtt_frozenxid, result)) + result = proc->session_gtt_frozenxid; + + if (max_size > 0) { + pids[i] = proc->pid; + xids[i] = proc->session_gtt_frozenxid; + i++; + } + } + } + LWLockRelease(ProcArrayLock); + if (max_size > 0) { + *n = i; + } + return result; +} + diff --git a/src/gausskernel/storage/lmgr/lwlock.cpp b/src/gausskernel/storage/lmgr/lwlock.cpp index 6164fdf62d2541b73cdad9201c39fa5be6bb1833..9c9da366a8d71ffd70a28bf1070980a37b4c6c8e 100755 --- a/src/gausskernel/storage/lmgr/lwlock.cpp +++ b/src/gausskernel/storage/lmgr/lwlock.cpp @@ -153,7 +153,9 @@ static const char *BuiltinTrancheNames[] = { "DoubleWriteLock", "BufFreeListLock", "LWTRANCHE_ACCOUNT_TABLE", - "GeneralExtendedLock" + "GeneralExtendedLock", + /* LWTRANCHE_GTT_CTL */ + "GlobalTempTableControl" }; static void RegisterLWLockTranches(void); diff --git a/src/gausskernel/storage/lmgr/proc.cpp b/src/gausskernel/storage/lmgr/proc.cpp index b048ff9600cd2194f5bdc4dc66fad6c4bfd600bf..d5d11727fa5ae26b370a78804b802c45ce48b9c0 100755 --- a/src/gausskernel/storage/lmgr/proc.cpp +++ b/src/gausskernel/storage/lmgr/proc.cpp @@ -573,6 +573,7 @@ void InitProcess(void) t_thrd.proc->backendId = InvalidBackendId; t_thrd.proc->databaseId = InvalidOid; t_thrd.proc->roleId = InvalidOid; + t_thrd.proc->session_gtt_frozenxid = InvalidTransactionId; /* init session level gtt frozenxid */ /* For backends, upgrade status is either passed down from remote backends or inherit from PM */ t_thrd.proc->workingVersionNum = (u_sess->proc_cxt.MyProcPort ? u_sess->proc_cxt.MyProcPort->SessionVersionNum : pg_atomic_read_u32(&WorkingGrandVersionNum)); @@ -786,6 +787,7 @@ void InitAuxiliaryProcess(void) t_thrd.proc->backendId = InvalidBackendId; t_thrd.proc->databaseId = InvalidOid; t_thrd.proc->roleId = InvalidOid; + t_thrd.proc->session_gtt_frozenxid = InvalidTransactionId; /* init session level gtt frozenxid */ t_thrd.pgxact->delayChkpt = false; t_thrd.pgxact->vacuumFlags = 0; t_thrd.proc->lwWaiting = false; diff --git a/src/gausskernel/storage/smgr/md.cpp b/src/gausskernel/storage/smgr/md.cpp index df8c460090805ba22320abf2d47f305b242e6f3f..e929a69769852903002820ba1e55ccc3c02db688 100755 --- a/src/gausskernel/storage/smgr/md.cpp +++ b/src/gausskernel/storage/smgr/md.cpp @@ -590,6 +590,9 @@ static MdfdVec* mdopen(SMgrRelation reln, ForkNumber forknum, ExtensionBehavior */ void mdclose(SMgrRelation reln, ForkNumber forknum) { + if (reln->md_fd == NULL) { + return; + } MdfdVec* v = reln->md_fd[forknum]; /* No work if already closed */ diff --git a/src/gausskernel/storage/smgr/smgr.cpp b/src/gausskernel/storage/smgr/smgr.cpp index 7ad6999e777db53819fac867cc49f041b2e040bc..470dd4f112ed98de32baccd075aebd9cf5bcb5cf 100755 --- a/src/gausskernel/storage/smgr/smgr.cpp +++ b/src/gausskernel/storage/smgr/smgr.cpp @@ -285,6 +285,9 @@ void smgrclearowner(SMgrRelation* owner, SMgrRelation reln) */ bool smgrexists(SMgrRelation reln, ForkNumber forknum) { + if (reln == NULL) { + return false; + } return (*(g_smgrsw[reln->smgr_which].smgr_exists))(reln, forknum); } @@ -296,6 +299,10 @@ void smgrclose(SMgrRelation reln) SMgrRelation* owner = NULL; int forknum; + if (reln == NULL) { + return; + } + for (forknum = 0; forknum < (int)(reln->md_fdarray_size); forknum++) { (*(g_smgrsw[reln->smgr_which].smgr_close))(reln, (ForkNumber)forknum); } diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h index 6f41b61c546b98158f03fb0d5bfc7396078f01f4..41d829c4bb3877abe5029d30d2dfd8309c56d7ef 100755 --- a/src/include/access/multixact.h +++ b/src/include/access/multixact.h @@ -50,6 +50,7 @@ extern MultiXactId MultiXactIdCreate(TransactionId xid1, TransactionId xid2); extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid); extern bool MultiXactIdIsRunning(MultiXactId multi); extern bool MultiXactIdIsCurrent(MultiXactId multi); +extern MultiXactId ReadNextMultiXactId(void); extern void MultiXactIdWait(MultiXactId multi, bool allow_con_update = false); extern bool ConditionalMultiXactIdWait(MultiXactId multi); extern void MultiXactIdSetOldestMember(void); diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index c0cca8ef7d260ce049c953ef90edf4558adeac0a..a0df9b3b543f3b293f54a1c7c747fed0a1b75c67 100755 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -47,7 +47,8 @@ typedef struct HashBucketInfo { extern Relation heap_create(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid relfilenode, Oid bucketOid, TupleDesc tupDesc, char relkind, char relpersistence, bool partitioned_relation, bool rowMovement, - bool shared_relation, bool mapped_relation, bool allow_system_table_mods, int8 row_compress, Oid ownerid); + bool shared_relation, bool mapped_relation, bool allow_system_table_mods, int8 row_compress, Oid ownerid, + bool skip_create_storage); extern Partition heapCreatePartition(const char* part_name, bool for_partitioned_table, Oid part_tablespace, Oid part_id, Oid partFileNode, Oid bucketOid, Oid ownerid); diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 546c8541428a6b498593fc22ac96255a5dfdc31d..c4de4f8b4eafd4d555b2da746c36ce64097f834e 100755 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -72,6 +72,7 @@ extern void index_constraint_create(Relation heapRelation, Oid indexRelationId, extern void index_drop(Oid indexId, bool concurrent); extern IndexInfo *BuildIndexInfo(Relation index); +extern IndexInfo *BuildDummyIndexInfo(Relation index); extern void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull); extern void index_build(Relation heapRelation, Partition heapPartition, Relation indexRelation, @@ -94,7 +95,9 @@ extern void reindex_indexpart_internal(Relation heapRelation, Relation iRel, IndexInfo* indexInfo, Oid indexPartId); -extern void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks, AdaptMem *memInfo, bool dbWide); +extern void reindex_index(Oid indexId, Oid indexPartId, + bool skip_constraint_checks, AdaptMem *memInfo, + bool dbWide, char persistence); /* Flag bits for reindex_relation(): */ #define REINDEX_REL_PROCESS_TOAST 0x01 diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index bf20e3a717c0f1bf899fd147cce0a04c5af7462f..ff565fefc05694cd79ba7018095b79c28f87b968 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -174,6 +174,7 @@ DESCR(""); #define RELPERSISTENCE_PERMANENT 'p' /* regular table */ #define RELPERSISTENCE_UNLOGGED 'u' /* unlogged permanent table */ #define RELPERSISTENCE_TEMP 't' /* temporary table */ +#define RELPERSISTENCE_GLOBAL_TEMP 'g' /* global temporary table */ /* default selection for replica identity (primary key or nothing) */ #define REPLICA_IDENTITY_DEFAULT 'd' @@ -181,6 +182,18 @@ DESCR(""); #define REPLICA_IDENTITY_NOTHING 'n' /* all columns are loged as replica identity */ #define REPLICA_IDENTITY_FULL 'f' + +/* + * Relation kinds that have physical storage. These relations normally have + * relfilenode set to non-zero, but it can also be zero if the relation is + * mapped. + */ +#define RELKIND_HAS_STORAGE(relkind) \ + ((relkind) == RELKIND_RELATION || \ + (relkind) == RELKIND_INDEX || \ + (relkind) == RELKIND_SEQUENCE || \ + (relkind) == RELKIND_TOASTVALUE) + /* * an explicitly chosen candidate key's columns are used as identity; * will still be set if the index has been dropped, in that case it diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h index dd27267f3a8c11e8cbd086e781bd0825462c70e0..2bf2859d8623890bb16c4432e60843c02221517d 100644 --- a/src/include/catalog/storage.h +++ b/src/include/catalog/storage.h @@ -25,7 +25,8 @@ #define DFS_STOR_FLAG -1 -extern void RelationCreateStorage(RelFileNode rnode, char relpersistence, Oid ownerid, Oid bucketOid = InvalidOid); +extern void RelationCreateStorage(RelFileNode rnode, char relpersistence, Oid ownerid, Oid bucketOid = InvalidOid, + Relation rel = NULL); extern void RelationDropStorage(Relation rel, bool isDfsTruncate = false); extern void RelationPreserveStorage(RelFileNode rnode, bool atCommit); extern void RelationTruncate(Relation rel, BlockNumber nblocks); @@ -57,7 +58,8 @@ extern void ColMainFileNodesCreate(void); extern void ColMainFileNodesDestroy(void); extern void ColMainFileNodesAppend(RelFileNode* bcmFileNode, BackendId backend); extern void ColumnRelationDoDeleteFiles(RelFileNode* bcmFileNode, ForkNumber forknum, BackendId backend, Oid ownerid); -extern void RowRelationDoDeleteFiles(RelFileNode rnode, BackendId backend, Oid ownerid); +extern void RowRelationDoDeleteFiles(RelFileNode rnode, BackendId backend, Oid ownerid, Oid relOid = InvalidOid, + bool isCommit = false); extern uint64 GetSMgrRelSize(RelFileNode* relfilenode, BackendId backend, ForkNumber forkNum); /* diff --git a/src/include/catalog/storage_gtt.h b/src/include/catalog/storage_gtt.h new file mode 100644 index 0000000000000000000000000000000000000000..d76a2dd5271c430abef379513a54f073b4520d49 --- /dev/null +++ b/src/include/catalog/storage_gtt.h @@ -0,0 +1,47 @@ +/* ------------------------------------------------------------------------- + * + * storage_gtt.h + * prototypes for functions in backend/catalog/storage_gtt.c + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2012 Postgres-XC Development Group + * + * src/include/catalog/storage_gtt.h + * + * ------------------------------------------------------------------------- + */ +#ifndef STORAGE_GTT_H +#define STORAGE_GTT_H + +#include "access/htup.h" +#include "storage/block.h" +#include "storage/relfilenode.h" +#include "nodes/execnodes.h" +#include "utils/relcache.h" + +extern Size active_gtt_shared_hash_size(void); +extern void active_gtt_shared_hash_init(void); +extern void remember_gtt_storage_info(const RelFileNode rnode, Relation rel); +extern void forget_gtt_storage_info(Oid relid, const RelFileNode relfilenode, bool isCommit); +extern bool is_other_backend_use_gtt(Oid relid); +extern bool gtt_storage_attached(Oid relid); +extern Bitmapset *copy_active_gtt_bitmap(Oid relid); +extern void up_gtt_att_statistic( + Oid reloid, int attnum, int natts, TupleDesc tupleDescriptor, Datum* values, bool* isnull); +extern HeapTuple get_gtt_att_statistic(Oid reloid, int attnum); +extern void release_gtt_statistic_cache(HeapTuple tup); +extern void up_gtt_relstats(const Relation relation, + BlockNumber numPages, + double numTuples, + BlockNumber numAllVisiblePages, + TransactionId relfrozenxid); +extern bool get_gtt_relstats( + Oid relid, BlockNumber* relpages, double* reltuples, BlockNumber* relallvisible, TransactionId* relfrozenxid); +extern void gtt_force_enable_index(Relation index); +extern void gtt_fix_index_state(Relation index); +extern void init_gtt_storage(CmdType operation, ResultRelInfo *resultRelInfo); +extern Oid gtt_fetch_current_relfilenode(Oid relid); +extern void gtt_switch_rel_relfilenode(Oid rel1, Oid relfilenode1, Oid rel2, Oid relfilenode2, bool footprint); + +#endif /* STORAGE_GTT_H */ diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index fb7edbb381026ca24ec3be97cc4c98bc69c2875a..6ca23fe336750f3266f03d10d9cb2ff3651daa9b 100755 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -36,7 +36,8 @@ extern void finishPartitionHeapSwap(Oid partitionOid, Oid tempTableOid, bool swa TransactionId frozenXid, bool tempTableIsPartition = false); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, - bool check_constraints, TransactionId frozenXid, AdaptMem* memInfo = NULL); + bool check_constraints, TransactionId frozenXid, AdaptMem* memInfo = NULL, + char newrelpersistence = RELPERSISTENCE_PERMANENT); extern void vacuumFullPart(Oid partOid, VacuumStmt* vacstmt, int freeze_min_age, int freeze_table_age); extern void updateRelationName(Oid relOid, bool isPartition, const char* relNewName); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index d69ca01697e8ca30e6034bd35be16f3da36fbb40..d4ddfc3c95f42d25cfbaacde6f44353f412e3a57 100755 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -363,7 +363,8 @@ extern char* buildTempSampleTable(Oid relid, Oid mian_relid, TempSmpleTblType ty extern void dropSampleTable(const char* tableName); extern const char* get_sample_tblname(AnalyzeMode analyzemode, List* tmpSampleTblNameList); extern VacAttrStats* examine_attribute(Relation onerel, Bitmapset* bms_attnums, bool isLog); -extern void update_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats** vacattrstats); +extern void update_attstats( + Oid relid, char relkind, bool inh, int natts, VacAttrStats** vacattrstats, char relpersistence); /* we should delete all records in pg_statistic when the data is dirty and current totalrows is null. */ extern void delete_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats** vacattrstats, unsigned int delete_stats_option = DELETE_STATS_SINGLE); diff --git a/src/include/knl/knl_guc/knl_session_attr_storage.h b/src/include/knl/knl_guc/knl_session_attr_storage.h index b2fdc7e3e30534eb35f8a2e0f76534ce85b82aa8..25910c071d58aee940efe360b1ed8c91d1222a1d 100755 --- a/src/include/knl/knl_guc/knl_session_attr_storage.h +++ b/src/include/knl/knl_guc/knl_session_attr_storage.h @@ -176,6 +176,10 @@ typedef struct knl_session_attr_storage { */ bool enable_xlog_prune; int defer_csn_cleanup_time; + + /* for GTT */ + int max_active_gtt; + int vacuum_gtt_defer_check_age; } knl_session_attr_storage; #endif /* SRC_INCLUDE_KNL_KNL_SESSION_ATTR_STORAGE */ diff --git a/src/include/knl/knl_thread.h b/src/include/knl/knl_thread.h index a823451eda14111ae0187865943a68f43bac4af0..72104f18fe222c5ecafa6d345069e329aa3aabd5 100644 --- a/src/include/knl/knl_thread.h +++ b/src/include/knl/knl_thread.h @@ -841,6 +841,10 @@ typedef struct knl_t_shemem_ptr_context { * where we have special measures to pass it down). */ union LWLockPadded *mainLWLockArray; + + // for GTT table to track sessions and their usage of GTTs + struct gtt_ctl_data* gtt_shared_ctl; + struct HTAB* active_gtt_shared_hash; } knl_t_shemem_ptr_context; typedef struct knl_t_cstore_context { @@ -2425,8 +2429,10 @@ typedef struct knl_t_storage_context { bool atexit_callback_setup; ONEXIT on_proc_exit_list[MAX_ON_EXITS]; ONEXIT on_shmem_exit_list[MAX_ON_EXITS]; + ONEXIT before_shmem_exit_list[MAX_ON_EXITS]; int on_proc_exit_index; int on_shmem_exit_index; + int before_shmem_exit_index; union CmprMetaUnion* cmprMetaInfo; diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 053a74dbd0a33f326f19a7268d254bbd9da8efa5..465b0676be0a949e8aebeb2e005039d36f018d63 100755 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -15,6 +15,7 @@ #define MAKEFUNC_H #include "nodes/parsenodes.h" +#include "nodes/execnodes.h" extern A_Expr* makeA_Expr(A_Expr_Kind kind, List* name, Node* lexpr, Node* rexpr, int location); @@ -70,4 +71,6 @@ extern Node* makeTidConst(ItemPointer item); extern FuncCall* makeFuncCall(List* funcname, List* args, int location); extern Param* makeParam( ParamKind paramkind, int paramid, Oid paramtype, int32 paramtypmod, Oid paramcollid, int location); +extern IndexInfo* makeIndexInfo(int numattrs, List *expressions, List *predicates, + bool unique, bool isready, bool concurrent); #endif /* MAKEFUNC_H */ diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index d4465b28a16b655c138644f4b520a8bac2ae8aeb..32c51a52d352a0548b763fc26d315b8a2d261cf9 100755 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -303,6 +303,8 @@ extern List* list_copy_tail(const List* list, int nskip); extern void dlist_add_tail_cell(DList* dlist, DListCell* cell); extern List* list_merge_int(List* list1, List* list2); +extern List* list_insert_nth_oid(List *list, int pos, Oid datum); + /* * To ease migration to the new list API, a set of compatibility * macros are provided that reduce the impact of the list API changes diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index 152d3e88cb3454e4f481a088e5baf543bae4f950..42cd133842362a8a8a24d380d091ebfa9a4555af 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -433,6 +433,9 @@ inline void PageSetLSN(Page page, XLogRecPtr LSN, bool check = true) } while (0) #define PageClearPrunable(page) (HeapPageSetPruneXid(page, InvalidTransactionId)) +#define GlobalTempRelationPageIsNotInitialized(rel, page) \ + ((rel)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP && PageIsNew(page)) + constexpr int PAGE_INDEX_CAN_TUPLE_DELETE = 2; /* ---------------------------------------------------------------- diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h index 521623f1bab0dc0cf098280c01f91127a41dd7d3..18c61a86294fe952fe96ef3479d9f53d4d46cbfc 100755 --- a/src/include/storage/ipc.h +++ b/src/include/storage/ipc.h @@ -72,6 +72,7 @@ extern void proc_exit(int code); extern void sess_exit(int code); extern void shmem_exit(int code); extern void on_proc_exit(pg_on_exit_callback function, Datum arg); +extern void before_shmem_exit(pg_on_exit_callback function, Datum arg); extern void on_shmem_exit(pg_on_exit_callback function, Datum arg); extern void cancel_shmem_exit(pg_on_exit_callback function, Datum arg); extern void on_exit_reset(void); diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 57923a3bec162b9e6d9e10ac6a4de951cae9ec2c..0bbb51ddd8a8767c33961acaffc8715d52bfe688 100755 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -142,6 +142,7 @@ enum BuiltinTrancheIds { LWTRANCHE_BUFFER_FREELIST, LWTRANCHE_ACCOUNT_TABLE, LWTRANCHE_EXTEND, // For general 3rd plugin + LWTRANCHE_GTT_CTL, // For GTT /* * Each trancheId above should have a corresponding item in BuiltinTrancheNames; @@ -227,6 +228,8 @@ typedef union LWLockPadded { char pad[LWLOCK_PADDED_SIZE]; } LWLockPadded; +extern PGDLLIMPORT LWLockPadded *MainLWLockArray; + /* * We use this structure to keep track of locked LWLocks for release * during error recovery. The maximum size could be determined at runtime @@ -269,6 +272,21 @@ extern void CreateLWLocks(void); extern void RequestAddinLWLocks(int n); extern const char *GetBuiltInTrancheName(int trancheId); +/* + * There is another, more flexible method of obtaining lwlocks. First, call + * LWLockNewTrancheId just once to obtain a tranche ID; this allocates from + * a shared counter. Next, each individual process using the tranche should + * call LWLockRegisterTranche() to associate that tranche ID with a name. + * Finally, LWLockInitialize should be called just once per lwlock, passing + * the tranche ID as an argument. + * + * It may seem strange that each process using the tranche must register it + * separately, but dynamic shared memory segments aren't guaranteed to be + * mapped at the same address in all coordinating backends, so storing the + * registration in the main shared memory segment wouldn't work for that case. + */ +extern void LWLockRegisterTranche(int tranche_id, const char *tranche_name); + extern void wakeup_victim(LWLock *lock, ThreadId victim_tid); extern int *get_held_lwlocks_num(void); extern uint32 get_held_lwlocks_maxnum(void); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index cb6580b0eb0f431be6534d7845f6cfb8c8f0733a..768540a05713807cdfa3bf24f19da97d784cee0a 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -128,6 +128,7 @@ struct PGPROC { ThreadId sessMemorySessionid; uint64 sessionid; /* if not zero, session id in thread pool*/ int logictid; /*logic thread id*/ + TransactionId session_gtt_frozenxid; /* session level global temp table relfrozenxid */ int pgprocno; int nodeno; diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index c26861f4f28ff47070f73bf459f8a32652bd304d..287ab509f41fd1e4beb8a95df2416b6068a0bb65 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -127,3 +127,6 @@ extern void UpdateCSNLogAtTransactionEND( extern void ResetProcXidCache(PGPROC* proc, bool needlock); #endif /* USE_UT */ +// For GTT +extern TransactionId list_all_session_gtt_frozenxids(int max_size, ThreadId *pids, TransactionId *xids, int *n); + diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h index bb6a4c9e54742ea97b0ca0de9707f1fe20225a38..891d4ee101ceaa00fa292138350ea8a4ccf4930b 100755 --- a/src/include/utils/catcache.h +++ b/src/include/utils/catcache.h @@ -174,6 +174,11 @@ typedef struct CatCacheHeader { extern void AtEOXact_CatCache(bool isCommit); +/* this extern duplicates utils/memutils.h... */ +extern THR_LOCAL PGDLLIMPORT MemoryContext CacheMemoryContext; + +extern void CreateCacheMemoryContext(void); + extern CatCache* InitCatCache(int id, Oid reloid, Oid indexoid, int nkeys, const int* key, int nbuckets); extern void InitCatCachePhase2(CatCache* cache, bool touch_index); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index f5706365058c3f4bc0d5ff1bbf2751f907cc4a23..2c83af0008640cbadc4875452bef5c50557fd0c6 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -94,6 +94,7 @@ extern int get_relnatts(Oid relid); extern char* get_rel_name(Oid relid); extern char* getPartitionName(Oid partid, bool missing_ok); extern Oid get_rel_namespace(Oid relid); +extern char get_rel_persistence(Oid relid); extern bool is_sys_table(Oid relid); extern Oid get_rel_type_id(Oid relid); extern char get_rel_relkind(Oid relid); diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h index b9b3a8fb90aa6c318f15489b3d969cea235ab771..e1e75f12a2ee670957389e8977c8a334da1a5888 100755 --- a/src/include/utils/memutils.h +++ b/src/include/utils/memutils.h @@ -86,6 +86,7 @@ extern MemoryContext PmTopMemoryContext; extern MemoryContext StreamInfoContext; extern THR_LOCAL PGDLLIMPORT MemoryContext ErrorContext; +extern THR_LOCAL PGDLLIMPORT MemoryContext CacheMemoryContext; /* * Memory-context-type-independent functions in mcxt.c diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 142988cf86b5801677aa66bd3859d97368fad911..0fdaf7b8f647e8d891dfe1df7e9dbd9751cb4f19 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -306,6 +306,7 @@ typedef struct StdRdOptions { char* start_ctid_internal; char* end_ctid_internal; char *merge_list; + bool on_commit_delete_rows; /* global temp table */ } StdRdOptions; #define HEAP_MIN_FILLFACTOR 10 @@ -477,7 +478,9 @@ typedef struct StdRdOptions { * RelationUsesLocalBuffers * True if relation's pages are stored in local buffers. */ -#define RelationUsesLocalBuffers(relation) ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP) +#define RelationUsesLocalBuffers(relation) \ + ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP || \ + (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) #define RelationIsLocalTemp(relation) \ ((relation)->rd_rel->relnamespace == u_sess->catalog_cxt.myTempNamespace || \ (relation)->rd_rel->relnamespace == u_sess->catalog_cxt.myTempToastNamespace) @@ -505,14 +508,38 @@ typedef struct StdRdOptions { /* * RELATION_IS_LOCAL - * If a rel is either temp or newly created in the current transaction, + * If a rel is either local temp or global temp relation + * or newly created in the current transaction, * it can be assumed to be accessible only to the current backend. * This is typically used to decide that we can skip acquiring locks. * * Beware of multiple eval of argument */ #define RELATION_IS_LOCAL(relation) \ - ((relation)->rd_islocaltemp || (relation)->rd_createSubid != InvalidSubTransactionId) + ((relation)->rd_islocaltemp || \ + (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP || \ + (relation)->rd_createSubid != InvalidSubTransactionId) + +/* + * RELATION_IS_TEMP + * Test a rel is either local temp relation of this session + * or global temp relation. + */ +#define RELATION_IS_TEMP(relation) \ + ((relation)->rd_islocaltemp || \ + (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) + +/* global temp table implementations */ +#define RELATION_IS_GLOBAL_TEMP(relation) \ + ((relation) != NULL && (relation)->rd_rel != NULL && \ + (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) + +#define RELATION_GTT_ON_COMMIT_DELETE(relation) \ + ((relation)->rd_options && (relation)->rd_rel->relkind == RELKIND_RELATION && \ + (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP ? \ + (reinterpret_cast((relation)->rd_options))->on_commit_delete_rows : false) + +#define RelationGetRelPersistence(relation) ((relation)->rd_rel->relpersistence) /* routines in utils/cache/relcache.c */ extern void RelationIncrementReferenceCount(Relation rel); diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 9d7fd46ccdbfd157365d8e11ef11abe5384bf8c3..4c9aa108ede45046b9b6064132e17d7657f7b385 100755 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -56,8 +56,11 @@ 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); + typedef enum IndexAttrBitmapKind { INDEX_ATTR_BITMAP_ALL, INDEX_ATTR_BITMAP_KEY, diff --git a/src/test/regress/expected/gtt_clean.out b/src/test/regress/expected/gtt_clean.out new file mode 100644 index 0000000000000000000000000000000000000000..184c0243539ac1e63107fe9045f72ee880135eec --- /dev/null +++ b/src/test/regress/expected/gtt_clean.out @@ -0,0 +1,14 @@ +reset search_path; +select pg_sleep(5); + pg_sleep +---------- + +(1 row) + +drop schema gtt cascade; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to table gtt.gtt1 +drop cascades to table gtt.gtt2 +drop cascades to table gtt.gtt3 +drop cascades to table gtt.gtt_t_kenyon +drop cascades to table gtt.gtt_with_seq \ No newline at end of file diff --git a/src/test/regress/expected/gtt_function.out b/src/test/regress/expected/gtt_function.out new file mode 100644 index 0000000000000000000000000000000000000000..42ae3729662233c2acc7e600181d6430a978e9a2 --- /dev/null +++ b/src/test/regress/expected/gtt_function.out @@ -0,0 +1,361 @@ +CREATE SCHEMA gtt_function; +set search_path=gtt_function,sys; +create global temp table gtt1(a int primary key, b text); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt1_pkey" for table "gtt1" +create global temp table gtt_test_rename(a int primary key, b text); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt_test_rename_pkey" for table "gtt_test_rename" +create global temp table gtt2(a int primary key, b text) on commit delete rows; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt2_pkey" for table "gtt2" +create global temp table gtt3(a int primary key, b text) on commit PRESERVE rows; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt3_pkey" for table "gtt3" +create global temp table tmp_t0(c0 tsvector,c1 varchar(100)); +create table tbl_inherits_parent( +a int not null, +b varchar(32) not null default 'Got u', +c int check (c > 0), +d date not null +); +create global temp table tbl_inherits_parent_global_temp( +a int not null, +b varchar(32) not null default 'Got u', +c int check (c > 0), +d date not null +)on commit delete rows; +CREATE global temp TABLE products ( + product_no integer PRIMARY KEY, + name text, + price numeric +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "products_pkey" for table "products" +create global temp table gtt6(n int) with (on_commit_delete_rows='true'); +begin; +insert into gtt6 values (9); +-- 1 row +select * from gtt6; + n +--- + 9 +(1 row) + +commit; +-- 0 row +select * from gtt6; + n +--- +(0 rows) + +-- ERROR +create index CONCURRENTLY idx_gtt1 on gtt1 (b); +ERROR: PGXC does not support concurrent INDEX yet +DETAIL: The feature is not currently supported +-- ERROR +cluster gtt1 using gtt1_pkey; +-- ERROR +create table gtt1(a int primary key, b text) on commit delete rows; +ERROR: ON COMMIT can only be used on temporary tables +-- ERROR +alter table gtt1 SET TABLESPACE pg_default; +ERROR: not support alter table set tablespace on global temp table. +-- ERROR +alter table gtt1 set ( on_commit_delete_rows='true'); +ERROR: table cannot add or modify on commit parameter by ALTER TABLE command. +-- ERROR +create table gtt1(a int primary key, b text) with(on_commit_delete_rows=true); +ERROR: The parameter on_commit_delete_rows is exclusive to the global temp table, which cannot be specified by a regular table +-- ERROR +create or replace global temp view gtt_v as select 5; +ERROR: views cannot be global temp because they do not have storage +create table foo(); +ERROR: must have at least one column +-- ERROR +alter table foo set (on_commit_delete_rows='true'); +ERROR: relation "foo" does not exist +-- ok +--CREATE global temp TABLE measurement ( +-- logdate date not null, +-- peaktemp int, +-- unitsales int +--) PARTITION BY RANGE (logdate); +--ok +--CREATE global temp TABLE p_table01 ( +--id bigserial NOT NULL, +--cre_time timestamp without time zone, +--note varchar(30) +--) PARTITION BY RANGE (cre_time) +--WITH ( +--OIDS = FALSE +--)on commit delete rows; + +--CREATE global temp TABLE p_table01_2018 +--PARTITION OF p_table01 +--FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00') on commit delete rows; + +--CREATE global temp TABLE p_table01_2017 +--PARTITION OF p_table01 +--FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00') on commit delete rows; +--begin; +--insert into p_table01 values(1,'2018-01-02 00:00:00','test1'); +--insert into p_table01 values(1,'2018-01-02 00:00:00','test2'); +--select count(*) from p_table01; +--commit; +--select count(*) from p_table01; +--ok +--CREATE global temp TABLE p_table02 ( +--id bigserial NOT NULL, +--cre_time timestamp without time zone, +--note varchar(30) +--) PARTITION BY RANGE (cre_time) +--WITH ( +--OIDS = FALSE +--) +--on commit PRESERVE rows; +--CREATE global temp TABLE p_table02_2018 +--PARTITION OF p_table02 +--FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00'); +--CREATE global temp TABLE p_table02_2017 +--PARTITION OF p_table02 +--FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00'); +-- ERROR +--create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent); +-- ok +--create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent_global_temp) on commit delete rows; +select relname ,relkind, relpersistence, reloptions from pg_class where relname like 'p_table0%' or relname like 'tbl_inherits%' order by relname; + relname | relkind | relpersistence | reloptions +---------------------------------+---------+----------------+------------------------------------------------------------- + tbl_inherits_parent | r | p | {orientation=row,compression=no} + tbl_inherits_parent_global_temp | r | g | {orientation=row,compression=no,on_commit_delete_rows=true} +(2 rows) + +-- ERROR +create global temp table gtt3(a int primary key, b text) on commit drop; +ERROR: ON COMMIT only support PRESERVE ROWS or DELETE ROWS option +-- ERROR +create global temp table gtt4(a int primary key, b text) with(on_commit_delete_rows=true) on commit delete rows; +ERROR: could not create global temporary table with on commit and with clause at same time +-- ok +create global temp table gtt5(a int primary key, b text) with(on_commit_delete_rows=true); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt5_pkey" for table "gtt5" +--ok +alter table gtt_test_rename rename to gtt_test_new; +-- ok +ALTER TABLE gtt_test_new ADD COLUMN address varchar(30); +-- ERROR +CREATE TABLE orders ( + order_id integer PRIMARY KEY, + product_no integer REFERENCES products (product_no), + quantity integer +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "orders_pkey" for table "orders" +ERROR: constraints on permanent tables may reference only permanent tables +-- ok +CREATE global temp TABLE orders ( + order_id integer PRIMARY KEY, + product_no integer REFERENCES products (product_no), + quantity integer +)on commit delete rows; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "orders_pkey" for table "orders" +--ERROR +insert into orders values(1,1,1); +ERROR: insert or update on table "orders" violates foreign key constraint "orders_product_no_fkey" +DETAIL: Key (product_no)=(1) is not present in table "products". +--ok +insert into products values(1,'test',1.0); +begin; +insert into orders values(1,1,1); +commit; +select count(*) from products; + count +------- + 1 +(1 row) + +select count(*) from orders; + count +------- + 0 +(1 row) + +-- ok +CREATE GLOBAL TEMPORARY TABLE mytable ( + id SERIAL PRIMARY KEY, + data text +) on commit preserve rows; +NOTICE: CREATE TABLE will create implicit sequence "mytable_id_seq" for serial column "mytable.id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "mytable_pkey" for table "mytable" +-- ok +--create global temp table gtt_seq(id int GENERATED ALWAYS AS IDENTITY (START WITH 2) primary key, a int) on commit PRESERVE rows; +--insert into gtt_seq (a) values(1); +--insert into gtt_seq (a) values(2); +--select * from gtt_seq order by id; +--truncate gtt_seq; +--select * from gtt_seq order by id; +--insert into gtt_seq (a) values(3); +--select * from gtt_seq order by id; +--ERROR +--CREATE MATERIALIZED VIEW mv_gtt1 as select * from gtt1; +-- ok +create index idx_gtt1_1 on gtt1 using hash (a); +ERROR: access method "hash" does not support row store +create index idx_tmp_t0_1 on tmp_t0 using gin (c0); +create index idx_tmp_t0_2 on tmp_t0 using gist (c0); +--ok +create global temp table gt (a SERIAL,b int); +NOTICE: CREATE TABLE will create implicit sequence "gt_a_seq" for serial column "gt.a" +begin; +set transaction_read_only = true; +insert into gt (b) values(1); +select * from gt; + a | b +---+--- + 1 | 1 +(1 row) + +commit; +--create sequence seq_1; +CREATE GLOBAL TEMPORARY TABLE gtt_s_1(c1 int PRIMARY KEY) ON COMMIT DELETE ROWS; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt_s_1_pkey" for table "gtt_s_1" +CREATE GLOBAL TEMPORARY TABLE gtt_s_2(c1 int PRIMARY KEY) ON COMMIT PRESERVE ROWS; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt_s_2_pkey" for table "gtt_s_2" +--alter table gtt_s_1 add c2 int default nextval('seq_1'); +--alter table gtt_s_2 add c2 int default nextval('seq_1'); +begin; +insert into gtt_s_1 (c1)values(1); +insert into gtt_s_2 (c1)values(1); +insert into gtt_s_1 (c1)values(2); +insert into gtt_s_2 (c1)values(2); +select * from gtt_s_1 order by c1; + c1 +---- + 1 + 2 +(2 rows) + +commit; +select * from gtt_s_1 order by c1; + c1 +---- +(0 rows) + +select * from gtt_s_2 order by c1; + c1 +---- + 1 + 2 +(2 rows) + +--ok +create global temp table gt1(a int); +insert into gt1 values(generate_series(1,100000)); +create index idx_gt1_1 on gt1 (a); +create index idx_gt1_2 on gt1((a + 1)); +create index idx_gt1_3 on gt1((a*10),(a+a),(a-1)); +explain (costs off) select * from gt1 where a=1; + QUERY PLAN +-------------------------------------- + Bitmap Heap Scan on gt1 + Recheck Cond: (a = 1) + -> Bitmap Index Scan on idx_gt1_1 + Index Cond: (a = 1) +(4 rows) + +explain (costs off) select * from gt1 where a=200000; + QUERY PLAN +-------------------------------------- + Bitmap Heap Scan on gt1 + Recheck Cond: (a = 200000) + -> Bitmap Index Scan on idx_gt1_1 + Index Cond: (a = 200000) +(4 rows) + +explain (costs off) select * from gt1 where a*10=300; + QUERY PLAN +-------------------------------------- + Bitmap Heap Scan on gt1 + Recheck Cond: ((a * 10) = 300) + -> Bitmap Index Scan on idx_gt1_3 + Index Cond: ((a * 10) = 300) +(4 rows) + +explain (costs off) select * from gt1 where a*10=3; + QUERY PLAN +-------------------------------------- + Bitmap Heap Scan on gt1 + Recheck Cond: ((a * 10) = 3) + -> Bitmap Index Scan on idx_gt1_3 + Index Cond: ((a * 10) = 3) +(4 rows) + +analyze gt1; +explain (costs off) select * from gt1 where a=1; + QUERY PLAN +---------------------------------------- + [Bypass] + Index Only Scan using idx_gt1_1 on gt1 + Index Cond: (a = 1) +(3 rows) + +explain (costs off) select * from gt1 where a=200000; + QUERY PLAN +---------------------------------------- + [Bypass] + Index Only Scan using idx_gt1_1 on gt1 + Index Cond: (a = 200000) +(3 rows) + +explain (costs off) select * from gt1 where a*10=300; + QUERY PLAN +----------------------------------- + [Bypass] + Index Scan using idx_gt1_3 on gt1 + Index Cond: ((a * 10) = 300) +(3 rows) + +explain (costs off) select * from gt1 where a*10=3; + QUERY PLAN +----------------------------------- + [Bypass] + Index Scan using idx_gt1_3 on gt1 + Index Cond: ((a * 10) = 3) +(3 rows) + +--ok +create global temp table gtt_test1(c1 int) with(on_commit_delete_rows='1'); +create global temp table gtt_test2(c1 int) with(on_commit_delete_rows='0'); +create global temp table gtt_test3(c1 int) with(on_commit_delete_rows='t'); +create global temp table gtt_test4(c1 int) with(on_commit_delete_rows='f'); +create global temp table gtt_test5(c1 int) with(on_commit_delete_rows='yes'); +create global temp table gtt_test6(c1 int) with(on_commit_delete_rows='no'); +create global temp table gtt_test7(c1 int) with(on_commit_delete_rows='y'); +create global temp table gtt_test8(c1 int) with(on_commit_delete_rows='n'); +--error +create global temp table gtt_test9(c1 int) with(on_commit_delete_rows='tr'); +create global temp table gtt_test10(c1 int) with(on_commit_delete_rows='ye'); +reset search_path; +drop schema gtt_function cascade; +NOTICE: drop cascades to 26 other objects +DETAIL: drop cascades to table gtt_function.gtt1 +drop cascades to table gtt_function.gtt_test_new +drop cascades to table gtt_function.gtt2 +drop cascades to table gtt_function.gtt3 +drop cascades to table gtt_function.tmp_t0 +drop cascades to table gtt_function.tbl_inherits_parent +drop cascades to table gtt_function.tbl_inherits_parent_global_temp +drop cascades to table gtt_function.products +drop cascades to table gtt_function.gtt6 +drop cascades to table gtt_function.gtt5 +drop cascades to table gtt_function.orders +drop cascades to table gtt_function.mytable +drop cascades to table gtt_function.gt +drop cascades to table gtt_function.gtt_s_1 +drop cascades to table gtt_function.gtt_s_2 +drop cascades to table gtt_function.gt1 +drop cascades to table gtt_function.gtt_test1 +drop cascades to table gtt_function.gtt_test2 +drop cascades to table gtt_function.gtt_test3 +drop cascades to table gtt_function.gtt_test4 +drop cascades to table gtt_function.gtt_test5 +drop cascades to table gtt_function.gtt_test6 +drop cascades to table gtt_function.gtt_test7 +drop cascades to table gtt_function.gtt_test8 +drop cascades to table gtt_function.gtt_test9 +drop cascades to table gtt_function.gtt_test10 diff --git a/src/test/regress/expected/gtt_parallel_1.out b/src/test/regress/expected/gtt_parallel_1.out new file mode 100644 index 0000000000000000000000000000000000000000..0646aaed73b7176ad89623fe228e822e695b8401 --- /dev/null +++ b/src/test/regress/expected/gtt_parallel_1.out @@ -0,0 +1,90 @@ +set search_path=gtt,sys; +select nextval('gtt_with_seq_c2_seq'); + nextval +--------- + 1 +(1 row) + +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +begin; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +commit; +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +begin; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +rollback; +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +truncate gtt1; +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +begin; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +truncate gtt1; +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +insert into gtt1 values(1, 'test1'); +rollback; +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +begin; +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +truncate gtt1; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +truncate gtt1; +commit; +select * from gtt1 order by a; + a | b +---+--- +(0 rows) + +reset search_path; diff --git a/src/test/regress/expected/gtt_parallel_2.out b/src/test/regress/expected/gtt_parallel_2.out new file mode 100644 index 0000000000000000000000000000000000000000..5022626b6e2f307c14e8c583e3d0e15ad8cb6d09 --- /dev/null +++ b/src/test/regress/expected/gtt_parallel_2.out @@ -0,0 +1,333 @@ +set search_path=gtt,sys; +insert into gtt3 values(1, 'test1'); +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +begin; +insert into gtt3 values(2, 'test1'); +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 + 2 | test1 +(2 rows) + +commit; +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 + 2 | test1 +(2 rows) + +begin; +insert into gtt3 values(3, 'test1'); +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 + 2 | test1 + 3 | test1 +(3 rows) + +rollback; +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 + 2 | test1 +(2 rows) + +truncate gtt3; +select * from gtt3 order by a; + a | b +---+--- +(0 rows) + +insert into gtt3 values(1, 'test1'); +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +begin; +insert into gtt3 values(2, 'test2'); +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 + 2 | test2 +(2 rows) + +truncate gtt3; +select * from gtt3 order by a; + a | b +---+--- +(0 rows) + +insert into gtt3 values(3, 'test3'); +update gtt3 set a = 3 where b = 'test1'; +select * from gtt3 order by a; + a | b +---+------- + 3 | test3 +(1 row) + +rollback; +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +begin; +select * from gtt3 order by a; + a | b +---+------- + 1 | test1 +(1 row) + +truncate gtt3; +insert into gtt3 values(5, 'test5'); +select * from gtt3 order by a; + a | b +---+------- + 5 | test5 +(1 row) + +truncate gtt3; +insert into gtt3 values(6, 'test6'); +commit; +select * from gtt3 order by a; + a | b +---+------- + 6 | test6 +(1 row) + +truncate gtt3; +insert into gtt3 values(1); +select * from gtt3; + a | b +---+--- + 1 | +(1 row) + +begin; +insert into gtt3 values(2); +select * from gtt3; + a | b +---+--- + 1 | + 2 | +(2 rows) + +SAVEPOINT save1; +truncate gtt3; +insert into gtt3 values(3); +select * from gtt3; + a | b +---+--- + 3 | +(1 row) + +SAVEPOINT save2; +truncate gtt3; +insert into gtt3 values(4); +select * from gtt3; + a | b +---+--- + 4 | +(1 row) + +SAVEPOINT save3; +rollback to savepoint save2; +Select * from gtt3; + a | b +---+--- + 3 | +(1 row) + +insert into gtt3 values(5); +select * from gtt3; + a | b +---+--- + 3 | + 5 | +(2 rows) + +rollback; +select * from gtt3; + a | b +---+--- + 1 | +(1 row) + +truncate gtt3; +insert into gtt3 values(1); +select * from gtt3; + a | b +---+--- + 1 | +(1 row) + +begin; +insert into gtt3 values(2); +select * from gtt3; + a | b +---+--- + 1 | + 2 | +(2 rows) + +SAVEPOINT save1; +truncate gtt3; +insert into gtt3 values(3); +select * from gtt3; + a | b +---+--- + 3 | +(1 row) + +SAVEPOINT save2; +truncate gtt3; +insert into gtt3 values(4); +select * from gtt3; + a | b +---+--- + 4 | +(1 row) + +SAVEPOINT save3; +rollback to savepoint save2; +Select * from gtt3; + a | b +---+--- + 3 | +(1 row) + +insert into gtt3 values(5); +select * from gtt3; + a | b +---+--- + 3 | + 5 | +(2 rows) + +commit; +select * from gtt3; + a | b +---+--- + 3 | + 5 | +(2 rows) + +truncate gtt3; +insert into gtt3 values(generate_series(1,100000), 'testing'); +select count(*) from gtt3; + count +-------- + 100000 +(1 row) + +analyze gtt3; +explain (COSTS FALSE) select * from gtt3 where a =300; + QUERY PLAN +------------------------------------ + [Bypass] + Index Scan using gtt3_pkey on gtt3 + Index Cond: (a = 300) +(3 rows) + +insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500); +insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000); +insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000); +insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500); +select relname, pg_relation_size(oid),pg_relation_size(reltoastrelid),pg_table_size(oid),pg_indexes_size(oid),pg_total_relation_size(oid) from pg_class where relname = 'gtt_t_kenyon'; + relname | pg_relation_size | pg_relation_size | pg_table_size | pg_indexes_size | pg_total_relation_size +--------------+------------------+------------------+---------------+-----------------+------------------------ +--? gtt_t_kenyon.* +(1 row) + +select relname from pg_class where relname = 'gtt_t_kenyon' and reltoastrelid != 0; + relname +-------------- + gtt_t_kenyon +(1 row) + +select +c.relname, pg_relation_size(c.oid),pg_table_size(c.oid),pg_total_relation_size(c.oid) +from +pg_class c +where +c.oid in +( +select +i.indexrelid as indexrelid +from +pg_index i ,pg_class cc +where cc.relname = 'gtt_t_kenyon' and cc.oid = i.indrelid +) +order by c.relname; + relname | pg_relation_size | pg_table_size | pg_total_relation_size +--------------------+------------------+---------------+------------------------ +--? idx_gtt_t_kenyon_1.* +--? idx_gtt_t_kenyon_2.* +(2 rows) + +select count(*) from gtt_t_kenyon; + count +------- + 2006 +(1 row) + +cluster gtt_t_kenyon using idx_gtt_t_kenyon_1; +select count(*) from gtt_t_kenyon; + count +------- + 2006 +(1 row) + +vacuum full gtt_t_kenyon; +select count(*) from gtt_t_kenyon; + count +------- + 2006 +(1 row) + +begin; +truncate gtt_t_kenyon; +insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500); +commit; +cluster gtt_t_kenyon using idx_gtt_t_kenyon_1; +select count(*) from gtt_t_kenyon; + count +------- + 2000 +(1 row) + +insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000); +insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000); +insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500); +begin; +truncate gtt_t_kenyon; +insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500); +rollback; +cluster gtt_t_kenyon using idx_gtt_t_kenyon_1; +select count(*) from gtt_t_kenyon; + count +------- + 2006 +(1 row) + +insert into gtt_with_seq (c1) values(1); +select * from gtt_with_seq; + c1 | c2 +----+---- + 1 | 2 +(1 row) + +reset search_path; diff --git a/src/test/regress/expected/gtt_prepare.out b/src/test/regress/expected/gtt_prepare.out new file mode 100644 index 0000000000000000000000000000000000000000..8e2c79a3c5b0eeec456fcf21476d0fc00ae600bd --- /dev/null +++ b/src/test/regress/expected/gtt_prepare.out @@ -0,0 +1,14 @@ +CREATE SCHEMA gtt; +set search_path=gtt,sys; +create global temp table gtt1(a int primary key, b text) on commit delete rows; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt1_pkey" for table "gtt1" +create global temp table gtt2(a int primary key, b text) on commit delete rows; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt2_pkey" for table "gtt2" +create global temp table gtt3(a int primary key, b text); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt3_pkey" for table "gtt3" +create global temp table gtt_t_kenyon(id int,vname varchar(48),remark text) on commit PRESERVE rows; +create index idx_gtt_t_kenyon_1 on gtt_t_kenyon(id); +create index idx_gtt_t_kenyon_2 on gtt_t_kenyon(remark); +CREATE GLOBAL TEMPORARY TABLE gtt_with_seq(c1 bigint, c2 bigserial) on commit PRESERVE rows; +NOTICE: CREATE TABLE will create implicit sequence "gtt_with_seq_c2_seq" for serial column "gtt_with_seq.c2" +reset search_path; diff --git a/src/test/regress/expected/gtt_stats.out b/src/test/regress/expected/gtt_stats.out new file mode 100644 index 0000000000000000000000000000000000000000..503a201afc65dcd10ded76107a5bc5b833053064 --- /dev/null +++ b/src/test/regress/expected/gtt_stats.out @@ -0,0 +1,95 @@ +CREATE SCHEMA gtt_stats; +set search_path=gtt_stats,sys; +-- expect 0 +select count(*) from pg_gtt_attached_pids; + count +------- + 0 +(1 row) + +-- expect 0 +select count(*) from pg_list_gtt_relfrozenxids(); + count +------- + 0 +(1 row) + +create global temp table gtt_stats.gtt(a int primary key, b text) on commit PRESERVE rows; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "gtt_pkey" for table "gtt" +-- expect 0 +select count(*) from pg_gtt_attached_pids; + count +------- + 1 +(1 row) + +-- expect 0 +select count(*) from pg_list_gtt_relfrozenxids(); + count +------- + 2 +(1 row) + +insert into gtt values(generate_series(1,10000),'test'); +-- expect 1 +select count(*) from pg_gtt_attached_pids; + count +------- + 1 +(1 row) + +-- expect 2 +select count(*) from pg_list_gtt_relfrozenxids(); + count +------- + 2 +(1 row) + +-- expect 2 +select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename; + schemaname | tablename | relpages | reltuples | relallvisible +------------+-----------+----------+-----------+--------------- + gtt_stats | gtt | 0 | 0 | 0 + gtt_stats | gtt_pkey | 1 | 0 | 0 +(2 rows) + +-- expect 0 +select * from pg_gtt_stats order by tablename, attname; + schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram +------------+----------------------+------------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+---------------------- + gtt_stats | gtt | a | | | | | | | | | | | + gtt_stats | gtt | b | | | | | | | | | | | + gtt_stats | gtt_pkey | a | | | | | | | | | | | + pg_toast | pg_toast_16386 | chunk_data | | | | | | | | | | | + pg_toast | pg_toast_16386 | chunk_id | | | | | | | | | | | + pg_toast | pg_toast_16386 | chunk_seq | | | | | | | | | | | + pg_toast | pg_toast_16386_index | chunk_id | | | | | | | | | | | + pg_toast | pg_toast_16386_index | chunk_seq | | | | | | | | | | | +(22 rows) + +reindex table gtt; +reindex index gtt_pkey; +analyze gtt; +select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename; + schemaname | tablename | relpages | reltuples | relallvisible +------------+-----------+----------+-----------+--------------- + gtt_stats | gtt | 55 | 10000 | 0 + gtt_stats | gtt_pkey | 30 | 10000 | 0 +(2 rows) + +select * from pg_gtt_stats order by tablename, attname; + schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram +------------+----------------------+------------+-----------+-----------+-----------+------------+------------------+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------+------------------------+---------------------- + gtt_stats | gtt | a | f | 0 | 4 | -1 | | | {1,100,200,300,400,500,600,700,800,900,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000,2100,2200,2300,2400,2500,2600,2700,2800,2900,3000,3100,3200,3300,3400,3500,3600,3700,3800,3900,4000,4100,4200,4300,4400,4500,4600,4700,4800,4900,5000,5100,5200,5300,5400,5500,5600,5700,5800,5900,6000,6100,6200,6300,6400,6500,6600,6700,6800,6900,7000,7100,7200,7300,7400,7500,7600,7700,7800,7900,8000,8100,8200,8300,8400,8500,8600,8700,8800,8900,9000,9100,9200,9300,9400,9500,9600,9700,9800,9900,10000} | 1 | | | + gtt_stats | gtt | b | f | 0 | 5 | 1 | {test} | {1} | | 1 | | | + gtt_stats | gtt_pkey | a | | | | | | | | | | | + pg_toast | pg_toast_16386 | chunk_data | | | | | | | | | | | + pg_toast | pg_toast_16386 | chunk_id | | | | | | | | | | | + pg_toast | pg_toast_16386 | chunk_seq | | | | | | | | | | | + pg_toast | pg_toast_16386_index | chunk_id | | | | | | | | | | | + pg_toast | pg_toast_16386_index | chunk_seq | | | | | | | | | | | +(22 rows) + +reset search_path; +drop schema gtt_stats cascade; +NOTICE: drop cascades to table gtt_stats.gtt diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index b498b1a637ddb229de45fb1a33256cbdbf729daa..5dd1bae06908ba2671060d8c8581be4c45989286 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2024,6 +2024,10 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 3589 | timestamptz_list_agg_noarg2_transfn 3590 | listagg 3591 | pgxc_get_thread_wait_status + 3596 | pg_get_gtt_relstats + 3597 | pg_get_gtt_statistics + 3598 | pg_gtt_attached_pid + 3599 | pg_list_gtt_relfrozenxids 3610 | tsvectorin 3611 | tsvectorout 3612 | tsqueryin @@ -2632,7 +2636,7 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 9016 | pg_advisory_lock 9017 | pgxc_unlock_for_sp_database 9999 | pg_test_err_contain_err -(2270 rows) +(2274 rows) -- **************** pg_cast **************** -- Catch bogus values in pg_cast columns (other than cases detected by diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index f66f6b1b868e6a5143684aa599fa9ac76cf450a5..6d3ad294f0f6f00a58aa57009105cd6c9e5e7e09 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1346,6 +1346,94 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem pg_get_invalid_backends | SELECT c.pid, c.node_name, s.datname AS dbname, s.backend_start, s.query FROM (pg_pool_validate(false) c(pid, node_name) LEFT JOIN pg_stat_activity s ON ((c.pid = s.pid))); pg_get_senders_catchup_time | SELECT w.pid, w.sender_pid AS lwpid, w.local_role, w.peer_role, w.state, 'Wal'::text AS type, w.catchup_start, w.catchup_end FROM pg_stat_get_wal_senders() w(pid, sender_pid, local_role, peer_role, peer_state, state, catchup_start, catchup_end, sender_sent_location, sender_write_location, sender_flush_location, sender_replay_location, receiver_received_location, receiver_write_location, receiver_flush_location, receiver_replay_location, sync_percent, sync_state, sync_priority, sync_most_available, channel) UNION ALL SELECT d.pid, d.sender_pid AS lwpid, d.local_role, d.peer_role, d.state, 'Data'::text AS type, d.catchup_start, d.catchup_end FROM pg_stat_get_data_senders() d(pid, sender_pid, local_role, peer_role, state, catchup_start, catchup_end, queue_size, queue_lower_tail, queue_header, queue_upper_tail, send_position, receive_position); pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); + pg_gtt_attached_pids| SELECT n.nspname AS schemaname, + c.relname AS tablename, + s.relid, + s.pid + FROM (pg_class c + LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))), + LATERAL pg_gtt_attached_pid(c.oid) s(relid, pid) + WHERE ((c.relpersistence = 'g'::"char") AND (c.relkind = ANY (ARRAY['r'::"char", 'S'::"char"])) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid)))); +pg_gtt_relstats| SELECT n.nspname AS schemaname, + c.relname AS tablename, + s.relfilenode, + s.relpages, + s.reltuples, + s.relallvisible, + s.relfrozenxid, + s.relminmxid + FROM (pg_class c + LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))), + LATERAL pg_get_gtt_relstats(c.oid) s(relfilenode, relpages, reltuples, relallvisible, relfrozenxid, relminmxid) + WHERE ((c.relpersistence = 'g'::"char") AND (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char", 'i'::"char", 't'::"char"])) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid)))); +pg_gtt_stats| SELECT n.nspname AS schemaname, + c.relname AS tablename, + a.attname, + s.stainherit AS inherited, + s.stanullfrac AS null_frac, + s.stawidth AS avg_width, + s.stadistinct AS n_distinct, + CASE + WHEN (s.stakind1 = 1) THEN s.stavalues1 + WHEN (s.stakind2 = 1) THEN s.stavalues2 + WHEN (s.stakind3 = 1) THEN s.stavalues3 + WHEN (s.stakind4 = 1) THEN s.stavalues4 + WHEN (s.stakind5 = 1) THEN s.stavalues5 + ELSE NULL::text[] + END AS most_common_vals, + CASE + WHEN (s.stakind1 = 1) THEN s.stanumbers1 + WHEN (s.stakind2 = 1) THEN s.stanumbers2 + WHEN (s.stakind3 = 1) THEN s.stanumbers3 + WHEN (s.stakind4 = 1) THEN s.stanumbers4 + WHEN (s.stakind5 = 1) THEN s.stanumbers5 + ELSE NULL::real[] + END AS most_common_freqs, + CASE + WHEN (s.stakind1 = 2) THEN s.stavalues1 + WHEN (s.stakind2 = 2) THEN s.stavalues2 + WHEN (s.stakind3 = 2) THEN s.stavalues3 + WHEN (s.stakind4 = 2) THEN s.stavalues4 + WHEN (s.stakind5 = 2) THEN s.stavalues5 + ELSE NULL::text[] + END AS histogram_bounds, + CASE + WHEN (s.stakind1 = 3) THEN s.stanumbers1[1] + WHEN (s.stakind2 = 3) THEN s.stanumbers2[1] + WHEN (s.stakind3 = 3) THEN s.stanumbers3[1] + WHEN (s.stakind4 = 3) THEN s.stanumbers4[1] + WHEN (s.stakind5 = 3) THEN s.stanumbers5[1] + ELSE NULL::real + END AS correlation, + CASE + WHEN (s.stakind1 = 4) THEN s.stavalues1 + WHEN (s.stakind2 = 4) THEN s.stavalues2 + WHEN (s.stakind3 = 4) THEN s.stavalues3 + WHEN (s.stakind4 = 4) THEN s.stavalues4 + WHEN (s.stakind5 = 4) THEN s.stavalues5 + ELSE NULL::text[] + END AS most_common_elems, + CASE + WHEN (s.stakind1 = 4) THEN s.stanumbers1 + WHEN (s.stakind2 = 4) THEN s.stanumbers2 + WHEN (s.stakind3 = 4) THEN s.stanumbers3 + WHEN (s.stakind4 = 4) THEN s.stanumbers4 + WHEN (s.stakind5 = 4) THEN s.stanumbers5 + ELSE NULL::real[] + END AS most_common_elem_freqs, + CASE + WHEN (s.stakind1 = 5) THEN s.stanumbers1 + WHEN (s.stakind2 = 5) THEN s.stanumbers2 + WHEN (s.stakind3 = 5) THEN s.stanumbers3 + WHEN (s.stakind4 = 5) THEN s.stanumbers4 + WHEN (s.stakind5 = 5) THEN s.stanumbers5 + ELSE NULL::real[] + END AS elem_count_histogram + FROM ((pg_class c + JOIN pg_attribute a ON ((c.oid = a.attrelid))) + LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))), + LATERAL pg_get_gtt_statistics(c.oid, (a.attnum)::integer, ''::text) s(starelid, staattnum, stainherit, stanullfrac, stawidth, stadistinct, stakind1, stakind2, stakind3, stakind4, stakind5, staop1, staop2, staop3, staop4, staop5, stacoll1, stacoll2, stacoll3, stacoll4, stac + WHERE ((c.relpersistence = 'g'::"char") AND (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char", 'i'::"char", 't'::"char"])) AND (NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid)))); pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS tablespace, pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); pg_locks | SELECT l.locktype, l.database, l.relation, l.page, l.tuple, l.virtualxid, l.transactionid, l.classid, l.objid, l.objsubid, l.virtualtransaction, l.pid, l.mode, l.granted, l.fastpath FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath); pg_node_env | SELECT s.node_name, s.host, s.process, s.port, s.installpath, s.datapath, s.log_directory FROM pg_stat_get_env() s(node_name, host, process, port, installpath, datapath, log_directory); diff --git a/src/test/regress/expected/single_node_opr_sanity.out b/src/test/regress/expected/single_node_opr_sanity.out index 74caa37f61d950d07278b5899e45fb26ac14d473..bab75f93b4b7efc53f86c682b23742a1a49210e1 100755 --- a/src/test/regress/expected/single_node_opr_sanity.out +++ b/src/test/regress/expected/single_node_opr_sanity.out @@ -2063,6 +2063,10 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 3589 | timestamptz_list_agg_noarg2_transfn 3590 | listagg 3591 | pgxc_get_thread_wait_status + 3596 | pg_get_gtt_relstats + 3597 | pg_get_gtt_statistics + 3598 | pg_gtt_attached_pid + 3599 | pg_list_gtt_relfrozenxids 3610 | tsvectorin 3611 | tsvectorout 3612 | tsqueryin @@ -2671,7 +2675,7 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 9016 | pg_advisory_lock 9017 | pgxc_unlock_for_sp_database 9999 | pg_test_err_contain_err -(2270 rows) +(2274 rows) -- Check prokind select count(*) from pg_proc where prokind = 'a'; @@ -2689,7 +2693,7 @@ select count(*) from pg_proc where prokind = 'w'; select count(*) from pg_proc where prokind = 'f'; count ------- - 3130 + 3134 (1 row) select count(*) from pg_proc where prokind = 'p'; diff --git a/src/test/regress/expected/temp.out b/src/test/regress/expected/temp.out index f5910d8c2ee788f10709eb328367cdabbe574885..736cf0c090a42bcc94317add02cb372073cfe696 100644 --- a/src/test/regress/expected/temp.out +++ b/src/test/regress/expected/temp.out @@ -238,15 +238,7 @@ insert into temp_col2 select * from temp_col1; --Other features --temp function --Create temp table -create global temp table tg(a int, b varchar2(3000)); -WARNING: GLOBAL is deprecated in temporary table creation -LINE 1: create global temp table tg(a int, b varchar2(3000)); - ^ create local temp table tl(a int, b varchar2(3000)); -create global temporary table gt(a int, b varchar2(3000)); -WARNING: GLOBAL is deprecated in temporary table creation -LINE 1: create global temporary table gt(a int, b varchar2(3000)); - ^ create local temporary table lt(a int, b varchar2(3000)); create temp table pg_temp.temp_t(a int); ERROR: temporary tables cannot specify a schema name @@ -819,7 +811,7 @@ create temp table test_serial(a serial, b varchar2(100)); ERROR: It's not supported to create serial column on temporary table create temp table test_serial2(a int); alter table test_serial2 add b serial; -ERROR: It's not supported to alter table add serial column +ERROR: It's not supported to create serial column on temporary table drop table test_serial; ERROR: table "test_serial" does not exist drop table test_serial2; diff --git a/src/test/regress/make_fastcheck_postgresql.conf b/src/test/regress/make_fastcheck_postgresql.conf index 9527e52a39845d8ccc1079d49f52a69328965544..9d768dce27d4dee5077d841e5a42d4bdebce9c97 100644 --- a/src/test/regress/make_fastcheck_postgresql.conf +++ b/src/test/regress/make_fastcheck_postgresql.conf @@ -24,4 +24,4 @@ enable_sonic_hashagg=on enable_sonic_hashjoin=on enable_opfusion=on uncontrolled_memory_context='HashCacheContext,TupleHashTable,TupleSort,AggContext,SRF multi-call context,CteScan*,FunctionScan*,RemoteQuery*,VecAgg*,HashContext,TopTransactionContext' -enable_thread_pool = on +enable_thread_pool = off diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 5cad370a8e80e3616aff18959888671c95ae74c5..10c1a0097128b9d96d02fd6a65562347a47ee07a 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -571,3 +571,9 @@ test: udf_crem create_c_function #test: redis_truncate_col #test: redis_alter_truncate_partition #test: redis_alter_truncate_col_partition +# global temp table test +test: gtt_stats +test: gtt_function +test: gtt_prepare +test: gtt_parallel_1 gtt_parallel_2 +test: gtt_clean diff --git a/src/test/regress/sql/gtt_clean.sql b/src/test/regress/sql/gtt_clean.sql new file mode 100644 index 0000000000000000000000000000000000000000..e1728e6cece77879841ffdab9ec5213a5b5d996a --- /dev/null +++ b/src/test/regress/sql/gtt_clean.sql @@ -0,0 +1,6 @@ +reset search_path; + +select pg_sleep(5); + +drop schema gtt cascade; + diff --git a/src/test/regress/sql/gtt_function.sql b/src/test/regress/sql/gtt_function.sql new file mode 100644 index 0000000000000000000000000000000000000000..c07658a21b850d25f41c0ced61edb439e3d90834 --- /dev/null +++ b/src/test/regress/sql/gtt_function.sql @@ -0,0 +1,253 @@ + +CREATE SCHEMA gtt_function; + +set search_path=gtt_function,sys; + +create global temp table gtt1(a int primary key, b text); + +create global temp table gtt_test_rename(a int primary key, b text); + +create global temp table gtt2(a int primary key, b text) on commit delete rows; + +create global temp table gtt3(a int primary key, b text) on commit PRESERVE rows; + +create global temp table tmp_t0(c0 tsvector,c1 varchar(100)); + +create table tbl_inherits_parent( +a int not null, +b varchar(32) not null default 'Got u', +c int check (c > 0), +d date not null +); + +create global temp table tbl_inherits_parent_global_temp( +a int not null, +b varchar(32) not null default 'Got u', +c int check (c > 0), +d date not null +)on commit delete rows; + +CREATE global temp TABLE products ( + product_no integer PRIMARY KEY, + name text, + price numeric +); + +create global temp table gtt6(n int) with (on_commit_delete_rows='true'); + +begin; +insert into gtt6 values (9); +-- 1 row +select * from gtt6; +commit; +-- 0 row +select * from gtt6; + +-- ERROR +create index CONCURRENTLY idx_gtt1 on gtt1 (b); + +-- ERROR +cluster gtt1 using gtt1_pkey; + +-- ERROR +create table gtt1(a int primary key, b text) on commit delete rows; + +-- ERROR +alter table gtt1 SET TABLESPACE pg_default; + +-- ERROR +alter table gtt1 set ( on_commit_delete_rows='true'); + +-- ERROR +create table gtt1(a int primary key, b text) with(on_commit_delete_rows=true); + +-- ERROR +create or replace global temp view gtt_v as select 5; + +create table foo(); +-- ERROR +alter table foo set (on_commit_delete_rows='true'); + +-- ok +--CREATE global temp TABLE measurement ( +-- logdate date not null, +-- peaktemp int, +-- unitsales int +--) PARTITION BY RANGE (logdate); + +--ok +--CREATE global temp TABLE p_table01 ( +--id bigserial NOT NULL, +--cre_time timestamp without time zone, +--note varchar(30) +--) PARTITION BY RANGE (cre_time) +--WITH ( +--OIDS = FALSE +--)on commit delete rows; + +--CREATE global temp TABLE p_table01_2018 +--PARTITION OF p_table01 +--FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00') on commit delete rows; + +--CREATE global temp TABLE p_table01_2017 +--PARTITION OF p_table01 +--FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00') on commit delete rows; + +--begin; +--insert into p_table01 values(1,'2018-01-02 00:00:00','test1'); +--insert into p_table01 values(1,'2018-01-02 00:00:00','test2'); +--select count(*) from p_table01; +--commit; + +--select count(*) from p_table01; + +--ok +--CREATE global temp TABLE p_table02 ( +--id bigserial NOT NULL, +--cre_time timestamp without time zone, +--note varchar(30) +--) PARTITION BY RANGE (cre_time) +--WITH ( +--OIDS = FALSE +--) +--on commit PRESERVE rows; + +--CREATE global temp TABLE p_table02_2018 +--PARTITION OF p_table02 +--FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00'); + +--CREATE global temp TABLE p_table02_2017 +--PARTITION OF p_table02 +--FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00'); + +-- ERROR +--create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent); + +-- ok +--create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent_global_temp) on commit delete rows; + +select relname ,relkind, relpersistence, reloptions from pg_class where relname like 'p_table0%' or relname like 'tbl_inherits%' order by relname; + +-- ERROR +create global temp table gtt3(a int primary key, b text) on commit drop; + +-- ERROR +create global temp table gtt4(a int primary key, b text) with(on_commit_delete_rows=true) on commit delete rows; + +-- ok +create global temp table gtt5(a int primary key, b text) with(on_commit_delete_rows=true); + +--ok +alter table gtt_test_rename rename to gtt_test_new; + +-- ok +ALTER TABLE gtt_test_new ADD COLUMN address varchar(30); + +-- ERROR +CREATE TABLE orders ( + order_id integer PRIMARY KEY, + product_no integer REFERENCES products (product_no), + quantity integer +); + +-- ok +CREATE global temp TABLE orders ( + order_id integer PRIMARY KEY, + product_no integer REFERENCES products (product_no), + quantity integer +)on commit delete rows; + +--ERROR +insert into orders values(1,1,1); + +--ok +insert into products values(1,'test',1.0); + +begin; +insert into orders values(1,1,1); +commit; + +select count(*) from products; +select count(*) from orders; + +-- ok +CREATE GLOBAL TEMPORARY TABLE mytable ( + id SERIAL PRIMARY KEY, + data text +) on commit preserve rows; + +-- ok +--create global temp table gtt_seq(id int GENERATED ALWAYS AS IDENTITY (START WITH 2) primary key, a int) on commit PRESERVE rows; +--insert into gtt_seq (a) values(1); +--insert into gtt_seq (a) values(2); +--select * from gtt_seq order by id; +--truncate gtt_seq; +--select * from gtt_seq order by id; +--insert into gtt_seq (a) values(3); +--select * from gtt_seq order by id; + +--ERROR +--CREATE MATERIALIZED VIEW mv_gtt1 as select * from gtt1; + +-- ok +create index idx_gtt1_1 on gtt1 using hash (a); +create index idx_tmp_t0_1 on tmp_t0 using gin (c0); +create index idx_tmp_t0_2 on tmp_t0 using gist (c0); + +--ok +create global temp table gt (a SERIAL,b int); +begin; +set transaction_read_only = true; +insert into gt (b) values(1); +select * from gt; +commit; + +--create sequence seq_1; +CREATE GLOBAL TEMPORARY TABLE gtt_s_1(c1 int PRIMARY KEY) ON COMMIT DELETE ROWS; +CREATE GLOBAL TEMPORARY TABLE gtt_s_2(c1 int PRIMARY KEY) ON COMMIT PRESERVE ROWS; +--alter table gtt_s_1 add c2 int default nextval('seq_1'); +--alter table gtt_s_2 add c2 int default nextval('seq_1'); +begin; +insert into gtt_s_1 (c1)values(1); +insert into gtt_s_2 (c1)values(1); +insert into gtt_s_1 (c1)values(2); +insert into gtt_s_2 (c1)values(2); +select * from gtt_s_1 order by c1; +commit; +select * from gtt_s_1 order by c1; +select * from gtt_s_2 order by c1; + +--ok +create global temp table gt1(a int); +insert into gt1 values(generate_series(1,100000)); +create index idx_gt1_1 on gt1 (a); +create index idx_gt1_2 on gt1((a + 1)); +create index idx_gt1_3 on gt1((a*10),(a+a),(a-1)); +explain (costs off) select * from gt1 where a=1; +explain (costs off) select * from gt1 where a=200000; +explain (costs off) select * from gt1 where a*10=300; +explain (costs off) select * from gt1 where a*10=3; +analyze gt1; +explain (costs off) select * from gt1 where a=1; +explain (costs off) select * from gt1 where a=200000; +explain (costs off) select * from gt1 where a*10=300; +explain (costs off) select * from gt1 where a*10=3; + +--ok +create global temp table gtt_test1(c1 int) with(on_commit_delete_rows='1'); +create global temp table gtt_test2(c1 int) with(on_commit_delete_rows='0'); +create global temp table gtt_test3(c1 int) with(on_commit_delete_rows='t'); +create global temp table gtt_test4(c1 int) with(on_commit_delete_rows='f'); +create global temp table gtt_test5(c1 int) with(on_commit_delete_rows='yes'); +create global temp table gtt_test6(c1 int) with(on_commit_delete_rows='no'); +create global temp table gtt_test7(c1 int) with(on_commit_delete_rows='y'); +create global temp table gtt_test8(c1 int) with(on_commit_delete_rows='n'); + +--error +create global temp table gtt_test9(c1 int) with(on_commit_delete_rows='tr'); +create global temp table gtt_test10(c1 int) with(on_commit_delete_rows='ye'); + +reset search_path; + +drop schema gtt_function cascade; + diff --git a/src/test/regress/sql/gtt_parallel_1.sql b/src/test/regress/sql/gtt_parallel_1.sql new file mode 100644 index 0000000000000000000000000000000000000000..10a0c055253b708dfb0b36f775a4f0e04c82e72a --- /dev/null +++ b/src/test/regress/sql/gtt_parallel_1.sql @@ -0,0 +1,43 @@ + +set search_path=gtt,sys; + +select nextval('gtt_with_seq_c2_seq'); + +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; + +begin; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; +commit; +select * from gtt1 order by a; + +begin; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; +rollback; +select * from gtt1 order by a; + +truncate gtt1; +select * from gtt1 order by a; + +begin; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; +truncate gtt1; +select * from gtt1 order by a; +insert into gtt1 values(1, 'test1'); +rollback; +select * from gtt1 order by a; + +begin; +select * from gtt1 order by a; +truncate gtt1; +insert into gtt1 values(1, 'test1'); +select * from gtt1 order by a; +truncate gtt1; +commit; +select * from gtt1 order by a; + +reset search_path; + diff --git a/src/test/regress/sql/gtt_parallel_2.sql b/src/test/regress/sql/gtt_parallel_2.sql new file mode 100644 index 0000000000000000000000000000000000000000..64766767ec8c584218d61d1da7711e948c9ca786 --- /dev/null +++ b/src/test/regress/sql/gtt_parallel_2.sql @@ -0,0 +1,146 @@ + +set search_path=gtt,sys; + +insert into gtt3 values(1, 'test1'); +select * from gtt3 order by a; + +begin; +insert into gtt3 values(2, 'test1'); +select * from gtt3 order by a; +commit; +select * from gtt3 order by a; + +begin; +insert into gtt3 values(3, 'test1'); +select * from gtt3 order by a; +rollback; +select * from gtt3 order by a; + +truncate gtt3; +select * from gtt3 order by a; + +insert into gtt3 values(1, 'test1'); +select * from gtt3 order by a; + +begin; +insert into gtt3 values(2, 'test2'); +select * from gtt3 order by a; +truncate gtt3; +select * from gtt3 order by a; +insert into gtt3 values(3, 'test3'); +update gtt3 set a = 3 where b = 'test1'; +select * from gtt3 order by a; +rollback; +select * from gtt3 order by a; + +begin; +select * from gtt3 order by a; +truncate gtt3; +insert into gtt3 values(5, 'test5'); +select * from gtt3 order by a; +truncate gtt3; +insert into gtt3 values(6, 'test6'); +commit; +select * from gtt3 order by a; + +truncate gtt3; +insert into gtt3 values(1); +select * from gtt3; +begin; +insert into gtt3 values(2); +select * from gtt3; +SAVEPOINT save1; +truncate gtt3; +insert into gtt3 values(3); +select * from gtt3; +SAVEPOINT save2; +truncate gtt3; +insert into gtt3 values(4); +select * from gtt3; +SAVEPOINT save3; +rollback to savepoint save2; +Select * from gtt3; +insert into gtt3 values(5); +select * from gtt3; +rollback; +select * from gtt3; + +truncate gtt3; +insert into gtt3 values(1); +select * from gtt3; +begin; +insert into gtt3 values(2); +select * from gtt3; +SAVEPOINT save1; +truncate gtt3; +insert into gtt3 values(3); +select * from gtt3; +SAVEPOINT save2; +truncate gtt3; +insert into gtt3 values(4); +select * from gtt3; +SAVEPOINT save3; +rollback to savepoint save2; +Select * from gtt3; +insert into gtt3 values(5); +select * from gtt3; +commit; +select * from gtt3; + +truncate gtt3; +insert into gtt3 values(generate_series(1,100000), 'testing'); +select count(*) from gtt3; +analyze gtt3; +explain (COSTS FALSE) select * from gtt3 where a =300; + +insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500); +insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000); +insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000); +insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500); +select relname, pg_relation_size(oid),pg_relation_size(reltoastrelid),pg_table_size(oid),pg_indexes_size(oid),pg_total_relation_size(oid) from pg_class where relname = 'gtt_t_kenyon'; +select relname from pg_class where relname = 'gtt_t_kenyon' and reltoastrelid != 0; + +select +c.relname, pg_relation_size(c.oid),pg_table_size(c.oid),pg_total_relation_size(c.oid) +from +pg_class c +where +c.oid in +( +select +i.indexrelid as indexrelid +from +pg_index i ,pg_class cc +where cc.relname = 'gtt_t_kenyon' and cc.oid = i.indrelid +) +order by c.relname; + +select count(*) from gtt_t_kenyon; +cluster gtt_t_kenyon using idx_gtt_t_kenyon_1; +select count(*) from gtt_t_kenyon; + +vacuum full gtt_t_kenyon; +select count(*) from gtt_t_kenyon; + +begin; +truncate gtt_t_kenyon; +insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500); +commit; +cluster gtt_t_kenyon using idx_gtt_t_kenyon_1; +select count(*) from gtt_t_kenyon; + +insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000); +insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000); +insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500); +begin; +truncate gtt_t_kenyon; +insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500); +rollback; +cluster gtt_t_kenyon using idx_gtt_t_kenyon_1; +select count(*) from gtt_t_kenyon; + +insert into gtt_with_seq (c1) values(1); +select * from gtt_with_seq; + +reset search_path; + diff --git a/src/test/regress/sql/gtt_prepare.sql b/src/test/regress/sql/gtt_prepare.sql new file mode 100644 index 0000000000000000000000000000000000000000..20539a4ae31d559f353e02c7aa1d8962fce3fc91 --- /dev/null +++ b/src/test/regress/sql/gtt_prepare.sql @@ -0,0 +1,19 @@ + +CREATE SCHEMA gtt; + +set search_path=gtt,sys; + +create global temp table gtt1(a int primary key, b text) on commit delete rows; + +create global temp table gtt2(a int primary key, b text) on commit delete rows; + +create global temp table gtt3(a int primary key, b text); + +create global temp table gtt_t_kenyon(id int,vname varchar(48),remark text) on commit PRESERVE rows; +create index idx_gtt_t_kenyon_1 on gtt_t_kenyon(id); +create index idx_gtt_t_kenyon_2 on gtt_t_kenyon(remark); + +CREATE GLOBAL TEMPORARY TABLE gtt_with_seq(c1 bigint, c2 bigserial) on commit PRESERVE rows; + +reset search_path; + diff --git a/src/test/regress/sql/gtt_stats.sql b/src/test/regress/sql/gtt_stats.sql new file mode 100644 index 0000000000000000000000000000000000000000..93d9515e5f6633afa5d175e134d079d162f69f2b --- /dev/null +++ b/src/test/regress/sql/gtt_stats.sql @@ -0,0 +1,46 @@ + +CREATE SCHEMA gtt_stats; + +set search_path=gtt_stats,sys; + +-- expect 0 +select count(*) from pg_gtt_attached_pids; + +-- expect 0 +select count(*) from pg_list_gtt_relfrozenxids(); + +create global temp table gtt_stats.gtt(a int primary key, b text) on commit PRESERVE rows; +-- expect 0 +select count(*) from pg_gtt_attached_pids; + +-- expect 0 +select count(*) from pg_list_gtt_relfrozenxids(); + +insert into gtt values(generate_series(1,10000),'test'); + +-- expect 1 +select count(*) from pg_gtt_attached_pids; + +-- expect 2 +select count(*) from pg_list_gtt_relfrozenxids(); + +-- expect 2 +select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename; + +-- expect 0 +select * from pg_gtt_stats order by tablename, attname; + +reindex table gtt; + +reindex index gtt_pkey; + +analyze gtt; + +select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename; + +select * from pg_gtt_stats order by tablename, attname; + +reset search_path; + +drop schema gtt_stats cascade; + diff --git a/src/test/regress/sql/temp.sql b/src/test/regress/sql/temp.sql index d0a0d8158c0246f7ed2c3f053cf28af504653588..cae2972a6c94cb2011c96d862d2ed5a71ae3e5e9 100644 --- a/src/test/regress/sql/temp.sql +++ b/src/test/regress/sql/temp.sql @@ -211,9 +211,7 @@ insert into temp_col2 select * from temp_col1; --temp function --Create temp table -create global temp table tg(a int, b varchar2(3000)); create local temp table tl(a int, b varchar2(3000)); -create global temporary table gt(a int, b varchar2(3000)); create local temporary table lt(a int, b varchar2(3000)); create temp table pg_temp.temp_t(a int); create table pg_temp.temp_t1(a int);