Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1clxVS-0000Dg-GS for pgsql-hackers@arkaria.postgresql.org; Thu, 09 Mar 2017 12:49:22 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84_2) (envelope-from ) id 1clxVR-0005QO-TB for pgsql-hackers@arkaria.postgresql.org; Thu, 09 Mar 2017 12:49:21 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1clxVR-0005QF-7G for pgsql-hackers@postgresql.org; Thu, 09 Mar 2017 12:49:21 +0000 Received: from mail.postgrespro.ru ([93.174.131.138]) by magus.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1clxVN-0001k8-57 for pgsql-hackers@postgresql.org; Thu, 09 Mar 2017 12:49:20 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.postgrespro.ru (Postfix) with ESMTP id 264E521C2233; Thu, 9 Mar 2017 15:49:16 +0300 (MSK) X-Virus-Scanned: Debian amavisd-new at postgrespro.ru From: Ivan Kartyshov To: Masahiko Sawada Subject: Re: make async slave to wait for lsn to be replayed References: <0240c26c-9f84-30ea-fca9-93ab2df5f305@postgrespro.ru> <9dcc266e-d258-1a94-f4a9-30c0f435e5d2@pgmasters.net> Cc: David Steele , Thomas Munro , Thom Brown , "pgsql-hackers@postgresql.org" Message-ID: <001601f4-c70e-2fc2-c45a-8d8192ae9d77@postgrespro.ru> Date: Thu, 9 Mar 2017 15:49:15 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.7.0 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------B30169FD350F4A4D2703F01B" X-Pg-Spam-Score: -1.9 (-) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-hackers Precedence: bulk Sender: pgsql-hackers-owner@postgresql.org This is a multi-part message in MIME format. --------------B30169FD350F4A4D2703F01B Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Hello On 07.03.2017 15:58, Masahiko Sawada wrote: > I've read the discussion so far but I didn't see the reason why you've > changed it to as a contrib module. Could you tell me about that? I did it on the initiative of our customer, who preferred the implementation in the form of contrib. Contrib realization of WAITLSN adds to core the only hook. On 07.03.2017 15:58, Masahiko Sawada wrote: > I guess this feature would be more useful if provided as a core > feature and we need to discuss about syntax as Thomas mentioned. Here I attached rebased patch waitlsn_10dev_v3 (core feature) I will leave the choice of implementation (core/contrib) to the discretion of the community. Will be glad to hear your suggestion about syntax, code and patch. -- Ivan Kartyshov Postgres Professional: http://www.postgrespro.com The Russian Postgres Company --------------B30169FD350F4A4D2703F01B Content-Type: text/x-patch; name="waitlsn_10dev_v3_rebased.patch" Content-Transfer-Encoding: 8bit Content-Disposition: attachment; filename="waitlsn_10dev_v3_rebased.patch" diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index 2bc4d9f..6d5a81e 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -178,6 +178,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/waitlsn.sgml b/doc/src/sgml/ref/waitlsn.sgml new file mode 100644 index 0000000..338187b --- /dev/null +++ b/doc/src/sgml/ref/waitlsn.sgml @@ -0,0 +1,134 @@ + + + + + WAITLSN + + + + WAITLSN + 7 + SQL - Language Statements + + + + WAITLSN + wait until target LSN has been replayed + + + + +WAITLSN 'LSN' [ , delay ] +WAITLSN_INFINITE 'LSN' +WAITLSN_NO_WAIT 'LSN' + + + + + Description + + + The WAITLSN wait till target LSN will + be replayed with an optional delay (milliseconds by default + infinity) to be wait for LSN to replayed. + + + + WAITLSN provides a simple + interprocess LSN wait mechanism for a backends on slave + in master-slave replication scheme on PostgreSQL database. + + + + The WAITLSN_INFINITE wait till target LSN will + be replayed on slave . + + + + The WAITLSN_NO_WAIT will tell if target LSN was replayed without any wait. + + + + + Parameters + + + + LSN + + + Target log sequence number to be wait for. + + + + + delay + + + Time in miliseconds to waiting for LSN to be replayed. + + + + + + + + Notes + + + Delay time for waiting till LSN to be replayed must be integer. For + default it is infinity. Waiting can be interupped using Ctl+C, or + by Postmaster death. + + + + + GUCs + + + Add two GUCs which help tuning influence on StartupXLOG: + count_waitlsn (denominator to check not each LSN) + int count_waitlsn = 10; + + + + interval_waitlsn (Interval in milliseconds to additional LSN check) + int interval_waitlsn = 100; + + + + + Examples + + + Configure and execute a waitlsn from + psql: + + +WAITLSN '0/3F07A6B1', 10000; +NOTICE: LSN is not reached. Try to make bigger delay. +WAITLSN + +WAITLSN '0/3F07A611'; +WAITLSN + +WAITLSN '0/3F0FF791', 500000; +^CCancel request sent +NOTICE: LSN is not reached. Try to make bigger delay. +ERROR: canceling statement due to user request + + + + + + Compatibility + + + There is no WAITLSN statement in the SQL + standard. + + + diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index c8191de..a5100a2 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -206,6 +206,7 @@ &update; &vacuum; &values; + &waitlsn; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 2dcff7f..9c2def1 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -39,6 +39,7 @@ #include "catalog/pg_control.h" #include "catalog/pg_database.h" #include "commands/tablespace.h" +#include "commands/waitlsn.h" #include "miscadmin.h" #include "pgstat.h" #include "port/atomics.h" @@ -145,6 +146,9 @@ const struct config_enum_entry sync_method_options[] = { {NULL, 0, false} }; +/* GUC variable */ +int count_waitlsn = 10; +int interval_waitlsn = 100; /* * Although only "on", "off", and "always" are documented, @@ -6948,6 +6952,8 @@ StartupXLOG(void) { ErrorContextCallback errcallback; TimestampTz xtime; + TimestampTz time_waitlsn = GetCurrentTimestamp(); + int counter_waitlsn = 0; InRedo = true; @@ -7174,6 +7180,17 @@ StartupXLOG(void) break; } + /* + * After update lastReplayedEndRecPtr set Latches in SHMEM array + */ + if (counter_waitlsn % count_waitlsn == 0 + || TimestampDifferenceExceeds(time_waitlsn,GetCurrentTimestamp(),interval_waitlsn)) + { + WaitLSNSetLatch(); + time_waitlsn = GetCurrentTimestamp(); + } + counter_waitlsn++; + /* Else, try to fetch the next WAL record */ record = ReadRecord(xlogreader, InvalidXLogRecPtr, LOG, false); } while (record != NULL); diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index e0fab38..8a7e2bd 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -20,6 +20,6 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ policy.o portalcmds.o prepare.o proclang.o publicationcmds.o \ schemacmds.o seclabel.o sequence.o subscriptioncmds.o tablecmds.o \ tablespace.o trigger.o tsearchcmds.o typecmds.o user.o vacuum.o \ - vacuumlazy.o variable.o view.o + vacuumlazy.o variable.o view.o waitlsn.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index e32d7a1..ce1c7f7 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -139,7 +139,6 @@ #include "utils/ps_status.h" #include "utils/timestamp.h" - /* * Maximum size of a NOTIFY payload, including terminating NULL. This * must be kept small enough so that a notification message fits on one diff --git a/src/backend/commands/waitlsn.c b/src/backend/commands/waitlsn.c new file mode 100644 index 0000000..47bd90d --- /dev/null +++ b/src/backend/commands/waitlsn.c @@ -0,0 +1,241 @@ +/*------------------------------------------------------------------------- + * + * waitlsn.c + * WaitLSN statment: WAITLSN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 2017, Regents of PostgresPro + * + * IDENTIFICATION + * src/backend/commands/waitlsn.c + * + *------------------------------------------------------------------------- + */ + +/* + * ------------------------------------------------------------------------- + * Wait for LSN been replayed on slave + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "fmgr.h" +#include "pgstat.h" +#include "utils/pg_lsn.h" +#include "storage/latch.h" +#include "miscadmin.h" +#include "storage/spin.h" +#include "storage/backendid.h" +#include "access/xact.h" +#include "storage/shmem.h" +#include "storage/ipc.h" +#include "utils/timestamp.h" +#include "storage/pmsignal.h" +#include "access/xlog.h" +#include "access/xlogdefs.h" +#include "commands/waitlsn.h" +#include "storage/proc.h" +#include "access/transam.h" +#include "funcapi.h" +#include "catalog/pg_type.h" +#include "utils/builtins.h" + +/* Latches Own-DisownLatch and AbortCаllBack */ +static uint32 WaitLSNShmemSize(void); +static void WLDisownLatchAbort(XactEvent event, void *arg); +static void WLOwnLatch(void); +static void WLDisownLatch(void); + +void _PG_init(void); + +/* Shared memory structures */ +typedef struct +{ + int pid; + volatile slock_t slock; + Latch latch; +} BIDLatch; + +typedef struct +{ + char dummy; + int backend_maxid; + BIDLatch l_arr[FLEXIBLE_ARRAY_MEMBER]; +} GlobState; + +static volatile GlobState *state; +bool is_latch_owned = false; + +/* Take Latch for current backend at the begining of WAITLSN */ +static void +WLOwnLatch(void) +{ + SpinLockAcquire(&state->l_arr[MyBackendId].slock); + OwnLatch(&state->l_arr[MyBackendId].latch); + is_latch_owned = true; + + if (state->backend_maxid < MyBackendId) + state->backend_maxid = MyBackendId; + + state->l_arr[MyBackendId].pid = MyProcPid; + SpinLockRelease(&state->l_arr[MyBackendId].slock); +} + +/* Release Latch for current backend at the end of WAITLSN */ +static void +WLDisownLatch(void) +{ + int i; + SpinLockAcquire(&state->l_arr[MyBackendId].slock); + DisownLatch(&state->l_arr[MyBackendId].latch); + is_latch_owned = false; + state->l_arr[MyBackendId].pid = 0; + + if (state->backend_maxid == MyBackendId) + for (i = (MaxConnections+1); i >=0; i--) + if (state->l_arr[i].pid != 0) + { + state->backend_maxid = i; + break; + } + + SpinLockRelease(&state->l_arr[MyBackendId].slock); +} + +/* CallBack function on abort*/ +static void +WLDisownLatchAbort(XactEvent event, void *arg) +{ + if (is_latch_owned && (event == XACT_EVENT_PARALLEL_ABORT || + event == XACT_EVENT_ABORT)) + { + WLDisownLatch(); + } +} + +/* Module load callback */ +void +_PG_init(void) +{ + if (!IsUnderPostmaster) + RegisterXactCallback(WLDisownLatchAbort, NULL); +} + +/* Get size of shared memory to room GlobState */ +static uint32 +WaitLSNShmemSize(void) +{ + return offsetof(GlobState, l_arr) + sizeof(BIDLatch) * (MaxConnections+1); +} + +/* Init array of Latches in shared memory */ +void +WaitLSNShmemInit(void) +{ + bool found; + uint i; + + state = (GlobState *) ShmemInitStruct("pg_wait_lsn", + WaitLSNShmemSize(), + &found); + if (!found) + { + for (i = 0; i < (MaxConnections+1); i++) + { + state->l_arr[i].pid = 0; + SpinLockInit(&state->l_arr[i].slock); + InitSharedLatch(&state->l_arr[i].latch); + } + state->backend_maxid = 0; + } +} + +/* Set all Latches in shared memorys cause new LSN been replayed*/ +void +WaitLSNSetLatch(void) +{ + uint i; + for (i = 0; i <= state->backend_maxid; i++) + { + SpinLockAcquire(&state->l_arr[i].slock); + if (state->l_arr[i].pid != 0) + SetLatch(&state->l_arr[i].latch); + SpinLockRelease(&state->l_arr[i].slock); + } +} + +/* + * On WAITLSN own latch and wait till LSN is replayed, Postmaster death, interruption + * or timeout. + */ +void +WaitLSNUtility(const char *lsn, const int delay, DestReceiver *dest) +{ + XLogRecPtr trg_lsn = DatumGetLSN(DirectFunctionCall1(pg_lsn_in, CStringGetDatum(lsn))); + XLogRecPtr cur_lsn; + int latch_events; + uint64_t tdelay = delay; + long secs; + int microsecs; + TimestampTz timer = GetCurrentTimestamp(); + TupOutputState *tstate; + TupleDesc tupdesc; + char *value = "false"; + + if (delay > 0) + latch_events = WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH; + else + latch_events = WL_LATCH_SET | WL_POSTMASTER_DEATH; + + WLOwnLatch(); + + for (;;) + { + cur_lsn = GetXLogReplayRecPtr(NULL); + + /* If LSN had been Replayed */ + if (trg_lsn <= cur_lsn) + break; + + /* If the postmaster dies, finish immediately */ + if (!PostmasterIsAlive()) + break; + + /* If Delay time is over */ + if (latch_events & WL_TIMEOUT) + { + if (TimestampDifferenceExceeds(timer,GetCurrentTimestamp(),delay)) + break; + TimestampDifference(timer,GetCurrentTimestamp(),&secs, µsecs); + tdelay = delay - (secs*1000 + microsecs/1000); + } + + MyPgXact->xmin = InvalidTransactionId; + WaitLatch(&state->l_arr[MyBackendId].latch, latch_events, tdelay, WAIT_EVENT_CLIENT_READ); + ResetLatch(&state->l_arr[MyBackendId].latch); + + /* CHECK_FOR_INTERRUPTS if they comes then disown latch current */ + if (InterruptPending) + { + WLDisownLatch(); + ProcessInterrupts(); + } + + } + + WLDisownLatch(); + + if (trg_lsn > cur_lsn) + elog(NOTICE,"LSN is not reached. Try to make bigger delay."); + else + value = "true"; + + /* need a tuple descriptor representing a single TEXT column */ + tupdesc = CreateTemplateTupleDesc(1, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "LSN reached", TEXTOID, -1, 0); + /* prepare for projection of tuples */ + tstate = begin_tup_output_tupdesc(dest, tupdesc); + /* Send it */ + do_text_output_oneline(tstate, value); + end_tup_output(tstate); +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 174773b..dcaed39 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -275,7 +275,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); SecLabelStmt SelectStmt TransactionStmt TruncateStmt UnlistenStmt UpdateStmt VacuumStmt VariableResetStmt VariableSetStmt VariableShowStmt - ViewStmt CheckPointStmt CreateConversionStmt + ViewStmt WaitLSNStmt CheckPointStmt CreateConversionStmt DeallocateStmt PrepareStmt ExecuteStmt DropOwnedStmt ReassignOwnedStmt AlterTSConfigurationStmt AlterTSDictionaryStmt @@ -320,7 +320,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type OptSchemaEltList %type TriggerForSpec TriggerForType -%type TriggerActionTime +%type TriggerActionTime WaitDelay %type TriggerEvents TriggerOneEvent %type TriggerFuncArg %type TriggerWhen @@ -678,7 +678,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE - WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE + WAITLSN WAITLSN_INFINITE WAITLSN_NO_WAIT WHEN WHERE WHITESPACE_P WINDOW + WITH WITHIN WITHOUT WORK WRAPPER WRITE XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE XMLPI XMLROOT XMLSERIALIZE @@ -935,6 +936,7 @@ stmt : | VariableSetStmt | VariableShowStmt | ViewStmt + | WaitLSNStmt | /*EMPTY*/ { $$ = NULL; } ; @@ -13507,7 +13509,41 @@ frame_bound: } ; +/***************************************************************************** + * + * QUERY: + * WAITLSN can appear as a query-level command + * + * + *****************************************************************************/ +WaitLSNStmt: + WAITLSN Sconst WaitDelay + { + WaitLSNStmt *n = makeNode(WaitLSNStmt); + n->lsn = $2; + n->delay = $3; + $$ = (Node *)n; + } + | WAITLSN_INFINITE Sconst + { + WaitLSNStmt *n = makeNode(WaitLSNStmt); + n->lsn = $2; + n->delay = 0; + $$ = (Node *)n; + } + | WAITLSN_NO_WAIT Sconst + { + WaitLSNStmt *n = makeNode(WaitLSNStmt); + n->lsn = $2; + n->delay = 1; + $$ = (Node *)n; + } + ; +WaitDelay: + ',' Iconst { $$ = $2; } + | /*EMPTY*/ { $$ = 0; } + ; /* * Supporting nonterminals for expressions. */ @@ -14541,6 +14577,9 @@ unreserved_keyword: | VIEW | VIEWS | VOLATILE + | WAITLSN + | WAITLSN_INFINITE + | WAITLSN_NO_WAIT | WHITESPACE_P | WITHIN | WITHOUT diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 2d1ed14..932136f 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -22,6 +22,7 @@ #include "access/subtrans.h" #include "access/twophase.h" #include "commands/async.h" +#include "commands/waitlsn.h" #include "miscadmin.h" #include "pgstat.h" #include "postmaster/autovacuum.h" @@ -271,6 +272,11 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) AsyncShmemInit(); BackendRandomShmemInit(); + /* + * Init array of Latches in SHMEM for WAITLSN + */ + WaitLSNShmemInit(); + #ifdef EXEC_BACKEND /* diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 5d3be38..6595be6 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -56,6 +56,7 @@ #include "commands/user.h" #include "commands/vacuum.h" #include "commands/view.h" +#include "commands/waitlsn.h" #include "miscadmin.h" #include "parser/parse_utilcmd.h" #include "postmaster/bgwriter.h" @@ -917,6 +918,20 @@ standard_ProcessUtility(PlannedStmt *pstmt, break; } + case T_WaitLSNStmt: + { + WaitLSNStmt *stmt = (WaitLSNStmt *) parsetree; + if (!RecoveryInProgress()) + { + ereport(ERROR,(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("cannot execute %s not during recovery", + "WaitLSN"))); + } + else + WaitLSNUtility(stmt->lsn, stmt->delay, dest); + } + break; + default: /* All other statement types have event trigger support */ ProcessUtilitySlow(pstate, pstmt, queryString, @@ -2453,6 +2468,10 @@ CreateCommandTag(Node *parsetree) tag = "NOTIFY"; break; + case T_WaitLSNStmt: + tag = "WAITLSN"; + break; + case T_ListenStmt: tag = "LISTEN"; break; @@ -3064,6 +3083,10 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_ALL; break; + case T_WaitLSNStmt: + lev = LOGSTMT_ALL; + break; + case T_ListenStmt: lev = LOGSTMT_ALL; break; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 0249721..ab52ed4 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2835,6 +2835,30 @@ static struct config_int ConfigureNamesInt[] = NULL, NULL, NULL }, + { + {"interval_waitlsn", PGC_SUSET, DEVELOPER_OPTIONS, + gettext_noop("Set interval of time (ms) how often LSN will be checked."), + gettext_noop("Set interval of time (ms) how often LSN will be checked to " + "make less influence on StartupXLOG() process."), + GUC_UNIT_MS + }, + &interval_waitlsn, + 100, 0, INT_MAX, + NULL, NULL, NULL + }, + + { + {"count_waitlsn", PGC_SUSET, DEVELOPER_OPTIONS, + gettext_noop("How often LSN will be checked."), + gettext_noop("Set count of LSNs that will be passed befor LSN check to " + "make less influence on StartupXLOG() process."), + GUC_NOT_IN_SAMPLE + }, + &count_waitlsn, + 10, 1, INT_MAX, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 9f036c7..5249c8e 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -111,6 +111,9 @@ extern bool log_checkpoints; extern int CheckPointSegments; +extern int interval_waitlsn; +extern int count_waitlsn; + /* Archive modes */ typedef enum ArchiveMode { diff --git a/src/include/commands/waitlsn.h b/src/include/commands/waitlsn.h new file mode 100644 index 0000000..2e39608 --- /dev/null +++ b/src/include/commands/waitlsn.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * waitlsn.h + * WaitLSN notification: WAITLSN + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2016, Regents of PostgresPRO + * + * src/include/commands/waitlsn.h + * + *------------------------------------------------------------------------- + */ +#ifndef WAITLSN_H +#define WAITLSN_H +#include "tcop/dest.h" + +extern void WaitLSNUtility(const char *lsn, const int delay, DestReceiver *dest); +extern void WaitLSNShmemInit(void); +extern void WaitLSNSetLatch(void); + +#endif /* WAITLSN_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 95dd8ba..5f02105 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -478,6 +478,7 @@ typedef enum NodeTag T_DropReplicationSlotCmd, T_StartReplicationCmd, T_TimeLineHistoryCmd, + T_WaitLSNStmt, /* * TAGS FOR RANDOM OTHER STUFF diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 07a8436..732304d 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3299,4 +3299,16 @@ typedef struct DropSubscriptionStmt bool missing_ok; /* Skip error if missing? */ } DropSubscriptionStmt; +/* ---------------------- + * WaitLSN Statement + * ---------------------- + */ +typedef struct WaitLSNStmt +{ + NodeTag type; + char *lsn; /* Taraget LSN to wait for */ + int delay; /* Delay to wait for LSN*/ + bool nowait; /* No wait for LSN just result*/ +} WaitLSNStmt; + #endif /* PARSENODES_H */ diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 985d650..c77f7b5 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -430,6 +430,9 @@ PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD) PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD) PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD) PG_KEYWORD("volatile", VOLATILE, UNRESERVED_KEYWORD) +PG_KEYWORD("waitlsn", WAITLSN, UNRESERVED_KEYWORD) +PG_KEYWORD("waitlsn_infinite", WAITLSN_INFINITE, UNRESERVED_KEYWORD) +PG_KEYWORD("waitlsn_no_wait", WAITLSN_NO_WAIT, UNRESERVED_KEYWORD) PG_KEYWORD("when", WHEN, RESERVED_KEYWORD) PG_KEYWORD("where", WHERE, RESERVED_KEYWORD) PG_KEYWORD("whitespace", WHITESPACE_P, UNRESERVED_KEYWORD) --------------B30169FD350F4A4D2703F01B Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B30169FD350F4A4D2703F01B--