mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Advance transaction timestamp for intra-procedure transactions.
Per discussion, this behavior seems less astonishing than not doing so. Peter Eisentraut and Tom Lane Discussion: https://postgr.es/m/20180920234040.GC29981@momjian.us
This commit is contained in:
		@@ -1906,20 +1906,26 @@ StartTransaction(void)
 | 
			
		||||
	TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * set transaction_timestamp() (a/k/a now()).  We want this to be the same
 | 
			
		||||
	 * as the first command's statement_timestamp(), so don't do a fresh
 | 
			
		||||
	 * GetCurrentTimestamp() call (which'd be expensive anyway).  In a
 | 
			
		||||
	 * parallel worker, this should already have been provided by a call to
 | 
			
		||||
	 * set transaction_timestamp() (a/k/a now()).  Normally, we want this to
 | 
			
		||||
	 * be the same as the first command's statement_timestamp(), so don't do a
 | 
			
		||||
	 * fresh GetCurrentTimestamp() call (which'd be expensive anyway).  But
 | 
			
		||||
	 * for transactions started inside procedures (i.e., nonatomic SPI
 | 
			
		||||
	 * contexts), we do need to advance the timestamp.  Also, in a parallel
 | 
			
		||||
	 * worker, the timestamp should already have been provided by a call to
 | 
			
		||||
	 * SetParallelStartTimestamps().
 | 
			
		||||
	 *
 | 
			
		||||
	 * Also, mark xactStopTimestamp as unset.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!IsParallelWorker())
 | 
			
		||||
	{
 | 
			
		||||
		if (!SPI_inside_nonatomic_context())
 | 
			
		||||
			xactStartTimestamp = stmtStartTimestamp;
 | 
			
		||||
		else
 | 
			
		||||
			xactStartTimestamp = GetCurrentTimestamp();
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		Assert(xactStartTimestamp != 0);
 | 
			
		||||
	xactStopTimestamp = 0;
 | 
			
		||||
	pgstat_report_xact_timestamp(xactStartTimestamp);
 | 
			
		||||
	/* Mark xactStopTimestamp as unset. */
 | 
			
		||||
	xactStopTimestamp = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * initialize current transaction state fields
 | 
			
		||||
 
 | 
			
		||||
@@ -423,6 +423,19 @@ AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Are we executing inside a procedure (that is, a nonatomic SPI context)?
 | 
			
		||||
 */
 | 
			
		||||
bool
 | 
			
		||||
SPI_inside_nonatomic_context(void)
 | 
			
		||||
{
 | 
			
		||||
	if (_SPI_current == NULL)
 | 
			
		||||
		return false;			/* not in any SPI context at all */
 | 
			
		||||
	if (_SPI_current->atomic)
 | 
			
		||||
		return false;			/* it's atomic (ie function not procedure) */
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Parse, plan, and execute a query string */
 | 
			
		||||
int
 | 
			
		||||
 
 | 
			
		||||
@@ -166,5 +166,6 @@ extern void SPI_rollback(void);
 | 
			
		||||
extern void SPICleanup(void);
 | 
			
		||||
extern void AtEOXact_SPI(bool isCommit);
 | 
			
		||||
extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
 | 
			
		||||
extern bool SPI_inside_nonatomic_context(void);
 | 
			
		||||
 | 
			
		||||
#endif							/* SPI_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -493,6 +493,36 @@ CALL transaction_test10b(10);
 | 
			
		||||
 9
 | 
			
		||||
(1 row)
 | 
			
		||||
 | 
			
		||||
-- transaction timestamp vs. statement timestamp
 | 
			
		||||
CREATE PROCEDURE transaction_test11()
 | 
			
		||||
LANGUAGE plpgsql
 | 
			
		||||
AS $$
 | 
			
		||||
DECLARE
 | 
			
		||||
  s1 timestamp with time zone;
 | 
			
		||||
  s2 timestamp with time zone;
 | 
			
		||||
  s3 timestamp with time zone;
 | 
			
		||||
  t1 timestamp with time zone;
 | 
			
		||||
  t2 timestamp with time zone;
 | 
			
		||||
  t3 timestamp with time zone;
 | 
			
		||||
BEGIN
 | 
			
		||||
  s1 := statement_timestamp();
 | 
			
		||||
  t1 := transaction_timestamp();
 | 
			
		||||
  ASSERT s1 = t1;
 | 
			
		||||
  PERFORM pg_sleep(0.001);
 | 
			
		||||
  COMMIT;
 | 
			
		||||
  s2 := statement_timestamp();
 | 
			
		||||
  t2 := transaction_timestamp();
 | 
			
		||||
  ASSERT s2 = s1;
 | 
			
		||||
  ASSERT t2 > t1;
 | 
			
		||||
  PERFORM pg_sleep(0.001);
 | 
			
		||||
  ROLLBACK;
 | 
			
		||||
  s3 := statement_timestamp();
 | 
			
		||||
  t3 := transaction_timestamp();
 | 
			
		||||
  ASSERT s3 = s1;
 | 
			
		||||
  ASSERT t3 > t2;
 | 
			
		||||
END;
 | 
			
		||||
$$;
 | 
			
		||||
CALL transaction_test11();
 | 
			
		||||
DROP TABLE test1;
 | 
			
		||||
DROP TABLE test2;
 | 
			
		||||
DROP TABLE test3;
 | 
			
		||||
 
 | 
			
		||||
@@ -412,6 +412,39 @@ $$;
 | 
			
		||||
CALL transaction_test10b(10);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- transaction timestamp vs. statement timestamp
 | 
			
		||||
CREATE PROCEDURE transaction_test11()
 | 
			
		||||
LANGUAGE plpgsql
 | 
			
		||||
AS $$
 | 
			
		||||
DECLARE
 | 
			
		||||
  s1 timestamp with time zone;
 | 
			
		||||
  s2 timestamp with time zone;
 | 
			
		||||
  s3 timestamp with time zone;
 | 
			
		||||
  t1 timestamp with time zone;
 | 
			
		||||
  t2 timestamp with time zone;
 | 
			
		||||
  t3 timestamp with time zone;
 | 
			
		||||
BEGIN
 | 
			
		||||
  s1 := statement_timestamp();
 | 
			
		||||
  t1 := transaction_timestamp();
 | 
			
		||||
  ASSERT s1 = t1;
 | 
			
		||||
  PERFORM pg_sleep(0.001);
 | 
			
		||||
  COMMIT;
 | 
			
		||||
  s2 := statement_timestamp();
 | 
			
		||||
  t2 := transaction_timestamp();
 | 
			
		||||
  ASSERT s2 = s1;
 | 
			
		||||
  ASSERT t2 > t1;
 | 
			
		||||
  PERFORM pg_sleep(0.001);
 | 
			
		||||
  ROLLBACK;
 | 
			
		||||
  s3 := statement_timestamp();
 | 
			
		||||
  t3 := transaction_timestamp();
 | 
			
		||||
  ASSERT s3 = s1;
 | 
			
		||||
  ASSERT t3 > t2;
 | 
			
		||||
END;
 | 
			
		||||
$$;
 | 
			
		||||
 | 
			
		||||
CALL transaction_test11();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DROP TABLE test1;
 | 
			
		||||
DROP TABLE test2;
 | 
			
		||||
DROP TABLE test3;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user