Skip to content

Commit df3b681

Browse files
committed
Add support for nesting temporary namespaces for ATX transactions.
PGPRO-3882
1 parent 55754e0 commit df3b681

File tree

8 files changed

+169
-33
lines changed

8 files changed

+169
-33
lines changed

expected/regression_ee.diff

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,14 +570,43 @@ diff ../../../src/test/regress/expected/rowsecurity.out ../tmp_check/regress_out
570570
diff ../../../src/test/regress/expected/atx.out ../tmp_check/regress_outdir/results/atx.out
571571
--- ../../../src/test/regress/expected/atx.out CENSORED
572572
+++ ../tmp_check/regress_outdir/results/atx.out CENSORED
573-
@@ -1143,6 +1143,7 @@
573+
@@ -1139,6 +1139,7 @@
574574
RESET client_min_messages;
575575
create database regression_atx_test_database;
576576
ALTER DATABASE "regression_atx_test_database" SET lc_messages TO 'C';
577577
+ERROR: [MTM] failed to prepare transaction at peer node
578578
\c regression_atx_test_database
579579
create table atx_test as select 1 as id;
580580
begin;
581+
diff ../../../src/test/regress/expected/atx4.out ../tmp_check/regress_outdir/results/atx4.out
582+
--- ../../../src/test/regress/expected/atx4.out CENSORED
583+
+++ ../tmp_check/regress_outdir/results/atx4.out CENSORED
584+
@@ -142,8 +142,10 @@
585+
(1 row)
586+
587+
commit autonomous;
588+
+ERROR: [MTM] failed to prepare transaction at peer node
589+
-- Multimaster: t2 table will not be created due to pg_temp_N not found on replicas
590+
commit;
591+
+WARNING: there is no transaction in progress
592+
begin;
593+
-- create temp table in top level temptable but abort
594+
begin autonomous;
595+
@@ -213,11 +215,9 @@
596+
commit;
597+
-- Multimaster: t2 were not created
598+
select * from t2;
599+
- a
600+
-----
601+
- hi
602+
-(1 row)
603+
-
604+
+ERROR: relation "t2" does not exist
605+
+LINE 1: select * from t2;
606+
+ ^
607+
select * from t3;
608+
ERROR: relation "t3" does not exist
609+
LINE 1: select * from t3;
581610
diff ../../../src/test/regress/expected/atx5.out ../tmp_check/regress_outdir/results/atx5.out
582611
--- ../../../src/test/regress/expected/atx5.out CENSORED
583612
+++ ../tmp_check/regress_outdir/results/atx5.out CENSORED

multimaster--1.0.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ CREATE FUNCTION mtm.set_temp_schema(nsp text) RETURNS void
195195
AS 'MODULE_PATHNAME','mtm_set_temp_schema'
196196
LANGUAGE C;
197197

198+
CREATE FUNCTION mtm.set_temp_schema(nsp text, force bool) RETURNS void
199+
AS 'MODULE_PATHNAME','mtm_set_temp_schema'
200+
LANGUAGE C;
201+
198202
CREATE TABLE mtm.local_tables(
199203
rel_schema name,
200204
rel_name name,

src/commit.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ static bool inside_mtm_begin;
4646
static MtmConfig *mtm_cfg;
4747

4848
MtmCurrentTrans MtmTx;
49+
int MtmTxAtxLevel = 0;
4950

5051
/* holds state defining cleanup actions in case of failure during commit */
5152
static struct MtmCommitState
@@ -400,6 +401,9 @@ MtmTwoPhaseCommit(void)
400401
StartTransactionCommand();
401402
}
402403

404+
if (MtmTxAtxLevel > 0)
405+
temp_schema_reset(true);
406+
403407
/* prepare for cleanup */
404408
mtm_commit_state.gtx = NULL;
405409
mtm_commit_state.inside_commit_sequence = true;

src/ddl.c

Lines changed: 109 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ MtmDDLInProgress DDLApplyInProgress;
9494

9595
static char MtmTempSchema[NAMEDATALEN];
9696
static bool TempDropRegistered;
97+
static int TempDropAtxLevel;
9798

9899
static void const *MtmDDLStatement;
99100

