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)