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"
@@ -258,8 +259,6 @@ bool MtmPreserveCommitOrder;
258
259
bool MtmVolksWagenMode ; /* Pretend to be normal postgres. This means skip some NOTICE's and use local sequences */
259
260
bool MtmMajorNode ;
260
261
261
- TransactionId MtmUtilityProcessedInXid ;
262
-
263
262
static char * MtmConnStrs ;
264
263
static char * MtmRemoteFunctionsList ;
265
264
static char * MtmClusterName ;
@@ -277,6 +276,7 @@ static bool MtmClusterLocked;
277
276
static bool MtmInsideTransaction ;
278
277
static bool MtmReferee ;
279
278
static bool MtmMonotonicSequences ;
279
+ static void const * MtmDDLStatement ;
280
280
281
281
static ExecutorStart_hook_type PreviousExecutorStartHook ;
282
282
static ExecutorFinish_hook_type PreviousExecutorFinishHook ;
@@ -923,6 +923,7 @@ MtmResetTransaction()
923
923
x -> csn = INVALID_CSN ;
924
924
x -> status = TRANSACTION_STATUS_UNKNOWN ;
925
925
x -> gid [0 ] = '\0' ;
926
+ MtmDDLStatement = NULL ;
926
927
}
927
928
928
929
#if 0
@@ -986,6 +987,7 @@ MtmBeginTransaction(MtmCurrentTrans* x)
986
987
MtmCheckClusterLock ();
987
988
}
988
989
MtmInsideTransaction = true;
990
+ MtmDDLStatement = NULL ;
989
991
Mtm -> nRunningTransactions += 1 ;
990
992
991
993
x -> snapshot = MtmAssignCSN ();
@@ -3117,7 +3119,7 @@ _PG_init(void)
3117
3119
& MtmRemoteFunctionsList ,
3118
3120
"lo_create,lo_unlink" ,
3119
3121
PGC_USERSET , /* context */
3120
- 0 , /* flags */
3122
+ GUC_LIST_INPUT | GUC_LIST_QUOTE , /* flags */
3121
3123
NULL , /* GucStringCheckHook check_hook */
3122
3124
MtmSetRemoteFunction , /* GucStringAssignHook assign_hook */
3123
3125
NULL /* GucShowHook show_hook */
@@ -4630,14 +4632,17 @@ static void MtmGucDiscard()
4630
4632
dlist_init (& MtmGucList );
4631
4633
4632
4634
hash_destroy (MtmGucHash );
4633
- MtmGucInit () ;
4635
+ MtmGucHash = NULL ;
4634
4636
}
4635
4637
4636
4638
static inline void MtmGucUpdate (const char * key , char * value )
4637
4639
{
4638
4640
MtmGucEntry * hentry ;
4639
4641
bool found ;
4640
4642
4643
+ if (!MtmGucHash )
4644
+ MtmGucInit ();
4645
+
4641
4646
hentry = (MtmGucEntry * )hash_search (MtmGucHash , key , HASH_ENTER , & found );
4642
4647
if (found )
4643
4648
{
@@ -4653,6 +4658,9 @@ static inline void MtmGucRemove(const char *key)
4653
4658
MtmGucEntry * hentry ;
4654
4659
bool found ;
4655
4660
4661
+ if (!MtmGucHash )
4662
+ MtmGucInit ();
4663
+
4656
4664
hentry = (MtmGucEntry * )hash_search (MtmGucHash , key , HASH_FIND , & found );
4657
4665
if (found )
4658
4666
{
@@ -4711,23 +4719,19 @@ char* MtmGucSerialize(void)
4711
4719
4712
4720
serialized_gucs = makeStringInfo ();
4713
4721
4714
- /*
4715
- * Crutch for scheduler. It sets search_path through SetConfigOption()
4716
- * so our callback do not react on that.
4717
- */
4718
- search_path = GetConfigOption ("search_path" , false, true);
4719
- appendStringInfo (serialized_gucs , "SET search_path TO %s; " , search_path );
4720
-
4721
4722
dlist_foreach (iter , & MtmGucList )
4722
4723
{
4723
4724
MtmGucEntry * cur_entry = dlist_container (MtmGucEntry , list_node , iter .cur );
4724
4725
4726
+ if (strcmp (cur_entry -> key , "search_path" ) == 0 )
4727
+ continue ;
4728
+
4725
4729
appendStringInfoString (serialized_gucs , "SET " );
4726
4730
appendStringInfoString (serialized_gucs , cur_entry -> key );
4727
4731
appendStringInfoString (serialized_gucs , " TO " );
4728
4732
4729
4733
/* quite a crutch */
4730
- if (strstr (cur_entry -> key , "_mem" ) != NULL || * (cur_entry -> value ) == '\0' || strchr ( cur_entry -> value , ',' ) != NULL )
4734
+ if (strstr (cur_entry -> key , "_mem" ) != NULL || * (cur_entry -> value ) == '\0' )
4731
4735
{
4732
4736
appendStringInfoString (serialized_gucs , "'" );
4733
4737
appendStringInfoString (serialized_gucs , cur_entry -> value );
@@ -4740,6 +4744,13 @@ char* MtmGucSerialize(void)
4740
4744
appendStringInfoString (serialized_gucs , "; " );
4741
4745
}
4742
4746
4747
+ /*
4748
+ * Crutch for scheduler. It sets search_path through SetConfigOption()
4749
+ * so our callback do not react on that.
4750
+ */
4751
+ search_path = GetConfigOption ("search_path" , false, true);
4752
+ appendStringInfo (serialized_gucs , "SET search_path TO %s; " , search_path );
4753
+
4743
4754
return serialized_gucs -> data ;
4744
4755
}
4745
4756
@@ -5032,6 +5043,11 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5032
5043
return ;
5033
5044
}
5034
5045
}
5046
+ else if (stmt -> removeType == OBJECT_FUNCTION && MtmTx .isReplicated )
5047
+ {
5048
+ /* Make it possible to drop functions which were not replicated */
5049
+ stmt -> missing_ok = true;
5050
+ }
5035
5051
}
5036
5052
break ;
5037
5053
@@ -5064,16 +5080,14 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5064
5080
break ;
5065
5081
}
5066
5082
5067
- if (!skipCommand && !MtmTx .isReplicated && ( context == PROCESS_UTILITY_TOPLEVEL || MtmUtilityProcessedInXid != GetCurrentTransactionId ()) )
5083
+ if (!skipCommand && !MtmTx .isReplicated && ! MtmDDLStatement )
5068
5084
{
5069
- MtmUtilityProcessedInXid = GetCurrentTransactionId ();
5070
- if (context == PROCESS_UTILITY_TOPLEVEL || !ActivePortal ) {
5071
- MtmProcessDDLCommand (queryString , true);
5072
- } else {
5073
- MtmProcessDDLCommand (ActivePortal -> sourceText , true);
5074
- }
5085
+ MTM_LOG3 ("Process DDL statement '%s', MtmTx.isReplicated=%d, MtmIsLogicalReceiver=%d" , queryString , MtmTx .isReplicated , MtmIsLogicalReceiver );
5086
+ MtmProcessDDLCommand (queryString , true);
5075
5087
executed = true;
5088
+ MtmDDLStatement = queryString ;
5076
5089
}
5090
+ else MTM_LOG3 ("Skip utility statement '%s': skip=%d, insideDDL=%d" , queryString , skipCommand , MtmDDLStatement != NULL );
5077
5091
5078
5092
if (PreviousProcessUtilityHook != NULL )
5079
5093
{
@@ -5092,16 +5106,17 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5092
5106
#endif
5093
5107
if (MyXactAccessedTempRel )
5094
5108
{
5095
- MTM_LOG1 ("Xact accessed temp table, stopping replication" );
5109
+ MTM_LOG1 ("Xact accessed temp table, stopping replication of statement '%s'" , queryString );
5096
5110
MtmTx .isDistributed = false; /* Skip */
5097
5111
MtmTx .snapshot = INVALID_CSN ;
5098
5112
}
5099
5113
5100
5114
if (executed )
5101
5115
{
5102
5116
MtmFinishDDLCommand ();
5117
+ MtmDDLStatement = NULL ;
5103
5118
}
5104
- if (nodeTag (parsetree ) == T_CreateStmt )
5119
+ if (IsA (parsetree , CreateStmt ) )
5105
5120
{
5106
5121
CreateStmt * create = (CreateStmt * )parsetree ;
5107
5122
Oid relid = RangeVarGetRelid (create -> relation , NoLock , true);
@@ -5118,15 +5133,12 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
5118
5133
}
5119
5134
}
5120
5135
}
5121
- if (context == PROCESS_UTILITY_TOPLEVEL ) {
5122
- MtmUtilityProcessedInXid = InvalidTransactionId ;
5123
- }
5124
5136
}
5125
5137
5126
5138
static void
5127
5139
MtmExecutorStart (QueryDesc * queryDesc , int eflags )
5128
5140
{
5129
- if (!MtmTx .isReplicated && ActivePortal )
5141
+ if (!MtmTx .isReplicated && ! MtmDDLStatement )
5130
5142
{
5131
5143
ListCell * tlist ;
5132
5144
@@ -5140,11 +5152,32 @@ MtmExecutorStart(QueryDesc *queryDesc, int eflags)
5140
5152
TargetEntry * tle = (TargetEntry * ) lfirst (tlist );
5141
5153
if (tle -> expr && IsA (tle -> expr , FuncExpr ))
5142
5154
{
5143
- if (hash_search (MtmRemoteFunctions , & ((FuncExpr * )tle -> expr )-> funcid , HASH_FIND , NULL ))
5155
+ Oid func_oid = ((FuncExpr * )tle -> expr )-> funcid ;
5156
+ if (!hash_search (MtmRemoteFunctions , & func_oid , HASH_FIND , NULL ))
5144
5157
{
5145
- MtmProcessDDLCommand (ActivePortal -> sourceText , true);
5146
- break ;
5158
+ Form_pg_proc funcform ;
5159
+ bool is_sec_def ;
5160
+ HeapTuple func_tuple = SearchSysCache1 (PROCOID , ObjectIdGetDatum (func_oid ));
5161
+ if (!HeapTupleIsValid (func_tuple ))
5162
+ elog (ERROR , "cache lookup failed for function %u" , func_oid );
5163
+ funcform = (Form_pg_proc ) GETSTRUCT (func_tuple );
5164
+ is_sec_def = funcform -> prosecdef ;
5165
+ ReleaseSysCache (func_tuple );
5166
+ elog (LOG , "Function %s security defined=%d" , tle -> resname , is_sec_def );
5167
+ if (!is_sec_def )
5168
+ {
5169
+ continue ;
5170
+ }
5147
5171
}
5172
+ /*
5173
+ * Execute security defined functions or functions marked as remote at replicated nodes.
5174
+ * Them are executed as DDL statements.
5175
+ * All data modifications done inside this function are not replicated.
5176
+ * As a result generated content can vary at different nodes.
5177
+ */
5178
+ MtmProcessDDLCommand (queryDesc -> sourceText , true);
5179
+ MtmDDLStatement = queryDesc ;
5180
+ break ;
5148
5181
}
5149
5182
}
5150
5183
}
@@ -5193,6 +5226,12 @@ MtmExecutorFinish(QueryDesc *queryDesc)
5193
5226
{
5194
5227
standard_ExecutorFinish (queryDesc );
5195
5228
}
5229
+
5230
+ if (MtmDDLStatement == queryDesc )
5231
+ {
5232
+ MtmFinishDDLCommand ();
5233
+ MtmDDLStatement = NULL ;
5234
+ }
5196
5235
}
5197
5236
5198
5237
static void MtmSeqNextvalHook (Oid seqid , int64 next )
0 commit comments