@@ -247,9 +248,11 @@ temp_schema_reset_all(int my_node_id)
247248
" nsp record; "
248249
"begin "
249250
" reset session_authorization; "
250-
" for nsp in select nspname from pg_namespace where nspname ~ '^mtm_tmp_%d_.*' loop "
251+
" for nsp in select nspname from pg_namespace where "
252+
" nspname ~ '^mtm_tmp_%d_.*' and"
253+
" nspname !~ '_toast$' loop "
251254
" perform mtm.set_temp_schema(nsp.nspname); "
252-
" execute format('drop schema if exists %%I cascade', format('%%s_toast', nsp.nspname)); "
255+
" execute format('drop schema if exists %%I cascade', nsp.nspname||'_toast'); "
253256
" execute format('drop schema if exists %%I cascade', nsp.nspname); "
254257
" end loop; "
255258
"end $$; ",
@@ -258,26 +261,27 @@ temp_schema_reset_all(int my_node_id)
258261
}
259262

260263
/* Drop temp schemas on peer nodes */
261-
static void
262-
temp_schema_reset(void)
264+
void
265+
temp_schema_reset(bool transactional)
263266
{
264267
Assert(TempDropRegistered);
268+
Assert(TempDropAtxLevel == MtmTxAtxLevel);
265269

266270
/*
267271
* reset session_authorization restores permissions if previous ddl
268272
* dropped them; set_temp_schema allows us to see temporary objects,
269273
* otherwise they can't be dropped
270274
*
271-
* It is important to run it as 'V', otherwise it might interfere with
272-
* later (if drop is due to DISCARD) or earlier command using the schema.
275+
* If drop is due to DISCARD, it is important to run it as 'V', otherwise
276+
* it might interfere with later or earlier command using the schema.
273277
*/
274278
MtmProcessDDLCommand(
275279
psprintf("RESET session_authorization; "
276-
"select mtm.set_temp_schema('%s'); "
280+
"select mtm.set_temp_schema('%s', false); "
277281
"DROP SCHEMA IF EXISTS %s_toast CASCADE; "
278282
"DROP SCHEMA IF EXISTS %s CASCADE;",
279283
MtmTempSchema, MtmTempSchema, MtmTempSchema),
280-
false,
284+
transactional,
281285
false
282286
);
283287
MtmFinishDDLCommand();
@@ -290,52 +294,127 @@ temp_schema_at_exit(int status, Datum arg)
290294
Assert(TempDropRegistered);
291295
AbortOutOfAnyTransaction();
292296
StartTransactionCommand();
293-
temp_schema_reset();
297+
for (; MtmTxAtxLevel >= 0; MtmTxAtxLevel--)
298+
{
299+
temp_schema_init();
300+
temp_schema_reset(false);
301+
}
294302
CommitTransactionCommand();
295303
}
296304

297305
/* Register cleanup callback and generate temp schema name */
298-
static void
306+
void
299307
temp_schema_init(void)
300308
{
301309
if (!TempDropRegistered)
302310
{
303-
char *temp_schema;
304-
305-
/*
306-
* NB: namespace.c:isMtmTemp() assumes 'mtm_tmp_' prefix for mtm temp
307-
* tables to defuse autovacuum.
308-
*/
309-
temp_schema = psprintf("mtm_tmp_%d_%d",
310-
Mtm->my_node_id, MyBackendId);
311-
memcpy(&MtmTempSchema, temp_schema, strlen(temp_schema) + 1);
312-
before_shmem_exit(temp_schema_at_exit, (Datum) 0);
313311
TempDropRegistered = true;
314-
pfree(temp_schema);
312+
before_shmem_exit(temp_schema_at_exit, (Datum) 0);
313+
}
314+
if (MtmTxAtxLevel == 0)
315+
snprintf(MtmTempSchema, sizeof(MtmTempSchema),
316+
"mtm_tmp_%d_%d", Mtm->my_node_id, MyBackendId);
317+
else
318+
snprintf(MtmTempSchema, sizeof(MtmTempSchema),
319+
"mtm_tmp_%d_%d_%d", Mtm->my_node_id, MyBackendId, MtmTxAtxLevel);
320+
TempDropAtxLevel = MtmTxAtxLevel;
321+
}
322+
323+
/*
324+
* temp_schema_valid check format of temp schema name.
325+
* Namespace name should be either mtm_tmp_\d+_\d+ or
326+
* mtm_tmp_\d+_\d+_\d+ for non-zero atx level.
327+
*/
328+
static bool
329+
temp_schema_valid(const char *temp_namespace, const char **atx_level)
330+
{
331+
const char *c;
332+
const int mtm_tmp_len = strlen("mtm_tmp_");
333+
int underscores = 0;
334+
bool need_digit = true;
335+
bool valid = true;
336+
337+
*atx_level = NULL;
338+
if (strlen(temp_namespace) + strlen("_toast") + 1 > NAMEDATALEN)
339+
valid = false;
340+
else if(strncmp(temp_namespace, "mtm_tmp_", mtm_tmp_len) != 0)
341+
valid = false;
342+
for (c = temp_namespace+mtm_tmp_len; *c != 0 && valid; c++)
343+
{
344+
if (!need_digit && *c == '_')
345+
{
346+
underscores++;
347+
if (underscores == 2)
348+
*atx_level = c;
349+
need_digit = true;
350+
}
351+
else if ((unsigned)*c - '0' <= '9' - '0')
352+
need_digit = false;
353+
else
354+
valid = false;
315355
}
356+
if (need_digit || underscores < 1 || underscores > 2)
357+
valid = false;
358+
#ifndef PGPRO_EE
359+
if (underscores == 2)
360+
valid = false;
361+
#endif
362+
363+
return valid;
316364
}
317365

318366
Datum
319367
mtm_set_temp_schema(PG_FUNCTION_ARGS)
320368
{
321369
char *temp_namespace = text_to_cstring(PG_GETARG_TEXT_P(0));
322-
char *temp_toast_namespace = psprintf("%s_toast", temp_namespace);
323-
Oid nsp_oid;
324-
Oid toast_nsp_oid;
370+
bool force = PG_NARGS() > 1 ? PG_GETARG_BOOL(1) : true;
371+
char temp_toast_namespace[NAMEDATALEN] = {0};
372+
Oid nsp_oid = InvalidOid;
373+
Oid toast_nsp_oid = InvalidOid;
374+
const char *atx_level_start = NULL;
375+
#ifdef PGPRO_EE
376+
char top_temp_namespace[NAMEDATALEN] = {0};
377+
Oid top_nsp_oid = InvalidOid;
378+
Oid top_toast_nsp_oid = InvalidOid;
379+
#endif
380+
381+
if (!temp_schema_valid(temp_namespace, &atx_level_start))
382+
mtm_log(ERROR, "mtm_set_temp_schema: wrong namespace name '%s'",
383+
temp_namespace);
325384

326-
if (!SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(temp_namespace)))
385+
snprintf(temp_toast_namespace, NAMEDATALEN, "%s_toast", temp_namespace);
386+
if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(temp_namespace)))
387+
{
388+
nsp_oid = get_namespace_oid(temp_namespace, false);
389+
toast_nsp_oid = get_namespace_oid(temp_toast_namespace, false);
390+
}
391+
else if (force)
327392
{
328393
nsp_oid = NamespaceCreate(temp_namespace, BOOTSTRAP_SUPERUSERID, true);
329394
toast_nsp_oid = NamespaceCreate(temp_toast_namespace, BOOTSTRAP_SUPERUSERID, true);
330395
CommandCounterIncrement();
331396
}
332-
else
397+
398+
#ifdef PGPRO_EE
399+
if (atx_level_start != NULL)
333400
{
334-
nsp_oid = get_namespace_oid(temp_namespace, false);
335-
toast_nsp_oid = get_namespace_oid(temp_toast_namespace, false);
401+
memcpy(top_temp_namespace, temp_namespace, atx_level_start - temp_namespace);
402+
403+
if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(top_temp_namespace)))
404+
{
405+
top_nsp_oid = get_namespace_oid(top_temp_namespace, false);
406+
strlcat(top_temp_namespace, "_toast", NAMEDATALEN);
407+
top_toast_nsp_oid = get_namespace_oid(top_temp_namespace, false);
408+
}
336409
}
337410

