6868#include "parser/analyze.h"
6969#include "parser/parse_relation.h"
7070#include "parser/parse_type.h"
71+ #include "parser/parse_func.h"
7172#include "catalog/pg_class.h"
7273#include "catalog/pg_type.h"
7374#include "tcop/pquery.h"
@@ -158,6 +159,7 @@ static void MtmInitializeSequence(int64* start, int64* step);
158159static void * MtmCreateSavepointContext (void );
159160static void MtmRestoreSavepointContext (void * ctx );
160161static void MtmReleaseSavepointContext (void * ctx );
162+ static void MtmSetRemoteFunction (char const * list , void * extra );
161163
162164static void MtmCheckClusterLock (void );
163165static void MtmCheckSlots (void );
@@ -184,6 +186,7 @@ MtmConnectionInfo* MtmConnections;
184186
185187HTAB * MtmXid2State ;
186188HTAB * MtmGid2State ;
189+ static HTAB * MtmRemoteFunctions ;
187190static HTAB * MtmLocalTables ;
188191
189192static bool MtmIsRecoverySession ;
@@ -258,6 +261,7 @@ bool MtmMajorNode;
258261TransactionId MtmUtilityProcessedInXid ;
259262
260263static char * MtmConnStrs ;
264+ static char * MtmRemoteFunctionsList ;
261265static char * MtmClusterName ;
262266static int MtmQueueSize ;
263267static int MtmWorkers ;
@@ -2229,7 +2233,7 @@ MtmCreateLocalTableMap(void)
22292233 "MtmLocalTables" ,
22302234 MULTIMASTER_MAX_LOCAL_TABLES , MULTIMASTER_MAX_LOCAL_TABLES ,
22312235 & info ,
2232- HASH_ELEM
2236+ HASH_ELEM | HASH_BLOBS
22332237 );
22342238 return htab ;
22352239}
@@ -2423,6 +2427,48 @@ MtmShmemStartup(void)
24232427 MtmInitialize ();
24242428}
24252429
2430+ static void MtmSetRemoteFunction (char const * list , void * extra )
2431+ {
2432+ if (MtmRemoteFunctions ) {
2433+ hash_destroy (MtmRemoteFunctions );
2434+ MtmRemoteFunctions = NULL ;
2435+ }
2436+ }
2437+
2438+ static void MtmInitializeRemoteFunctionsMap ()
2439+ {
2440+ HASHCTL info ;
2441+ char * p , * q ;
2442+ int n_funcs = 1 ;
2443+ FuncCandidateList clist ;
2444+
2445+ for (p = MtmRemoteFunctionsList ; (q = strchr (p , ',' )) != NULL ; p = q + 1 , n_funcs ++ );
2446+
2447+ Assert (MtmRemoteFunctions == NULL );
2448+
2449+ memset (& info , 0 , sizeof (info ));
2450+ info .entrysize = info .keysize = sizeof (Oid );
2451+ info .hcxt = TopMemoryContext ;
2452+ MtmRemoteFunctions = hash_create ("MtmRemoteFunctions" , n_funcs , & info ,
2453+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
2454+
2455+ p = pstrdup (MtmRemoteFunctionsList );
2456+ do {
2457+ q = strchr (p , ',' );
2458+ if (q != NULL ) {
2459+ * q ++ = '\0' ;
2460+ }
2461+ clist = FuncnameGetCandidates (stringToQualifiedNameList (p ), -1 , NIL , false, false, true);
2462+ if (clist == NULL ) {
2463+ MTM_ELOG (ERROR , "Failed to lookup function %s" , p );
2464+ } else if (clist -> next != NULL ) {
2465+ MTM_ELOG (ERROR , "Ambigious function %s" , p );
2466+ }
2467+ hash_search (MtmRemoteFunctions , & clist -> oid , HASH_ENTER , NULL );
2468+ p = q ;
2469+ } while (p != NULL );
2470+ }
2471+
24262472/*
24272473 * Parse node connection string.
24282474 * This function is called at cluster startup and while adding new cluster node
@@ -3052,6 +3098,19 @@ _PG_init(void)
30523098 NULL /* GucShowHook show_hook */
30533099 );
30543100
3101+ DefineCustomStringVariable (
3102+ "multimaster.remote_functions" ,
3103+ "List of fnuction names which should be executed remotely at all multimaster nodes instead of executing them at master and replicating result of their work" ,
3104+ NULL ,
3105+ & MtmRemoteFunctionsList ,
3106+ "lo_create,lo_unlink" ,
3107+ PGC_USERSET , /* context */
3108+ 0 , /* flags */
3109+ NULL , /* GucStringCheckHook check_hook */
3110+ MtmSetRemoteFunction , /* GucStringAssignHook assign_hook */
3111+ NULL /* GucShowHook show_hook */
3112+ );
3113+
30553114 DefineCustomStringVariable (
30563115 "multimaster.cluster_name" ,
30573116 "Name of the cluster" ,
@@ -3541,7 +3600,7 @@ lsn_t MtmGetFlushPosition(int nodeId)
35413600 * Keep track of progress of WAL writer.
35423601 * We need to notify WAL senders at other nodes which logical records
35433602 * are flushed to the disk and so can survive failure. In asynchronous commit mode
3544- * WAL is flushed by WAL writer. Current flish position can be obtained by GetFlushRecPtr().
3603+ * WAL is flushed by WAL writer. Current flush position can be obtained by GetFlushRecPtr().
35453604 * So on applying new logical record we insert it in the MtmLsnMapping and compare
35463605 * their poistions in local WAL log with current flush position.
35473606 * The records which are flushed to the disk by WAL writer are removed from the list
@@ -4656,7 +4715,7 @@ char* MtmGucSerialize(void)
46564715 appendStringInfoString (serialized_gucs , " TO " );
46574716
46584717 /* quite a crutch */
4659- if (strstr (cur_entry -> key , "_mem" ) != NULL || * (cur_entry -> value ) == '\0' )
4718+ if (strstr (cur_entry -> key , "_mem" ) != NULL || * (cur_entry -> value ) == '\0' || strchr ( cur_entry -> value , ',' ) != NULL )
46604719 {
46614720 appendStringInfoString (serialized_gucs , "'" );
46624721 appendStringInfoString (serialized_gucs , cur_entry -> value );
@@ -4686,10 +4745,7 @@ static void MtmProcessDDLCommand(char const* queryString, bool transactional)
46864745 if (transactional )
46874746 {
46884747 char * gucCtx = MtmGucSerialize ();
4689- if (* gucCtx )
4690- queryString = psprintf ("RESET SESSION AUTHORIZATION; reset all; %s %s" , gucCtx , queryString );
4691- else
4692- queryString = psprintf ("RESET SESSION AUTHORIZATION; reset all; %s" , queryString );
4748+ queryString = psprintf ("RESET SESSION AUTHORIZATION; reset all; %s %s" , gucCtx , queryString );
46934749
46944750 /* Transactional DDL */
46954751 MTM_LOG3 ("Sending DDL: %s" , queryString );
@@ -5058,29 +5114,28 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
50585114static void
50595115MtmExecutorStart (QueryDesc * queryDesc , int eflags )
50605116{
5061- bool ddl_generating_call = false;
5062- ListCell * tlist ;
5063-
5064- foreach (tlist , queryDesc -> plannedstmt -> planTree -> targetlist )
5117+ if (!MtmTx .isReplicated && ActivePortal )
50655118 {
5066- TargetEntry * tle = ( TargetEntry * ) lfirst ( tlist ) ;
5119+ ListCell * tlist ;
50675120
5068- if (tle -> resname && strcmp ( tle -> resname , "lo_create" ) == 0 )
5121+ if (! MtmRemoteFunctions )
50695122 {
5070- ddl_generating_call = true;
5071- break ;
5123+ MtmInitializeRemoteFunctionsMap ();
50725124 }
50735125
5074- if ( tle -> resname && strcmp ( tle -> resname , "lo_unlink" ) == 0 )
5126+ foreach ( tlist , queryDesc -> plannedstmt -> planTree -> targetlist )
50755127 {
5076- ddl_generating_call = true;
5077- break ;
5128+ TargetEntry * tle = (TargetEntry * ) lfirst (tlist );
5129+ if (tle -> expr && IsA (tle -> expr , FuncExpr ))
5130+ {
5131+ if (hash_search (MtmRemoteFunctions , & ((FuncExpr * )tle -> expr )-> funcid , HASH_FIND , NULL ))
5132+ {
5133+ MtmProcessDDLCommand (ActivePortal -> sourceText , true);
5134+ break ;
5135+ }
5136+ }
50785137 }
50795138 }
5080-
5081- if (ddl_generating_call && !MtmTx .isReplicated )
5082- MtmProcessDDLCommand (ActivePortal -> sourceText , true);
5083-
50845139 if (PreviousExecutorStartHook != NULL )
50855140 PreviousExecutorStartHook (queryDesc , eflags );
50865141 else
0 commit comments