Revert 0f5ca02f53
0f5ca02f53
introduces 3 new keywords. It appears to be too much for relatively
small feature. Given now we past feature freeze, it's already late for
discussion of the new syntax. So, revert.
Discussion: https://postgr.es/m/28209.1586294824%40sss.pgh.pa.us
This commit is contained in:
parent
02a2e8b442
commit
1aac32df89
|
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
BEGIN [ WORK | TRANSACTION ] [ <replaceable class="parameter">transaction_mode</replaceable> [, ...] ] [ WAIT FOR LSN <replaceable class="parameter">lsn_value</replaceable> [TIMEOUT <replaceable class="parameter">number_of_milliseconds</replaceable> ] ]
|
||||
BEGIN [ WORK | TRANSACTION ] [ <replaceable class="parameter">transaction_mode</replaceable> [, ...] ]
|
||||
|
||||
<phrase>where <replaceable class="parameter">transaction_mode</replaceable> is one of:</phrase>
|
||||
|
||||
|
@ -63,17 +63,6 @@ BEGIN [ WORK | TRANSACTION ] [ <replaceable class="parameter">transaction_mode</
|
|||
<xref linkend="sql-set-transaction"/>
|
||||
was executed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>WAIT FOR</literal> clause allows to wait for the target log
|
||||
sequence number (<acronym>LSN</acronym>) to be replayed on standby before
|
||||
starting the transaction in <productname>PostgreSQL</productname> databases
|
||||
with master-standby asynchronous replication. Wait time can be limited by
|
||||
specifying a timeout, which is measured in milliseconds and must be a positive
|
||||
integer. If <acronym>LSN</acronym> was not reached before timeout, transaction
|
||||
doesn't begin. Waiting can be interrupted by cancelling
|
||||
<literal>BEGIN</literal> command.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -157,10 +146,6 @@ BEGIN;
|
|||
different purpose in embedded SQL. You are advised to be careful
|
||||
about the transaction semantics when porting database applications.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There is no <literal>WAIT FOR</literal> clause in the SQL standard.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
|
|
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
START TRANSACTION [ <replaceable class="parameter">transaction_mode</replaceable> [, ...] ] [ WAIT FOR LSN <replaceable class="parameter">lsn_value</replaceable> [TIMEOUT <replaceable class="parameter">number_of_milliseconds</replaceable> ] ]
|
||||
START TRANSACTION [ <replaceable class="parameter">transaction_mode</replaceable> [, ...] ]
|
||||
|
||||
<phrase>where <replaceable class="parameter">transaction_mode</replaceable> is one of:</phrase>
|
||||
|
||||
|
@ -40,17 +40,6 @@ START TRANSACTION [ <replaceable class="parameter">transaction_mode</replaceable
|
|||
characteristics, as if <xref linkend="sql-set-transaction"/> was executed. This is the same
|
||||
as the <xref linkend="sql-begin"/> command.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>WAIT FOR</literal> clause allows to wait for the target log
|
||||
sequence number (<acronym>LSN</acronym>) to be replayed on standby before
|
||||
starting the transaction in <productname>PostgreSQL</productname> databases
|
||||
with master-standby asynchronous replication. Wait time can be limited by
|
||||
specifying a timeout, which is measured in milliseconds and must be a positive
|
||||
integer. If <acronym>LSN</acronym> was not reached before timeout, transaction
|
||||
doesn't begin. Waiting can be interrupted by cancelling
|
||||
<literal>START TRANSACTION</literal> command.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -89,10 +78,6 @@ START TRANSACTION [ <replaceable class="parameter">transaction_mode</replaceable
|
|||
omitted.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There is no <literal>WAIT FOR</literal> clause in the SQL standard.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
See also the compatibility section of <xref linkend="sql-set-transaction"/>.
|
||||
</para>
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
#include "catalog/pg_database.h"
|
||||
#include "commands/progress.h"
|
||||
#include "commands/tablespace.h"
|
||||
#include "commands/wait.h"
|
||||
#include "common/controldata_utils.h"
|
||||
#include "executor/instrument.h"
|
||||
#include "miscadmin.h"
|
||||
|
@ -7157,7 +7156,6 @@ StartupXLOG(void)
|
|||
do
|
||||
{
|
||||
bool switchedTLI = false;
|
||||
XLogRecPtr minWaitedLSN;
|
||||
|
||||
#ifdef WAL_DEBUG
|
||||
if (XLOG_DEBUG ||
|
||||
|
@ -7361,17 +7359,6 @@ StartupXLOG(void)
|
|||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we replayed an LSN that someone was waiting for, set
|
||||
* latches in shared memory array to notify the waiter.
|
||||
*/
|
||||
minWaitedLSN = WaitLSNGetMin();
|
||||
if (!XLogRecPtrIsInvalid(minWaitedLSN) &&
|
||||
minWaitedLSN <= XLogCtl->lastReplayedEndRecPtr)
|
||||
{
|
||||
WaitLSNSetLatch(XLogCtl->lastReplayedEndRecPtr);
|
||||
}
|
||||
|
||||
/* Else, try to fetch the next WAL record */
|
||||
record = ReadRecord(xlogreader, LOG, false);
|
||||
} while (record != NULL);
|
||||
|
|
|
@ -57,7 +57,6 @@ OBJS = \
|
|||
user.o \
|
||||
vacuum.o \
|
||||
variable.o \
|
||||
view.o \
|
||||
wait.o
|
||||
view.o
|
||||
|
||||
include $(top_srcdir)/src/backend/common.mk
|
||||
|
|
|
@ -1,295 +0,0 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* wait.c
|
||||
* Implements WAIT FOR clause for BEGIN and START TRANSACTION commands.
|
||||
* This clause allows waiting for given LSN to be replayed on standby.
|
||||
*
|
||||
* Copyright (c) 2020, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/backend/commands/wait.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "access/xlog.h"
|
||||
#include "access/xlogdefs.h"
|
||||
#include "commands/wait.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "pgstat.h"
|
||||
#include "storage/backendid.h"
|
||||
#include "storage/pmsignal.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/shmem.h"
|
||||
#include "storage/sinvaladt.h"
|
||||
#include "storage/spin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/pg_lsn.h"
|
||||
#include "utils/timestamp.h"
|
||||
|
||||
/*
|
||||
* Shared memory structure representing information about LSNs, which backends
|
||||
* are waiting for replay.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
slock_t mutex; /* mutex protecting the fields below */
|
||||
int max_backend_id; /* max backend_id present in lsns[] */
|
||||
pg_atomic_uint64 min_lsn; /* minimal waited LSN */
|
||||
/* per-backend array of waited LSNs */
|
||||
XLogRecPtr lsns[FLEXIBLE_ARRAY_MEMBER];
|
||||
} WaitLSNState;
|
||||
|
||||
static WaitLSNState * state;
|
||||
|
||||
/*
|
||||
* Add the wait event of the current backend to shared memory array
|
||||
*/
|
||||
static void
|
||||
WaitLSNAdd(XLogRecPtr lsn_to_wait)
|
||||
{
|
||||
SpinLockAcquire(&state->mutex);
|
||||
if (state->max_backend_id < MyBackendId)
|
||||
state->max_backend_id = MyBackendId;
|
||||
|
||||
state->lsns[MyBackendId] = lsn_to_wait;
|
||||
|
||||
if (lsn_to_wait < state->min_lsn.value)
|
||||
state->min_lsn.value = lsn_to_wait;
|
||||
SpinLockRelease(&state->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete wait event of the current backend from the shared memory array.
|
||||
*/
|
||||
void
|
||||
WaitLSNDelete(void)
|
||||
{
|
||||
int i;
|
||||
XLogRecPtr deleted_lsn;
|
||||
|
||||
SpinLockAcquire(&state->mutex);
|
||||
|
||||
deleted_lsn = state->lsns[MyBackendId];
|
||||
state->lsns[MyBackendId] = InvalidXLogRecPtr;
|
||||
|
||||
/* If we are deleting the minimal LSN, then choose the next min_lsn */
|
||||
if (!XLogRecPtrIsInvalid(deleted_lsn) &&
|
||||
deleted_lsn == state->min_lsn.value)
|
||||
{
|
||||
state->min_lsn.value = InvalidXLogRecPtr;
|
||||
for (i = 2; i <= state->max_backend_id; i++)
|
||||
{
|
||||
if (!XLogRecPtrIsInvalid(state->lsns[i]) &&
|
||||
(state->lsns[i] < state->min_lsn.value ||
|
||||
XLogRecPtrIsInvalid(state->min_lsn.value)))
|
||||
{
|
||||
state->min_lsn.value = state->lsns[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If deleting from the end of the array, shorten the array's used part */
|
||||
if (state->max_backend_id == MyBackendId)
|
||||
{
|
||||
for (i = (MyBackendId); i >= 2; i--)
|
||||
if (!XLogRecPtrIsInvalid(state->lsns[i]))
|
||||
{
|
||||
state->max_backend_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SpinLockRelease(&state->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Report amount of shared memory space needed for WaitLSNState
|
||||
*/
|
||||
Size
|
||||
WaitLSNShmemSize(void)
|
||||
{
|
||||
Size size;
|
||||
|
||||
size = offsetof(WaitLSNState, lsns);
|
||||
size = add_size(size, mul_size(MaxBackends + 1, sizeof(XLogRecPtr)));
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an shared memory structure for waiting for LSN
|
||||
*/
|
||||
void
|
||||
WaitLSNShmemInit(void)
|
||||
{
|
||||
bool found;
|
||||
uint32 i;
|
||||
|
||||
state = (WaitLSNState *) ShmemInitStruct("pg_wait_lsn",
|
||||
WaitLSNShmemSize(),
|
||||
&found);
|
||||
if (!found)
|
||||
{
|
||||
SpinLockInit(&state->mutex);
|
||||
|
||||
for (i = 0; i < (MaxBackends + 1); i++)
|
||||
state->lsns[i] = InvalidXLogRecPtr;
|
||||
|
||||
state->max_backend_id = 0;
|
||||
pg_atomic_init_u64(&state->min_lsn, InvalidXLogRecPtr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set latches in shared memory to signal that new LSN has been replayed
|
||||
*/
|
||||
void
|
||||
WaitLSNSetLatch(XLogRecPtr cur_lsn)
|
||||
{
|
||||
uint32 i;
|
||||
int max_backend_id;
|
||||
PGPROC *backend;
|
||||
|
||||
SpinLockAcquire(&state->mutex);
|
||||
max_backend_id = state->max_backend_id;
|
||||
|
||||
for (i = 2; i <= max_backend_id; i++)
|
||||
{
|
||||
backend = BackendIdGetProc(i);
|
||||
|
||||
if (backend && state->lsns[i] != 0 &&
|
||||
state->lsns[i] <= cur_lsn)
|
||||
{
|
||||
SetLatch(&backend->procLatch);
|
||||
}
|
||||
}
|
||||
SpinLockRelease(&state->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get minimal LSN that some backend is waiting for
|
||||
*/
|
||||
XLogRecPtr
|
||||
WaitLSNGetMin(void)
|
||||
{
|
||||
return state->min_lsn.value;
|
||||
}
|
||||
|
||||
/*
|
||||
* On WAIT use a latch to wait till LSN is replayed, postmaster dies or timeout
|
||||
* happens. Timeout is specified in milliseconds. Returns true if LSN was
|
||||
* reached and false otherwise.
|
||||
*/
|
||||
bool
|
||||
WaitLSNUtility(XLogRecPtr target_lsn, const int timeout_ms)
|
||||
{
|
||||
XLogRecPtr cur_lsn;
|
||||
int latch_events;
|
||||
float8 endtime;
|
||||
bool res = false;
|
||||
bool wait_forever = (timeout_ms <= 0);
|
||||
|
||||
endtime = GetNowFloat() + timeout_ms / 1000.0;
|
||||
|
||||
latch_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
|
||||
|
||||
/* Check if we already reached the needed LSN */
|
||||
cur_lsn = GetXLogReplayRecPtr(NULL);
|
||||
if (cur_lsn >= target_lsn)
|
||||
return true;
|
||||
|
||||
WaitLSNAdd(target_lsn);
|
||||
ResetLatch(MyLatch);
|
||||
|
||||
/* Recheck if LSN was reached while WaitLSNAdd() and ResetLatch() */
|
||||
cur_lsn = GetXLogReplayRecPtr(NULL);
|
||||
if (cur_lsn >= target_lsn)
|
||||
return true;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int rc;
|
||||
float8 time_left = 0;
|
||||
long time_left_ms = 0;
|
||||
|
||||
time_left = endtime - GetNowFloat();
|
||||
|
||||
/* Use 1 second as the default timeout to check for interrupts */
|
||||
if (wait_forever || time_left < 0 || time_left > 1.0)
|
||||
time_left_ms = 1000;
|
||||
else
|
||||
time_left_ms = (long) ceil(time_left * 1000.0);
|
||||
|
||||
/* If interrupt, LockErrorCleanup() will do WaitLSNDelete() for us */
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
/* If postmaster dies, finish immediately */
|
||||
if (!PostmasterIsAlive())
|
||||
break;
|
||||
|
||||
rc = WaitLatch(MyLatch, latch_events, time_left_ms,
|
||||
WAIT_EVENT_CLIENT_READ);
|
||||
|
||||
ResetLatch(MyLatch);
|
||||
|
||||
if (rc & WL_LATCH_SET)
|
||||
cur_lsn = GetXLogReplayRecPtr(NULL);
|
||||
|
||||
if (rc & WL_TIMEOUT)
|
||||
{
|
||||
time_left = endtime - GetNowFloat();
|
||||
/* If the time specified by user has passed, stop waiting */
|
||||
if (!wait_forever && time_left <= 0.0)
|
||||
break;
|
||||
cur_lsn = GetXLogReplayRecPtr(NULL);
|
||||
}
|
||||
|
||||
/* If LSN has been replayed */
|
||||
if (target_lsn <= cur_lsn)
|
||||
break;
|
||||
}
|
||||
|
||||
WaitLSNDelete();
|
||||
|
||||
if (cur_lsn < target_lsn)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
errmsg("didn't start transaction because LSN was not reached"),
|
||||
errhint("Try to increase wait timeout.")));
|
||||
else
|
||||
res = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of WAIT FOR clause for BEGIN and START TRANSACTION commands
|
||||
*/
|
||||
int
|
||||
WaitLSNMain(WaitClause *stmt, DestReceiver *dest)
|
||||
{
|
||||
TupleDesc tupdesc;
|
||||
TupOutputState *tstate;
|
||||
XLogRecPtr target_lsn;
|
||||
bool res = false;
|
||||
|
||||
target_lsn = DatumGetLSN(DirectFunctionCall1(pg_lsn_in,
|
||||
CStringGetDatum(stmt->lsn)));
|
||||
res = WaitLSNUtility(target_lsn, stmt->timeout);
|
||||
|
||||
/* Need a tuple descriptor representing a single TEXT column */
|
||||
tupdesc = CreateTemplateTupleDesc(1);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "LSN reached", TEXTOID, -1, 0);
|
||||
|
||||
/* Prepare for projection of tuples */
|
||||
tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsMinimalTuple);
|
||||
|
||||
/* Send the result */
|
||||
do_text_output_oneline(tstate, res ? "t" : "f");
|
||||
end_tup_output(tstate);
|
||||
return res;
|
||||
}
|
|
@ -3748,22 +3748,10 @@ _copyTransactionStmt(const TransactionStmt *from)
|
|||
COPY_STRING_FIELD(savepoint_name);
|
||||
COPY_STRING_FIELD(gid);
|
||||
COPY_SCALAR_FIELD(chain);
|
||||
COPY_NODE_FIELD(wait);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static WaitClause *
|
||||
_copyWaitClause(const WaitClause *from)
|
||||
{
|
||||
WaitClause *newnode = makeNode(WaitClause);
|
||||
|
||||
COPY_STRING_FIELD(lsn);
|
||||
COPY_SCALAR_FIELD(timeout);
|
||||
|
||||
return newnode;
|
||||
};
|
||||
|
||||
static CompositeTypeStmt *
|
||||
_copyCompositeTypeStmt(const CompositeTypeStmt *from)
|
||||
{
|
||||
|
@ -5351,9 +5339,6 @@ copyObjectImpl(const void *from)
|
|||
case T_TransactionStmt:
|
||||
retval = _copyTransactionStmt(from);
|
||||
break;
|
||||
case T_WaitClause:
|
||||
retval = _copyWaitClause(from);
|
||||
break;
|
||||
case T_CompositeTypeStmt:
|
||||
retval = _copyCompositeTypeStmt(from);
|
||||
break;
|
||||
|
|
|
@ -1541,16 +1541,6 @@ _equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b)
|
|||
COMPARE_STRING_FIELD(savepoint_name);
|
||||
COMPARE_STRING_FIELD(gid);
|
||||
COMPARE_SCALAR_FIELD(chain);
|
||||
COMPARE_NODE_FIELD(wait);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalWaitClause(const WaitClause *a, const WaitClause *b)
|
||||
{
|
||||
COMPARE_STRING_FIELD(lsn);
|
||||
COMPARE_SCALAR_FIELD(timeout);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3401,9 +3391,6 @@ equal(const void *a, const void *b)
|
|||
case T_TransactionStmt:
|
||||
retval = _equalTransactionStmt(a, b);
|
||||
break;
|
||||
case T_WaitClause:
|
||||
retval = _equalWaitClause(a, b);
|
||||
break;
|
||||
case T_CompositeTypeStmt:
|
||||
retval = _equalCompositeTypeStmt(a, b);
|
||||
break;
|
||||
|
|
|
@ -2786,28 +2786,6 @@ _outDefElem(StringInfo str, const DefElem *node)
|
|||
WRITE_LOCATION_FIELD(location);
|
||||
}
|
||||
|
||||
static void
|
||||
_outTransactionStmt(StringInfo str, const TransactionStmt *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("TRANSACTIONSTMT");
|
||||
|
||||
WRITE_STRING_FIELD(savepoint_name);
|
||||
WRITE_STRING_FIELD(gid);
|
||||
WRITE_NODE_FIELD(options);
|
||||
WRITE_BOOL_FIELD(chain);
|
||||
WRITE_ENUM_FIELD(kind, TransactionStmtKind);
|
||||
WRITE_NODE_FIELD(wait);
|
||||
}
|
||||
|
||||
static void
|
||||
_outWaitClause(StringInfo str, const WaitClause *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("WAITCLAUSE");
|
||||
|
||||
WRITE_STRING_FIELD(lsn);
|
||||
WRITE_UINT_FIELD(timeout);
|
||||
}
|
||||
|
||||
static void
|
||||
_outTableLikeClause(StringInfo str, const TableLikeClause *node)
|
||||
{
|
||||
|
@ -4358,12 +4336,6 @@ outNode(StringInfo str, const void *obj)
|
|||
case T_PartitionRangeDatum:
|
||||
_outPartitionRangeDatum(str, obj);
|
||||
break;
|
||||
case T_TransactionStmt:
|
||||
_outTransactionStmt(str, obj);
|
||||
break;
|
||||
case T_WaitClause:
|
||||
_outWaitClause(str, obj);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
|
|
|
@ -601,8 +601,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||
%type <partboundspec> PartitionBoundSpec
|
||||
%type <list> hash_partbound
|
||||
%type <defelt> hash_partbound_elem
|
||||
%type <ival> wait_time
|
||||
%type <node> wait_for
|
||||
|
||||
/*
|
||||
* Non-keyword token types. These are hard-wired into the "flex" lexer.
|
||||
|
@ -672,7 +670,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||
|
||||
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
|
||||
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
|
||||
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED LSN
|
||||
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
|
||||
|
||||
MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
|
||||
|
||||
|
@ -703,7 +701,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||
SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P
|
||||
|
||||
TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
|
||||
TIES TIME TIMEOUT TIMESTAMP TO TRAILING TRANSACTION TRANSFORM
|
||||
TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM
|
||||
TREAT TRIGGER TRIM TRUE_P
|
||||
TRUNCATE TRUSTED TYPE_P TYPES_P
|
||||
|
||||
|
@ -713,8 +711,7 @@ 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
|
||||
|
||||
WAIT WHEN WHERE WHITESPACE_P WINDOW
|
||||
WITH WITHIN WITHOUT WORK WRAPPER WRITE
|
||||
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
|
||||
|
||||
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES
|
||||
XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE
|
||||
|
@ -9958,20 +9955,18 @@ TransactionStmt:
|
|||
n->chain = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| BEGIN_P opt_transaction transaction_mode_list_or_empty wait_for
|
||||
| BEGIN_P opt_transaction transaction_mode_list_or_empty
|
||||
{
|
||||
TransactionStmt *n = makeNode(TransactionStmt);
|
||||
n->kind = TRANS_STMT_BEGIN;
|
||||
n->options = $3;
|
||||
n->wait = $4;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| START TRANSACTION transaction_mode_list_or_empty wait_for
|
||||
| START TRANSACTION transaction_mode_list_or_empty
|
||||
{
|
||||
TransactionStmt *n = makeNode(TransactionStmt);
|
||||
n->kind = TRANS_STMT_START;
|
||||
n->options = $3;
|
||||
n->wait = $4;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| COMMIT opt_transaction opt_transaction_chain
|
||||
|
@ -14245,25 +14240,6 @@ xml_passing_mech:
|
|||
| BY VALUE_P
|
||||
;
|
||||
|
||||
/*
|
||||
* WAIT FOR clause of BEGIN and START TRANSACTION statements
|
||||
*/
|
||||
wait_for:
|
||||
WAIT FOR LSN Sconst wait_time
|
||||
{
|
||||
WaitClause *n = makeNode(WaitClause);
|
||||
n->lsn = $4;
|
||||
n->timeout = $5;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
wait_time:
|
||||
TIMEOUT Iconst { $$ = $2; }
|
||||
| /* EMPTY */ { $$ = 0; }
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
* Aggregate decoration clauses
|
||||
|
@ -15415,7 +15391,6 @@ unreserved_keyword:
|
|||
| LOCK_P
|
||||
| LOCKED
|
||||
| LOGGED
|
||||
| LSN
|
||||
| MAPPING
|
||||
| MATCH
|
||||
| MATERIALIZED
|
||||
|
@ -15543,7 +15518,6 @@ unreserved_keyword:
|
|||
| TEMPORARY
|
||||
| TEXT_P
|
||||
| TIES
|
||||
| TIMEOUT
|
||||
| TRANSACTION
|
||||
| TRANSFORM
|
||||
| TRIGGER
|
||||
|
@ -15570,7 +15544,6 @@ unreserved_keyword:
|
|||
| VIEW
|
||||
| VIEWS
|
||||
| VOLATILE
|
||||
| WAIT
|
||||
| WHITESPACE_P
|
||||
| WITHIN
|
||||
| WITHOUT
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "access/subtrans.h"
|
||||
#include "access/twophase.h"
|
||||
#include "commands/async.h"
|
||||
#include "commands/wait.h"
|
||||
#include "miscadmin.h"
|
||||
#include "pgstat.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
|
@ -148,7 +147,6 @@ CreateSharedMemoryAndSemaphores(void)
|
|||
size = add_size(size, BTreeShmemSize());
|
||||
size = add_size(size, SyncScanShmemSize());
|
||||
size = add_size(size, AsyncShmemSize());
|
||||
size = add_size(size, WaitLSNShmemSize());
|
||||
#ifdef EXEC_BACKEND
|
||||
size = add_size(size, ShmemBackendArraySize());
|
||||
#endif
|
||||
|
@ -266,11 +264,6 @@ CreateSharedMemoryAndSemaphores(void)
|
|||
SyncScanShmemInit();
|
||||
AsyncShmemInit();
|
||||
|
||||
/*
|
||||
* Init array of Latches in shared memory for WAIT
|
||||
*/
|
||||
WaitLSNShmemInit();
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
|
||||
/*
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include "access/transam.h"
|
||||
#include "access/twophase.h"
|
||||
#include "access/xact.h"
|
||||
#include "commands/wait.h"
|
||||
#include "miscadmin.h"
|
||||
#include "pgstat.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
|
@ -718,9 +717,6 @@ LockErrorCleanup(void)
|
|||
|
||||
AbortStrongLockAcquire();
|
||||
|
||||
/* If BEGIN WAIT FOR LSN was interrupted, then stop waiting for that LSN */
|
||||
WaitLSNDelete();
|
||||
|
||||
/* Nothing to do if we weren't waiting for a lock */
|
||||
if (lockAwaited == NULL)
|
||||
{
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
#include "commands/user.h"
|
||||
#include "commands/vacuum.h"
|
||||
#include "commands/view.h"
|
||||
#include "commands/wait.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_utilcmd.h"
|
||||
#include "postmaster/bgwriter.h"
|
||||
|
@ -592,18 +591,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
|
|||
case TRANS_STMT_START:
|
||||
{
|
||||
ListCell *lc;
|
||||
WaitClause *waitstmt = (WaitClause *) stmt->wait;
|
||||
|
||||
/* WAIT FOR cannot be used on master */
|
||||
if (stmt->wait && !RecoveryInProgress())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("WAIT FOR can only be "
|
||||
"used on standby")));
|
||||
|
||||
/* If needed to WAIT FOR something but failed */
|
||||
if (stmt->wait && WaitLSNMain(waitstmt, dest) == 0)
|
||||
break;
|
||||
|
||||
BeginTransactionBlock();
|
||||
foreach(lc, stmt->options)
|
||||
|
|
|
@ -372,6 +372,8 @@ pg_sleep(PG_FUNCTION_ARGS)
|
|||
* less than the specified time when WaitLatch is terminated early by a
|
||||
* non-query-canceling signal such as SIGHUP.
|
||||
*/
|
||||
#define GetNowFloat() ((float8) GetCurrentTimestamp() / 1000000.0)
|
||||
|
||||
endtime = GetNowFloat() + secs;
|
||||
|
||||
for (;;)
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* wait.h
|
||||
* prototypes for commands/wait.c
|
||||
*
|
||||
* Copyright (c) 2020, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/include/commands/wait.h
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef WAIT_H
|
||||
#define WAIT_H
|
||||
|
||||
#include "tcop/dest.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
extern bool WaitLSNUtility(XLogRecPtr lsn, const int timeout_ms);
|
||||
extern Size WaitLSNShmemSize(void);
|
||||
extern void WaitLSNShmemInit(void);
|
||||
extern void WaitLSNSetLatch(XLogRecPtr cur_lsn);
|
||||
extern XLogRecPtr WaitLSNGetMin(void);
|
||||
extern int WaitLSNMain(WaitClause *stmt, DestReceiver *dest);
|
||||
extern void WaitLSNDelete(void);
|
||||
|
||||
#endif /* WAIT_H */
|
|
@ -492,7 +492,6 @@ typedef enum NodeTag
|
|||
T_StartReplicationCmd,
|
||||
T_TimeLineHistoryCmd,
|
||||
T_SQLCmd,
|
||||
T_WaitClause,
|
||||
|
||||
/*
|
||||
* TAGS FOR RANDOM OTHER STUFF
|
||||
|
|
|
@ -1431,17 +1431,6 @@ typedef struct OnConflictClause
|
|||
int location; /* token location, or -1 if unknown */
|
||||
} OnConflictClause;
|
||||
|
||||
/*
|
||||
* WaitClause -
|
||||
* representation of WAIT FOR clause for BEGIN and START TRANSACTION.
|
||||
*/
|
||||
typedef struct WaitClause
|
||||
{
|
||||
NodeTag type;
|
||||
char *lsn; /* LSN to wait for */
|
||||
int timeout; /* Number of milliseconds to limit wait time */
|
||||
} WaitClause;
|
||||
|
||||
/*
|
||||
* CommonTableExpr -
|
||||
* representation of WITH list element
|
||||
|
@ -3071,7 +3060,6 @@ typedef struct TransactionStmt
|
|||
char *savepoint_name; /* for savepoint commands */
|
||||
char *gid; /* for two-phase-commit related commands */
|
||||
bool chain; /* AND CHAIN option */
|
||||
Node *wait; /* WAIT FOR clause */
|
||||
} TransactionStmt;
|
||||
|
||||
/* ----------------------
|
||||
|
|
|
@ -243,7 +243,6 @@ PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD)
|
|||
PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("lsn", LSN, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
|
||||
|
@ -411,7 +410,6 @@ PG_KEYWORD("text", TEXT_P, UNRESERVED_KEYWORD)
|
|||
PG_KEYWORD("then", THEN, RESERVED_KEYWORD)
|
||||
PG_KEYWORD("ties", TIES, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("time", TIME, COL_NAME_KEYWORD)
|
||||
PG_KEYWORD("timeout", TIMEOUT, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD)
|
||||
PG_KEYWORD("to", TO, RESERVED_KEYWORD)
|
||||
PG_KEYWORD("trailing", TRAILING, RESERVED_KEYWORD)
|
||||
|
@ -452,7 +450,6 @@ 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("wait", WAIT, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("when", WHEN, RESERVED_KEYWORD)
|
||||
PG_KEYWORD("where", WHERE, RESERVED_KEYWORD)
|
||||
PG_KEYWORD("whitespace", WHITESPACE_P, UNRESERVED_KEYWORD)
|
||||
|
|
|
@ -109,6 +109,4 @@ extern int date2isoyearday(int year, int mon, int mday);
|
|||
|
||||
extern bool TimestampTimestampTzRequiresRewrite(void);
|
||||
|
||||
#define GetNowFloat() ((float8) GetCurrentTimestamp() / 1000000.0)
|
||||
|
||||
#endif /* TIMESTAMP_H */
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
# Checks for BEGIN WAIT FOR LSN
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use PostgresNode;
|
||||
use TestLib;
|
||||
use Test::More tests => 8;
|
||||
|
||||
# Initialize master node
|
||||
my $node_master = get_new_node('master');
|
||||
$node_master->init(allows_streaming => 1);
|
||||
$node_master->start;
|
||||
|
||||
# And some content and take a backup
|
||||
$node_master->safe_psql('postgres',
|
||||
"CREATE TABLE wait_test AS SELECT generate_series(1,10) AS a");
|
||||
my $backup_name = 'my_backup';
|
||||
$node_master->backup($backup_name);
|
||||
|
||||
# Using the backup, create a streaming standby with a 1 second delay
|
||||
my $node_standby = get_new_node('standby');
|
||||
my $delay = 1;
|
||||
$node_standby->init_from_backup($node_master, $backup_name,
|
||||
has_streaming => 1);
|
||||
$node_standby->append_conf('postgresql.conf', qq[
|
||||
recovery_min_apply_delay = '${delay}s'
|
||||
]);
|
||||
$node_standby->start;
|
||||
|
||||
|
||||
# Check that timeouts make us wait for the specified time (1s here)
|
||||
my $current_time = $node_standby->safe_psql('postgres', "SELECT now()");
|
||||
my $two_seconds = 2000; # in milliseconds
|
||||
my $start_time = time();
|
||||
$node_standby->safe_psql('postgres',
|
||||
"BEGIN WAIT FOR LSN '0/FFFFFFFF' TIMEOUT $two_seconds");
|
||||
my $time_waited = (time() - $start_time) * 1000; # convert to milliseconds
|
||||
ok($time_waited >= $two_seconds, "WAIT FOR TIMEOUT waits for enough time");
|
||||
|
||||
|
||||
# Check that timeouts let us stop waiting right away, before reaching target LSN
|
||||
$node_master->safe_psql('postgres',
|
||||
"INSERT INTO wait_test VALUES (generate_series(11, 20))");
|
||||
my $lsn1 = $node_master->safe_psql('postgres', "SELECT pg_current_wal_lsn()");
|
||||
my ($ret, $out, $err) = $node_standby->psql('postgres',
|
||||
"BEGIN WAIT FOR LSN '$lsn1' TIMEOUT 1");
|
||||
|
||||
ok($ret == 0, "zero return value when failed to WAIT FOR LSN on standby");
|
||||
ok($err =~ /WARNING: didn't start transaction because LSN was not reached/,
|
||||
"correct error message when failed to WAIT FOR LSN on standby");
|
||||
ok($out eq "f", "if given too little wait time, WAIT doesn't reach target LSN");
|
||||
|
||||
|
||||
# Check that WAIT FOR works fine and reaches target LSN if given no timeout
|
||||
|
||||
# Add data on master, memorize master's last LSN
|
||||
$node_master->safe_psql('postgres',
|
||||
"INSERT INTO wait_test VALUES (generate_series(21, 30))");
|
||||
my $lsn2 = $node_master->safe_psql('postgres', "SELECT pg_current_wal_lsn()");
|
||||
|
||||
# Wait for it to appear on replica, memorize replica's last LSN
|
||||
$node_standby->safe_psql('postgres',
|
||||
"BEGIN WAIT FOR LSN '$lsn2'");
|
||||
my $reached_lsn = $node_standby->safe_psql('postgres',
|
||||
"SELECT pg_last_wal_replay_lsn()");
|
||||
|
||||
# Make sure that master's and replica's LSNs are the same after WAIT
|
||||
my $compare_lsns = $node_standby->safe_psql('postgres',
|
||||
"SELECT pg_lsn_cmp('$reached_lsn'::pg_lsn, '$lsn2'::pg_lsn)");
|
||||
ok($compare_lsns eq 0,
|
||||
"standby reached the same LSN as master before starting transaction");
|
||||
|
||||
|
||||
# Make sure that it's not allowed to use WAIT FOR on master
|
||||
($ret, $out, $err) = $node_master->psql('postgres',
|
||||
"BEGIN WAIT FOR LSN '0/FFFFFFFF'");
|
||||
|
||||
ok($ret != 0, "non-zero return value when trying to WAIT FOR LSN on master");
|
||||
ok($err =~ /ERROR: WAIT FOR can only be used on standby/,
|
||||
"correct error message when trying to WAIT FOR LSN on master");
|
||||
ok($out eq '', "empty output when trying to WAIT FOR LSN on master");
|
||||
|
||||
|
||||
$node_standby->stop;
|
||||
$node_master->stop;
|
|
@ -2621,7 +2621,6 @@ WSABUF
|
|||
WSADATA
|
||||
WSANETWORKEVENTS
|
||||
WSAPROTOCOL_INFO
|
||||
WaitClause
|
||||
WaitEvent
|
||||
WaitEventActivity
|
||||
WaitEventClient
|
||||
|
@ -2629,7 +2628,6 @@ WaitEventIO
|
|||
WaitEventIPC
|
||||
WaitEventSet
|
||||
WaitEventTimeout
|
||||
WaitLSNState
|
||||
WaitPMResult
|
||||
WalCloseMethod
|
||||
WalLevel
|
||||
|
|
Loading…
Reference in New Issue