diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 76ce0bf56b8..b40d5daa611 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -5938,7 +5938,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple) errmsg("cannot update tuples during a parallel operation"))); /* Don't proceed further if the tuple is for an ENR. We just update there.*/ - if (ENRupdateTuple(relation, tuple)) + if (ENRUpdateTuple(relation, tuple)) return; buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self))); diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index b7ee424f58a..b277ed3f0e7 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -602,8 +602,7 @@ heapam_relation_set_new_filelocator(Relation rel, */ *minmulti = GetOldestMultiXactId(); - srel = RelationCreateStorage(*newrlocator, persistence, - (sql_dialect != SQL_DIALECT_TSQL || !RelationIsBBFTableVariable(rel))); + srel = RelationCreateStorage(*newrlocator, persistence, !IsTsqlTableVariable(rel)); /* * If required, set up an init fork for an unlogged table so that it can diff --git a/src/backend/access/heap/heapam_visibility.c b/src/backend/access/heap/heapam_visibility.c index 3205341e5bc..bbcf4420e93 100644 --- a/src/backend/access/heap/heapam_visibility.c +++ b/src/backend/access/heap/heapam_visibility.c @@ -80,6 +80,7 @@ #include "utils/builtins.h" #include "utils/combocid.h" #include "utils/snapmgr.h" +#include "utils/queryenvironment.h" table_variable_satisfies_visibility_hook_type table_variable_satisfies_visibility_hook = NULL; table_variable_satisfies_update_hook_type table_variable_satisfies_update_hook = NULL; @@ -468,7 +469,7 @@ HeapTupleSatisfiesUpdate(Relation relation, HeapTuple htup, CommandId curcid, HeapTupleHeader tuple = htup->t_data; /* See HeapTupleSatisfiesVisibility why */ - if (sql_dialect == SQL_DIALECT_TSQL && RelationIsBBFTableVariable(relation)) + if (IsTsqlTableVariable(relation)) return table_variable_satisfies_update_hook(htup, curcid, buffer); Assert(ItemPointerIsValid(&htup->t_self)); @@ -1177,7 +1178,7 @@ HeapTupleSatisfiesVacuum(Relation relation, HeapTuple htup, TransactionId Oldest HTSV_Result res; /* See HeapTupleSatisfiesVisibility why */ - if (sql_dialect == SQL_DIALECT_TSQL && RelationIsBBFTableVariable(relation)) + if (IsTsqlTableVariable(relation)) return table_variable_satisfies_vacuum_hook(htup, OldestXmin, buffer); res = HeapTupleSatisfiesVacuumHorizon(NULL, htup, buffer, &dead_after); @@ -1219,7 +1220,7 @@ HeapTupleSatisfiesVacuumHorizon(Relation relation, HeapTuple htup, Buffer buffer *dead_after = InvalidTransactionId; /* See HeapTupleSatisfiesVisibility why */ - if (sql_dialect == SQL_DIALECT_TSQL && relation && RelationIsBBFTableVariable(relation)) + if (IsTsqlTableVariable(relation)) return table_variable_satisfies_vacuum_horizon_hook(htup, buffer, dead_after); /* @@ -1789,7 +1790,7 @@ HeapTupleSatisfiesVisibility(Relation relation, HeapTuple htup, Snapshot snapsho * Babelfish extension has a different type of heap table but non-transactional. * It is not worth introducing a new table AM because the new table still uses heap format. */ - if (sql_dialect == SQL_DIALECT_TSQL && relation && RelationIsBBFTableVariable(relation)) + if (IsTsqlTableVariable(relation)) return table_variable_satisfies_visibility_hook(htup, snapshot, buffer); switch (snapshot->snapshot_type) diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index d67c165caba..5c79829a1a8 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -425,7 +425,7 @@ systable_beginscan(Relation heapRelation, } /* Catalog tuples for ENR are not in the on-disk catalogs */ - if (ENRgetSystableScan(heapRelation, indexId, nkeys, key, &sysscan->enr_tuplist, &sysscan->enr_tuplist_i, &sysscan->enr_tuplist_flags)) + if (ENRGetSystableScan(heapRelation, indexId, nkeys, key, &sysscan->enr_tuplist, &sysscan->enr_tuplist_i, &sysscan->enr_tuplist_flags)) { sysscan->enr = true; index_close(sysscan->irel, AccessShareLock); diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 9906d43e632..28152b8b1bf 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -583,8 +583,7 @@ GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence) */ rlocator.backend = backend; - use_bbf_oid_buffer = (relpersistence == RELPERSISTENCE_TEMP && sql_dialect == SQL_DIALECT_TSQL - && GetNewTempOidWithIndex_hook && temp_oid_buffer_size > 0); + use_bbf_oid_buffer = (relpersistence == RELPERSISTENCE_TEMP && UseTempOidBuffer()); do { diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 4573246a6bd..18ccc95cc4b 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1367,7 +1367,7 @@ deleteOneObject(const ObjectAddress *object, Relation *depRel, int flags) while (HeapTupleIsValid(tup = systable_getnext(scan))) { if (scan->enr) - ENRdropTuple(*depRel, tup); + ENRDropTuple(*depRel, tup); else CatalogTupleDelete(*depRel, &tup->t_self); } diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 00c704f5cbe..23b961b578b 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -395,8 +395,7 @@ heap_create(const char *relname, relpersistence, relfrozenxid, relminmxid); else if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) - RelationCreateStorage(rel->rd_locator, relpersistence, - (sql_dialect != SQL_DIALECT_TSQL || !RelationIsBBFTableVariable(rel))); + RelationCreateStorage(rel->rd_locator, relpersistence, !IsTsqlTableVariable(rel)); else Assert(false); } @@ -1185,7 +1184,7 @@ heap_create_with_catalog(const char *relname, MultiXactId relminmxid; bool is_enr = false; - if (relpersistence == RELPERSISTENCE_TEMP && sql_dialect == SQL_DIALECT_TSQL) + if (IsTsqlTempTable(relpersistence)) { /* * in TSQL, temporary table name should start with '#'. @@ -1413,7 +1412,7 @@ heap_create_with_catalog(const char *relname, * * For temp tables, we use temp OID assignment code here as well. */ - if (is_enr && useTempOidBuffer()) + if (is_enr && UseTempOidBuffer()) { Relation pg_type; @@ -1671,7 +1670,7 @@ DeleteRelationTuple(Oid relid) elog(ERROR, "cache lookup failed for relation %u", relid); /* delete the relation tuple from pg_class, and finish up */ - if (!ENRdropTuple(pg_class_desc, tup)) + if (!ENRDropTuple(pg_class_desc, tup)) CatalogTupleDelete(pg_class_desc, &tup->t_self); ReleaseSysCache(tup); @@ -1761,7 +1760,7 @@ DeleteSystemAttributeTuples(Oid relid) /* Delete all the matching tuples */ while ((atttup = systable_getnext(scan)) != NULL) - if (!ENRdropTuple(attrel, atttup)) + if (!ENRDropTuple(attrel, atttup)) CatalogTupleDelete(attrel, &atttup->t_self); /* Clean up after the scan */ @@ -1808,7 +1807,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum) { /* System attribute (probably OID) ... just delete the row */ - if (!ENRdropTuple(attr_rel, tuple)) + if (!ENRDropTuple(attr_rel, tuple)) CatalogTupleDelete(attr_rel, &tuple->t_self); } else @@ -3099,7 +3098,7 @@ RemoveStatistics(Oid relid, AttrNumber attnum) /* we must loop even when attnum != 0, in case of inherited stats */ while (HeapTupleIsValid(tuple = systable_getnext(scan))) - if (!ENRdropTuple(pgstatistic, tuple)) + if (!ENRDropTuple(pgstatistic, tuple)) CatalogTupleDelete(pgstatistic, &tuple->t_self); systable_endscan(scan); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index db58faac987..1fcb5e3a70c 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -766,7 +766,7 @@ index_create(Relation heapRelation, * PRIMARY KEYs and/or CONSTRAINTs. */ is_enr = (indexRelationName && strlen(indexRelationName) > 0 && - (indexRelationName[0] == '@' || get_ENR_withoid(currentQueryEnv, heapRelationId, ENR_TSQL_TEMP))); + (indexRelationName[0] == '@' || GetENRTempTableWithOid(heapRelationId))); } relkind = partitioned ? RELKIND_PARTITIONED_INDEX : RELKIND_INDEX; @@ -2391,7 +2391,7 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode) hasexprs = !heap_attisnull(tuple, Anum_pg_index_indexprs, RelationGetDescr(indexRelation)); - if (!ENRdropTuple(indexRelation, tuple)) + if (!ENRDropTuple(indexRelation, tuple)) CatalogTupleDelete(indexRelation, &tuple->t_self); ReleaseSysCache(tuple); diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 383fa51aa73..9a6d93b726a 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -240,7 +240,7 @@ CatalogTupleInsert(Relation heapRel, HeapTuple tup) CatalogTupleCheckConstraints(heapRel, tup); /* ENR carries catalog tuples themselves. Do not insert, and skip index.*/ - if (ENRaddTuple(heapRel, tup)) + if (ENRAddTuple(heapRel, tup)) return; indstate = CatalogOpenIndexes(heapRel); @@ -266,7 +266,7 @@ CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup, CatalogTupleCheckConstraints(heapRel, tup); /* ENR carries catalog tuples themselves. Do not insert, and skip index.*/ - if (ENRaddTuple(heapRel, tup)) + if (ENRAddTuple(heapRel, tup)) return; simple_heap_insert(heapRel, tup); @@ -353,7 +353,7 @@ CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup) CatalogTupleCheckConstraints(heapRel, tup); /* ENR carries catalog tuples themselves. Do not update, and skip index.*/ - if (ENRupdateTuple(heapRel, tup)) + if (ENRUpdateTuple(heapRel, tup)) return; indstate = CatalogOpenIndexes(heapRel); @@ -381,7 +381,7 @@ CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, CatalogTupleCheckConstraints(heapRel, tup); /* ENR carries catalog tuples themselves. Do not update.*/ - if (ENRupdateTuple(heapRel, tup)) + if (ENRUpdateTuple(heapRel, tup)) return; simple_heap_update(heapRel, otid, tup, &updateIndexes); diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c index 27d34bb16aa..6f46ee02488 100644 --- a/src/backend/catalog/pg_attrdef.c +++ b/src/backend/catalog/pg_attrdef.c @@ -299,7 +299,7 @@ RemoveAttrDefaultById(Oid attrdefId) myrel = relation_open(myrelid, AccessExclusiveLock); /* Now we can delete the pg_attrdef row */ - if (!ENRdropTuple(attrdef_rel, tuple)) + if (!ENRDropTuple(attrdef_rel, tuple)) CatalogTupleDelete(attrdef_rel, &tuple->t_self); systable_endscan(scan); diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index d7e7603bc4e..73cdbfc7f71 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -176,7 +176,7 @@ CreateConstraintEntry(const char *constraintName, values[i] = (Datum) NULL; } - if (useTempOidBufferForOid(relId) && isTempNamespace(constraintNamespace)) + if (UseTempOidBufferForOid(relId) && isTempNamespace(constraintNamespace)) conOid = GetNewTempOidWithIndex_hook(conDesc, ConstraintOidIndexId, Anum_pg_constraint_oid); else @@ -647,7 +647,7 @@ RemoveConstraintById(Oid conId) elog(ERROR, "constraint %u is not of a known type", conId); /* Fry the constraint itself */ - if (!ENRdropTuple(conDesc, tup)) + if (!ENRDropTuple(conDesc, tup)) CatalogTupleDelete(conDesc, &tup->t_self); /* Clean up */ diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index ee324c43e09..f1ca3fa98ca 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -326,7 +326,7 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId, ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION) continue; - if (!ENRdropTuple(depRel, tup)) + if (!ENRDropTuple(depRel, tup)) CatalogTupleDelete(depRel, &tup->t_self); count++; } diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index 0a9be812203..abedfd7a2d2 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -33,6 +33,7 @@ #include "storage/smgr.h" #include "utils/hsearch.h" #include "utils/memutils.h" +#include "utils/queryenvironment.h" #include "utils/rel.h" /* GUC variables */ @@ -223,7 +224,7 @@ RelationDropStorage(Relation rel) * However, we need to unlink the files on explicit DROP TABLE command regardless * if the transaction state is committing or aborting. */ - if (sql_dialect == SQL_DIALECT_TSQL && RelationIsBBFTableVariable(rel)) + if (IsTsqlTableVariable(rel)) { pending = (PendingRelDelete *) MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index bdc7fc93a79..6e9295982ed 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -198,9 +198,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, /* * Create the toast table and its index */ - if (sql_dialect == SQL_DIALECT_TSQL && RelationIsBBFTableVariable(rel)) + if (IsTsqlTableVariable(rel)) pg_toast_prefix = "@pg_toast"; - else if (sql_dialect == SQL_DIALECT_TSQL && rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_ENR_withoid(currentQueryEnv, rel->rd_id, ENR_TSQL_TEMP)) + else if (IsTsqlTempTable(rel->rd_rel->relpersistence) && GetENRTempTableWithOid(rel->rd_id)) pg_toast_prefix = "#pg_toast"; snprintf(toast_relname, sizeof(toast_relname), diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 4c4de0ef250..7b1af848e6d 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -740,7 +740,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, * We also must ensure that these temp tables are properly named in TSQL * so that the metadata is properly cleaned up after in this function. */ - if (sql_dialect == SQL_DIALECT_TSQL && relpersistence == RELPERSISTENCE_TEMP && get_ENR_withoid(currentQueryEnv, OIDOldHeap, ENR_TSQL_TEMP)) + if (IsTsqlTempTable(relpersistence) && GetENRTempTableWithOid(OIDOldHeap)) snprintf(NewHeapName, sizeof(NewHeapName), "#pg_temp_%u", OIDOldHeap); else snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap); @@ -1612,9 +1612,9 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, NoLock); /* rename the toast table ... */ - if (sql_dialect == SQL_DIALECT_TSQL && RelationIsBBFTableVariable(newrel)) + if (IsTsqlTableVariable(newrel)) pg_toast_prefix = "@pg_toast"; - else if (sql_dialect == SQL_DIALECT_TSQL && newrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_ENR_withoid(currentQueryEnv, newrel->rd_id, ENR_TSQL_TEMP)) + else if (IsTsqlTempTable(newrel->rd_rel->relpersistence) && GetENRTempTableWithOid(newrel->rd_id)) pg_toast_prefix = "#pg_toast"; snprintf(NewToastName, NAMEDATALEN, "%s_%u", diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index e689ccc0ea2..3b98d345c1a 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -597,7 +597,7 @@ DeleteSequenceTuple(Oid relid) if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); - if (!ENRdropTuple(rel, tuple)) + if (!ENRDropTuple(rel, tuple)) CatalogTupleDelete(rel, &tuple->t_self); ReleaseSysCache(tuple); diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index f21dcfc5467..ce569d37338 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -719,7 +719,7 @@ RemoveStatisticsDataById(Oid statsOid, bool inh) /* We don't know if the data row for inh value exists. */ if (HeapTupleIsValid(tup)) { - if (!ENRdropTuple(relation, tup)) + if (!ENRDropTuple(relation, tup)) CatalogTupleDelete(relation, &tup->t_self); ReleaseSysCache(tup); @@ -767,7 +767,7 @@ RemoveStatisticsById(Oid statsOid) CacheInvalidateRelcacheByRelid(relid); - if (!ENRdropTuple(relation, tup)) + if (!ENRDropTuple(relation, tup)) CatalogTupleDelete(relation, &tup->t_self); ReleaseSysCache(tup); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 94930b9d339..b39c4dd1238 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -13080,7 +13080,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, if (scan->enr) { - ENRdropTuple(depRel, depTup); + ENRDropTuple(depRel, depTup); } else { diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 37b6524d305..74a14f5b08a 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -674,7 +674,7 @@ RemoveTypeById(Oid typeOid) if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for type %u", typeOid); - if (!ENRdropTuple(relation, tup)) + if (!ENRDropTuple(relation, tup)) CatalogTupleDelete(relation, &tup->t_self); /* diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 8c88208b0e8..b51eba1db6d 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -174,7 +174,7 @@ void pgstat_create_relation(Relation rel) { /* Skip pg_stat */ - if (sql_dialect == SQL_DIALECT_TSQL && rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && temp_oid_buffer_size > 0) + if (IsTsqlTempTable(rel->rd_rel->relpersistence) && UseTempOidBuffer()) return; pgstat_create_transactional(PGSTAT_KIND_RELATION, rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId, @@ -190,7 +190,7 @@ pgstat_drop_relation(Relation rel) int nest_level = GetCurrentTransactionNestLevel(); PgStat_TableStatus *pgstat_info; - if (sql_dialect == SQL_DIALECT_TSQL && rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && temp_oid_buffer_size > 0) + if (IsTsqlTempTable(rel->rd_rel->relpersistence) && UseTempOidBuffer()) return; pgstat_drop_transactional(PGSTAT_KIND_RELATION, rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId, diff --git a/src/backend/utils/misc/queryenvironment.c b/src/backend/utils/misc/queryenvironment.c index 4df349cd2ff..92f75788faa 100644 --- a/src/backend/utils/misc/queryenvironment.c +++ b/src/backend/utils/misc/queryenvironment.c @@ -67,6 +67,15 @@ struct QueryEnvironment topLevelQueryEnvData; struct QueryEnvironment *topLevelQueryEnv = &topLevelQueryEnvData; struct QueryEnvironment *currentQueryEnv = NULL; +static void tsql_setup_queryEnv(QueryEnvironment *queryEnv); +static void tsql_cleanup_queryEnv(QueryEnvironment *queryEnv); +static void free_ENR(EphemeralNamedRelation enr); +static bool _ENR_tuple_operation(Relation catalog_rel, HeapTuple tup, ENRTupleOperationType op, bool skip_cache_inval, bool in_enr_rollback); +static void ENRAddUncommittedTupleData(EphemeralNamedRelation enr, Oid catoid, ENRTupleOperationType op, HeapTuple tup, bool in_enr_rollback); +static void ENRDeleteUncommittedTupleData(SubTransactionId subid, EphemeralNamedRelation enr); +static void ENRRollbackUncommittedTuple(QueryEnvironment *queryEnv, ENRUncommittedTuple uncommitted_tup); +static EphemeralNamedRelation find_enr(Form_pg_depend entry); + QueryEnvironment * create_queryEnv(void) { @@ -86,9 +95,7 @@ create_queryEnv2(MemoryContext cxt, bool top_level) if (top_level) { queryEnv = topLevelQueryEnv; - queryEnv->namedRelList = NIL; - queryEnv->dropped_namedRelList = NIL; - queryEnv->savedCatcacheMessages = NIL; + tsql_setup_queryEnv(queryEnv); queryEnv->parentEnv = NULL; queryEnv->memctx = cxt; } else { @@ -103,38 +110,10 @@ create_queryEnv2(MemoryContext cxt, bool top_level) return queryEnv; } -/* Loop through structures in the ENR and make sure to free anything that needs to be freed. */ -static void free_ENR(EphemeralNamedRelation enr) -{ - for (int i = 0; i < ENR_CATTUP_END; i++) - { - List *uncommitted_cattups = enr->md.uncommitted_cattups[i]; - List *cattups = enr->md.cattups[i]; - ListCell *lc2, *lc3; - - foreach(lc2, uncommitted_cattups) - { - ENRUncommittedTuple uncommitted_tup = (ENRUncommittedTuple) lfirst(lc2); - - heap_freetuple(uncommitted_tup->tup); - } - - foreach(lc3, cattups) - { - HeapTuple tup = (HeapTuple) lfirst(lc3); - - heap_freetuple(tup); - } - } - - pfree(enr->md.name); -} - /* Remove the current query environment and make its parent current. */ void remove_queryEnv() { MemoryContext oldcxt; QueryEnvironment *tmp; - ListCell *lc; /* We should never "free" top level query env as it's in stack memory. */ if (!currentQueryEnv || currentQueryEnv == topLevelQueryEnv) @@ -142,6 +121,25 @@ void remove_queryEnv() { tmp = currentQueryEnv->parentEnv; oldcxt = MemoryContextSwitchTo(currentQueryEnv->memctx); + tsql_cleanup_queryEnv(currentQueryEnv); + pfree(currentQueryEnv); + MemoryContextSwitchTo(oldcxt); + + currentQueryEnv = tmp; +} + +/* Any tsql-specific queryEnv initialization should go here. */ +static void tsql_setup_queryEnv(QueryEnvironment *queryEnv) +{ + queryEnv->namedRelList = NIL; + queryEnv->dropped_namedRelList = NIL; + queryEnv->savedCatcacheMessages = NIL; +} + +/* Any tsql-specific queryEnv cleanup should go here. */ +static void tsql_cleanup_queryEnv(QueryEnvironment *queryEnv) +{ + ListCell *lc; /* Clean up structures in currentQueryEnv */ foreach(lc, currentQueryEnv->dropped_namedRelList) @@ -156,11 +154,6 @@ void remove_queryEnv() { list_free(currentQueryEnv->savedCatcacheMessages); currentQueryEnv->savedCatcacheMessages = NIL; - - pfree(currentQueryEnv); - MemoryContextSwitchTo(oldcxt); - - currentQueryEnv = tmp; } EphemeralNamedRelationMetadata @@ -232,98 +225,6 @@ List *get_namedRelList() return relList; } -bool has_existing_enr_relations() -{ - QueryEnvironment *queryEnv = currentQueryEnv; - - while (queryEnv) - { - if (queryEnv->namedRelList != NIL) - return true; - - queryEnv = queryEnv->parentEnv; - } - - return false; -} - -/* This only stores catcache inval messages meant for ENR tables in the current transaction. */ -void SaveCatcacheMessage(int cacheId, - uint32 hashValue, - Oid dbId) -{ - SharedInvalCatcacheMsg *msg = (SharedInvalCatcacheMsg *) palloc0(sizeof(SharedInvalCatcacheMsg)); - msg->id = cacheId; - msg->dbId = dbId; - msg->hashValue = hashValue; - - currentQueryEnv->savedCatcacheMessages = lappend(currentQueryEnv->savedCatcacheMessages, msg); -} - -/* Clear any saved catcache messages at end of xact. */ -void ClearSavedCatcacheMessages() -{ - if (!currentQueryEnv || !currentQueryEnv->savedCatcacheMessages) - return; - - list_free_deep(currentQueryEnv->savedCatcacheMessages); - currentQueryEnv->savedCatcacheMessages = NIL; -} - -/* - * SIMessageIsForTempTable - * - * Determine whether the msg sent is for a temp table. - * TSQL style temp tables do not need to add messages to the - * SI queue, as catalog changes are all session-local. - * - * See LocalExecuteInvalidationMessage - */ -bool SIMessageIsForTempTable(const SharedInvalidationMessage *msg) -{ - if (sql_dialect != SQL_DIALECT_TSQL || temp_oid_buffer_size == 0) - return false; - - if (msg->id >= 0) - { - ListCell *lc; - if (!currentQueryEnv) - return false; - foreach(lc, currentQueryEnv->savedCatcacheMessages) - { - SharedInvalCatcacheMsg *saved_msg = (SharedInvalCatcacheMsg *) lfirst(lc); - if (saved_msg->dbId == msg->cc.dbId - && saved_msg->hashValue == msg->cc.hashValue - && saved_msg->id == msg->cc.id) - return true; - } - return false; - } - else if (msg->id == SHAREDINVALCATALOG_ID) - { - return false; - } - else if (msg->id == SHAREDINVALRELCACHE_ID) - { - /* This is set in AddRelcacheInvalidationMessage. */ - return msg->rc.local_only; - } - else if (msg->id == SHAREDINVALSMGR_ID) - { - return false; - } - else if (msg->id == SHAREDINVALRELMAP_ID) - { - return false; - } - else if (msg->id == SHAREDINVALSNAPSHOT_ID) - { - return false; - } - else - elog(ERROR, "unrecognized SI message ID: %d", msg->id); -} - /* * This returns an ENR if there is a name match in the given collection. It * must quietly return NULL if no match is found. @@ -385,6 +286,16 @@ get_ENR_withoid(QueryEnvironment *queryEnv, Oid id, EphemeralNameRelationType ty return NULL; } +/* + * This is a wrapper function to get_ENR_withoid with the commonly + * provided arguments of currentQueryEnv and ENR_TSQL_TEMP. + */ +EphemeralNamedRelation +GetENRTempTableWithOid(Oid id) +{ + return get_ENR_withoid(currentQueryEnv, id, ENR_TSQL_TEMP); +} + /* * Gets the TupleDesc for a Ephemeral Named Relation, based on which field was * filled. @@ -421,7 +332,7 @@ ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd) * * Returns true if we have found a qualified tuple and stored in *tuplist and *tuplist_i. */ -bool ENRgetSystableScan(Relation rel, Oid indexId, int nkeys, ScanKey key, List **tuplist, int *tuplist_i, int *tuplist_flags) +bool ENRGetSystableScan(Relation rel, Oid indexId, int nkeys, ScanKey key, List **tuplist, int *tuplist_i, int *tuplist_flags) { QueryEnvironment *queryEnv = currentQueryEnv; bool found = false; @@ -773,193 +684,62 @@ bool ENRgetSystableScan(Relation rel, Oid indexId, int nkeys, ScanKey key, List return found; } -static EphemeralNamedRelation -find_enr(Form_pg_depend entry) -{ - QueryEnvironment *queryEnv = currentQueryEnv; - Oid catalog_oid = entry->classid; - - ListCell *curlc; - - while (queryEnv) - { - switch (catalog_oid) { - /* - * pg_depend entry shows relation/type/constraint depends on a given object. - * Find the relation from ENR. If found, make sure - * to register the dependency of the ENR relation to this object. - */ - case RelationRelationId: - return get_ENR_withoid(queryEnv, entry->objid, ENR_TSQL_TEMP); - - case TypeRelationId: - foreach(curlc, queryEnv->namedRelList) { - EphemeralNamedRelation tmp_enr; - ListCell *type_lc; - - tmp_enr = (EphemeralNamedRelation) lfirst(curlc); - if (tmp_enr->md.enrtype != ENR_TSQL_TEMP) - continue; - foreach(type_lc, tmp_enr->md.cattups[ENR_CATTUP_TYPE]) - { - Form_pg_type tup = ((Form_pg_type)GETSTRUCT((HeapTuple)lfirst(type_lc))); - if (tup->oid == entry->objid) - return tmp_enr; - } - foreach(type_lc, tmp_enr->md.cattups[ENR_CATTUP_ARRAYTYPE]) - { - Form_pg_type tup = ((Form_pg_type)GETSTRUCT((HeapTuple)lfirst(type_lc))); - if (tup->oid == entry->objid) - return tmp_enr; - } - } - break; +/* + * Add tuple to an ENR. It assumes that an ENR entry has been created with + * the relation name and relation oid. + */ +bool ENRAddTuple(Relation rel, HeapTuple tup) +{ + return _ENR_tuple_operation(rel, tup, ENR_OP_ADD, false, false); +} - case ConstraintRelationId: - case AttrDefaultRelationId: - return get_ENR_withoid(queryEnv, entry->refobjid, ENR_TSQL_TEMP); +/* + * Drop tuple of an ENR. + * We shouldn't assume the origin of the input tuples (i.e. whether it comes + * from the ENR itself) so we need to search in ENR based on the given tuple. + */ +bool ENRDropTuple(Relation rel, HeapTuple tup) +{ + /* + * If we've already dropped it in this transaction but haven't committed, pretend the drop is done. + */ + return _ENR_tuple_operation(rel, tup, ENR_OP_DROP, false, false); +} - default: - break; - } - queryEnv = queryEnv->parentEnv; - } - return NULL; +/* + * Update tuple of an ENR. + */ +bool ENRUpdateTuple(Relation rel, HeapTuple tup) +{ + return _ENR_tuple_operation(rel, tup, ENR_OP_UPDATE, false, false); } /* - * Store uncommitted tuple metadata in ENR. + * Workhorse for add/update/drop tuples in the ENR. + * + * Return true if the asked operation is done. + * Return false if the asked operation is not possible. */ -static void -ENRAddUncommittedTupleData(EphemeralNamedRelation enr, Oid catoid, ENRTupleOperationType op, HeapTuple tup, bool in_enr_rollback) +static bool _ENR_tuple_operation(Relation catalog_rel, HeapTuple tup, ENRTupleOperationType op, bool skip_cache_inval, bool in_enr_rollback) { - ENRUncommittedTuple uncommitted_tup; - List **list_ptr = NULL; - - if (in_enr_rollback) - return; - - uncommitted_tup = (ENRUncommittedTuple) palloc0(sizeof(ENRUncommittedTupleData)); - uncommitted_tup->catalog_oid = catoid; - uncommitted_tup->optype = op; - uncommitted_tup->tup = heap_copytuple(tup); - uncommitted_tup->subid = GetCurrentSubTransactionId(); + EphemeralNamedRelation enr = NULL; + HeapTuple oldtup, newtup; + MemoryContext oldcxt; + Oid catalog_oid, rel_oid; + HeapTuple tmp; + bool ret = false; + List **list_ptr = NULL; + ListCell *lc = NULL; + QueryEnvironment *queryEnv = currentQueryEnv; + int insert_at = 0; - switch (catoid) - { - case RelationRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_CLASS]; - break; - case DependRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_DEPEND]; - break; - case SharedDependRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_SHDEPEND]; - break; - case IndexRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_INDEX]; - break; - case TypeRelationId: - /* Composite type */ - if (((Form_pg_type) GETSTRUCT(tup))->typelem == InvalidOid) { - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_TYPE]; - break; - /* Array type */ - } else { - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_ARRAYTYPE]; - break; - } - case AttributeRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_ATTRIBUTE]; - break; - case ConstraintRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_CONSTRAINT]; - break; - case StatisticRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_STATISTIC]; - break; - case StatisticExtRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_STATISTIC_EXT]; - break; - case SequenceRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_SEQUENCE]; - break; - case AttrDefaultRelationId: - list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_ATTR_DEF_REL]; - break; - default: - /* Shouldn't reach here */ - elog(ERROR, "Did not find matching ENR catalog entry for catoid %d", catoid); - break; - } - - /* - * lcons here takes the length of the list, so it is less efficient than lappend. - * However, we want the uncommitted_tup order to behave more like a stack to - * process ROLLBACK in the right order of operations. - */ - *list_ptr = lcons(uncommitted_tup, *list_ptr); -} - -/* - * Clean up all uncommitted tuple data from this ENR. - * - * This is called on COMMIT, to wipe unnecessary uncommitted tuple data away - * and on ROLLBACK, once the changes have been rolled back. - */ -static void -ENRDeleteUncommittedTupleData(SubTransactionId subid, EphemeralNamedRelation enr) -{ - for (int i = 0; i < ENR_CATTUP_END; i++) - { - ListCell *lc; - - foreach(lc, enr->md.uncommitted_cattups[i]) - { - ENRUncommittedTuple uncommitted_tup = (ENRUncommittedTuple) lfirst(lc); - - if (subid != InvalidSubTransactionId && uncommitted_tup->subid < subid) - continue; - - heap_freetuple(uncommitted_tup->tup); - pfree(uncommitted_tup); - - enr->md.uncommitted_cattups[i] = foreach_delete_current(enr->md.uncommitted_cattups[i], lc); - } - if (subid == InvalidSubTransactionId) - { - list_free(enr->md.uncommitted_cattups[i]); - enr->md.uncommitted_cattups[i] = NIL; - } - } -} - -/* - * Workhorse for add/update/drop tuples in the ENR. - * - * Return true if the asked operation is done. - * Return false if the asked operation is not possible. - */ -static bool _ENR_tuple_operation(Relation catalog_rel, HeapTuple tup, ENRTupleOperationType op, bool skip_cache_inval, bool in_enr_rollback) -{ - EphemeralNamedRelation enr = NULL; - HeapTuple oldtup, newtup; - MemoryContext oldcxt; - Oid catalog_oid, rel_oid; - HeapTuple tmp; - bool ret = false; - List **list_ptr = NULL; - ListCell *lc = NULL; - QueryEnvironment *queryEnv = currentQueryEnv; - int insert_at = 0; - - if (sql_dialect != SQL_DIALECT_TSQL) - return false; - - catalog_oid = RelationGetRelid(catalog_rel); - - while (queryEnv && !ret) + if (sql_dialect != SQL_DIALECT_TSQL) + return false; + + catalog_oid = RelationGetRelid(catalog_rel); + + while (queryEnv && !ret) { switch (catalog_oid) { case RelationRelationId: @@ -1217,33 +997,60 @@ static bool _ENR_tuple_operation(Relation catalog_rel, HeapTuple tup, ENRTupleOp } /* - * Add tuple to an ENR. It assumes that an ENR entry has been created with - * the relation name and relation oid. + * Helper for _ENR_tuple_operation */ -bool ENRaddTuple(Relation rel, HeapTuple tup) +static EphemeralNamedRelation find_enr(Form_pg_depend entry) { - return _ENR_tuple_operation(rel, tup, ENR_OP_ADD, false, false); -} + QueryEnvironment *queryEnv = currentQueryEnv; + Oid catalog_oid = entry->classid; -/* - * Drop tuple of an ENR. - * We shouldn't assume the origin of the input tuples (i.e. whether it comes - * from the ENR itself) so we need to search in ENR based on the given tuple. - */ -bool ENRdropTuple(Relation rel, HeapTuple tup) -{ - /* - * If we've already dropped it in this transaction but haven't committed, pretend the drop is done. - */ - return _ENR_tuple_operation(rel, tup, ENR_OP_DROP, false, false); -} + ListCell *curlc; -/* - * Update tuple of an ENR. - */ -bool ENRupdateTuple(Relation rel, HeapTuple tup) -{ - return _ENR_tuple_operation(rel, tup, ENR_OP_UPDATE, false, false); + while (queryEnv) + { + switch (catalog_oid) { + /* + * pg_depend entry shows relation/type/constraint depends on a given object. + * Find the relation from ENR. If found, make sure + * to register the dependency of the ENR relation to this object. + */ + case RelationRelationId: + return get_ENR_withoid(queryEnv, entry->objid, ENR_TSQL_TEMP); + + case TypeRelationId: + foreach(curlc, queryEnv->namedRelList) { + EphemeralNamedRelation tmp_enr; + ListCell *type_lc; + + tmp_enr = (EphemeralNamedRelation) lfirst(curlc); + if (tmp_enr->md.enrtype != ENR_TSQL_TEMP) + continue; + + foreach(type_lc, tmp_enr->md.cattups[ENR_CATTUP_TYPE]) + { + Form_pg_type tup = ((Form_pg_type)GETSTRUCT((HeapTuple)lfirst(type_lc))); + if (tup->oid == entry->objid) + return tmp_enr; + } + foreach(type_lc, tmp_enr->md.cattups[ENR_CATTUP_ARRAYTYPE]) + { + Form_pg_type tup = ((Form_pg_type)GETSTRUCT((HeapTuple)lfirst(type_lc))); + if (tup->oid == entry->objid) + return tmp_enr; + } + } + break; + + case ConstraintRelationId: + case AttrDefaultRelationId: + return get_ENR_withoid(queryEnv, entry->refobjid, ENR_TSQL_TEMP); + + default: + break; + } + queryEnv = queryEnv->parentEnv; + } + return NULL; } /* @@ -1257,7 +1064,7 @@ void ENRDropEntry(Oid id) if (sql_dialect != SQL_DIALECT_TSQL || !currentQueryEnv) return; - if ((enr = get_ENR_withoid(currentQueryEnv, id, ENR_TSQL_TEMP)) == NULL) + if ((enr = GetENRTempTableWithOid(id)) == NULL) return; oldcxt = MemoryContextSwitchTo(currentQueryEnv->memctx); @@ -1278,98 +1085,213 @@ void ENRDropEntry(Oid id) } /* - * ENRCommitChanges - * - * 1. Free uncommitted tuple data from each ENR, then mark it as committed. - * 2. Finish freeing ENRs that were dropped in this transaction. + * Drop all the temp tables registered as ENR in the given query environment. */ void -ENRCommitChanges(QueryEnvironment *queryEnv) +ENRDropTempTables(QueryEnvironment *queryEnv) { - ListCell *lc = NULL; - MemoryContext oldcxt; + ListCell *lc = NULL; + ObjectAddress object; + ObjectAddresses *objects; + int nrels = 0, + maxrels = 0; + SMgrRelation *srels = NULL; if (!queryEnv) return; - oldcxt = MemoryContextSwitchTo(queryEnv->memctx); + objects = new_object_addresses(); + /* + * Loop through the registered ENRs to drop temp tables. + */ foreach(lc, queryEnv->namedRelList) { EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); + Relation rel; + SMgrRelation srel; - if (enr->md.enrtype != ENR_TSQL_TEMP || !enr->md.is_bbf_temp_table) + if (enr->md.enrtype != ENR_TSQL_TEMP) continue; - ENRDeleteUncommittedTupleData(InvalidSubTransactionId, enr); - enr->md.is_committed = true; - } + object.classId = RelationRelationId; + object.objectSubId = 0; + object.objectId = enr->md.reliddesc; + add_exact_object_address(&object, objects); + /* + * Delete the physical storage for the relation. + * See: smgrDoPendingDeletes() + */ + rel = relation_open(enr->md.reliddesc, AccessExclusiveLock); + srel = smgropen(rel->rd_locator, rel->rd_backend); - lc = NULL; + /* allocate the initial array, or extend it, if needed */ + if (maxrels == 0) + { + maxrels = 8; + srels = palloc(sizeof(SMgrRelation) * maxrels); + } + else if (maxrels <= nrels) + { + maxrels *= 2; + srels = repalloc(srels, sizeof(SMgrRelation) * maxrels); + } - foreach(lc, queryEnv->dropped_namedRelList) - { - EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); + srels[nrels++] = srel; - free_ENR(enr); - pfree(enr); + relation_close(rel, NoLock); } - list_free(queryEnv->dropped_namedRelList); - queryEnv->dropped_namedRelList = NIL; + /* + * The below call to performMultpleDeletions() only registers the + * underlying files for deletion at the end of transaction, but they + * need to be deleted now since we are exiting the current scope. We + * still need to run the function though, to make sure that all of the + * dependencies are properly cleaned up. + */ + if (nrels > 0) + { + smgrdounlinkall(srels, nrels, false); - MemoryContextSwitchTo(oldcxt); -} + for (int i = 0; i < nrels; i++) + smgrclose(srels[i]); -/* - * Helper function - finish the ROLLBACK processing for a single uncommitted_tup entry. - */ -static void -ENRRollbackUncommittedTuple(QueryEnvironment *queryEnv, ENRUncommittedTuple uncommitted_tup) -{ - Relation rel; - bool skip_cache_inval = false; + pfree(srels); + } - if (!OidIsValid(uncommitted_tup->catalog_oid) || !HeapTupleIsValid(uncommitted_tup->tup)) + /* + * performMultipleDeletions() will remove the table AND the ENR entry, + * so no need to remove the entry afterwards. It also takes care of + * proper object drop order, to prevent dependency issues. + */ + performMultipleDeletions(objects, DROP_CASCADE, PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY); + free_object_addresses(objects); +} + +/* + * Drop all records of the relid from catalog_relation. + * ie: delete * from catalog_relation where *relid= + */ +void ENRDropCatalogEntry(Relation catalog_relation, Oid relid) +{ + QueryEnvironment *queryEnv = currentQueryEnv; + bool ret = false; + Oid catalog_oid; + List **list_ptr = NULL; + EphemeralNamedRelation enr; + + catalog_oid = RelationGetRelid(catalog_relation); + while (queryEnv && !ret) { - elog(WARNING, "Invalid temp table entries were found during ROLLBACK"); - return; + switch (catalog_oid) { + case AttributeRelationId: + if ((enr = get_ENR_withoid(queryEnv, relid, ENR_TSQL_TEMP))) { + list_ptr = &enr->md.cattups[ENR_CATTUP_ATTRIBUTE]; + ret = true; + } + break; + default: + ereport(ERROR, (errmsg("Unreachable codepath"))); + } + + if (ret) { + HeapTuple htup; + MemoryContext oldcxt; + + Assert(queryEnv->memctx); + oldcxt = MemoryContextSwitchTo(queryEnv->memctx); + + while (*list_ptr) + { + htup = list_nth(*list_ptr, 0); + *list_ptr = list_delete_ptr(*list_ptr, htup); + + if (temp_table_xact_support) + ENRAddUncommittedTupleData(enr, catalog_oid, ENR_OP_DROP, htup, false); + + CacheInvalidateHeapTuple(catalog_relation, htup, NULL); + heap_freetuple(htup); // heap_copytuple was called during ADD + } + + MemoryContextSwitchTo(oldcxt); + } + + queryEnv = queryEnv->parentEnv; } +} - if (uncommitted_tup->catalog_oid == IndexRelationId && uncommitted_tup->optype == ENR_OP_ADD) +/* Loop through structures in the ENR and make sure to free anything that needs to be freed. */ +static void free_ENR(EphemeralNamedRelation enr) +{ + for (int i = 0; i < ENR_CATTUP_END; i++) { - /* - * We skip invalidating IndexRelationId on drop (ie committed table, index created in transaction) - * because it will be taken care of when the index itself is dropped - otherwise we risk - * throwing an error because the entry is already wiped away. - */ - - Form_pg_index idx_form = (Form_pg_index) GETSTRUCT(uncommitted_tup->tup); - EphemeralNamedRelation tmp_enr = get_ENR_withoid(queryEnv, idx_form->indrelid, ENR_TSQL_TEMP); - if (tmp_enr) + List *uncommitted_cattups = enr->md.uncommitted_cattups[i]; + List *cattups = enr->md.cattups[i]; + ListCell *lc2, *lc3; + + foreach(lc2, uncommitted_cattups) { - skip_cache_inval = true; + ENRUncommittedTuple uncommitted_tup = (ENRUncommittedTuple) lfirst(lc2); + + heap_freetuple(uncommitted_tup->tup); + } + + foreach(lc3, cattups) + { + HeapTuple tup = (HeapTuple) lfirst(lc3); + + heap_freetuple(tup); } } - rel = relation_open(uncommitted_tup->catalog_oid, RowExclusiveLock); + pfree(enr->md.name); +} - switch(uncommitted_tup->optype) +/* + * ENRCommitChanges + * + * 1. Free uncommitted tuple data from each ENR, then mark it as committed. + * 2. Finish freeing ENRs that were dropped in this transaction. + */ +void +ENRCommitChanges(QueryEnvironment *queryEnv) +{ + ListCell *lc = NULL; + MemoryContext oldcxt; + + if (!queryEnv) + return; + + oldcxt = MemoryContextSwitchTo(queryEnv->memctx); + + foreach(lc, queryEnv->namedRelList) { - /* Peform the opposite operation as the saved optype. */ - case ENR_OP_ADD: - _ENR_tuple_operation(rel, uncommitted_tup->tup, ENR_OP_DROP, skip_cache_inval, true); - break; - case ENR_OP_UPDATE: - _ENR_tuple_operation(rel, uncommitted_tup->tup, ENR_OP_UPDATE, skip_cache_inval, true); - break; - case ENR_OP_DROP: - _ENR_tuple_operation(rel, uncommitted_tup->tup, ENR_OP_ADD, skip_cache_inval, true); - break; + EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); + + if (enr->md.enrtype != ENR_TSQL_TEMP || !enr->md.is_bbf_temp_table) + continue; + + ENRDeleteUncommittedTupleData(InvalidSubTransactionId, enr); + enr->md.is_committed = true; } - relation_close(rel, RowExclusiveLock); + + lc = NULL; + + foreach(lc, queryEnv->dropped_namedRelList) + { + EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); + + free_ENR(enr); + pfree(enr); + } + + list_free(queryEnv->dropped_namedRelList); + queryEnv->dropped_namedRelList = NIL; + + MemoryContextSwitchTo(oldcxt); } + /* * ENRRollbackChanges * @@ -1525,157 +1447,284 @@ void ENRRollbackSubtransaction(SubTransactionId subid, QueryEnvironment *queryEn MemoryContextSwitchTo(oldcxt); } -/* - * Simple check for whether to use temp OID buffer. - */ -bool useTempOidBuffer() -{ - return sql_dialect == SQL_DIALECT_TSQL - && GetNewTempOidWithIndex_hook - && temp_oid_buffer_size > 0; -} - -/* Simple check for whether to use temp OID buffer given an existing OID. */ -bool useTempOidBufferForOid(Oid relId) -{ - return sql_dialect == SQL_DIALECT_TSQL - && GetNewTempOidWithIndex_hook - && temp_oid_buffer_size > 0 - && get_ENR_withoid(currentQueryEnv, relId, ENR_TSQL_TEMP); -} - /* - * Drop all the temp tables registered as ENR in the given query environment. + * Store uncommitted tuple metadata in ENR. */ -void -ENRDropTempTables(QueryEnvironment *queryEnv) +static void +ENRAddUncommittedTupleData(EphemeralNamedRelation enr, Oid catoid, ENRTupleOperationType op, HeapTuple tup, bool in_enr_rollback) { - ListCell *lc = NULL; - ObjectAddress object; - ObjectAddresses *objects; - int nrels = 0, - maxrels = 0; - SMgrRelation *srels = NULL; - - if (!queryEnv) + ENRUncommittedTuple uncommitted_tup; + List **list_ptr = NULL; + + if (in_enr_rollback) return; + + uncommitted_tup = (ENRUncommittedTuple) palloc0(sizeof(ENRUncommittedTupleData)); + uncommitted_tup->catalog_oid = catoid; + uncommitted_tup->optype = op; + uncommitted_tup->tup = heap_copytuple(tup); + uncommitted_tup->subid = GetCurrentSubTransactionId(); - objects = new_object_addresses(); + switch (catoid) + { + case RelationRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_CLASS]; + break; + case DependRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_DEPEND]; + break; + case SharedDependRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_SHDEPEND]; + break; + case IndexRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_INDEX]; + break; + case TypeRelationId: + /* Composite type */ + if (((Form_pg_type) GETSTRUCT(tup))->typelem == InvalidOid) { + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_TYPE]; + break; + /* Array type */ + } else { + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_ARRAYTYPE]; + break; + } + case AttributeRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_ATTRIBUTE]; + break; + case ConstraintRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_CONSTRAINT]; + break; + case StatisticRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_STATISTIC]; + break; + case StatisticExtRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_STATISTIC_EXT]; + break; + case SequenceRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_SEQUENCE]; + break; + case AttrDefaultRelationId: + list_ptr = &enr->md.uncommitted_cattups[ENR_CATTUP_ATTR_DEF_REL]; + break; + default: + /* Shouldn't reach here */ + elog(ERROR, "Did not find matching ENR catalog entry for catoid %d", catoid); + break; + } /* - * Loop through the registered ENRs to drop temp tables. + * lcons here takes the length of the list, so it is less efficient than lappend. + * However, we want the uncommitted_tup order to behave more like a stack to + * process ROLLBACK in the right order of operations. */ - foreach(lc, queryEnv->namedRelList) + *list_ptr = lcons(uncommitted_tup, *list_ptr); +} + +/* + * Clean up all uncommitted tuple data from this ENR. + * + * This is called on COMMIT, to wipe unnecessary uncommitted tuple data away + * and on ROLLBACK, once the changes have been rolled back. + */ +static void +ENRDeleteUncommittedTupleData(SubTransactionId subid, EphemeralNamedRelation enr) +{ + for (int i = 0; i < ENR_CATTUP_END; i++) { - EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); - Relation rel; - SMgrRelation srel; + ListCell *lc; - if (enr->md.enrtype != ENR_TSQL_TEMP) - continue; + foreach(lc, enr->md.uncommitted_cattups[i]) + { + ENRUncommittedTuple uncommitted_tup = (ENRUncommittedTuple) lfirst(lc); - object.classId = RelationRelationId; - object.objectSubId = 0; - object.objectId = enr->md.reliddesc; - add_exact_object_address(&object, objects); - /* - * Delete the physical storage for the relation. - * See: smgrDoPendingDeletes() - */ - rel = relation_open(enr->md.reliddesc, AccessExclusiveLock); - srel = smgropen(rel->rd_locator, rel->rd_backend); + if (subid != InvalidSubTransactionId && uncommitted_tup->subid < subid) + continue; - /* allocate the initial array, or extend it, if needed */ - if (maxrels == 0) - { - maxrels = 8; - srels = palloc(sizeof(SMgrRelation) * maxrels); + heap_freetuple(uncommitted_tup->tup); + pfree(uncommitted_tup); + + enr->md.uncommitted_cattups[i] = foreach_delete_current(enr->md.uncommitted_cattups[i], lc); } - else if (maxrels <= nrels) + if (subid == InvalidSubTransactionId) { - maxrels *= 2; - srels = repalloc(srels, sizeof(SMgrRelation) * maxrels); + list_free(enr->md.uncommitted_cattups[i]); + enr->md.uncommitted_cattups[i] = NIL; } + } +} - srels[nrels++] = srel; +/* + * Helper function - finish the ROLLBACK processing for a single uncommitted_tup entry. + */ +static void +ENRRollbackUncommittedTuple(QueryEnvironment *queryEnv, ENRUncommittedTuple uncommitted_tup) +{ + Relation rel; + bool skip_cache_inval = false; - relation_close(rel, NoLock); + if (!OidIsValid(uncommitted_tup->catalog_oid) || !HeapTupleIsValid(uncommitted_tup->tup)) + { + elog(WARNING, "Invalid temp table entries were found during ROLLBACK"); + return; } - /* - * The below call to performMultpleDeletions() only registers the - * underlying files for deletion at the end of transaction, but they - * need to be deleted now since we are exiting the current scope. We - * still need to run the function though, to make sure that all of the - * dependencies are properly cleaned up. - */ - if (nrels > 0) + if (uncommitted_tup->catalog_oid == IndexRelationId && uncommitted_tup->optype == ENR_OP_ADD) { - smgrdounlinkall(srels, nrels, false); + /* + * We skip invalidating IndexRelationId on drop (ie committed table, index created in transaction) + * because it will be taken care of when the index itself is dropped - otherwise we risk + * throwing an error because the entry is already wiped away. + */ + + Form_pg_index idx_form = (Form_pg_index) GETSTRUCT(uncommitted_tup->tup); + if (get_ENR_withoid(queryEnv, idx_form->indrelid, ENR_TSQL_TEMP)) + { + skip_cache_inval = true; + } + } - for (int i = 0; i < nrels; i++) - smgrclose(srels[i]); + rel = relation_open(uncommitted_tup->catalog_oid, RowExclusiveLock); - pfree(srels); + switch(uncommitted_tup->optype) + { + /* Peform the opposite operation as the saved optype. */ + case ENR_OP_ADD: + _ENR_tuple_operation(rel, uncommitted_tup->tup, ENR_OP_DROP, skip_cache_inval, true); + break; + case ENR_OP_UPDATE: + _ENR_tuple_operation(rel, uncommitted_tup->tup, ENR_OP_UPDATE, skip_cache_inval, true); + break; + case ENR_OP_DROP: + _ENR_tuple_operation(rel, uncommitted_tup->tup, ENR_OP_ADD, skip_cache_inval, true); + break; } + relation_close(rel, RowExclusiveLock); +} - /* - * performMultipleDeletions() will remove the table AND the ENR entry, - * so no need to remove the entry afterwards. It also takes care of - * proper object drop order, to prevent dependency issues. - */ - performMultipleDeletions(objects, DROP_CASCADE, PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY); - free_object_addresses(objects); +/* This only stores catcache inval messages meant for ENR tables in the current transaction. */ +void SaveCatcacheMessage(int cacheId, + uint32 hashValue, + Oid dbId) +{ + SharedInvalCatcacheMsg *msg = (SharedInvalCatcacheMsg *) palloc0(sizeof(SharedInvalCatcacheMsg)); + msg->id = cacheId; + msg->dbId = dbId; + msg->hashValue = hashValue; + + currentQueryEnv->savedCatcacheMessages = lappend(currentQueryEnv->savedCatcacheMessages, msg); +} + +/* Clear any saved catcache messages at end of xact. */ +void ClearSavedCatcacheMessages() +{ + if (!currentQueryEnv || !currentQueryEnv->savedCatcacheMessages) + return; + + list_free_deep(currentQueryEnv->savedCatcacheMessages); + currentQueryEnv->savedCatcacheMessages = NIL; } /* - * Drop all records of the relid from catalog_relation. - * ie: delete * from catalog_relation where *relid= -*/ -extern void ENRDropCatalogEntry(Relation catalog_relation, Oid relid) + * SIMessageIsForTempTable + * + * Determine whether the msg sent is for a temp table. + * TSQL style temp tables do not need to add messages to the + * SI queue, as catalog changes are all session-local. + * + * See LocalExecuteInvalidationMessage + */ +bool SIMessageIsForTempTable(const SharedInvalidationMessage *msg) { - QueryEnvironment *queryEnv = currentQueryEnv; - bool ret = false; - Oid catalog_oid; - List **list_ptr = NULL; - EphemeralNamedRelation enr; + if (sql_dialect != SQL_DIALECT_TSQL || temp_oid_buffer_size == 0) + return false; - catalog_oid = RelationGetRelid(catalog_relation); - while (queryEnv && !ret) + if (msg->id >= 0) { - switch (catalog_oid) { - case AttributeRelationId: - if ((enr = get_ENR_withoid(queryEnv, relid, ENR_TSQL_TEMP))) { - list_ptr = &enr->md.cattups[ENR_CATTUP_ATTRIBUTE]; - ret = true; - } - break; - default: - ereport(ERROR, (errmsg("Unreachable codepath"))); + ListCell *lc; + if (!currentQueryEnv) + return false; + foreach(lc, currentQueryEnv->savedCatcacheMessages) + { + SharedInvalCatcacheMsg *saved_msg = (SharedInvalCatcacheMsg *) lfirst(lc); + if (saved_msg->dbId == msg->cc.dbId + && saved_msg->hashValue == msg->cc.hashValue + && saved_msg->id == msg->cc.id) + return true; } + return false; + } + else if (msg->id == SHAREDINVALCATALOG_ID) + { + return false; + } + else if (msg->id == SHAREDINVALRELCACHE_ID) + { + /* This is set in AddRelcacheInvalidationMessage. */ + return msg->rc.local_only; + } + else if (msg->id == SHAREDINVALSMGR_ID) + { + return false; + } + else if (msg->id == SHAREDINVALRELMAP_ID) + { + return false; + } + else if (msg->id == SHAREDINVALSNAPSHOT_ID) + { + return false; + } + else + elog(ERROR, "unrecognized SI message ID: %d", msg->id); +} - if (ret) { - HeapTuple htup; - MemoryContext oldcxt; +/* Simple wrapper for Table Variable checks */ +bool IsTsqlTableVariable(Relation relation) +{ + return sql_dialect == SQL_DIALECT_TSQL + && relation + && relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP + && relation->rd_rel->relname.data + && strlen(relation->rd_rel->relname.data) >= 1 + && relation->rd_rel->relname.data[0] == '@'; +} - Assert(queryEnv->memctx); - oldcxt = MemoryContextSwitchTo(queryEnv->memctx); +/* Simple wrapper for Temp Table checks */ +bool IsTsqlTempTable(char relpersistence) +{ + return sql_dialect == SQL_DIALECT_TSQL + && relpersistence == RELPERSISTENCE_TEMP; +} - while (*list_ptr) - { - htup = list_nth(*list_ptr, 0); - *list_ptr = list_delete_ptr(*list_ptr, htup); +/* + * Simple check for whether to use temp OID buffer. + */ +bool UseTempOidBuffer() +{ + return sql_dialect == SQL_DIALECT_TSQL + && GetNewTempOidWithIndex_hook + && temp_oid_buffer_size > 0; +} - if (temp_table_xact_support) - ENRAddUncommittedTupleData(enr, catalog_oid, ENR_OP_DROP, htup, false); +/* Simple check for whether to use temp OID buffer given an existing OID. */ +bool UseTempOidBufferForOid(Oid relId) +{ + return UseTempOidBuffer() + && GetENRTempTableWithOid(relId); +} - CacheInvalidateHeapTuple(catalog_relation, htup, NULL); - heap_freetuple(htup); // heap_copytuple was called during ADD - } +bool has_existing_enr_relations() +{ + QueryEnvironment *queryEnv = currentQueryEnv; - MemoryContextSwitchTo(oldcxt); - } + while (queryEnv) + { + if (queryEnv->namedRelList != NIL) + return true; queryEnv = queryEnv->parentEnv; } -} + + return false; +} \ No newline at end of file diff --git a/src/include/utils/queryenvironment.h b/src/include/utils/queryenvironment.h index 3902e333847..8db5eb38b19 100644 --- a/src/include/utils/queryenvironment.h +++ b/src/include/utils/queryenvironment.h @@ -136,29 +136,37 @@ extern void unregister_ENR(QueryEnvironment *queryEnv, const char *name); extern PGDLLEXPORT List *get_namedRelList(void); extern EphemeralNamedRelation get_ENR(QueryEnvironment *queryEnv, const char *name, bool search); extern PGDLLEXPORT EphemeralNamedRelation get_ENR_withoid(QueryEnvironment *queryEnv, Oid oid, EphemeralNameRelationType type); +extern EphemeralNamedRelation GetENRTempTableWithOid(Oid id); extern TupleDesc ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd); -extern bool ENRaddTuple(Relation rel, HeapTuple tup); -extern bool ENRdropTuple(Relation rel, HeapTuple tup); -extern bool ENRupdateTuple(Relation rel, HeapTuple tup); -extern bool ENRgetSystableScan(Relation rel, Oid indexoid, int nkeys, ScanKey key, List **tuplist, int *tuplist_i, int *tuplist_flags); -extern PGDLLEXPORT void ENRDropTempTables(QueryEnvironment *queryEnv); +extern bool ENRGetSystableScan(Relation rel, Oid indexoid, int nkeys, ScanKey key, List **tuplist, int *tuplist_i, int *tuplist_flags); +extern bool ENRAddTuple(Relation rel, HeapTuple tup); +extern bool ENRDropTuple(Relation rel, HeapTuple tup); +extern bool ENRUpdateTuple(Relation rel, HeapTuple tup); + extern void ENRDropEntry(Oid id); +extern PGDLLEXPORT void ENRDropTempTables(QueryEnvironment *queryEnv); extern void ENRDropCatalogEntry(Relation catalog_relation, Oid relid); -extern bool has_existing_enr_relations(void); +/* ENR Rollback functions */ extern bool ENRTupleIsDropped(Relation rel, HeapTuple tup); extern void ENRCommitChanges(QueryEnvironment *queryEnv); extern void ENRRollbackChanges(QueryEnvironment *queryEnv); extern void ENRRollbackSubtransaction(SubTransactionId subid, QueryEnvironment *queryEnv); -extern bool useTempOidBuffer(void); -extern bool useTempOidBufferForOid(Oid relId); - -typedef EphemeralNamedRelation (*pltsql_get_tsql_enr_from_oid_hook_type) (Oid oid); -extern PGDLLIMPORT pltsql_get_tsql_enr_from_oid_hook_type pltsql_get_tsql_enr_from_oid_hook; - +/* Temp Table Cache Inval */ extern void SaveCatcacheMessage(int cacheId, uint32 hashValue, Oid dbId); extern void ClearSavedCatcacheMessages(void); extern bool SIMessageIsForTempTable(const SharedInvalidationMessage *msg); +/* Various checks */ +extern bool IsTsqlTableVariable(Relation rel); +extern bool IsTsqlTempTable(char relpersistence); +extern bool UseTempOidBuffer(void); +extern bool UseTempOidBufferForOid(Oid relId); +extern bool has_existing_enr_relations(void); + +/* Hooks */ +typedef EphemeralNamedRelation (*pltsql_get_tsql_enr_from_oid_hook_type) (Oid oid); +extern PGDLLIMPORT pltsql_get_tsql_enr_from_oid_hook_type pltsql_get_tsql_enr_from_oid_hook; + #endif /* QUERYENVIRONMENT_H */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index c04999aee6c..1426a353cd0 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -709,10 +709,4 @@ RelationCloseSmgr(Relation relation) extern void RelationIncrementReferenceCount(Relation rel); extern void RelationDecrementReferenceCount(Relation rel); -#define RelationIsBBFTableVariable(relation) \ - ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP && \ - (relation)->rd_rel->relname.data && \ - strlen((relation)->rd_rel->relname.data) >= 1 && \ - (relation)->rd_rel->relname.data[0] == '@') - #endif /* REL_H */