diff --git a/src/postgres/src/backend/access/transam/xact.c b/src/postgres/src/backend/access/transam/xact.c index c06b1524e7bc..a2c9e0459b6f 100644 --- a/src/postgres/src/backend/access/transam/xact.c +++ b/src/postgres/src/backend/access/transam/xact.c @@ -218,8 +218,8 @@ typedef struct TransactionStateData bool ybDataSentForCurrQuery; /* Whether any data has been sent to * frontend as part of current query's * execution */ - bool isYBTxnWithPostgresRel; /* does the current transaction - * operate on a postgres table? */ + uint8 ybPostgresOpsInTxn; /* An OR'ed list of operations performed on + * postgres (temp) tables by current txn. */ List *YBPostponedDdlOps; /* We postpone execution of non-revertable * DocDB operations (e.g. drop * table/index) until the rest of the txn @@ -263,7 +263,7 @@ static TransactionStateData TopTransactionStateData = { .topXidLogged = false, .ybDataSent = false, .ybDataSentForCurrQuery = false, - .isYBTxnWithPostgresRel = false, + .ybPostgresOpsInTxn = 0, .YBPostponedDdlOps = NULL, }; @@ -1384,9 +1384,18 @@ RecordTransactionCommit(void) bool RelcacheInitFileInval = false; bool wrote_xlog; - if (IsYugaByteEnabled() && !IsCurrentTxnWithPGRel()) + if (IsYugaByteEnabled() && !YbGetPgOpsInCurrentTxn()) + return InvalidTransactionId; + /* TODO(kramanathan): The bitwise flags returned by YbGetPgOpsInCurrentTxn() + * are not independent of each other. The flag YB_TXN_USES_REFRESH_MAT_VIEW_CONCURRENTLY + * only requires a subset of YB_TXN_USES_TEMPORARY_RELATIONS's operations at + * commit. Logical equality is used intentionally here to exit early. + */ + else if (IsYugaByteEnabled() && + YbGetPgOpsInCurrentTxn() == YB_TXN_USES_REFRESH_MAT_VIEW_CONCURRENTLY) { - return latestXid; + nchildren = xactGetCommittedChildren(&children); + return TransactionIdLatest(xid, nchildren, children); } /* @@ -2086,7 +2095,7 @@ static void YBStartTransaction(TransactionState s) { elog(DEBUG2, "YBStartTransaction"); - s->isYBTxnWithPostgresRel = !IsYugaByteEnabled(); + s->ybPostgresOpsInTxn = 0; s->ybDataSent = false; s->ybDataSentForCurrQuery = false; s->YBPostponedDdlOps = NULL; @@ -3292,25 +3301,27 @@ RestoreTransactionCharacteristics(const SavedTransactionCharacteristics *s) } void -SetTxnWithPGRel(void) +YbSetTxnWithPgOps(uint8 pg_op_type) { TransactionState s = CurrentTransactionState; /* - * YB doesn't support subtransactions for now and only top level transaction is committed. - * So the isYBTxnWithPostgresRel flag must be set on current and all top level transactions. + * TODO(kramanathan): This flag needs to be rolled back appropriately when + * rolling back a sub-transaction. Currently, the flag(s) being set is + * persisted until commit/abort, even if the transaction at commit does not + * end up performing the operation whose flag is being set. */ - while (s != NULL && !s->isYBTxnWithPostgresRel) + while (s != NULL) { - s->isYBTxnWithPostgresRel = true; + s->ybPostgresOpsInTxn |= pg_op_type; s = s->parent; } } -bool -IsCurrentTxnWithPGRel(void) +uint8 +YbGetPgOpsInCurrentTxn(void) { - return CurrentTransactionState->isYBTxnWithPostgresRel; + return CurrentTransactionState->ybPostgresOpsInTxn; } /* @@ -6711,13 +6722,6 @@ YBClearDdlHandles() CurrentTransactionState->YBPostponedDdlOps = NULL; } -void -YbClearCurrentTransactionId() -{ - CurrentTransactionState->fullTransactionId = InvalidFullTransactionId; - MyProc->xid = InvalidTransactionId; -} - /* * YbClearParallelContexts * Clean up parallel contexts as a part of transparent query restart. diff --git a/src/postgres/src/backend/commands/copy.c b/src/postgres/src/backend/commands/copy.c index 51a9cf357d1f..2bfdb2afc87e 100644 --- a/src/postgres/src/backend/commands/copy.c +++ b/src/postgres/src/backend/commands/copy.c @@ -130,7 +130,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && IsYugaByteEnabled()) - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); relid = RelationGetRelid(rel); diff --git a/src/postgres/src/backend/commands/createas.c b/src/postgres/src/backend/commands/createas.c index 2dd3416efde0..9f51e1d27f83 100644 --- a/src/postgres/src/backend/commands/createas.c +++ b/src/postgres/src/backend/commands/createas.c @@ -602,7 +602,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) */ if (myState->rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && IsYugaByteEnabled()) - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); if (IsYBRelation(myState->rel)) { diff --git a/src/postgres/src/backend/commands/matview.c b/src/postgres/src/backend/commands/matview.c index 0ce51cd9d5bb..2bf1ad706da2 100644 --- a/src/postgres/src/backend/commands/matview.c +++ b/src/postgres/src/backend/commands/matview.c @@ -533,14 +533,6 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self) myState->output_cid, myState->ti_options, myState->bistate); - - /* - * In this case when the transient relation is a temporary relation, heap_insert - * will set a transaction id. However, we must clear this transaction id, since - * REFRESH matview should not invoke any TxnWithPGRel code paths - * (that are used for temp tables). - */ - YbClearCurrentTransactionId(); } /* We know this is a newly created relation, so there are no indexes */ diff --git a/src/postgres/src/backend/commands/prepare.c b/src/postgres/src/backend/commands/prepare.c index 0001455e92e8..62dd0dc4cc45 100644 --- a/src/postgres/src/backend/commands/prepare.c +++ b/src/postgres/src/backend/commands/prepare.c @@ -191,7 +191,7 @@ ExecuteQuery(ParseState *pstate, */ if (entry->plansource->usesPostgresRel) { - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); } /* Evaluate parameters, if any */ diff --git a/src/postgres/src/backend/commands/tablecmds.c b/src/postgres/src/backend/commands/tablecmds.c index 0081af51c216..64d5e44f2389 100644 --- a/src/postgres/src/backend/commands/tablecmds.c +++ b/src/postgres/src/backend/commands/tablecmds.c @@ -5983,7 +5983,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, * determine what rows are visible to a specific transaction. */ if (!IsYBRelationById(tab->relid) && tab->rewrite > 0) - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); /* * If we change column data types, the operation has to be propagated diff --git a/src/postgres/src/backend/executor/spi.c b/src/postgres/src/backend/executor/spi.c index 63b5fc2a699f..afe90e47507c 100644 --- a/src/postgres/src/backend/executor/spi.c +++ b/src/postgres/src/backend/executor/spi.c @@ -2485,7 +2485,7 @@ _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options, */ if (plansource->usesPostgresRel) { - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); } spicallbackarg.query = plansource->query_string; diff --git a/src/postgres/src/backend/parser/analyze.c b/src/postgres/src/backend/parser/analyze.c index 54f005fca1a4..6641b10cee2c 100644 --- a/src/postgres/src/backend/parser/analyze.c +++ b/src/postgres/src/backend/parser/analyze.c @@ -130,7 +130,7 @@ parse_analyze_fixedparams(RawStmt *parseTree, const char *sourceText, pstate->p_target_relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP && IsYugaByteEnabled()) { - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); } if (IsQueryIdEnabled()) @@ -176,7 +176,7 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText, pstate->p_target_relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP && IsYugaByteEnabled()) { - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); } /* make sure all is well with parameter types */ @@ -223,7 +223,7 @@ parse_analyze_withcb(RawStmt *parseTree, const char *sourceText, pstate->p_target_relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP && IsYugaByteEnabled()) { - SetTxnWithPGRel(); + YbSetTxnWithPgOps(YB_TXN_USES_TEMPORARY_RELATIONS); } if (IsQueryIdEnabled()) diff --git a/src/postgres/src/backend/storage/ipc/procarray.c b/src/postgres/src/backend/storage/ipc/procarray.c index f7faafea1eba..0800eed9f376 100644 --- a/src/postgres/src/backend/storage/ipc/procarray.c +++ b/src/postgres/src/backend/storage/ipc/procarray.c @@ -696,7 +696,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid) */ Assert(TransactionIdIsValid(proc->xid)); - if (!IsCurrentTxnWithPGRel()) + if (!YbGetPgOpsInCurrentTxn()) return; /* * If we can immediately acquire ProcArrayLock, we clear our own XID diff --git a/src/postgres/src/backend/utils/cache/plancache.c b/src/postgres/src/backend/utils/cache/plancache.c index cdf7220692ac..477e43d6206b 100644 --- a/src/postgres/src/backend/utils/cache/plancache.c +++ b/src/postgres/src/backend/utils/cache/plancache.c @@ -446,7 +446,7 @@ CompleteCachedPlan(CachedPlanSource *plansource, plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list); /* If the planner txn uses a pg relation, so will the execution txn */ - plansource->usesPostgresRel = IsCurrentTxnWithPGRel(); + plansource->usesPostgresRel = YbGetPgOpsInCurrentTxn() & YB_TXN_USES_TEMPORARY_RELATIONS; MemoryContextSwitchTo(oldcxt); diff --git a/src/postgres/src/backend/utils/misc/pg_yb_utils.c b/src/postgres/src/backend/utils/misc/pg_yb_utils.c index 69e511e2e68a..b75434b018ba 100644 --- a/src/postgres/src/backend/utils/misc/pg_yb_utils.c +++ b/src/postgres/src/backend/utils/misc/pg_yb_utils.c @@ -3096,12 +3096,20 @@ YbGetDdlMode(PlannedStmt *pstmt, ProcessUtilityContext context) is_breaking_change = false; if (stmt->concurrent) + { /* * REFRESH MATERIALIZED VIEW CONCURRENTLY does not need * a catalog version increment as it does not alter any * metadata. The command only performs data changes. */ is_version_increment = false; + /* + * REFRESH MATERIALIZED VIEW CONCURRENTLY uses temp tables + * which generates a PostgreSQL XID. Mark the transaction + * as such, so that it can be handled at commit time. + */ + YbSetTxnWithPgOps(YB_TXN_USES_REFRESH_MAT_VIEW_CONCURRENTLY); + } else /* * REFRESH MATERIALIZED VIEW NONCONCURRENTLY needs a catalog diff --git a/src/postgres/src/include/access/xact.h b/src/postgres/src/include/access/xact.h index 237f3c61dfe2..677c85e708cb 100644 --- a/src/postgres/src/include/access/xact.h +++ b/src/postgres/src/include/access/xact.h @@ -464,8 +464,8 @@ extern void YBInitializeTransaction(void); extern void YBResetTransactionReadPoint(void); extern void YBRestartReadPoint(void); extern void YBCRestartWriteTransaction(void); -extern void SetTxnWithPGRel(void); -extern bool IsCurrentTxnWithPGRel(void); +extern void YbSetTxnWithPgOps(uint8 pg_op_type); +extern uint8 YbGetPgOpsInCurrentTxn(void); extern void StartTransactionCommand(void); extern void SaveTransactionCharacteristics(SavedTransactionCharacteristics *s); extern void RestoreTransactionCharacteristics(const SavedTransactionCharacteristics *s); @@ -568,6 +568,9 @@ extern void YBClearDdlHandles(void); /* * Utility for clearing transaction ID. */ -extern void YbClearCurrentTransactionId(void); extern void YbClearParallelContexts(void); + +#define YB_TXN_USES_REFRESH_MAT_VIEW_CONCURRENTLY 0x0001 +#define YB_TXN_USES_TEMPORARY_RELATIONS 0x0002 + #endif /* XACT_H */ diff --git a/src/postgres/src/test/regress/expected/yb.orig.matview.out b/src/postgres/src/test/regress/expected/yb.orig.matview.out index dac0fa0032a5..4c738d114843 100644 --- a/src/postgres/src/test/regress/expected/yb.orig.matview.out +++ b/src/postgres/src/test/regress/expected/yb.orig.matview.out @@ -1,6 +1,74 @@ -- -- YB tests for materialized views -- +CREATE TABLE t_simple (k INT PRIMARY KEY); +INSERT INTO t_simple (SELECT i FROM generate_series(1, 10) AS i); +-- GH-26205: Test that PostgreSQL XID increases by 1 for a concurrent refresh +-- and remains the same for a non-concurrent refresh. Placing this at the top +-- of the file to ensure that this is close to being the first set of commands +-- that are executed upon cluster creation and there is no variability in the +-- XIDs generated. +CREATE MATERIALIZED VIEW mv_xid_test AS (SELECT * FROM t_simple); +CREATE UNIQUE INDEX ON mv_xid_test (k); +SELECT age('3'::xid); + age +----- + 0 +(1 row) + +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT age('3'::xid); + age +----- + 1 +(1 row) + +REFRESH MATERIALIZED VIEW mv_xid_test; +SELECT age('3'::xid); + age +----- + 1 +(1 row) + +-- A few variations of the above test. +BEGIN ISOLATION LEVEL REPEATABLE READ; +REFRESH MATERIALIZED VIEW mv_xid_test; +INSERT INTO t_simple VALUES (11), (12); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT COUNT(*) FROM mv_xid_test; + count +------- + 10 +(1 row) + +COMMIT; +SELECT age('3'::xid); + age +----- + 2 +(1 row) + +BEGIN ISOLATION LEVEL READ COMMITTED; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +REFRESH MATERIALIZED VIEW mv_xid_test; +INSERT INTO t_simple VALUES (13), (14); +SELECT COUNT(*) FROM mv_xid_test; + count +------- + 12 +(1 row) + +COMMIT; +-- The XID should jump by 2 in Read Committed mode as the concurrent refresh of +-- the matview is run as a sub-transaction. So, an additional XID is assigned to +-- its parent. In release builds where Read Committed isolation is disabled, the +-- XID should increase by 1. +SELECT age('3'::xid); + age +----- + 4 +(1 row) + CREATE TABLE test_yb (col int); INSERT INTO test_yb VALUES (null); CREATE UNLOGGED MATERIALIZED VIEW unlogged_mv_yb AS SELECT * FROM test_yb; -- not supported @@ -492,3 +560,64 @@ DETAIL: Data movement is a long running asynchronous process and can be monitor t | integer | | | j | integer | | | Tablespace: "mv_tblspace2" + +-- Matview with temp tables + transactions +CREATE TEMPORARY TABLE t_temp (a INT, b INT); +BEGIN; +INSERT INTO t_temp VALUES (1, 1), (2, 2); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +-- Rolling back operations on temp tables should also roll back any +-- transactional side effects of the corresponding postgres transaction. +BEGIN; +SAVEPOINT sp1; +INSERT INTO t_temp VALUES (3, 3), (4, 4); +SAVEPOINT sp2; +ROLLBACK TO sp1; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +BEGIN; +SAVEPOINT sp1; +REFRESH MATERIALIZED VIEW mv_xid_test; +SAVEPOINT sp2; +INSERT INTO t_temp VALUES (3, 3), (4, 4); +ROLLBACK TO sp2; +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +-- Matview with prepared statements + temp tables +PREPARE temp_stmt AS INSERT INTO t_temp VALUES (5, 5), (6, 6); +BEGIN; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SAVEPOINT sp1; +EXECUTE temp_stmt; +ROLLBACK TO sp1; +EXECUTE temp_stmt; +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 4 +(1 row) diff --git a/src/postgres/src/test/regress/expected/yb.orig.matview_1.out b/src/postgres/src/test/regress/expected/yb.orig.matview_1.out new file mode 100644 index 000000000000..e496c362eb09 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb.orig.matview_1.out @@ -0,0 +1,625 @@ +-- +-- YB tests for materialized views +-- +CREATE TABLE t_simple (k INT PRIMARY KEY); +INSERT INTO t_simple (SELECT i FROM generate_series(1, 10) AS i); +-- GH-26205: Test that PostgreSQL XID increases by 1 for a concurrent refresh +-- and remains the same for a non-concurrent refresh. Placing this at the top +-- of the file to ensure that this is close to being the first set of commands +-- that are executed upon cluster creation and there is no variability in the +-- XIDs generated. +CREATE MATERIALIZED VIEW mv_xid_test AS (SELECT * FROM t_simple); +CREATE UNIQUE INDEX ON mv_xid_test (k); +SELECT age('3'::xid); + age +----- + 0 +(1 row) + +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT age('3'::xid); + age +----- + 1 +(1 row) + +REFRESH MATERIALIZED VIEW mv_xid_test; +SELECT age('3'::xid); + age +----- + 1 +(1 row) + +-- A few variations of the above test. +BEGIN ISOLATION LEVEL REPEATABLE READ; +REFRESH MATERIALIZED VIEW mv_xid_test; +INSERT INTO t_simple VALUES (11), (12); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT COUNT(*) FROM mv_xid_test; + count +------- + 10 +(1 row) + +COMMIT; +SELECT age('3'::xid); + age +----- + 2 +(1 row) + +BEGIN ISOLATION LEVEL READ COMMITTED; +WARNING: read committed isolation is disabled +DETAIL: Set yb_enable_read_committed_isolation to enable. When disabled, read committed falls back to using repeatable read isolation. +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +REFRESH MATERIALIZED VIEW mv_xid_test; +INSERT INTO t_simple VALUES (13), (14); +SELECT COUNT(*) FROM mv_xid_test; + count +------- + 12 +(1 row) + +COMMIT; +-- The XID should jump by 2 in Read Committed mode as the concurrent refresh of +-- the matview is run as a sub-transaction. So, an additional XID is assigned to +-- its parent. In release builds where Read Committed isolation is disabled, the +-- XID should increase by 1. +SELECT age('3'::xid); + age +----- + 3 +(1 row) + +CREATE TABLE test_yb (col int); +INSERT INTO test_yb VALUES (null); +CREATE UNLOGGED MATERIALIZED VIEW unlogged_mv_yb AS SELECT * FROM test_yb; -- not supported +ERROR: materialized views cannot be unlogged +CREATE MATERIALIZED VIEW mtest_yb AS SELECT * FROM test_yb; +CREATE UNIQUE INDEX ON mtest_yb(col); +REFRESH MATERIALIZED VIEW NONCONCURRENTLY mtest_yb; +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; -- should fail +ERROR: new data for materialized view "mtest_yb" contains rows with all null values +DETAIL: Row: (null) +CREATE TABLE pg_temp_foo (col int); +INSERT INTO pg_temp_foo values (1); +SELECT * FROM pg_temp_foo; + col +----- + 1 +(1 row) + +CREATE TABLE pg_temp__123 (col int); +INSERT INTO pg_temp__123 values (1); +SELECT * from pg_temp__123; + col +----- + 1 +(1 row) + +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view mtest_yb +-- Alter materialized view - rename matview and rename columns +CREATE TABLE test_yb (id int NOT NULL PRIMARY KEY, type text NOT NULL, val numeric NOT NULL); +INSERT INTO test_yb VALUES (1, 'xyz', 2); +CREATE MATERIALIZED VIEW mtest_yb AS SELECT * FROM test_yb; +CREATE UNIQUE INDEX unique_IDX ON mtest_YB(id); +ALTER MATERIALIZED VIEW mtest_yb RENAME TO mtest_yb1; +SELECT * FROM mtest_yb; -- error +ERROR: relation "mtest_yb" does not exist +LINE 1: SELECT * FROM mtest_yb; + ^ +SELECT * from mtest_yb1; -- ok + id | type | val +----+------+----- + 1 | xyz | 2 +(1 row) + +REFRESH MATERIALIZED VIEW mtest_yb1; +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb1; +ALTER MATERIALIZED VIEW mtest_yb1 RENAME TO mtest_yb2; +SELECT * from mtest_yb2; + id | type | val +----+------+----- + 1 | xyz | 2 +(1 row) + +REFRESH MATERIALIZED VIEW mtest_yb2; +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb2; +ALTER MATERIALIZED VIEW mtest_yb2 RENAME val TO total; -- test Alter Rename Column +SELECT * FROM mtest_yb2; + id | type | total +----+------+------- + 1 | xyz | 2 +(1 row) + +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view mtest_yb2 +-- Alter materialized view - change owner +CREATE TABLE test_yb (id int NOT NULL PRIMARY KEY, type text NOT NULL, val numeric NOT NULL); +INSERT INTO test_yb VALUES (1, 'xyz', 2); +CREATE MATERIALIZED VIEW mtest_yb AS SELECT * FROM test_yb; +CREATE UNIQUE INDEX unique_IDX ON mtest_yb(id); +CREATE ROLE test_mv_user; +SET ROLE test_mv_user; +REFRESH MATERIALIZED VIEW mtest_yb; -- error +ERROR: must be owner of materialized view mtest_yb +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; -- error +ERROR: must be owner of materialized view mtest_yb +SET ROLE yugabyte; +ALTER MATERIALIZED VIEW mtest_yb OWNER TO test_mv_user; +REFRESH MATERIALIZED VIEW mtest_yb; -- error +ERROR: permission denied for table test_yb +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; -- error +ERROR: permission denied for table test_yb +ALTER TABLE test_yb OWNER TO test_mv_user; +REFRESH MATERIALIZED VIEW mtest_yb; -- ok +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; -- ok +ALTER MATERIALIZED VIEW mtest_yb OWNER TO SESSION_USER; +ALTER TABLE test_yb OWNER TO SESSION_USER; +REFRESH MATERIALIZED VIEW mtest_yb; -- ok +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; -- ok +ALTER MATERIALIZED VIEW mtest_yb RENAME val TO amt; +ALTER MATERIALIZED VIEW mtest_yb RENAME TO mtest_yb1; +CREATE ROLE test_mv_superuser SUPERUSER; +ALTER MATERIALIZED VIEW mtest_yb1 OWNER TO test_mv_superuser; +REFRESH MATERIALIZED VIEW mtest_yb1; -- ok +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb1; -- ok +ALTER MATERIALIZED VIEW mtest_yb1 OWNER TO CURRENT_USER; +DROP ROLE test_mv_user; +DROP ROLE test_mv_superuser; +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view mtest_yb1 +-- Test special characters in an attribute's name +CREATE TABLE test_yb ("xyzID''\\b" int NOT NULL, "y" int); +INSERT INTO test_yb VALUES (1); +CREATE MATERIALIZED VIEW mtest_yb AS SELECT * FROM test_yb WITH NO DATA; +CREATE UNIQUE INDEX ON mtest_yb("xyzID''\\b"); +REFRESH MATERIALIZED VIEW mtest_yb; +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view mtest_yb +-- Test with special characters in the base relation's name +CREATE TABLE "test_YB''\\b" ("xyzid" int NOT NULL); +INSERT INTO "test_YB''\\b" VALUES (1); +CREATE MATERIALIZED VIEW mtest_yb AS SELECT * FROM "test_YB''\\b" WITH NO DATA; +CREATE UNIQUE INDEX ON mtest_yb("xyzid"); +REFRESH MATERIALIZED VIEW mtest_yb; +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; +DROP TABLE "test_YB''\\b" CASCADE; +NOTICE: drop cascades to materialized view mtest_yb +-- Test with special characters in the matview's name +CREATE TABLE test_yb ("xyzid" int NOT NULL); +INSERT INTO test_yb VALUES (1); +CREATE MATERIALIZED VIEW "mtest_YB''\\b" AS SELECT * FROM test_yb WITH NO DATA; +CREATE UNIQUE INDEX ON mtest_YB("xyzid"); +ERROR: relation "mtest_yb" does not exist +REFRESH MATERIALIZED VIEW mtest_YB; +ERROR: relation "mtest_yb" does not exist +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_YB; +ERROR: relation "mtest_yb" does not exist +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view "mtest_YB''\\b" +-- Test with special characters in the unique index's name +CREATE TABLE test_yb ("xyzid" int NOT NULL); +INSERT INTO test_yb VALUES (1); +CREATE MATERIALIZED VIEW mtest_yb AS SELECT * FROM test_yb WITH NO DATA; +CREATE UNIQUE INDEX "unique_IDX''\\b" ON mtest_YB("xyzid"); +REFRESH MATERIALIZED VIEW mtest_yb; +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view mtest_yb +-- Test with unicode characters +CREATE TABLE test_yb ("U&'\0022hi\0022'" int NOT NULL); +INSERT INTO test_yb VALUES (1); +CREATE MATERIALIZED VIEW mtest_yb AS SELECT * FROM test_yb WITH NO DATA; +CREATE UNIQUE INDEX unique_IDX ON mtest_YB("U&'\0022hi\0022'"); +REFRESH MATERIALIZED VIEW mtest_yb; +REFRESH MATERIALIZED VIEW CONCURRENTLY mtest_yb; +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view mtest_yb +-- Test with unicode characters from table +CREATE TABLE test_yb ("xyzid" int NOT NULL); +INSERT INTO test_yb VALUES (1); +CREATE MATERIALIZED VIEW "mtest_YB''\\b" AS SELECT * FROM test_yb WITH NO DATA; +CREATE UNIQUE INDEX ON "mtest_YB''\\b"("xyzid"); +REFRESH MATERIALIZED VIEW "mtest_YB''\\b"; +REFRESH MATERIALIZED VIEW CONCURRENTLY "mtest_YB''\\b"; +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to materialized view "mtest_YB''\\b" +-- Materialized view of a materialized view +CREATE TABLE test_yb ("xyzid" int NOT NULL); +INSERT INTO test_yb VALUES (1); +CREATE MATERIALIZED VIEW "mtest_YB''\\b" AS SELECT * FROM test_yb WITH NO DATA; +CREATE MATERIALIZED VIEW "mtest_YB''\\b\\b" AS SELECT * FROM "mtest_YB''\\b" WITH NO DATA; +CREATE UNIQUE INDEX ON "mtest_YB''\\b\\b"("xyzid"); +REFRESH MATERIALIZED VIEW "mtest_YB''\\b"; +REFRESH MATERIALIZED VIEW "mtest_YB''\\b\\b"; +REFRESH MATERIALIZED VIEW CONCURRENTLY "mtest_YB''\\b\\b"; +DROP TABLE test_yb CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to materialized view "mtest_YB''\\b" +drop cascades to materialized view "mtest_YB''\\b\\b" +-- Materialized view of a regular view +CREATE TABLE mvtest_t (id int NOT NULL PRIMARY KEY, type text NOT NULL, amt numeric NOT NULL); +CREATE VIEW mvtest_tv AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type; +CREATE MATERIALIZED VIEW mvtest_tm2 AS SELECT * FROM mvtest_tv; +SELECT * FROM mvtest_tm2; + type | totamt +------+-------- +(0 rows) + +DROP VIEW mvtest_tv CASCADE; +NOTICE: drop cascades to materialized view mvtest_tm2 +-- SELECT FOR SHARE on Materialized view +CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type; +REFRESH MATERIALIZED VIEW mvtest_tm WITH NO DATA; +SELECT * FROM mvtest_tm FOR SHARE; +ERROR: cannot lock rows in materialized view "mvtest_tm" +DROP TABLE mvtest_t CASCADE; +NOTICE: drop cascades to materialized view mvtest_tm +-- Materialized view with GIN Indexes +create table mvtest_t3 (id int NOT NULL PRIMARY KEY, a integer[] not null); +INSERT INTO mvtest_t3 VALUES +(1, ARRAY[1, 2, 3, 4, 5]), +(2, ARRAY[1, 2, 3, 4, 5]), +(3, ARRAY[1, 2, 3, 4, 5]), +(4, ARRAY[1, 2, 3, 4, 5]); +create index on mvtest_t3 using ybgin (a); +CREATE MATERIALIZED VIEW mvtest_tv5 AS SELECT a[1], sum(id) FROM mvtest_t3 GROUP BY a[1]; +select * from mvtest_tv5; + a | sum +---+----- + 1 | 10 +(1 row) + +CREATE MATERIALIZED VIEW mvtest_tv6 AS SELECT * FROM mvtest_t3; +create index on mvtest_tv6 using ybgin (a); +select a[1] from mvtest_tv6; + a +--- + 1 + 1 + 1 + 1 +(4 rows) + +CREATE TABLE arrays (a int[], k serial PRIMARY KEY); +CREATE INDEX NONCONCURRENTLY ON arrays USING ybgin (a); +INSERT INTO arrays VALUES +('{1,1,6}'), +('{1,6,1}'), +('{2,3,6}'), +('{2,5,8}'), +('{null}'), +('{}'), +(null); +INSERT INTO arrays SELECT '{0}' FROM generate_series(1, 1000); +CREATE MATERIALIZED VIEW mvtest_tv7 AS SELECT * FROM arrays; +explain (costs off) select * from mvtest_tv7 where a @> '{6}'; + QUERY PLAN +----------------------------------- + Seq Scan on mvtest_tv7 + Filter: (a @> '{6}'::integer[]) +(2 rows) + +CREATE INDEX NONCONCURRENTLY ON mvtest_tv7 using ybgin (a); +explain (costs off) select * from mvtest_tv7 where a @> '{6}'; + QUERY PLAN +------------------------------------------------- + Index Scan using mvtest_tv7_a_idx on mvtest_tv7 + Index Cond: (a @> '{6}'::integer[]) +(2 rows) + +select * from mvtest_tv7 where a @> '{6}' order by k; + a | k +---------+--- + {1,1,6} | 1 + {1,6,1} | 2 + {2,3,6} | 3 +(3 rows) + +INSERT INTO arrays SELECT '{0}' FROM generate_series(1, 1000); +INSERT INTO arrays VALUES +('{6,6,6}'), +('{7,6,7}'); +refresh materialized view mvtest_tv7; +select * from mvtest_tv7 where a @> '{6}' order by k; + a | k +---------+------ + {1,1,6} | 1 + {1,6,1} | 2 + {2,3,6} | 3 + {6,6,6} | 2008 + {7,6,7} | 2009 +(5 rows) + +-- Materialized view with Collation +CREATE TABLE collate_test_POSIX ( + a int, + b text COLLATE "POSIX" NOT NULL +); +CREATE MATERIALIZED VIEW mv_collate_test_POSIX AS SELECT * FROM collate_test_POSIX; +INSERT INTO collate_test_POSIX VALUES (1, 'abc'), (2, 'Abc'), (3, 'bbc'), (4, 'ABD'), (5, 'zzz'), (6, 'ZZZ'); +REFRESH MATERIALIZED VIEW mv_collate_test_POSIX; +SELECT * FROM mv_collate_test_POSIX ORDER BY b; + a | b +---+----- + 4 | ABD + 2 | Abc + 6 | ZZZ + 1 | abc + 3 | bbc + 5 | zzz +(6 rows) + +SELECT * FROM mv_collate_test_POSIX ORDER BY b COLLATE "en-US-x-icu"; + a | b +---+----- + 1 | abc + 2 | Abc + 4 | ABD + 3 | bbc + 5 | zzz + 6 | ZZZ +(6 rows) + +CREATE MATERIALIZED VIEW mv_collate_test_explicit_collation AS SELECT b COLLATE "en-US-x-icu" FROM collate_test_POSIX; +SELECT * FROM mv_collate_test_explicit_collation ORDER BY b; + b +----- + abc + Abc + ABD + bbc + zzz + ZZZ +(6 rows) + +-- Test EXPLAIN ANALYZE + CREATE MATERIALIZED VIEW AS. +-- Use EXECUTE to hide the output since it won't be stable. +DO $$ +BEGIN + EXECUTE 'EXPLAIN ANALYZE CREATE MATERIALIZED VIEW view_as_1 AS SELECT 1'; +END$$; +SELECT * FROM view_as_1; + ?column? +---------- + 1 +(1 row) + +-- Colocated materialized view +CREATE DATABASE mydb WITH colocation = true; +\c mydb; +CREATE TABLE base (col int); +CREATE MATERIALIZED VIEW mv AS SELECT * FROM base; +CREATE UNIQUE INDEX mv_idx ON mv(col); +SELECT * FROM mv; + col +----- +(0 rows) + +INSERT INTO base VALUES (1); +REFRESH MATERIALIZED VIEW mv; +SELECT num_tablets, num_hash_key_columns, is_colocated FROM + yb_table_properties('mv'::regclass); + num_tablets | num_hash_key_columns | is_colocated +-------------+----------------------+-------------- + 1 | 0 | t +(1 row) + +SELECT num_tablets, num_hash_key_columns, is_colocated FROM + yb_table_properties('mv_idx'::regclass); + num_tablets | num_hash_key_columns | is_colocated +-------------+----------------------+-------------- + 1 | 0 | t +(1 row) + +SELECT * FROM mv; + col +----- + 1 +(1 row) + +INSERT INTO base VALUES (2); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv; +SELECT * FROM mv ORDER BY col; + col +----- + 1 + 2 +(2 rows) + +DROP MATERIALIZED VIEW mv; +SELECT * FROM mv; -- should fail. +ERROR: relation "mv" does not exist +LINE 1: SELECT * FROM mv; + ^ +CREATE MATERIALIZED VIEW mv WITH (COLOCATION=false) AS SELECT * FROM base; +CREATE UNIQUE INDEX mv_idx ON mv(col); +SELECT * FROM mv ORDER BY col; + col +----- + 1 + 2 +(2 rows) + +INSERT INTO base VALUES (3); +REFRESH MATERIALIZED VIEW mv; +SELECT num_tablets, num_hash_key_columns, is_colocated FROM + yb_table_properties('mv'::regclass); + num_tablets | num_hash_key_columns | is_colocated +-------------+----------------------+-------------- + 3 | 1 | f +(1 row) + +SELECT num_tablets, num_hash_key_columns, is_colocated FROM + yb_table_properties('mv_idx'::regclass); + num_tablets | num_hash_key_columns | is_colocated +-------------+----------------------+-------------- + 3 | 1 | f +(1 row) + +SELECT * FROM mv ORDER BY col; + col +----- + 1 + 2 + 3 +(3 rows) + +INSERT INTO base VALUES (4); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv; +SELECT * FROM mv ORDER BY col; + col +----- + 1 + 2 + 3 + 4 +(4 rows) + +DROP MATERIALIZED VIEW mv; +SELECT * FROM mv; -- should fail. +ERROR: relation "mv" does not exist +LINE 1: SELECT * FROM mv; + ^ +-- Tablegroup materialized view +CREATE DATABASE testdb; +\c testdb; +CREATE TABLEGROUP test_tg; +CREATE TABLE test_t (col int) TABLEGROUP test_tg; +CREATE MATERIALIZED VIEW mv AS SELECT * FROM test_t; +SELECT * FROM mv; + col +----- +(0 rows) + +INSERT INTO test_t VALUES (1); +REFRESH MATERIALIZED VIEW mv; +SELECT * FROM mv; + col +----- + 1 +(1 row) + +INSERT INTO test_t VALUES (2); +CREATE UNIQUE INDEX ON mv(col); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv; +SELECT * FROM mv ORDER BY col; + col +----- + 1 + 2 +(2 rows) + +DROP MATERIALIZED VIEW mv; +SELECT * FROM mv; +ERROR: relation "mv" does not exist +LINE 1: SELECT * FROM mv; + ^ +-- Split options on indexes should not be copied after a non-concurrent refresh +-- on a matview. +\c yugabyte; +CREATE TABLE test_yb(t int, j int); +CREATE MATERIALIZED VIEW mv AS SELECT * FROM test_yb; +CREATE INDEX idx ON mv(t) SPLIT INTO 5 TABLETS; +REFRESH MATERIALIZED VIEW mv; +SELECT num_tablets, num_hash_key_columns FROM yb_table_properties('idx'::regclass); + num_tablets | num_hash_key_columns +-------------+---------------------- + 3 | 1 +(1 row) + +-- Matview with tablespace +DROP MATERIALIZED VIEW mv; +CREATE TABLESPACE mv_tblspace1 LOCATION '/data'; +WARNING: LOCATION not supported yet and will be ignored +LINE 1: CREATE TABLESPACE mv_tblspace1 LOCATION '/data'; + ^ +HINT: See https://github.com/yugabyte/yugabyte-db/issues/6569. React with thumbs up to raise its priority +CREATE TABLESPACE mv_tblspace2 LOCATION '/data'; +WARNING: LOCATION not supported yet and will be ignored +LINE 1: CREATE TABLESPACE mv_tblspace2 LOCATION '/data'; + ^ +HINT: See https://github.com/yugabyte/yugabyte-db/issues/6569. React with thumbs up to raise its priority +CREATE MATERIALIZED VIEW mv TABLESPACE mv_tblspace1 AS SELECT * FROM test_yb; +\d mv; + Materialized view "public.mv" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + t | integer | | | + j | integer | | | +Tablespace: "mv_tblspace1" + +ALTER MATERIALIZED VIEW mv SET TABLESPACE mv_tblspace2; +NOTICE: data movement for table mv is successfully initiated +DETAIL: Data movement is a long running asynchronous process and can be monitored by checking the tablet placement in http://:7000/tables. +\d mv; + Materialized view "public.mv" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + t | integer | | | + j | integer | | | +Tablespace: "mv_tblspace2" + +-- Matview with temp tables + transactions +CREATE TEMPORARY TABLE t_temp (a INT, b INT); +BEGIN; +INSERT INTO t_temp VALUES (1, 1), (2, 2); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +-- Rolling back operations on temp tables should also roll back any +-- transactional side effects of the corresponding postgres transaction. +BEGIN; +SAVEPOINT sp1; +INSERT INTO t_temp VALUES (3, 3), (4, 4); +SAVEPOINT sp2; +ROLLBACK TO sp1; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +BEGIN; +SAVEPOINT sp1; +REFRESH MATERIALIZED VIEW mv_xid_test; +SAVEPOINT sp2; +INSERT INTO t_temp VALUES (3, 3), (4, 4); +ROLLBACK TO sp2; +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 2 +(1 row) + +-- Matview with prepared statements + temp tables +PREPARE temp_stmt AS INSERT INTO t_temp VALUES (5, 5), (6, 6); +BEGIN; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SAVEPOINT sp1; +EXECUTE temp_stmt; +ROLLBACK TO sp1; +EXECUTE temp_stmt; +COMMIT; +SELECT COUNT(*) FROM t_temp; + count +------- + 4 +(1 row) diff --git a/src/postgres/src/test/regress/sql/yb.orig.matview.sql b/src/postgres/src/test/regress/sql/yb.orig.matview.sql index 1604236bb5ba..ce92c9d813c8 100644 --- a/src/postgres/src/test/regress/sql/yb.orig.matview.sql +++ b/src/postgres/src/test/regress/sql/yb.orig.matview.sql @@ -1,6 +1,41 @@ -- -- YB tests for materialized views -- +CREATE TABLE t_simple (k INT PRIMARY KEY); +INSERT INTO t_simple (SELECT i FROM generate_series(1, 10) AS i); + +-- GH-26205: Test that PostgreSQL XID increases by 1 for a concurrent refresh +-- and remains the same for a non-concurrent refresh. Placing this at the top +-- of the file to ensure that this is close to being the first set of commands +-- that are executed upon cluster creation and there is no variability in the +-- XIDs generated. +CREATE MATERIALIZED VIEW mv_xid_test AS (SELECT * FROM t_simple); +CREATE UNIQUE INDEX ON mv_xid_test (k); +SELECT age('3'::xid); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT age('3'::xid); +REFRESH MATERIALIZED VIEW mv_xid_test; +SELECT age('3'::xid); +-- A few variations of the above test. +BEGIN ISOLATION LEVEL REPEATABLE READ; +REFRESH MATERIALIZED VIEW mv_xid_test; +INSERT INTO t_simple VALUES (11), (12); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT COUNT(*) FROM mv_xid_test; +COMMIT; +SELECT age('3'::xid); + +BEGIN ISOLATION LEVEL READ COMMITTED; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +REFRESH MATERIALIZED VIEW mv_xid_test; +INSERT INTO t_simple VALUES (13), (14); +SELECT COUNT(*) FROM mv_xid_test; +COMMIT; +-- The XID should jump by 2 in Read Committed mode as the concurrent refresh of +-- the matview is run as a sub-transaction. So, an additional XID is assigned to +-- its parent. In release builds where Read Committed isolation is disabled, the +-- XID should increase by 1. +SELECT age('3'::xid); CREATE TABLE test_yb (col int); INSERT INTO test_yb VALUES (null); @@ -271,4 +306,44 @@ CREATE TABLESPACE mv_tblspace2 LOCATION '/data'; CREATE MATERIALIZED VIEW mv TABLESPACE mv_tblspace1 AS SELECT * FROM test_yb; \d mv; ALTER MATERIALIZED VIEW mv SET TABLESPACE mv_tblspace2; -\d mv; \ No newline at end of file +\d mv; + +-- Matview with temp tables + transactions +CREATE TEMPORARY TABLE t_temp (a INT, b INT); +BEGIN; +INSERT INTO t_temp VALUES (1, 1), (2, 2); +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SELECT COUNT(*) FROM t_temp; +COMMIT; +SELECT COUNT(*) FROM t_temp; + +-- Rolling back operations on temp tables should also roll back any +-- transactional side effects of the corresponding postgres transaction. +BEGIN; +SAVEPOINT sp1; +INSERT INTO t_temp VALUES (3, 3), (4, 4); +SAVEPOINT sp2; +ROLLBACK TO sp1; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +COMMIT; +SELECT COUNT(*) FROM t_temp; + +BEGIN; +SAVEPOINT sp1; +REFRESH MATERIALIZED VIEW mv_xid_test; +SAVEPOINT sp2; +INSERT INTO t_temp VALUES (3, 3), (4, 4); +ROLLBACK TO sp2; +COMMIT; +SELECT COUNT(*) FROM t_temp; + +-- Matview with prepared statements + temp tables +PREPARE temp_stmt AS INSERT INTO t_temp VALUES (5, 5), (6, 6); +BEGIN; +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_xid_test; +SAVEPOINT sp1; +EXECUTE temp_stmt; +ROLLBACK TO sp1; +EXECUTE temp_stmt; +COMMIT; +SELECT COUNT(*) FROM t_temp;