From df3b6817a2d5e1d2defa60c2092ab79d81bdedda Mon Sep 17 00:00:00 2001 From: Yura Sokolov Date: Fri, 23 Jul 2021 05:25:21 +0300 Subject: [PATCH] Add support for nesting temporary namespaces for ATX transactions. PGPRO-3882 --- expected/regression_ee.diff | 31 +++++++- multimaster--1.0.sql | 4 ++ src/commit.c | 4 ++ src/ddl.c | 139 ++++++++++++++++++++++++++++-------- src/include/ddl.h | 2 + src/include/multimaster.h | 1 + src/multimaster.c | 4 ++ src/state.c | 17 ++++- 8 files changed, 169 insertions(+), 33 deletions(-) diff --git a/expected/regression_ee.diff b/expected/regression_ee.diff index f75fa64e6f..8a8802e25e 100644 --- a/expected/regression_ee.diff +++ b/expected/regression_ee.diff @@ -570,7 +570,7 @@ diff ../../../src/test/regress/expected/rowsecurity.out ../tmp_check/regress_out diff ../../../src/test/regress/expected/atx.out ../tmp_check/regress_outdir/results/atx.out --- ../../../src/test/regress/expected/atx.out CENSORED +++ ../tmp_check/regress_outdir/results/atx.out CENSORED -@@ -1143,6 +1143,7 @@ +@@ -1139,6 +1139,7 @@ RESET client_min_messages; create database regression_atx_test_database; ALTER DATABASE "regression_atx_test_database" SET lc_messages TO 'C'; @@ -578,6 +578,35 @@ diff ../../../src/test/regress/expected/atx.out ../tmp_check/regress_outdir/resu \c regression_atx_test_database create table atx_test as select 1 as id; begin; +diff ../../../src/test/regress/expected/atx4.out ../tmp_check/regress_outdir/results/atx4.out +--- ../../../src/test/regress/expected/atx4.out CENSORED ++++ ../tmp_check/regress_outdir/results/atx4.out CENSORED +@@ -142,8 +142,10 @@ + (1 row) + + commit autonomous; ++ERROR: [MTM] failed to prepare transaction at peer node + -- Multimaster: t2 table will not be created due to pg_temp_N not found on replicas + commit; ++WARNING: there is no transaction in progress + begin; + -- create temp table in top level temptable but abort + begin autonomous; +@@ -213,11 +215,9 @@ + commit; + -- Multimaster: t2 were not created + select * from t2; +- a +----- +- hi +-(1 row) +- ++ERROR: relation "t2" does not exist ++LINE 1: select * from t2; ++ ^ + select * from t3; + ERROR: relation "t3" does not exist + LINE 1: select * from t3; diff ../../../src/test/regress/expected/atx5.out ../tmp_check/regress_outdir/results/atx5.out --- ../../../src/test/regress/expected/atx5.out CENSORED +++ ../tmp_check/regress_outdir/results/atx5.out CENSORED diff --git a/multimaster--1.0.sql b/multimaster--1.0.sql index 2c119e6bef..9cd072353f 100644 --- a/multimaster--1.0.sql +++ b/multimaster--1.0.sql @@ -195,6 +195,10 @@ CREATE FUNCTION mtm.set_temp_schema(nsp text) RETURNS void AS 'MODULE_PATHNAME','mtm_set_temp_schema' LANGUAGE C; +CREATE FUNCTION mtm.set_temp_schema(nsp text, force bool) RETURNS void +AS 'MODULE_PATHNAME','mtm_set_temp_schema' +LANGUAGE C; + CREATE TABLE mtm.local_tables( rel_schema name, rel_name name, diff --git a/src/commit.c b/src/commit.c index 16b7e4393d..2fada26f1e 100644 --- a/src/commit.c +++ b/src/commit.c @@ -46,6 +46,7 @@ static bool inside_mtm_begin; static MtmConfig *mtm_cfg; MtmCurrentTrans MtmTx; +int MtmTxAtxLevel = 0; /* holds state defining cleanup actions in case of failure during commit */ static struct MtmCommitState @@ -400,6 +401,9 @@ MtmTwoPhaseCommit(void) StartTransactionCommand(); } + if (MtmTxAtxLevel > 0) + temp_schema_reset(true); + /* prepare for cleanup */ mtm_commit_state.gtx = NULL; mtm_commit_state.inside_commit_sequence = true; diff --git a/src/ddl.c b/src/ddl.c index 0bd82284c0..be47f75b14 100644 --- a/src/ddl.c +++ b/src/ddl.c @@ -94,6 +94,7 @@ MtmDDLInProgress DDLApplyInProgress; static char MtmTempSchema[NAMEDATALEN]; static bool TempDropRegistered; +static int TempDropAtxLevel; static void const *MtmDDLStatement; @@ -247,9 +248,11 @@ temp_schema_reset_all(int my_node_id) " nsp record; " "begin " " reset session_authorization; " - " for nsp in select nspname from pg_namespace where nspname ~ '^mtm_tmp_%d_.*' loop " + " for nsp in select nspname from pg_namespace where " + " nspname ~ '^mtm_tmp_%d_.*' and" + " nspname !~ '_toast$' loop " " perform mtm.set_temp_schema(nsp.nspname); " - " execute format('drop schema if exists %%I cascade', format('%%s_toast', nsp.nspname)); " + " execute format('drop schema if exists %%I cascade', nsp.nspname||'_toast'); " " execute format('drop schema if exists %%I cascade', nsp.nspname); " " end loop; " "end $$; ", @@ -258,26 +261,27 @@ temp_schema_reset_all(int my_node_id) } /* Drop temp schemas on peer nodes */ -static void -temp_schema_reset(void) +void +temp_schema_reset(bool transactional) { Assert(TempDropRegistered); + Assert(TempDropAtxLevel == MtmTxAtxLevel); /* * reset session_authorization restores permissions if previous ddl * dropped them; set_temp_schema allows us to see temporary objects, * otherwise they can't be dropped * - * It is important to run it as 'V', otherwise it might interfere with - * later (if drop is due to DISCARD) or earlier command using the schema. + * If drop is due to DISCARD, it is important to run it as 'V', otherwise + * it might interfere with later or earlier command using the schema. */ MtmProcessDDLCommand( psprintf("RESET session_authorization; " - "select mtm.set_temp_schema('%s'); " + "select mtm.set_temp_schema('%s', false); " "DROP SCHEMA IF EXISTS %s_toast CASCADE; " "DROP SCHEMA IF EXISTS %s CASCADE;", MtmTempSchema, MtmTempSchema, MtmTempSchema), - false, + transactional, false ); MtmFinishDDLCommand(); @@ -290,52 +294,127 @@ temp_schema_at_exit(int status, Datum arg) Assert(TempDropRegistered); AbortOutOfAnyTransaction(); StartTransactionCommand(); - temp_schema_reset(); + for (; MtmTxAtxLevel >= 0; MtmTxAtxLevel--) + { + temp_schema_init(); + temp_schema_reset(false); + } CommitTransactionCommand(); } /* Register cleanup callback and generate temp schema name */ -static void +void temp_schema_init(void) { if (!TempDropRegistered) { - char *temp_schema; - - /* - * NB: namespace.c:isMtmTemp() assumes 'mtm_tmp_' prefix for mtm temp - * tables to defuse autovacuum. - */ - temp_schema = psprintf("mtm_tmp_%d_%d", - Mtm->my_node_id, MyBackendId); - memcpy(&MtmTempSchema, temp_schema, strlen(temp_schema) + 1); - before_shmem_exit(temp_schema_at_exit, (Datum) 0); TempDropRegistered = true; - pfree(temp_schema); + before_shmem_exit(temp_schema_at_exit, (Datum) 0); + } + if (MtmTxAtxLevel == 0) + snprintf(MtmTempSchema, sizeof(MtmTempSchema), + "mtm_tmp_%d_%d", Mtm->my_node_id, MyBackendId); + else + snprintf(MtmTempSchema, sizeof(MtmTempSchema), + "mtm_tmp_%d_%d_%d", Mtm->my_node_id, MyBackendId, MtmTxAtxLevel); + TempDropAtxLevel = MtmTxAtxLevel; +} + +/* + * temp_schema_valid check format of temp schema name. + * Namespace name should be either mtm_tmp_\d+_\d+ or + * mtm_tmp_\d+_\d+_\d+ for non-zero atx level. + */ +static bool +temp_schema_valid(const char *temp_namespace, const char **atx_level) +{ + const char *c; + const int mtm_tmp_len = strlen("mtm_tmp_"); + int underscores = 0; + bool need_digit = true; + bool valid = true; + + *atx_level = NULL; + if (strlen(temp_namespace) + strlen("_toast") + 1 > NAMEDATALEN) + valid = false; + else if(strncmp(temp_namespace, "mtm_tmp_", mtm_tmp_len) != 0) + valid = false; + for (c = temp_namespace+mtm_tmp_len; *c != 0 && valid; c++) + { + if (!need_digit && *c == '_') + { + underscores++; + if (underscores == 2) + *atx_level = c; + need_digit = true; + } + else if ((unsigned)*c - '0' <= '9' - '0') + need_digit = false; + else + valid = false; } + if (need_digit || underscores < 1 || underscores > 2) + valid = false; +#ifndef PGPRO_EE + if (underscores == 2) + valid = false; +#endif + + return valid; } Datum mtm_set_temp_schema(PG_FUNCTION_ARGS) { char *temp_namespace = text_to_cstring(PG_GETARG_TEXT_P(0)); - char *temp_toast_namespace = psprintf("%s_toast", temp_namespace); - Oid nsp_oid; - Oid toast_nsp_oid; + bool force = PG_NARGS() > 1 ? PG_GETARG_BOOL(1) : true; + char temp_toast_namespace[NAMEDATALEN] = {0}; + Oid nsp_oid = InvalidOid; + Oid toast_nsp_oid = InvalidOid; + const char *atx_level_start = NULL; +#ifdef PGPRO_EE + char top_temp_namespace[NAMEDATALEN] = {0}; + Oid top_nsp_oid = InvalidOid; + Oid top_toast_nsp_oid = InvalidOid; +#endif + + if (!temp_schema_valid(temp_namespace, &atx_level_start)) + mtm_log(ERROR, "mtm_set_temp_schema: wrong namespace name '%s'", + temp_namespace); - if (!SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(temp_namespace))) + snprintf(temp_toast_namespace, NAMEDATALEN, "%s_toast", temp_namespace); + if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(temp_namespace))) + { + nsp_oid = get_namespace_oid(temp_namespace, false); + toast_nsp_oid = get_namespace_oid(temp_toast_namespace, false); + } + else if (force) { nsp_oid = NamespaceCreate(temp_namespace, BOOTSTRAP_SUPERUSERID, true); toast_nsp_oid = NamespaceCreate(temp_toast_namespace, BOOTSTRAP_SUPERUSERID, true); CommandCounterIncrement(); } - else + +#ifdef PGPRO_EE + if (atx_level_start != NULL) { - nsp_oid = get_namespace_oid(temp_namespace, false); - toast_nsp_oid = get_namespace_oid(temp_toast_namespace, false); + memcpy(top_temp_namespace, temp_namespace, atx_level_start - temp_namespace); + + if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(top_temp_namespace))) + { + top_nsp_oid = get_namespace_oid(top_temp_namespace, false); + strlcat(top_temp_namespace, "_toast", NAMEDATALEN); + top_toast_nsp_oid = get_namespace_oid(top_temp_namespace, false); + } } - SetTempNamespaceState(nsp_oid, toast_nsp_oid); + SetTempNamespaceForMultimaster(); + SetTempNamespaceStateEx(nsp_oid, toast_nsp_oid, + top_nsp_oid, top_toast_nsp_oid, + atx_level_start != NULL); +#else + SetTempNamespace(nsp_oid, toast_nsp_oid); +#endif PG_RETURN_VOID(); } @@ -1030,7 +1109,7 @@ MtmProcessUtilitySender(PlannedStmt *pstmt, const char *queryString, { /* nothing to do if temp schema wasn't created at all */ if (TempDropRegistered) - temp_schema_reset(); + temp_schema_reset(false); SkipCommand(true); MtmGucDiscard(); } diff --git a/src/include/ddl.h b/src/include/ddl.h index 8313e931b8..522848a891 100644 --- a/src/include/ddl.h +++ b/src/include/ddl.h @@ -33,6 +33,8 @@ extern MtmDDLInProgress DDLApplyInProgress; extern void MtmDDLReplicationInit(void); extern void MtmDDLReplicationShmemStartup(void); extern void temp_schema_reset_all(int my_node_id); +extern void temp_schema_reset(bool transactional); +extern void temp_schema_init(void); extern bool MtmIsRelationLocal(Relation rel); extern void MtmDDLResetStatement(void); extern void MtmApplyDDLMessage(const char *messageBody, bool transactional); diff --git a/src/include/multimaster.h b/src/include/multimaster.h index fd82388057..2521efa117 100644 --- a/src/include/multimaster.h +++ b/src/include/multimaster.h @@ -216,6 +216,7 @@ extern MtmShared *Mtm; /* XXX: to delete */ extern MtmCurrentTrans MtmTx; +extern int MtmTxAtxLevel; extern MemoryContext MtmApplyContext; /* bgworker identities */ diff --git a/src/multimaster.c b/src/multimaster.c index 0bbf78fb89..ed03f915ae 100644 --- a/src/multimaster.c +++ b/src/multimaster.c @@ -369,6 +369,8 @@ MtmSuspendTransaction(void) MtmCurrentTrans *ctx = malloc(sizeof(MtmCurrentTrans)); *ctx = MtmTx; + MtmTxAtxLevel++; + temp_schema_init(); CallXactCallbacks(XACT_EVENT_START); return ctx; } @@ -378,6 +380,8 @@ MtmResumeTransaction(void *ctx) { MtmTx = *(MtmCurrentTrans *) ctx; free(ctx); + MtmTxAtxLevel--; + temp_schema_init(); } #endif diff --git a/src/state.c b/src/state.c index ff291ffbb7..77222687d5 100644 --- a/src/state.c +++ b/src/state.c @@ -3920,6 +3920,7 @@ MtmMonitor(Datum arg) */ { int rc; + uint64 nfuncs; StartTransactionCommand(); if (SPI_connect() != SPI_OK_CONNECT) @@ -3955,7 +3956,8 @@ MtmMonitor(Datum arg) true, 0); if (rc < 0 || rc != SPI_OK_SELECT) mtm_log(ERROR, "Failed to query pg_proc"); - if (SPI_processed == 0) + nfuncs = SPI_processed; + if (nfuncs == 0) { rc = SPI_execute("CREATE FUNCTION mtm.set_temp_schema(nsp text) RETURNS void " "AS '$libdir/multimaster','mtm_set_temp_schema' " @@ -3963,7 +3965,18 @@ MtmMonitor(Datum arg) if (rc < 0 || rc != SPI_OK_UTILITY) mtm_log(ERROR, "Failed to create mtm.set_temp_schema()"); - mtm_log(LOG, "Creating mtm.set_temp_schema()"); + mtm_log(LOG, "Creating mtm.set_temp_schema(nsp)"); + } + + if (nfuncs <= 1) + { + rc = SPI_execute("CREATE FUNCTION mtm.set_temp_schema(nsp text, force bool) RETURNS void " + "AS '$libdir/multimaster','mtm_set_temp_schema' " + "LANGUAGE C; ", false, 0); + if (rc < 0 || rc != SPI_OK_UTILITY) + mtm_log(ERROR, "Failed to create mtm.set_temp_schema()"); + + mtm_log(LOG, "Creating mtm.set_temp_schema(nsp, force)"); } SPI_finish(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy