64
64
#include "catalog/indexing.h"
65
65
#include "catalog/namespace.h"
66
66
#include "catalog/pg_constraint_fn.h"
67
+ #include "catalog/pg_proc.h"
67
68
#include "pglogical_output/hooks.h"
68
69
#include "parser/analyze.h"
69
70
#include "parser/parse_relation.h"
@@ -256,8 +257,6 @@ bool MtmUseRDMA;
256
257
bool MtmPreserveCommitOrder ;
257
258
bool MtmVolksWagenMode ; /* Pretend to be normal postgres. This means skip some NOTICE's and use local sequences */
258
259
259
- TransactionId MtmUtilityProcessedInXid ;
260
-
261
260
static char * MtmConnStrs ;
262
261
static char * MtmRemoteFunctionsList ;
263
262
static char * MtmClusterName ;
@@ -276,6 +275,7 @@ static bool MtmClusterLocked;
276
275
static bool MtmInsideTransaction ;
277
276
static bool MtmReferee ;
278
277
static bool MtmMonotonicSequences ;
278
+ static void const * MtmDDLStatement ;
279
279
280
280
static ExecutorStart_hook_type PreviousExecutorStartHook ;
281
281
static ExecutorFinish_hook_type PreviousExecutorFinishHook ;
@@ -924,6 +924,7 @@ MtmResetTransaction()
924
924
x -> csn = INVALID_CSN ;
925
925
x -> status = TRANSACTION_STATUS_UNKNOWN ;
926
926
x -> gid [0 ] = '\0' ;
927
+ MtmDDLStatement = NULL ;
927
928
}
928
929
929
930
#if 0
@@ -987,6 +988,7 @@ MtmBeginTransaction(MtmCurrentTrans* x)
987
988
MtmCheckClusterLock ();
988
989
}
989
990
MtmInsideTransaction = true;
991
+ MtmDDLStatement = NULL ;
990
992
Mtm -> nRunningTransactions += 1 ;
991
993
992
994
x -> snapshot = MtmAssignCSN ();
@@ -3461,7 +3463,7 @@ _PG_init(void)
3461
3463
& MtmRemoteFunctionsList ,
3462
3464
"lo_create,lo_unlink" ,
3463
3465
PGC_USERSET , /* context */
3464
- 0 , /* flags */
3466
+ GUC_LIST_INPUT | GUC_LIST_QUOTE , /* flags */
3465
3467
NULL , /* GucStringCheckHook check_hook */
3466
3468
MtmSetRemoteFunction , /* GucStringAssignHook assign_hook */
3467
3469
NULL /* GucShowHook show_hook */
@@ -4975,14 +4977,17 @@ static void MtmGucDiscard()
4975
4977
dlist_init (& MtmGucList );
4976
4978
4977
4979
hash_destroy (MtmGucHash );
4978
- MtmGucInit () ;
4980
+ MtmGucHash = NULL ;
4979
4981
}
4980
4982
4981
4983
static inline void MtmGucUpdate (const char * key , char * value )
4982
4984
{
4983
4985
MtmGucEntry * hentry ;
4984
4986
bool found ;
4985
4987
4988
+ if (!MtmGucHash )
4989
+ MtmGucInit ();
4990
+
4986
4991
hentry = (MtmGucEntry * )hash_search (MtmGucHash , key , HASH_ENTER , & found );
4987
4992
if (found )
4988
4993
{
@@ -4998,6 +5003,9 @@ static inline void MtmGucRemove(const char *key)
4998
5003
MtmGucEntry * hentry ;
4999
5004
bool found ;
5000
5005
5006
+ if (!MtmGucHash )
5007
+ MtmGucInit ();
5008
+
5001
5009
hentry = (MtmGucEntry * )hash_search (MtmGucHash , key , HASH_FIND , & found );
5002
5010
if (found )
5003
5011
{
@@ -5056,23 +5064,19 @@ char* MtmGucSerialize(void)
5056
5064
5057
5065
serialized_gucs = makeStringInfo ();
5058
5066
5059
- /*
5060
- * Crutch for scheduler. It sets search_path through SetConfigOption()
5061
- * so our callback do not react on that.
5062
- */
5063
- search_path = GetConfigOption ("search_path" , false, true);
5064
- appendStringInfo (serialized_gucs , "SET search_path TO %s; " , search_path );
5065
-
5066
5067
dlist_foreach (iter , & MtmGucList )
5067
5068
{
5068
5069
MtmGucEntry * cur_entry = dlist_container (MtmGucEntry , list_node , iter .cur );
5069
5070
5071
+ if (strcmp (cur_entry -> key , "search_path" ) == 0 )
5072
+ continue ;
5073
+
5070
5074
appendStringInfoString (serialized_gucs , "SET " );
5071
5075
appendStringInfoString (serialized_gucs , cur_entry -> key );
5072
5076
appendStringInfoString (serialized_gucs , " TO " );
5073
5077
5074
5078
/* quite a crutch */
5075
- if (strstr (cur_entry -> key , "_mem" ) != NULL || * (cur_entry -> value ) == '\0' || strchr ( cur_entry -> value , ',' ) != NULL )
5079
+ if (strstr (cur_entry -> key , "_mem" ) != NULL || * (cur_entry -> value ) == '\0' )
5076
5080
{
5077
5081
appendStringInfoString (serialized_gucs , "'" );
5078
5082
appendStringInfoString (serialized_gucs , cur_entry -> value );
@@ -5085,6 +5089,13 @@ char* MtmGucSerialize(void)
5085
5089
appendStringInfoString (serialized_gucs , "; " );
5086
5090
}
5087
5091
5092
+ /*
5093
+ * Crutch for scheduler. It sets search_path through SetConfigOption()
5094
+ * so our callback do not react on that.
5095
+ */
5096
+ search_path = GetConfigOption ("search_path" , false, true);
5097
+ appendStringInfo (serialized_gucs , "SET search_path TO %s; " , search_path );
5098
+
5088
5099
return serialized_gucs -> data ;
5089
5100
}
5090
5101
@@ -5142,12 +5153,60 @@ void MtmUpdateLockGraph(int nodeId, void const* messageBody, int messageSize)
5142
5153
MTM_LOG1 ("Update deadlock graph for node %d size %d" , nodeId , messageSize );
5143
5154
}
5144
5155
5156
+ static bool MtmIsTempType (TypeName * typeName )
5157
+ {
5158
+ bool isTemp = false;
5159
+
5160
+ if (typeName != NULL )
5161
+ {
5162
+ Type typeTuple = LookupTypeName (NULL , typeName , NULL , false);
5163
+ if (typeTuple != NULL )
5164
+ {
5165
+ Form_pg_type typeStruct = (Form_pg_type ) GETSTRUCT (typeTuple );
5166
+ Oid relid = typeStruct -> typrelid ;
5167
+ ReleaseSysCache (typeTuple );
5168
+
5169
+ if (relid != InvalidOid )
5170
+ {
5171
+ HeapTuple classTuple = SearchSysCache1 (RELOID , relid );
5172
+ Form_pg_class classStruct = (Form_pg_class ) GETSTRUCT (classTuple );
5173
+ if (classStruct -> relpersistence == 't' )
5174
+ isTemp = true;
5175
+ ReleaseSysCache (classTuple );
5176
+ }
5177
+ }
5178
+ }
5179
+ return isTemp ;
5180
+ }
5181
+
5182
+ static bool MtmFunctionProfileDependsOnTempTable (CreateFunctionStmt * func )
5183
+ {
5184
+ ListCell * elem ;
5185
+
5186
+ if (MtmIsTempType (func -> returnType ))
5187
+ {
5188
+ return true;
5189
+ }
5190
+ foreach (elem , func -> parameters )
5191
+ {
5192
+ FunctionParameter * param = (FunctionParameter * ) lfirst (elem );
5193
+ if (MtmIsTempType (param -> argType ))
5194
+ {
5195
+ return true;
5196
+ }
5197
+ }
5198
+ return false;
5199
+ }
5200
+
5201
+
5202
+
5145
5203
static void MtmProcessUtility (Node * parsetree , const char * queryString ,
5146
5204
ProcessUtilityContext context , ParamListInfo params ,
5147
5205
DestReceiver * dest , char * completionTag )
5148
5206
{
5149
5207
bool skipCommand = false;
5150
5208
bool executed = false;
5209
+ bool prevMyXactAccessedTempRel ;
5151
5210
5152
5211
MTM_LOG2 ("%d: Process utility statement tag=%d, context=%d, issubtrans=%d, creating_extension=%d, query=%s" ,
5153
5212
MyProcPid , nodeTag (parsetree ), context , IsSubTransaction (), creating_extension , queryString );
@@ -5229,19 +5288,24 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5229
5288
break ;
5230
5289
5231
5290
case T_VacuumStmt :
5232
- skipCommand = true;
5233
- if (context == PROCESS_UTILITY_TOPLEVEL ) {
5234
- MtmProcessDDLCommand (queryString , false);
5235
- MtmTx .isDistributed = false;
5236
- } else if (MtmApplyContext != NULL ) {
5237
- MemoryContext oldContext = MemoryContextSwitchTo (MtmApplyContext );
5238
- Assert (oldContext != MtmApplyContext );
5239
- MtmVacuumStmt = (VacuumStmt * )copyObject (parsetree );
5240
- MemoryContextSwitchTo (oldContext );
5241
- return ;
5242
- }
5243
- break ;
5244
-
5291
+ {
5292
+ VacuumStmt * vacuum = (VacuumStmt * )parsetree ;
5293
+ skipCommand = true;
5294
+ if ((vacuum -> options & VACOPT_LOCAL ) == 0 && !MtmVolksWagenMode )
5295
+ {
5296
+ if (context == PROCESS_UTILITY_TOPLEVEL ) {
5297
+ MtmProcessDDLCommand (queryString , false);
5298
+ MtmTx .isDistributed = false;
5299
+ } else if (MtmApplyContext != NULL ) {
5300
+ MemoryContext oldContext = MemoryContextSwitchTo (MtmApplyContext );
5301
+ Assert (oldContext != MtmApplyContext );
5302
+ MtmVacuumStmt = (VacuumStmt * )copyObject (parsetree );
5303
+ MemoryContextSwitchTo (oldContext );
5304
+ return ;
5305
+ }
5306
+ }
5307
+ break ;
5308
+ }
5245
5309
case T_CreateDomainStmt :
5246
5310
/* Detect temp tables access */
5247
5311
{
@@ -5377,6 +5441,11 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5377
5441
return ;
5378
5442
}
5379
5443
}
5444
+ else if (stmt -> removeType == OBJECT_FUNCTION && MtmTx .isReplicated )
5445
+ {
5446
+ /* Make it possible to drop functions which were not replicated */
5447
+ stmt -> missing_ok = true;
5448
+ }
5380
5449
}
5381
5450
break ;
5382
5451
@@ -5386,6 +5455,7 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5386
5455
CopyStmt * copyStatement = (CopyStmt * ) parsetree ;
5387
5456
skipCommand = true;
5388
5457
if (copyStatement -> is_from ) {
5458
+ ListCell * opt ;
5389
5459
RangeVar * relation = copyStatement -> relation ;
5390
5460
5391
5461
if (relation != NULL )
@@ -5400,6 +5470,25 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5400
5470
heap_close (rel , ShareLock );
5401
5471
}
5402
5472
}
5473
+
5474
+ foreach (opt , copyStatement -> options )
5475
+ {
5476
+ DefElem * elem = lfirst (opt );
5477
+ if (strcmp ("local" , elem -> defname ) == 0 ) {
5478
+ MtmTx .isDistributed = false; /* Skip */
5479
+ MtmTx .snapshot = INVALID_CSN ;
5480
+ MtmTx .containsDML = false;
5481
+ break ;
5482
+ }
5483
+ }
5484
+ }
5485
+ case T_CreateFunctionStmt :
5486
+ {
5487
+ if (MtmTx .isReplicated )
5488
+ {
5489
+ // disable functiob body cehck at replica
5490
+ check_function_bodies = false;
5491
+ }
5403
5492
}
5404
5493
break ;
5405
5494
}
@@ -5409,16 +5498,16 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5409
5498
break ;
5410
5499
}
5411
5500
5412
- if (!skipCommand && !MtmTx .isReplicated && ( context == PROCESS_UTILITY_TOPLEVEL || MtmUtilityProcessedInXid != GetCurrentTransactionId ()) )
5501
+ if (!skipCommand && !MtmTx .isReplicated && ! MtmDDLStatement )
5413
5502
{
5414
- MtmUtilityProcessedInXid = GetCurrentTransactionId ();
5415
- if (context == PROCESS_UTILITY_TOPLEVEL || !ActivePortal ) {
5416
- MtmProcessDDLCommand (queryString , true);
5417
- } else {
5418
- MtmProcessDDLCommand (ActivePortal -> sourceText , true);
5419
- }
5503
+ MTM_LOG3 ("Process DDL statement '%s', MtmTx.isReplicated=%d, MtmIsLogicalReceiver=%d" , queryString , MtmTx .isReplicated , MtmIsLogicalReceiver );
5504
+ MtmProcessDDLCommand (queryString , true);
5420
5505
executed = true;
5506
+ MtmDDLStatement = queryString ;
5421
5507
}
5508
+ else MTM_LOG3 ("Skip utility statement '%s': skip=%d, insideDDL=%d" , queryString , skipCommand , MtmDDLStatement != NULL );
5509
+
5510
+ prevMyXactAccessedTempRel = MyXactAccessedTempRel ;
5422
5511
5423
5512
if (PreviousProcessUtilityHook != NULL )
5424
5513
{
@@ -5435,18 +5524,32 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5435
5524
MTM_ELOG (ERROR , "Isolation level %s is not supported by multimaster" , isoLevelStr [XactIsoLevel ]);
5436
5525
}
5437
5526
#endif
5527
+ /* Allow replication of functions operating on temporary tables.
5528
+ * Even through temporary table doesn't exist at replica, diasabling functoin body check makes it possible to create such function at replica.
5529
+ * And it can be accessed later at replica if correspondent temporary table will be created.
5530
+ * But disable replication of functions returning temporary tables: such functions can not be created at replica in any case.
5531
+ */
5532
+ if (IsA (parsetree , CreateFunctionStmt ))
5533
+ {
5534
+ if (MtmFunctionProfileDependsOnTempTable ((CreateFunctionStmt * )parsetree ))
5535
+ {
5536
+ prevMyXactAccessedTempRel = true;
5537
+ }
5538
+ MyXactAccessedTempRel = prevMyXactAccessedTempRel ;
5539
+ }
5438
5540
if (MyXactAccessedTempRel )
5439
5541
{
5440
- MTM_LOG1 ("Xact accessed temp table, stopping replication" );
5542
+ MTM_LOG1 ("Xact accessed temp table, stopping replication of statement '%s'" , queryString );
5441
5543
MtmTx .isDistributed = false; /* Skip */
5442
5544
MtmTx .snapshot = INVALID_CSN ;
5443
5545
}
5444
5546
5445
5547
if (executed )
5446
5548
{
5447
5549
MtmFinishDDLCommand ();
5550
+ MtmDDLStatement = NULL ;
5448
5551
}
5449
- if (nodeTag (parsetree ) == T_CreateStmt )
5552
+ if (IsA (parsetree , CreateStmt ) )
5450
5553
{
5451
5554
CreateStmt * create = (CreateStmt * )parsetree ;
5452
5555
Oid relid = RangeVarGetRelid (create -> relation , NoLock , true);
@@ -5463,15 +5566,12 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5463
5566
}
5464
5567
}
5465
5568
}
5466
- if (context == PROCESS_UTILITY_TOPLEVEL ) {
5467
- MtmUtilityProcessedInXid = InvalidTransactionId ;
5468
- }
5469
5569
}
5470
5570
5471
5571
static void
5472
5572
MtmExecutorStart (QueryDesc * queryDesc , int eflags )
5473
5573
{
5474
- if (!MtmTx .isReplicated && ActivePortal )
5574
+ if (!MtmTx .isReplicated && ! MtmDDLStatement )
5475
5575
{
5476
5576
ListCell * tlist ;
5477
5577
@@ -5485,11 +5585,32 @@ MtmExecutorStart(QueryDesc *queryDesc, int eflags)
5485
5585
TargetEntry * tle = (TargetEntry * ) lfirst (tlist );
5486
5586
if (tle -> expr && IsA (tle -> expr , FuncExpr ))
5487
5587
{
5488
- if (hash_search (MtmRemoteFunctions , & ((FuncExpr * )tle -> expr )-> funcid , HASH_FIND , NULL ))
5588
+ Oid func_oid = ((FuncExpr * )tle -> expr )-> funcid ;
5589
+ if (!hash_search (MtmRemoteFunctions , & func_oid , HASH_FIND , NULL ))
5489
5590
{
5490
- MtmProcessDDLCommand (ActivePortal -> sourceText , true);
5491
- break ;
5591
+ Form_pg_proc funcform ;
5592
+ bool is_sec_def ;
5593
+ HeapTuple func_tuple = SearchSysCache1 (PROCOID , ObjectIdGetDatum (func_oid ));
5594
+ if (!HeapTupleIsValid (func_tuple ))
5595
+ elog (ERROR , "cache lookup failed for function %u" , func_oid );
5596
+ funcform = (Form_pg_proc ) GETSTRUCT (func_tuple );
5597
+ is_sec_def = funcform -> prosecdef ;
5598
+ ReleaseSysCache (func_tuple );
5599
+ elog (LOG , "Function %s security defined=%d" , tle -> resname , is_sec_def );
5600
+ if (!is_sec_def )
5601
+ {
5602
+ continue ;
5603
+ }
5492
5604
}
5605
+ /*
5606
+ * Execute security defined functions or functions marked as remote at replicated nodes.
5607
+ * Them are executed as DDL statements.
5608
+ * All data modifications done inside this function are not replicated.
5609
+ * As a result generated content can vary at different nodes.
5610
+ */
5611
+ MtmProcessDDLCommand (queryDesc -> sourceText , true);
5612
+ MtmDDLStatement = queryDesc ;
5613
+ break ;
5493
5614
}
5494
5615
}
5495
5616
}
@@ -5538,6 +5659,12 @@ MtmExecutorFinish(QueryDesc *queryDesc)
5538
5659
{
5539
5660
standard_ExecutorFinish (queryDesc );
5540
5661
}
5662
+
5663
+ if (MtmDDLStatement == queryDesc )
5664
+ {
5665
+ MtmFinishDDLCommand ();
5666
+ MtmDDLStatement = NULL ;
5667
+ }
5541
5668
}
5542
5669
5543
5670
static void MtmSeqNextvalHook (Oid seqid , int64 next )
0 commit comments