v6-0003-Support-indexes-on-global-temporary-tables.patch
text/x-patch
Filename: v6-0003-Support-indexes-on-global-temporary-tables.patch
Type: text/x-patch
Part: 2
Message:
Re: Global temporary tables
Patch
Format: format-patch
Series: patch v6-0003
Subject: Support indexes on global temporary tables.
| File | + | − |
|---|---|---|
| contrib/bloom/blinsert.c | 3 | 3 |
| contrib/bloom/bloom.h | 1 | 1 |
| src/backend/access/brin/brin.c | 2 | 2 |
| src/backend/access/gin/gininsert.c | 4 | 4 |
| src/backend/access/gist/gist.c | 3 | 3 |
| src/backend/access/hash/hash.c | 6 | 6 |
| src/backend/access/nbtree/nbtree.c | 3 | 3 |
| src/backend/access/spgist/spginsert.c | 3 | 3 |
| src/backend/access/transam/xloginsert.c | 2 | 1 |
| src/backend/catalog/global_temp.c | 24 | 0 |
| src/backend/catalog/index.c | 3 | 2 |
| src/backend/commands/indexcmds.c | 21 | 6 |
| src/backend/optimizer/plan/planner.c | 1 | 0 |
| src/bin/scripts/reindexdb.c | 4 | 0 |
| src/include/access/amapi.h | 3 | 1 |
| src/include/access/brin_internal.h | 1 | 1 |
| src/include/access/gin_private.h | 1 | 1 |
| src/include/access/gist_private.h | 1 | 1 |
| src/include/access/hash.h | 1 | 1 |
| src/include/access/nbtree.h | 1 | 1 |
| src/include/access/spgist.h | 1 | 1 |
| src/test/isolation/expected/global-temp.out | 144 | 0 |
| src/test/isolation/specs/global-temp.spec | 23 | 2 |
| src/test/modules/dummy_index_am/dummy_index_am.c | 2 | 2 |
| src/test/regress/expected/global_temp.out | 111 | 47 |
| src/test/regress/sql/global_temp.sql | 36 | 5 |
From 1e7232fa97e65dbf43f6f8b5e562d4746a8ef847 Mon Sep 17 00:00:00 2001
From: Dean Rasheed <dean.a.rasheed@gmail.com>
Date: Tue, 9 Jun 2026 13:50:47 +0100
Subject: [PATCH v6 03/10] Support indexes on global temporary tables.
As with local temporary tables, concurrent index build/reindex/drop is
not supported.
---
contrib/bloom/blinsert.c | 6 +-
contrib/bloom/bloom.h | 2 +-
src/backend/access/brin/brin.c | 4 +-
src/backend/access/gin/gininsert.c | 8 +-
src/backend/access/gist/gist.c | 6 +-
src/backend/access/hash/hash.c | 12 +-
src/backend/access/nbtree/nbtree.c | 6 +-
src/backend/access/spgist/spginsert.c | 6 +-
src/backend/access/transam/xloginsert.c | 3 +-
src/backend/catalog/global_temp.c | 24 +++
src/backend/catalog/index.c | 5 +-
src/backend/commands/indexcmds.c | 27 ++-
src/backend/optimizer/plan/planner.c | 1 +
src/bin/scripts/reindexdb.c | 4 +
src/include/access/amapi.h | 4 +-
src/include/access/brin_internal.h | 2 +-
src/include/access/gin_private.h | 2 +-
src/include/access/gist_private.h | 2 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/spgist.h | 2 +-
src/test/isolation/expected/global-temp.out | 144 ++++++++++++++++
src/test/isolation/specs/global-temp.spec | 25 ++-
.../modules/dummy_index_am/dummy_index_am.c | 4 +-
src/test/regress/expected/global_temp.out | 158 ++++++++++++------
src/test/regress/sql/global_temp.sql | 41 ++++-
26 files changed, 405 insertions(+), 97 deletions(-)
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index df24856d9ae..804c77ae768 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -159,13 +159,13 @@ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * Build an empty bloom index in the initialization fork.
+ * Build an empty bloom index in the specified fork.
*/
void
-blbuildempty(Relation index)
+blbuildempty(Relation index, ForkNumber forknum)
{
/* Initialize the meta page */
- BloomInitMetapage(index, INIT_FORKNUM);
+ BloomInitMetapage(index, forknum);
}
/*
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 9a4125bdfb1..4f00584006b 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -197,7 +197,7 @@ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
extern void blendscan(IndexScanDesc scan);
extern IndexBuildResult *blbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void blbuildempty(Relation index);
+extern void blbuildempty(Relation index, ForkNumber forknum);
extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
void *callback_state);
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index bdb30752e09..94494c9d8fa 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -1276,12 +1276,12 @@ brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
void
-brinbuildempty(Relation index)
+brinbuildempty(Relation index, ForkNumber forknum)
{
Buffer metabuf;
/* An empty BRIN index has a metapage only. */
- metabuf = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,
+ metabuf = ExtendBufferedRel(BMR_REL(index), forknum, NULL,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
/* Initialize and xlog metabuffer. */
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index cb9ed3b563c..e9c2d1eedf1 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -806,18 +806,18 @@ ginbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * ginbuildempty() -- build an empty gin index in the initialization fork
+ * ginbuildempty() -- build an empty gin index in the specified fork
*/
void
-ginbuildempty(Relation index)
+ginbuildempty(Relation index, ForkNumber forknum)
{
Buffer RootBuffer,
MetaBuffer;
/* An empty GIN index has two pages. */
- MetaBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,
+ MetaBuffer = ExtendBufferedRel(BMR_REL(index), forknum, NULL,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
- RootBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,
+ RootBuffer = ExtendBufferedRel(BMR_REL(index), forknum, NULL,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
/* Initialize and xlog metabuffer and root buffer. */
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8565e225be7..9eae449786b 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -134,15 +134,15 @@ createTempGistContext(void)
}
/*
- * gistbuildempty() -- build an empty gist index in the initialization fork
+ * gistbuildempty() -- build an empty gist index in the specified fork
*/
void
-gistbuildempty(Relation index)
+gistbuildempty(Relation index, ForkNumber forknum)
{
Buffer buffer;
/* Initialize the root page */
- buffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,
+ buffer = ExtendBufferedRel(BMR_REL(index), forknum, NULL,
EB_SKIP_EXTENSION_LOCK | EB_LOCK_FIRST);
/* Initialize and xlog buffer */
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 8d8cd30dc38..cdcbb9efae0 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -175,10 +175,10 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
* metapage, nor the first bitmap page.
*/
sort_threshold = (maintenance_work_mem * (Size) 1024) / BLCKSZ;
- if (index->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
- sort_threshold = Min(sort_threshold, NBuffers);
- else
+ if (RelationUsesLocalBuffers(index))
sort_threshold = Min(sort_threshold, NLocBuffer);
+ else
+ sort_threshold = Min(sort_threshold, NBuffers);
if (num_buckets >= sort_threshold)
buildstate.spool = _h_spoolinit(heap, index, num_buckets);
@@ -215,12 +215,12 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * hashbuildempty() -- build an empty hash index in the initialization fork
+ * hashbuildempty() -- build an empty hash index in the specified fork
*/
void
-hashbuildempty(Relation index)
+hashbuildempty(Relation index, ForkNumber forknum)
{
- _hash_init(index, 0, INIT_FORKNUM);
+ _hash_init(index, 0, forknum);
}
/*
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 3df2c752ead..be1b5ce51f9 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -177,16 +177,16 @@ bthandler(PG_FUNCTION_ARGS)
}
/*
- * btbuildempty() -- build an empty btree index in the initialization fork
+ * btbuildempty() -- build an empty btree index in the specified fork
*/
void
-btbuildempty(Relation index)
+btbuildempty(Relation index, ForkNumber forknum)
{
bool allequalimage = _bt_allequalimage(index, false);
BulkWriteState *bulkstate;
BulkWriteBuffer metabuf;
- bulkstate = smgr_bulk_start_rel(index, INIT_FORKNUM);
+ bulkstate = smgr_bulk_start_rel(index, forknum);
/* Construct metapage. */
metabuf = smgr_bulk_get_buf(bulkstate);
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
index 780ef646a54..1a14f84174a 100644
--- a/src/backend/access/spgist/spginsert.c
+++ b/src/backend/access/spgist/spginsert.c
@@ -148,15 +148,15 @@ spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * Build an empty SPGiST index in the initialization fork
+ * Build an empty SPGiST index in the specified fork
*/
void
-spgbuildempty(Relation index)
+spgbuildempty(Relation index, ForkNumber forknum)
{
BulkWriteState *bulkstate;
BulkWriteBuffer buf;
- bulkstate = smgr_bulk_start_rel(index, INIT_FORKNUM);
+ bulkstate = smgr_bulk_start_rel(index, forknum);
/* Construct metapage. */
buf = smgr_bulk_get_buf(bulkstate);
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index f2e10b82b7d..9b099d87f35 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -561,7 +561,8 @@ XLogSimpleInsertInt64(RmgrId rmid, uint8 info, int64 value)
XLogRecPtr
XLogGetFakeLSN(Relation rel)
{
- if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
+ rel->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
{
/*
* Temporary relations are only accessible in our session, so a simple
diff --git a/src/backend/catalog/global_temp.c b/src/backend/catalog/global_temp.c
index c2195dce2e0..3863cb12837 100644
--- a/src/backend/catalog/global_temp.c
+++ b/src/backend/catalog/global_temp.c
@@ -53,8 +53,10 @@
*/
#include "postgres.h"
+#include "access/amapi.h"
#include "access/genam.h"
#include "access/parallel.h"
+#include "access/table.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "access/xlogutils.h"
@@ -849,6 +851,28 @@ InitGlobalTempRelation(Relation relation)
if (relation->rd_rel->reloncommit == RELONCOMMIT_DELETE_ROWS)
register_on_commit_action(relation->rd_id, ONCOMMIT_DELETE_ROWS);
+
+ /*
+ * If it's an index, build an empty index in the main fork.
+ *
+ * If the table is not empty (can happen if another session added the
+ * index after we populated the table), then mark it as invalid. The
+ * user will need to do a REINDEX to build it.
+ */
+ if (relation->rd_rel->relkind == RELKIND_INDEX)
+ {
+ Relation heapRelation;
+ BlockNumber nblocks;
+
+ relation->rd_indam->ambuildempty(relation, MAIN_FORKNUM);
+
+ heapRelation = table_open(relation->rd_index->indrelid, AccessShareLock);
+ nblocks = RelationGetNumberOfBlocks(heapRelation);
+ table_close(heapRelation, AccessShareLock);
+
+ if (nblocks > 0)
+ relation->rd_index->indisvalid = false;
+ }
}
/* The remaining initialization works as if we had created it locally */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index ffc7f6c254e..330c7756758 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2157,7 +2157,8 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode)
* lock (see comments in RemoveRelations), and a non-concurrent DROP is
* more efficient.
*/
- Assert(get_rel_persistence(indexId) != RELPERSISTENCE_TEMP ||
+ Assert((get_rel_persistence(indexId) != RELPERSISTENCE_TEMP &&
+ get_rel_persistence(indexId) != RELPERSISTENCE_GLOBAL_TEMP) ||
(!concurrent && !concurrent_lock_mode));
/*
@@ -3113,7 +3114,7 @@ index_build(Relation heapRelation,
{
smgrcreate(RelationGetSmgr(indexRelation), INIT_FORKNUM, false);
log_smgrcreate(&indexRelation->rd_locator, INIT_FORKNUM);
- indexRelation->rd_indam->ambuildempty(indexRelation);
+ indexRelation->rd_indam->ambuildempty(indexRelation, INIT_FORKNUM);
}
/*
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 713bb5d10f1..8baac58ef81 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -626,8 +626,16 @@ DefineIndex(ParseState *pstate,
* is more efficient. Do this before any use of the concurrent option is
* done.
*/
- if (stmt->concurrent && get_rel_persistence(tableId) != RELPERSISTENCE_TEMP)
- concurrent = true;
+ if (stmt->concurrent)
+ {
+ char relpersistence = get_rel_persistence(tableId);
+
+ if (relpersistence == RELPERSISTENCE_TEMP ||
+ relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+ concurrent = false;
+ else
+ concurrent = true;
+ }
else
concurrent = false;
@@ -3106,7 +3114,8 @@ ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLev
if (relkind == RELKIND_PARTITIONED_INDEX)
ReindexPartitions(stmt, indOid, params, isTopLevel);
else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
- persistence != RELPERSISTENCE_TEMP)
+ persistence != RELPERSISTENCE_TEMP &&
+ persistence != RELPERSISTENCE_GLOBAL_TEMP)
ReindexRelationConcurrently(stmt, indOid, params);
else
{
@@ -3202,6 +3211,7 @@ static Oid
ReindexTable(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
{
Oid heapOid;
+ char persistence;
bool result;
const RangeVar *relation = stmt->relation;
@@ -3219,10 +3229,13 @@ ReindexTable(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLev
0,
RangeVarCallbackMaintainsTable, NULL);
+ persistence = get_rel_persistence(heapOid);
+
if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
ReindexPartitions(stmt, heapOid, params, isTopLevel);
else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
- get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
+ persistence != RELPERSISTENCE_TEMP &&
+ persistence != RELPERSISTENCE_GLOBAL_TEMP)
{
result = ReindexRelationConcurrently(stmt, heapOid, params);
@@ -3646,7 +3659,8 @@ ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids, const Reind
Assert(!RELKIND_HAS_PARTITIONS(relkind));
if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
- relpersistence != RELPERSISTENCE_TEMP)
+ relpersistence != RELPERSISTENCE_TEMP &&
+ relpersistence != RELPERSISTENCE_GLOBAL_TEMP)
{
ReindexParams newparams = *params;
@@ -4088,7 +4102,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
idx->amId = indexRel->rd_rel->relam;
/* This function shouldn't be called for temporary relations. */
- if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
+ indexRel->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
elog(ERROR, "cannot reindex a temporary table concurrently");
pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, idx->tableId);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 846bd7c1fbe..495e48be638 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7364,6 +7364,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid)
* safe.
*/
if (heap->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
+ heap->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP ||
!is_parallel_safe(root, (Node *) RelationGetIndexExpressions(index)) ||
!is_parallel_safe(root, (Node *) RelationGetIndexPredicate(index)))
{
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index d7fb16d3c85..70da07d2369 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -655,6 +655,8 @@ get_parallel_tables_list(PGconn *conn, ReindexType type,
CppAsString2(RELKIND_MATVIEW) ")\n"
" AND c.relpersistence != "
CppAsString2(RELPERSISTENCE_TEMP) "\n"
+ " AND c.relpersistence != "
+ CppAsString2(RELPERSISTENCE_GLOBAL_TEMP) "\n"
" ORDER BY c.relpages DESC;");
break;
@@ -678,6 +680,8 @@ get_parallel_tables_list(PGconn *conn, ReindexType type,
CppAsString2(RELKIND_MATVIEW) ")\n"
" AND c.relpersistence != "
CppAsString2(RELPERSISTENCE_TEMP) "\n"
+ " AND c.relpersistence != "
+ CppAsString2(RELPERSISTENCE_GLOBAL_TEMP) "\n"
" AND ns.nspname IN (");
for (cell = user_list->head; cell; cell = cell->next)
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 79240333530..b3b9cb6039b 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -15,6 +15,7 @@
#include "access/cmptype.h"
#include "access/genam.h"
#include "access/stratnum.h"
+#include "common/relpath.h"
#include "nodes/nodes.h"
#include "nodes/pg_list.h"
@@ -115,7 +116,8 @@ typedef IndexBuildResult *(*ambuild_function) (Relation heapRelation,
IndexInfo *indexInfo);
/* build empty index */
-typedef void (*ambuildempty_function) (Relation indexRelation);
+typedef void (*ambuildempty_function) (Relation indexRelation,
+ ForkNumber forknum);
/* insert this tuple */
typedef bool (*aminsert_function) (Relation indexRelation,
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index e17c7fee511..10137275c4c 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -90,7 +90,7 @@ extern BrinDesc *brin_build_desc(Relation rel);
extern void brin_free_desc(BrinDesc *bdesc);
extern IndexBuildResult *brinbuild(Relation heap, Relation index,
IndexInfo *indexInfo);
-extern void brinbuildempty(Relation index);
+extern void brinbuildempty(Relation index, ForkNumber forknum);
extern bool brininsert(Relation idxRel, Datum *values, bool *nulls,
ItemPointer heaptid, Relation heapRel,
IndexUniqueCheck checkUnique,
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 6725ee2839f..1ce7bffbe70 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -110,7 +110,7 @@ extern char *ginbuildphasename(int64 phasenum);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void ginbuildempty(Relation index);
+extern void ginbuildempty(Relation index, ForkNumber forknum);
extern bool gininsert(Relation index, Datum *values, bool *isnull,
ItemPointer ht_ctid, Relation heapRel,
IndexUniqueCheck checkUnique,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 44514f1cb8d..334c3de4541 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -399,7 +399,7 @@ typedef struct GiSTOptions
} GiSTOptions;
/* gist.c */
-extern void gistbuildempty(Relation index);
+extern void gistbuildempty(Relation index, ForkNumber forknum);
extern bool gistinsert(Relation r, Datum *values, bool *isnull,
ItemPointer ht_ctid, Relation heapRel,
IndexUniqueCheck checkUnique,
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index a8702f0e5ea..a719d374af1 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -362,7 +362,7 @@ typedef struct HashOptions
extern IndexBuildResult *hashbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void hashbuildempty(Relation index);
+extern void hashbuildempty(Relation index, ForkNumber forknum);
extern bool hashinsert(Relation rel, Datum *values, bool *isnull,
ItemPointer ht_ctid, Relation heapRel,
IndexUniqueCheck checkUnique,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 3097e9bb1af..8053dfe3d6a 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1151,7 +1151,7 @@ typedef struct BTOptions
/*
* external entry points for btree, in nbtree.c
*/
-extern void btbuildempty(Relation index);
+extern void btbuildempty(Relation index, ForkNumber forknum);
extern bool btinsert(Relation rel, Datum *values, bool *isnull,
ItemPointer ht_ctid, Relation heapRel,
IndexUniqueCheck checkUnique,
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 083d93f8ffd..392c6538cbf 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -195,7 +195,7 @@ extern bytea *spgoptions(Datum reloptions, bool validate);
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void spgbuildempty(Relation index);
+extern void spgbuildempty(Relation index, ForkNumber forknum);
extern bool spginsert(Relation index, Datum *values, bool *isnull,
ItemPointer ht_ctid, Relation heapRel,
IndexUniqueCheck checkUnique,
diff --git a/src/test/isolation/expected/global-temp.out b/src/test/isolation/expected/global-temp.out
index 5ac8eccbba6..3a6af7eb23e 100644
--- a/src/test/isolation/expected/global-temp.out
+++ b/src/test/isolation/expected/global-temp.out
@@ -250,3 +250,147 @@ step create1dr: CREATE GLOBAL TEMP TABLE tmp2 (key int, val text) ON COMMIT DELE
step ins1_2: INSERT INTO tmp2 VALUES (1, 's1');
step ins2_2: INSERT INTO tmp2 VALUES (1, 's2');
step drop1: DROP TABLE tmp2;
+
+starting permutation: ins1 idx1 sel1_idx ins2 sel2_idx
+step ins1: INSERT INTO tmp VALUES (1, 's1');
+step idx1: CREATE INDEX tmp_val_idx ON tmp(val);
+step sel1_idx:
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's1';
+ SELECT * FROM tmp WHERE val = 's1';
+
+QUERY PLAN
+-----------------------------------
+Index Scan using tmp_val_idx on tmp
+ Index Cond: (val = 's1'::text)
+(2 rows)
+
+key|val
+---+---
+ 1|s1
+(1 row)
+
+step ins2: INSERT INTO tmp VALUES (1, 's2');
+step sel2_idx:
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's2';
+ SELECT * FROM tmp WHERE val = 's2';
+
+QUERY PLAN
+-----------------------------------
+Index Scan using tmp_val_idx on tmp
+ Index Cond: (val = 's2'::text)
+(2 rows)
+
+key|val
+---+---
+ 1|s2
+(1 row)
+
+
+starting permutation: ins1 ins2 idx1 sel1_idx sel2_idx
+step ins1: INSERT INTO tmp VALUES (1, 's1');
+step ins2: INSERT INTO tmp VALUES (1, 's2');
+step idx1: CREATE INDEX tmp_val_idx ON tmp(val);
+step sel1_idx:
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's1';
+ SELECT * FROM tmp WHERE val = 's1';
+
+QUERY PLAN
+-----------------------------------
+Index Scan using tmp_val_idx on tmp
+ Index Cond: (val = 's1'::text)
+(2 rows)
+
+key|val
+---+---
+ 1|s1
+(1 row)
+
+step sel2_idx:
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's2';
+ SELECT * FROM tmp WHERE val = 's2';
+
+QUERY PLAN
+----------------------------
+Seq Scan on tmp
+ Disabled: true
+ Filter: (val = 's2'::text)
+(3 rows)
+
+key|val
+---+---
+ 1|s2
+(1 row)
+
+
+starting permutation: ins1 ins2 idx1 sel1_idx sel2_idx reidx2 sel2_idx
+step ins1: INSERT INTO tmp VALUES (1, 's1');
+step ins2: INSERT INTO tmp VALUES (1, 's2');
+step idx1: CREATE INDEX tmp_val_idx ON tmp(val);
+step sel1_idx:
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's1';
+ SELECT * FROM tmp WHERE val = 's1';
+
+QUERY PLAN
+-----------------------------------
+Index Scan using tmp_val_idx on tmp
+ Index Cond: (val = 's1'::text)
+(2 rows)
+
+key|val
+---+---
+ 1|s1
+(1 row)
+
+step sel2_idx:
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's2';
+ SELECT * FROM tmp WHERE val = 's2';
+
+QUERY PLAN
+----------------------------
+Seq Scan on tmp
+ Disabled: true
+ Filter: (val = 's2'::text)
+(3 rows)
+
+key|val
+---+---
+ 1|s2
+(1 row)
+
+step reidx2: REINDEX INDEX tmp_val_idx;
+step sel2_idx:
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's2';
+ SELECT * FROM tmp WHERE val = 's2';
+
+QUERY PLAN
+-----------------------------------
+Index Scan using tmp_val_idx on tmp
+ Index Cond: (val = 's2'::text)
+(2 rows)
+
+key|val
+---+---
+ 1|s2
+(1 row)
+
diff --git a/src/test/isolation/specs/global-temp.spec b/src/test/isolation/specs/global-temp.spec
index db242426fe5..5136230e859 100644
--- a/src/test/isolation/specs/global-temp.spec
+++ b/src/test/isolation/specs/global-temp.spec
@@ -1,9 +1,9 @@
# Test global temporary relations
setup {
- CREATE GLOBAL TEMP TABLE tmp (key int, val text);
+ CREATE GLOBAL TEMP TABLE tmp (key int PRIMARY KEY, val text);
- CREATE GLOBAL TEMP TABLE tmp_parted (key int, val text) PARTITION BY LIST (key);
+ CREATE GLOBAL TEMP TABLE tmp_parted (key int PRIMARY KEY, val text) PARTITION BY LIST (key);
CREATE GLOBAL TEMP TABLE tmp_p1 PARTITION OF tmp_parted FOR VALUES IN (1);
CREATE GLOBAL TEMP TABLE tmp_p2 PARTITION OF tmp_parted FOR VALUES IN ((2), (3));
}
@@ -25,6 +25,14 @@ step alter1a { ALTER TABLE tmp2 ALTER COLUMN key SET DATA TYPE numeric; }
step alter1b { ALTER TABLE tmp2 ALTER COLUMN val SET NOT NULL; }
step seltype1 { SELECT key, pg_typeof(key), val FROM tmp2; }
step drop1 { DROP TABLE tmp2; }
+step idx1 { CREATE INDEX tmp_val_idx ON tmp(val); }
+step sel1_idx {
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's1';
+ SELECT * FROM tmp WHERE val = 's1';
+}
session s2
step b2 { BEGIN; }
@@ -40,6 +48,14 @@ step sp2 { SAVEPOINT sp; }
step rsp2 { ROLLBACK TO SAVEPOINT sp; }
step ins2_2 { INSERT INTO tmp2 VALUES (1, 's2'); }
step seltype2 { SELECT key, pg_typeof(key), val FROM tmp2; }
+step sel2_idx {
+ SET enable_seqscan = off;
+ SET enable_bitmapscan = off;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM tmp WHERE val = 's2';
+ SELECT * FROM tmp WHERE val = 's2';
+}
+step reidx2 { REINDEX INDEX tmp_val_idx; }
# Basic effects
permutation ins1 ins2 sel1 sel2
@@ -58,3 +74,8 @@ permutation create1 ins1_2 ins2_2 alter1a alter1b seltype1 seltype2 drop1
# Test DROP with ON COMMIT DELETE ROWS
permutation create1dr ins1_2 ins2_2 drop1 create1dr ins1_2 ins2_2 drop1
+
+# Test val index
+permutation ins1 idx1 sel1_idx ins2 sel2_idx
+permutation ins1 ins2 idx1 sel1_idx sel2_idx
+permutation ins1 ins2 idx1 sel1_idx sel2_idx reidx2 sel2_idx
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 31f8d2b8161..65b116280bf 100644
--- a/src/test/modules/dummy_index_am/dummy_index_am.c
+++ b/src/test/modules/dummy_index_am/dummy_index_am.c
@@ -166,10 +166,10 @@ dibuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * Build an empty index for the initialization fork.
+ * Build an empty index for the specified fork.
*/
static void
-dibuildempty(Relation index)
+dibuildempty(Relation index, ForkNumber forknum)
{
/* No need to build an init fork for a dummy index */
}
diff --git a/src/test/regress/expected/global_temp.out b/src/test/regress/expected/global_temp.out
index 43e9116f7d5..1b2450a49b5 100644
--- a/src/test/regress/expected/global_temp.out
+++ b/src/test/regress/expected/global_temp.out
@@ -13,7 +13,7 @@ CREATE GLOBAL TEMP TABLE pg_temp.tmp1 (a int); -- fail
ERROR: cannot create global temporary relation in temporary schema
LINE 1: CREATE GLOBAL TEMP TABLE pg_temp.tmp1 (a int);
^
-CREATE GLOBAL TEMP TABLE tmp1 (a int);
+CREATE GLOBAL TEMP TABLE tmp1 (a int PRIMARY KEY, b text);
CREATE SCHEMA global_temp_xxx CREATE GLOBAL TEMP TABLE tmp2 (a int);
CREATE SCHEMA global_temp_yyy;
CREATE GLOBAL TEMP TABLE global_temp_yyy.tmp3 (a int);
@@ -21,15 +21,18 @@ CREATE GLOBAL TEMP TABLE global_temp_yyy.tmp3 (a int);
Global temporary table "global_temp_tests.tmp1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
- a | integer | | |
+ a | integer | | not null |
+ b | text | | |
+Indexes:
+ "tmp1_pkey" PRIMARY KEY, btree (a)
\dt+ global_temp_*.tmp*
- List of tables
- Schema | Name | Type | Owner | Persistence | Size | Description
--------------------+------+-------+--------------------------+------------------+---------+-------------
- global_temp_tests | tmp1 | table | regress_global_temp_user | global temporary | 0 bytes |
- global_temp_xxx | tmp2 | table | regress_global_temp_user | global temporary | 0 bytes |
- global_temp_yyy | tmp3 | table | regress_global_temp_user | global temporary | 0 bytes |
+ List of tables
+ Schema | Name | Type | Owner | Persistence | Size | Description
+-------------------+------+-------+--------------------------+------------------+------------+-------------
+ global_temp_tests | tmp1 | table | regress_global_temp_user | global temporary | 8192 bytes |
+ global_temp_xxx | tmp2 | table | regress_global_temp_user | global temporary | 0 bytes |
+ global_temp_yyy | tmp3 | table | regress_global_temp_user | global temporary | 0 bytes |
(3 rows)
-- Information schema
@@ -49,20 +52,59 @@ NOTICE: drop cascades to table global_temp_xxx.tmp2
DROP SCHEMA global_temp_yyy CASCADE;
NOTICE: drop cascades to table global_temp_yyy.tmp3
-- Basic tests
-INSERT INTO tmp1 VALUES (1);
+INSERT INTO tmp1 VALUES (1, 'xxx');
SELECT * FROM tmp1;
- a
----
- 1
+ a | b
+---+-----
+ 1 | xxx
(1 row)
\c
SET search_path = global_temp_tests;
SELECT * FROM tmp1;
- a
----
+ a | b
+---+---
(0 rows)
+-- Test index
+INSERT INTO tmp1 VALUES (1, 'xxx');
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM tmp1 WHERE a = 1;
+ QUERY PLAN
+------------------------------------
+ Index Scan using tmp1_pkey on tmp1
+ Index Cond: (a = 1)
+(2 rows)
+
+SELECT * FROM tmp1 WHERE a = 1;
+ a | b
+---+-----
+ 1 | xxx
+(1 row)
+
+RESET enable_seqscan;
+-- Test concurrent index build -- CONCURRENTLY is ignored with temp tables
+CREATE INDEX CONCURRENTLY tmp1_b_idx ON tmp1(b);
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM tmp1 WHERE b = 'xxx';
+ QUERY PLAN
+-------------------------------------
+ Index Scan using tmp1_b_idx on tmp1
+ Index Cond: (b = 'xxx'::text)
+(2 rows)
+
+SELECT * FROM tmp1 WHERE b = 'xxx';
+ a | b
+---+-----
+ 1 | xxx
+(1 row)
+
+RESET enable_seqscan;
+REINDEX INDEX CONCURRENTLY tmp1_b_idx;
+REINDEX TABLE CONCURRENTLY tmp1;
+DROP INDEX CONCURRENTLY tmp1_b_idx;
-- Test ON COMMIT DELETE ROWS
CREATE GLOBAL TEMP TABLE tmp2 (a int) ON COMMIT DELETE ROWS;
BEGIN;
@@ -103,8 +145,8 @@ ERROR: ON COMMIT DROP cannot be used on global temporary tables
-- Two-phase commit not allowed with global temp tables
BEGIN;
SELECT * FROM tmp1;
- a
----
+ a | b
+---+---
(0 rows)
PREPARE TRANSACTION 'twophase'; -- fail
@@ -147,11 +189,33 @@ DROP TABLE tmp2;
-- Test foreign keys
CREATE TABLE perm_pk_rel (a int PRIMARY KEY);
CREATE TEMP TABLE temp_pk_rel (a int PRIMARY KEY);
+CREATE GLOBAL TEMP TABLE gtemp_pk_rel (a int PRIMARY KEY);
+CREATE TABLE tmp2 (a int REFERENCES gtemp_pk_rel); -- fail
+ERROR: constraints on permanent tables may reference only permanent tables
+CREATE TEMP TABLE tmp2 (a int REFERENCES gtemp_pk_rel); -- fail
+ERROR: constraints on local temporary tables may reference only local temporary tables
CREATE GLOBAL TEMP TABLE tmp2 (a int REFERENCES perm_pk_rel); -- fail
ERROR: constraints on global temporary tables may reference only global temporary tables
CREATE GLOBAL TEMP TABLE tmp2 (a int REFERENCES temp_pk_rel); -- fail
ERROR: constraints on global temporary tables may reference only global temporary tables
-DROP TABLE perm_pk_rel, temp_pk_rel;
+CREATE GLOBAL TEMP TABLE tmp2 (a int REFERENCES gtemp_pk_rel);
+INSERT INTO gtemp_pk_rel VALUES (1);
+INSERT INTO tmp2 VALUES (1);
+INSERT INTO tmp2 VALUES (2); -- fail
+ERROR: insert or update on table "tmp2" violates foreign key constraint "tmp2_a_fkey"
+DETAIL: Key (a)=(2) is not present in table "gtemp_pk_rel".
+DELETE FROM gtemp_pk_rel WHERE a = 1; -- fail
+ERROR: update or delete on table "gtemp_pk_rel" violates foreign key constraint "tmp2_a_fkey" on table "tmp2"
+DETAIL: Key (a)=(1) is still referenced from table "tmp2".
+ALTER TABLE tmp2 DROP CONSTRAINT tmp2_a_fkey;
+ALTER TABLE tmp2 ADD FOREIGN KEY (a) REFERENCES gtemp_pk_rel ON DELETE CASCADE;
+DELETE FROM gtemp_pk_rel WHERE a = 1;
+SELECT * FROM tmp2;
+ a
+---
+(0 rows)
+
+DROP TABLE perm_pk_rel, temp_pk_rel, tmp2;
-- Test ALTER TABLE ... SET TABLESPACE
CREATE GLOBAL TEMP TABLE tmp2 (a int);
INSERT INTO tmp2 VALUES (1);
@@ -204,85 +268,85 @@ DETAIL: tablespace for table tmp2
DROP TABLE tmp2;
DROP TABLESPACE regress_temp_test_tablespace;
-- Test TRUNCATE
-INSERT INTO tmp1 VALUES (1);
+INSERT INTO tmp1 VALUES (1, 'xxx');
BEGIN;
TRUNCATE tmp1;
SELECT * FROM tmp1;
- a
----
+ a | b
+---+---
(0 rows)
ROLLBACK;
SELECT * FROM tmp1;
- a
----
- 1
+ a | b
+---+-----
+ 1 | xxx
(1 row)
BEGIN;
SAVEPOINT sp1;
TRUNCATE tmp1;
SELECT * FROM tmp1;
- a
----
+ a | b
+---+---
(0 rows)
RELEASE sp1;
SELECT * FROM tmp1;
- a
----
+ a | b
+---+---
(0 rows)
ROLLBACK;
SELECT * FROM tmp1;
- a
----
- 1
+ a | b
+---+-----
+ 1 | xxx
(1 row)
BEGIN;
SAVEPOINT sp1;
TRUNCATE tmp1;
SELECT * FROM tmp1;
- a
----
+ a | b
+---+---
(0 rows)
ROLLBACK TO sp1;
SELECT * FROM tmp1;
- a
----
- 1
+ a | b
+---+-----
+ 1 | xxx
(1 row)
COMMIT;
SELECT * FROM tmp1;
- a
----
- 1
+ a | b
+---+-----
+ 1 | xxx
(1 row)
TRUNCATE tmp1;
SELECT * FROM tmp1;
- a
----
+ a | b
+---+---
(0 rows)
-- Test view creation
-INSERT INTO tmp1 VALUES (1);
+INSERT INTO tmp1 VALUES (1, 'xxx');
CREATE VIEW v AS SELECT * FROM tmp1;
SELECT * FROM v;
- a
----
- 1
+ a | b
+---+-----
+ 1 | xxx
(1 row)
DROP VIEW v;
CREATE TEMP VIEW v AS SELECT * FROM tmp1;
SELECT * FROM v;
- a
----
- 1
+ a | b
+---+-----
+ 1 | xxx
(1 row)
DROP VIEW v;
diff --git a/src/test/regress/sql/global_temp.sql b/src/test/regress/sql/global_temp.sql
index 139e0f74b4b..7255a35f2d4 100644
--- a/src/test/regress/sql/global_temp.sql
+++ b/src/test/regress/sql/global_temp.sql
@@ -11,7 +11,7 @@ SET ROLE regress_global_temp_user;
-- Test table creation
CREATE GLOBAL TEMP TABLE pg_temp.tmp1 (a int); -- fail
-CREATE GLOBAL TEMP TABLE tmp1 (a int);
+CREATE GLOBAL TEMP TABLE tmp1 (a int PRIMARY KEY, b text);
CREATE SCHEMA global_temp_xxx CREATE GLOBAL TEMP TABLE tmp2 (a int);
CREATE SCHEMA global_temp_yyy;
CREATE GLOBAL TEMP TABLE global_temp_yyy.tmp3 (a int);
@@ -29,12 +29,31 @@ DROP SCHEMA global_temp_xxx CASCADE;
DROP SCHEMA global_temp_yyy CASCADE;
-- Basic tests
-INSERT INTO tmp1 VALUES (1);
+INSERT INTO tmp1 VALUES (1, 'xxx');
SELECT * FROM tmp1;
\c
SET search_path = global_temp_tests;
SELECT * FROM tmp1;
+-- Test index
+INSERT INTO tmp1 VALUES (1, 'xxx');
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM tmp1 WHERE a = 1;
+SELECT * FROM tmp1 WHERE a = 1;
+RESET enable_seqscan;
+
+-- Test concurrent index build -- CONCURRENTLY is ignored with temp tables
+CREATE INDEX CONCURRENTLY tmp1_b_idx ON tmp1(b);
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM tmp1 WHERE b = 'xxx';
+SELECT * FROM tmp1 WHERE b = 'xxx';
+RESET enable_seqscan;
+REINDEX INDEX CONCURRENTLY tmp1_b_idx;
+REINDEX TABLE CONCURRENTLY tmp1;
+DROP INDEX CONCURRENTLY tmp1_b_idx;
+
-- Test ON COMMIT DELETE ROWS
CREATE GLOBAL TEMP TABLE tmp2 (a int) ON COMMIT DELETE ROWS;
BEGIN;
@@ -84,9 +103,21 @@ DROP TABLE tmp2;
-- Test foreign keys
CREATE TABLE perm_pk_rel (a int PRIMARY KEY);
CREATE TEMP TABLE temp_pk_rel (a int PRIMARY KEY);
+CREATE GLOBAL TEMP TABLE gtemp_pk_rel (a int PRIMARY KEY);
+CREATE TABLE tmp2 (a int REFERENCES gtemp_pk_rel); -- fail
+CREATE TEMP TABLE tmp2 (a int REFERENCES gtemp_pk_rel); -- fail
CREATE GLOBAL TEMP TABLE tmp2 (a int REFERENCES perm_pk_rel); -- fail
CREATE GLOBAL TEMP TABLE tmp2 (a int REFERENCES temp_pk_rel); -- fail
-DROP TABLE perm_pk_rel, temp_pk_rel;
+CREATE GLOBAL TEMP TABLE tmp2 (a int REFERENCES gtemp_pk_rel);
+INSERT INTO gtemp_pk_rel VALUES (1);
+INSERT INTO tmp2 VALUES (1);
+INSERT INTO tmp2 VALUES (2); -- fail
+DELETE FROM gtemp_pk_rel WHERE a = 1; -- fail
+ALTER TABLE tmp2 DROP CONSTRAINT tmp2_a_fkey;
+ALTER TABLE tmp2 ADD FOREIGN KEY (a) REFERENCES gtemp_pk_rel ON DELETE CASCADE;
+DELETE FROM gtemp_pk_rel WHERE a = 1;
+SELECT * FROM tmp2;
+DROP TABLE perm_pk_rel, temp_pk_rel, tmp2;
-- Test ALTER TABLE ... SET TABLESPACE
CREATE GLOBAL TEMP TABLE tmp2 (a int);
@@ -119,7 +150,7 @@ DROP TABLE tmp2;
DROP TABLESPACE regress_temp_test_tablespace;
-- Test TRUNCATE
-INSERT INTO tmp1 VALUES (1);
+INSERT INTO tmp1 VALUES (1, 'xxx');
BEGIN;
TRUNCATE tmp1;
SELECT * FROM tmp1;
@@ -148,7 +179,7 @@ TRUNCATE tmp1;
SELECT * FROM tmp1;
-- Test view creation
-INSERT INTO tmp1 VALUES (1);
+INSERT INTO tmp1 VALUES (1, 'xxx');
CREATE VIEW v AS SELECT * FROM tmp1;
SELECT * FROM v;
DROP VIEW v;
--
2.51.0