338-
SetTempNamespaceState(nsp_oid, toast_nsp_oid);
411+
SetTempNamespaceForMultimaster();
412+
SetTempNamespaceStateEx(nsp_oid, toast_nsp_oid,
413+
top_nsp_oid, top_toast_nsp_oid,
414+
atx_level_start != NULL);
415+
#else
416+
SetTempNamespace(nsp_oid, toast_nsp_oid);
417+
#endif
339418
PG_RETURN_VOID();
340419
}
341420

@@ -1030,7 +1109,7 @@ MtmProcessUtilitySender(PlannedStmt *pstmt, const char *queryString,
10301109
{
10311110
/* nothing to do if temp schema wasn't created at all */
10321111
if (TempDropRegistered)
1033-
temp_schema_reset();
1112+
temp_schema_reset(false);
10341113
SkipCommand(true);
10351114
MtmGucDiscard();
10361115
}

src/include/ddl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ extern MtmDDLInProgress DDLApplyInProgress;
3333
extern void MtmDDLReplicationInit(void);
3434
extern void MtmDDLReplicationShmemStartup(void);
3535
extern void temp_schema_reset_all(int my_node_id);
36+
extern void temp_schema_reset(bool transactional);
37+
extern void temp_schema_init(void);
3638
extern bool MtmIsRelationLocal(Relation rel);
3739
extern void MtmDDLResetStatement(void);
3840
extern void MtmApplyDDLMessage(const char *messageBody, bool transactional);

src/include/multimaster.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ extern MtmShared *Mtm;
216216

217217
/* XXX: to delete */
218218
extern MtmCurrentTrans MtmTx;
219+
extern int MtmTxAtxLevel;
219220
extern MemoryContext MtmApplyContext;
220221

221222
/* bgworker identities */

src/multimaster.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ MtmSuspendTransaction(void)
369369
MtmCurrentTrans *ctx = malloc(sizeof(MtmCurrentTrans));
370370

371371
*ctx = MtmTx;
372+
MtmTxAtxLevel++;
373+
temp_schema_init();
372374
CallXactCallbacks(XACT_EVENT_START);
373375
return ctx;
374376
}
@@ -378,6 +380,8 @@ MtmResumeTransaction(void *ctx)
378380
{
379381
MtmTx = *(MtmCurrentTrans *) ctx;
380382
free(ctx);
383+
MtmTxAtxLevel--;
384+
temp_schema_init();
381385
}
382386
#endif
383387

src/state.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3920,6 +3920,7 @@ MtmMonitor(Datum arg)
39203920
*/
39213921
{
39223922
int rc;
3923+
uint64 nfuncs;
39233924

39243925
StartTransactionCommand();
39253926
if (SPI_connect() != SPI_OK_CONNECT)
@@ -3955,15 +3956,27 @@ MtmMonitor(Datum arg)
39553956
true, 0);
39563957
if (rc < 0 || rc != SPI_OK_SELECT)
39573958
mtm_log(ERROR, "Failed to query pg_proc");
3958-
if (SPI_processed == 0)
3959+
nfuncs = SPI_processed;
3960+
if (nfuncs == 0)
39593961
{
39603962
rc = SPI_execute("CREATE FUNCTION mtm.set_temp_schema(nsp text) RETURNS void "
39613963
"AS '$libdir/multimaster','mtm_set_temp_schema' "
39623964
"LANGUAGE C; ", false, 0);
39633965
if (rc < 0 || rc != SPI_OK_UTILITY)
39643966
mtm_log(ERROR, "Failed to create mtm.set_temp_schema()");
39653967

3966-
mtm_log(LOG, "Creating mtm.set_temp_schema()");
3968+
mtm_log(LOG, "Creating mtm.set_temp_schema(nsp)");
3969+
}
3970+
3971+
if (nfuncs <= 1)
3972+
{
3973+
rc = SPI_execute("CREATE FUNCTION mtm.set_temp_schema(nsp text, force bool) RETURNS void "
3974+
"AS '$libdir/multimaster','mtm_set_temp_schema' "
3975+
"LANGUAGE C; ", false, 0);
3976+
if (rc < 0 || rc != SPI_OK_UTILITY)
3977+
mtm_log(ERROR, "Failed to create mtm.set_temp_schema()");
3978+
3979+
mtm_log(LOG, "Creating mtm.set_temp_schema(nsp, force)");
39673980
}
39683981

39693982
SPI_finish();

0 commit comments

Comments
 (0)
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