Commit 1aac32df authored by Alexander Korotkov's avatar Alexander Korotkov

Revert 0f5ca02f

0f5ca02f 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
parent 02a2e8b4
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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> <phrase>where <replaceable class="parameter">transaction_mode</replaceable> is one of:</phrase>
...@@ -63,17 +63,6 @@ BEGIN [ WORK | TRANSACTION ] [ <replaceable class="parameter">transaction_mode</ ...@@ -63,17 +63,6 @@ BEGIN [ WORK | TRANSACTION ] [ <replaceable class="parameter">transaction_mode</
<xref linkend="sql-set-transaction"/> <xref linkend="sql-set-transaction"/>
was executed. was executed.
</para> </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>
<refsect1> <refsect1>
...@@ -157,10 +146,6 @@ BEGIN; ...@@ -157,10 +146,6 @@ BEGIN;
different purpose in embedded SQL. You are advised to be careful different purpose in embedded SQL. You are advised to be careful
about the transaction semantics when porting database applications. about the transaction semantics when porting database applications.
</para> </para>
<para>
There is no <literal>WAIT FOR</literal> clause in the SQL standard.
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
......
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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> <phrase>where <replaceable class="parameter">transaction_mode</replaceable> is one of:</phrase>
...@@ -40,17 +40,6 @@ START TRANSACTION [ <replaceable class="parameter">transaction_mode</replaceable ...@@ -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 characteristics, as if <xref linkend="sql-set-transaction"/> was executed. This is the same
as the <xref linkend="sql-begin"/> command. as the <xref linkend="sql-begin"/> command.
</para> </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>
<refsect1> <refsect1>
...@@ -89,10 +78,6 @@ START TRANSACTION [ <replaceable class="parameter">transaction_mode</replaceable ...@@ -89,10 +78,6 @@ START TRANSACTION [ <replaceable class="parameter">transaction_mode</replaceable
omitted. omitted.
</para> </para>
<para>
There is no <literal>WAIT FOR</literal> clause in the SQL standard.
</para>
<para> <para>
See also the compatibility section of <xref linkend="sql-set-transaction"/>. See also the compatibility section of <xref linkend="sql-set-transaction"/>.
</para> </para>
......
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "commands/progress.h" #include "commands/progress.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
#include "commands/wait.h"
#include "common/controldata_utils.h" #include "common/controldata_utils.h"
#include "executor/instrument.h" #include "executor/instrument.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -7157,7 +7156,6 @@ StartupXLOG(void) ...@@ -7157,7 +7156,6 @@ StartupXLOG(void)
do do
{ {
bool switchedTLI = false; bool switchedTLI = false;
XLogRecPtr minWaitedLSN;
#ifdef WAL_DEBUG #ifdef WAL_DEBUG
if (XLOG_DEBUG || if (XLOG_DEBUG ||
...@@ -7361,17 +7359,6 @@ StartupXLOG(void) ...@@ -7361,17 +7359,6 @@ StartupXLOG(void)
break; 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 */ /* Else, try to fetch the next WAL record */
record = ReadRecord(xlogreader, LOG, false); record = ReadRecord(xlogreader, LOG, false);
} while (record != NULL); } while (record != NULL);
......
...@@ -57,7 +57,6 @@ OBJS = \ ...@@ -57,7 +57,6 @@ OBJS = \
user.o \ user.o \
vacuum.o \ vacuum.o \
variable.o \ variable.o \
view.o \ view.o
wait.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk
/*-------------------------------------------------------------------------
*
* 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) ...@@ -3748,22 +3748,10 @@ _copyTransactionStmt(const TransactionStmt *from)
COPY_STRING_FIELD(savepoint_name); COPY_STRING_FIELD(savepoint_name);
COPY_STRING_FIELD(gid); COPY_STRING_FIELD(gid);
COPY_SCALAR_FIELD(chain); COPY_SCALAR_FIELD(chain);
COPY_NODE_FIELD(wait);
return newnode; return newnode;
} }
static WaitClause *
_copyWaitClause(const WaitClause *from)
{
WaitClause *newnode = makeNode(WaitClause);
COPY_STRING_FIELD(lsn);
COPY_SCALAR_FIELD(timeout);
return newnode;
};
static CompositeTypeStmt * static CompositeTypeStmt *
_copyCompositeTypeStmt(const CompositeTypeStmt *from) _copyCompositeTypeStmt(const CompositeTypeStmt *from)
{ {
...@@ -5351,9 +5339,6 @@ copyObjectImpl(const void *from) ...@@ -5351,9 +5339,6 @@ copyObjectImpl(const void *from)
case T_TransactionStmt: case T_TransactionStmt:
retval = _copyTransactionStmt(from); retval = _copyTransactionStmt(from);
break; break;
case T_WaitClause:
retval = _copyWaitClause(from);
break;
case T_CompositeTypeStmt: case T_CompositeTypeStmt:
retval = _copyCompositeTypeStmt(from); retval = _copyCompositeTypeStmt(from);
break; break;
......
...@@ -1541,16 +1541,6 @@ _equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b) ...@@ -1541,16 +1541,6 @@ _equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b)
COMPARE_STRING_FIELD(savepoint_name); COMPARE_STRING_FIELD(savepoint_name);
COMPARE_STRING_FIELD(gid); COMPARE_STRING_FIELD(gid);
COMPARE_SCALAR_FIELD(chain); 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; return true;
} }
...@@ -3401,9 +3391,6 @@ equal(const void *a, const void *b) ...@@ -3401,9 +3391,6 @@ equal(const void *a, const void *b)
case T_TransactionStmt: case T_TransactionStmt:
retval = _equalTransactionStmt(a, b); retval = _equalTransactionStmt(a, b);
break; break;
case T_WaitClause:
retval = _equalWaitClause(a, b);
break;
case T_CompositeTypeStmt: case T_CompositeTypeStmt:
retval = _equalCompositeTypeStmt(a, b); retval = _equalCompositeTypeStmt(a, b);
break; break;
......
...@@ -2786,28 +2786,6 @@ _outDefElem(StringInfo str, const DefElem *node) ...@@ -2786,28 +2786,6 @@ _outDefElem(StringInfo str, const DefElem *node)
WRITE_LOCATION_FIELD(location); 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 static void
_outTableLikeClause(StringInfo str, const TableLikeClause *node) _outTableLikeClause(StringInfo str, const TableLikeClause *node)
{ {
...@@ -4358,12 +4336,6 @@ outNode(StringInfo str, const void *obj) ...@@ -4358,12 +4336,6 @@ outNode(StringInfo str, const void *obj)
case T_PartitionRangeDatum: case T_PartitionRangeDatum:
_outPartitionRangeDatum(str, obj); _outPartitionRangeDatum(str, obj);
break; break;
case T_TransactionStmt:
_outTransactionStmt(str, obj);
break;
case T_WaitClause:
_outWaitClause(str, obj);
break;
default: default:
......
...@@ -601,8 +601,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ...@@ -601,8 +601,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <partboundspec> PartitionBoundSpec %type <partboundspec> PartitionBoundSpec
%type <list> hash_partbound %type <list> hash_partbound
%type <defelt> hash_partbound_elem %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. * 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); ...@@ -672,7 +670,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL 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 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); ...@@ -703,7 +701,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P
TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN 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 TREAT TRIGGER TRIM TRUE_P
TRUNCATE TRUSTED TYPE_P TYPES_P TRUNCATE TRUSTED TYPE_P TYPES_P
...@@ -713,8 +711,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ...@@ -713,8 +711,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VIEWS VOLATILE VERBOSE VERSION_P VIEW VIEWS VOLATILE
WAIT WHEN WHERE WHITESPACE_P WINDOW WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
WITH WITHIN WITHOUT WORK WRAPPER WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES
XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE
...@@ -9958,20 +9955,18 @@ TransactionStmt: ...@@ -9958,20 +9955,18 @@ TransactionStmt:
n->chain = $3; n->chain = $3;
$$ = (Node *)n; $$ = (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); TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_BEGIN; n->kind = TRANS_STMT_BEGIN;
n->options = $3; n->options = $3;
n->wait = $4;
$$ = (Node *)n; $$ = (Node *)n;
} }
| START TRANSACTION transaction_mode_list_or_empty wait_for | START TRANSACTION transaction_mode_list_or_empty
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_START; n->kind = TRANS_STMT_START;
n->options = $3; n->options = $3;
n->wait = $4;
$$ = (Node *)n; $$ = (Node *)n;
} }
| COMMIT opt_transaction opt_transaction_chain | COMMIT opt_transaction opt_transaction_chain
...@@ -14245,25 +14240,6 @@ xml_passing_mech: ...@@ -14245,25 +14240,6 @@ xml_passing_mech:
| BY VALUE_P | 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 * Aggregate decoration clauses
...@@ -15415,7 +15391,6 @@ unreserved_keyword: ...@@ -15415,7 +15391,6 @@ unreserved_keyword:
| LOCK_P | LOCK_P
| LOCKED | LOCKED
| LOGGED | LOGGED
| LSN
| MAPPING | MAPPING
| MATCH | MATCH
| MATERIALIZED | MATERIALIZED
...@@ -15543,7 +15518,6 @@ unreserved_keyword: ...@@ -15543,7 +15518,6 @@ unreserved_keyword:
| TEMPORARY | TEMPORARY
| TEXT_P | TEXT_P
| TIES | TIES
| TIMEOUT
| TRANSACTION | TRANSACTION
| TRANSFORM | TRANSFORM
| TRIGGER | TRIGGER
...@@ -15570,7 +15544,6 @@ unreserved_keyword: ...@@ -15570,7 +15544,6 @@ unreserved_keyword:
| VIEW | VIEW
| VIEWS | VIEWS
| VOLATILE | VOLATILE
| WAIT
| WHITESPACE_P | WHITESPACE_P
| WITHIN | WITHIN
| WITHOUT | WITHOUT
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include "access/subtrans.h" #include "access/subtrans.h"
#include "access/twophase.h" #include "access/twophase.h"
#include "commands/async.h" #include "commands/async.h"
#include "commands/wait.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
...@@ -148,7 +147,6 @@ CreateSharedMemoryAndSemaphores(void) ...@@ -148,7 +147,6 @@ CreateSharedMemoryAndSemaphores(void)
size = add_size(size, BTreeShmemSize()); size = add_size(size, BTreeShmemSize());
size = add_size(size, SyncScanShmemSize()); size = add_size(size, SyncScanShmemSize());
size = add_size(size, AsyncShmemSize()); size = add_size(size, AsyncShmemSize());
size = add_size(size, WaitLSNShmemSize());
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize()); size = add_size(size, ShmemBackendArraySize());
#endif #endif
...@@ -266,11 +264,6 @@ CreateSharedMemoryAndSemaphores(void) ...@@ -266,11 +264,6 @@ CreateSharedMemoryAndSemaphores(void)
SyncScanShmemInit(); SyncScanShmemInit();
AsyncShmemInit(); AsyncShmemInit();
/*
* Init array of Latches in shared memory for WAIT
*/
WaitLSNShmemInit();
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
/* /*
......
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
#include "access/transam.h" #include "access/transam.h"
#include "access/twophase.h" #include "access/twophase.h"
#include "access/xact.h" #include "access/xact.h"
#include "commands/wait.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
...@@ -718,9 +717,6 @@ LockErrorCleanup(void) ...@@ -718,9 +717,6 @@ LockErrorCleanup(void)
AbortStrongLockAcquire(); 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 */ /* Nothing to do if we weren't waiting for a lock */
if (lockAwaited == NULL) if (lockAwaited == NULL)
{ {
......
...@@ -57,7 +57,6 @@ ...@@ -57,7 +57,6 @@
#include "commands/user.h" #include "commands/user.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "commands/view.h" #include "commands/view.h"
#include "commands/wait.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "parser/parse_utilcmd.h" #include "parser/parse_utilcmd.h"
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
...@@ -592,18 +591,6 @@ standard_ProcessUtility(PlannedStmt *pstmt, ...@@ -592,18 +591,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_START: case TRANS_STMT_START:
{ {
ListCell *lc; 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(); BeginTransactionBlock();
foreach(lc, stmt->options) foreach(lc, stmt->options)
......
...@@ -372,6 +372,8 @@ pg_sleep(PG_FUNCTION_ARGS) ...@@ -372,6 +372,8 @@ pg_sleep(PG_FUNCTION_ARGS)
* less than the specified time when WaitLatch is terminated early by a * less than the specified time when WaitLatch is terminated early by a
* non-query-canceling signal such as SIGHUP. * non-query-canceling signal such as SIGHUP.
*/ */
#define GetNowFloat() ((float8) GetCurrentTimestamp() / 1000000.0)
endtime = GetNowFloat() + secs; endtime = GetNowFloat() + secs;
for (;;) for (;;)
......
/*-------------------------------------------------------------------------
*
* 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 ...@@ -492,7 +492,6 @@ typedef enum NodeTag
T_StartReplicationCmd, T_StartReplicationCmd,
T_TimeLineHistoryCmd, T_TimeLineHistoryCmd,
T_SQLCmd, T_SQLCmd,
T_WaitClause,
/* /*
* TAGS FOR RANDOM OTHER STUFF * TAGS FOR RANDOM OTHER STUFF
......
...@@ -1431,17 +1431,6 @@ typedef struct OnConflictClause ...@@ -1431,17 +1431,6 @@ typedef struct OnConflictClause
int location; /* token location, or -1 if unknown */ int location; /* token location, or -1 if unknown */
} OnConflictClause; } 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 - * CommonTableExpr -
* representation of WITH list element * representation of WITH list element
...@@ -3071,7 +3060,6 @@ typedef struct TransactionStmt ...@@ -3071,7 +3060,6 @@ typedef struct TransactionStmt
char *savepoint_name; /* for savepoint commands */ char *savepoint_name; /* for savepoint commands */
char *gid; /* for two-phase-commit related commands */ char *gid; /* for two-phase-commit related commands */
bool chain; /* AND CHAIN option */ bool chain; /* AND CHAIN option */
Node *wait; /* WAIT FOR clause */
} TransactionStmt; } TransactionStmt;
/* ---------------------- /* ----------------------
......
...@@ -243,7 +243,6 @@ PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD) ...@@ -243,7 +243,6 @@ PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD)
PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD) PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD)
PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD) PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD)
PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD) PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
PG_KEYWORD("lsn", LSN, UNRESERVED_KEYWORD)
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD) PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD) PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD) PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
...@@ -411,7 +410,6 @@ PG_KEYWORD("text", TEXT_P, UNRESERVED_KEYWORD) ...@@ -411,7 +410,6 @@ PG_KEYWORD("text", TEXT_P, UNRESERVED_KEYWORD)
PG_KEYWORD("then", THEN, RESERVED_KEYWORD) PG_KEYWORD("then", THEN, RESERVED_KEYWORD)
PG_KEYWORD("ties", TIES, UNRESERVED_KEYWORD) PG_KEYWORD("ties", TIES, UNRESERVED_KEYWORD)
PG_KEYWORD("time", TIME, COL_NAME_KEYWORD) PG_KEYWORD("time", TIME, COL_NAME_KEYWORD)
PG_KEYWORD("timeout", TIMEOUT, UNRESERVED_KEYWORD)
PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD) PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD)
PG_KEYWORD("to", TO, RESERVED_KEYWORD) PG_KEYWORD("to", TO, RESERVED_KEYWORD)
PG_KEYWORD("trailing", TRAILING, RESERVED_KEYWORD) PG_KEYWORD("trailing", TRAILING, RESERVED_KEYWORD)
...@@ -452,7 +450,6 @@ PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD) ...@@ -452,7 +450,6 @@ PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD)
PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD) PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD)
PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD) PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD)
PG_KEYWORD("volatile", VOLATILE, UNRESERVED_KEYWORD) PG_KEYWORD("volatile", VOLATILE, UNRESERVED_KEYWORD)
PG_KEYWORD("wait", WAIT, UNRESERVED_KEYWORD)
PG_KEYWORD("when", WHEN, RESERVED_KEYWORD) PG_KEYWORD("when", WHEN, RESERVED_KEYWORD)
PG_KEYWORD("where", WHERE, RESERVED_KEYWORD) PG_KEYWORD("where", WHERE, RESERVED_KEYWORD)
PG_KEYWORD("whitespace", WHITESPACE_P, UNRESERVED_KEYWORD) PG_KEYWORD("whitespace", WHITESPACE_P, UNRESERVED_KEYWORD)
......
...@@ -109,6 +109,4 @@ extern int date2isoyearday(int year, int mon, int mday); ...@@ -109,6 +109,4 @@ extern int date2isoyearday(int year, int mon, int mday);
extern bool TimestampTimestampTzRequiresRewrite(void); extern bool TimestampTimestampTzRequiresRewrite(void);
#define GetNowFloat() ((float8) GetCurrentTimestamp() / 1000000.0)
#endif /* TIMESTAMP_H */ #endif /* TIMESTAMP_H */
# 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 ...@@ -2621,7 +2621,6 @@ WSABUF
WSADATA WSADATA
WSANETWORKEVENTS WSANETWORKEVENTS
WSAPROTOCOL_INFO WSAPROTOCOL_INFO
WaitClause
WaitEvent WaitEvent
WaitEventActivity WaitEventActivity
WaitEventClient WaitEventClient
...@@ -2629,7 +2628,6 @@ WaitEventIO ...@@ -2629,7 +2628,6 @@ WaitEventIO
WaitEventIPC WaitEventIPC
WaitEventSet WaitEventSet
WaitEventTimeout WaitEventTimeout
WaitLSNState
WaitPMResult WaitPMResult
WalCloseMethod WalCloseMethod
WalLevel WalLevel
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment