serializable-001.patch

text/plain

Filename: serializable-001.patch
Type: text/plain
Part: 0
Message: Re: Serializable implementation

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/catalog/index.c 1 0
src/backend/commands/trigger.c 1 0
src/backend/executor/execMain.c 1 0
src/backend/executor/nodeLockRows.c 1 0
src/backend/executor/nodeModifyTable.c 2 0
src/backend/utils/adt/ri_triggers.c 2 0
src/backend/utils/time/snapmgr.c 15 2
src/include/access/xact.h 7 0
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
***************
*** 2133,2139 **** IndexCheckExclusion(Relation heapRelation,
   *
   * After completing validate_index(), we wait until all transactions that
   * were alive at the time of the reference snapshot are gone; this is
!  * necessary to be sure there are none left with a serializable snapshot
   * older than the reference (and hence possibly able to see tuples we did
   * not index).	Then we mark the index "indisvalid" and commit.  Subsequent
   * transactions will be able to use it for queries.
--- 2133,2139 ----
   *
   * After completing validate_index(), we wait until all transactions that
   * were alive at the time of the reference snapshot are gone; this is
!  * necessary to be sure there are none left with a transaction-based snapshot
   * older than the reference (and hence possibly able to see tuples we did
   * not index).	Then we mark the index "indisvalid" and commit.  Subsequent
   * transactions will be able to use it for queries.
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 2319,2325 **** ltrmark:;
  
  			case HeapTupleUpdated:
  				ReleaseBuffer(buffer);
! 				if (IsXactIsoLevelSerializable)
  					ereport(ERROR,
  							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  							 errmsg("could not serialize access due to concurrent update")));
--- 2319,2325 ----
  
  			case HeapTupleUpdated:
  				ReleaseBuffer(buffer);
! 				if (IsXactIsoLevelXactSnapshotBased)
  					ereport(ERROR,
  							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  							 errmsg("could not serialize access due to concurrent update")));
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 1538,1544 **** EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
  
  				case HeapTupleUpdated:
  					ReleaseBuffer(buffer);
! 					if (IsXactIsoLevelSerializable)
  						ereport(ERROR,
  								(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  								 errmsg("could not serialize access due to concurrent update")));
--- 1538,1544 ----
  
  				case HeapTupleUpdated:
  					ReleaseBuffer(buffer);
! 					if (IsXactIsoLevelXactSnapshotBased)
  						ereport(ERROR,
  								(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  								 errmsg("could not serialize access due to concurrent update")));
*** a/src/backend/executor/nodeLockRows.c
--- b/src/backend/executor/nodeLockRows.c
***************
*** 130,136 **** lnext:
  				break;
  
  			case HeapTupleUpdated:
! 				if (IsXactIsoLevelSerializable)
  					ereport(ERROR,
  							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  							 errmsg("could not serialize access due to concurrent update")));
--- 130,136 ----
  				break;
  
  			case HeapTupleUpdated:
! 				if (IsXactIsoLevelXactSnapshotBased)
  					ereport(ERROR,
  							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  							 errmsg("could not serialize access due to concurrent update")));
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
***************
*** 326,332 **** ldelete:;
  			break;
  
  		case HeapTupleUpdated:
! 			if (IsXactIsoLevelSerializable)
  				ereport(ERROR,
  						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  						 errmsg("could not serialize access due to concurrent update")));
--- 326,332 ----
  			break;
  
  		case HeapTupleUpdated:
! 			if (IsXactIsoLevelXactSnapshotBased)
  				ereport(ERROR,
  						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  						 errmsg("could not serialize access due to concurrent update")));
***************
*** 514,520 **** lreplace:;
  			break;
  
  		case HeapTupleUpdated:
! 			if (IsXactIsoLevelSerializable)
  				ereport(ERROR,
  						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  						 errmsg("could not serialize access due to concurrent update")));
--- 514,520 ----
  			break;
  
  		case HeapTupleUpdated:
! 			if (IsXactIsoLevelXactSnapshotBased)
  				ereport(ERROR,
  						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
  						 errmsg("could not serialize access due to concurrent update")));
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
***************
*** 3314,3320 **** ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
  	/*
  	 * In READ COMMITTED mode, we just need to use an up-to-date regular
  	 * snapshot, and we will see all rows that could be interesting. But in
! 	 * SERIALIZABLE mode, we can't change the transaction snapshot. If the
  	 * caller passes detectNewRows == false then it's okay to do the query
  	 * with the transaction snapshot; otherwise we use a current snapshot, and
  	 * tell the executor to error out if it finds any rows under the current
--- 3314,3320 ----
  	/*
  	 * In READ COMMITTED mode, we just need to use an up-to-date regular
  	 * snapshot, and we will see all rows that could be interesting. But in
! 	 * xact-snapshot-based modes, we can't change the transaction snapshot. If the
  	 * caller passes detectNewRows == false then it's okay to do the query
  	 * with the transaction snapshot; otherwise we use a current snapshot, and
  	 * tell the executor to error out if it finds any rows under the current
***************
*** 3322,3328 **** ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
  	 * that SPI_execute_snapshot will register the snapshots, so we don't need
  	 * to bother here.
  	 */
! 	if (IsXactIsoLevelSerializable && detectNewRows)
  	{
  		CommandCounterIncrement();		/* be sure all my own work is visible */
  		test_snapshot = GetLatestSnapshot();
--- 3322,3328 ----
  	 * that SPI_execute_snapshot will register the snapshots, so we don't need
  	 * to bother here.
  	 */
! 	if (IsXactIsoLevelXactSnapshotBased && detectNewRows)
  	{
  		CommandCounterIncrement();		/* be sure all my own work is visible */
  		test_snapshot = GetLatestSnapshot();
*** a/src/backend/utils/time/snapmgr.c
--- b/src/backend/utils/time/snapmgr.c
***************
*** 37,44 ****
  
  
  /*
!  * CurrentSnapshot points to the only snapshot taken in a serializable
!  * transaction, and to the latest one taken in a read-committed transaction.
   * SecondarySnapshot is a snapshot that's always up-to-date as of the current
   * instant, even on a serializable transaction.  It should only be used for
   * special-purpose code (say, RI checking.)
--- 37,44 ----
  
  
  /*
!  * CurrentSnapshot points to the only snapshot taken in a xact-snapshot-based
!  * transaction; otherwise to the latest one taken.
   * SecondarySnapshot is a snapshot that's always up-to-date as of the current
   * instant, even on a serializable transaction.  It should only be used for
   * special-purpose code (say, RI checking.)
***************
*** 97,107 **** static int	RegisteredSnapshots = 0;
  bool		FirstSnapshotSet = false;
  
  /*
!  * Remembers whether this transaction registered a serializable snapshot at
   * start.  We cannot trust FirstSnapshotSet in combination with
!  * IsXactIsoLevelSerializable, because GUC may be reset before us.
   */
! static bool registered_serializable = false;
  
  
  static Snapshot CopySnapshot(Snapshot snapshot);
--- 97,107 ----
  bool		FirstSnapshotSet = false;
  
  /*
!  * Remembers whether this transaction registered a transaction-based snapshot at
   * start.  We cannot trust FirstSnapshotSet in combination with
!  * IsXactIsoLevelXactSnapshotBased, because GUC may be reset before us.
   */
! static bool registered_xact_snapshot = false;
  
  
  static Snapshot CopySnapshot(Snapshot snapshot);
***************
*** 130,150 **** GetTransactionSnapshot(void)
  		FirstSnapshotSet = true;
  
  		/*
! 		 * In serializable mode, the first snapshot must live until end of
  		 * xact regardless of what the caller does with it, so we must
  		 * register it internally here and unregister it at end of xact.
  		 */
! 		if (IsXactIsoLevelSerializable)
  		{
  			CurrentSnapshot = RegisterSnapshotOnOwner(CurrentSnapshot,
  												TopTransactionResourceOwner);
! 			registered_serializable = true;
  		}
  
  		return CurrentSnapshot;
  	}
  
! 	if (IsXactIsoLevelSerializable)
  		return CurrentSnapshot;
  
  	CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
--- 130,150 ----
  		FirstSnapshotSet = true;
  
  		/*
! 		 * In xact-snapshot-based isolation levels, the first snapshot must live until end of
  		 * xact regardless of what the caller does with it, so we must
  		 * register it internally here and unregister it at end of xact.
  		 */
! 		if (IsXactIsoLevelXactSnapshotBased)
  		{
  			CurrentSnapshot = RegisterSnapshotOnOwner(CurrentSnapshot,
  												TopTransactionResourceOwner);
! 			registered_xact_snapshot = true;
  		}
  
  		return CurrentSnapshot;
  	}
  
! 	if (IsXactIsoLevelXactSnapshotBased)
  		return CurrentSnapshot;
  
  	CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
***************
*** 155,161 **** GetTransactionSnapshot(void)
  /*
   * GetLatestSnapshot
   *		Get a snapshot that is up-to-date as of the current instant,
!  *		even if we are executing in SERIALIZABLE mode.
   */
  Snapshot
  GetLatestSnapshot(void)
--- 155,161 ----
  /*
   * GetLatestSnapshot
   *		Get a snapshot that is up-to-date as of the current instant,
!  *		even if we are executing in xact-snapshot-based mode.
   */
  Snapshot
  GetLatestSnapshot(void)
***************
*** 515,527 **** void
  AtEarlyCommit_Snapshot(void)
  {
  	/*
! 	 * On a serializable transaction we must unregister our private refcount
! 	 * to the serializable snapshot.
  	 */
! 	if (registered_serializable)
  		UnregisterSnapshotFromOwner(CurrentSnapshot,
  									TopTransactionResourceOwner);
! 	registered_serializable = false;
  
  }
  
--- 515,527 ----
  AtEarlyCommit_Snapshot(void)
  {
  	/*
! 	 * On a xact-snapshot-based transaction we must unregister our private refcount
! 	 * to the xact snapshot.
  	 */
! 	if (registered_xact_snapshot)
  		UnregisterSnapshotFromOwner(CurrentSnapshot,
  									TopTransactionResourceOwner);
! 	registered_xact_snapshot = false;
  
  }
  
***************
*** 557,561 **** AtEOXact_Snapshot(bool isCommit)
  	SecondarySnapshot = NULL;
  
  	FirstSnapshotSet = false;
! 	registered_serializable = false;
  }
--- 557,561 ----
  	SecondarySnapshot = NULL;
  
  	FirstSnapshotSet = false;
! 	registered_xact_snapshot = false;
  }
*** a/src/include/access/xact.h
--- b/src/include/access/xact.h
***************
*** 32,41 **** extern int	DefaultXactIsoLevel;
  extern int	XactIsoLevel;
  
  /*
!  * We only implement two isolation levels internally.  This macro should
!  * be used to check which one is selected.
   */
! #define IsXactIsoLevelSerializable (XactIsoLevel >= XACT_REPEATABLE_READ)
  
  /* Xact read-only state */
  extern bool DefaultXactReadOnly;
--- 32,45 ----
  extern int	XactIsoLevel;
  
  /*
!  * We implement three isolation levels internally.
!  * The two stronger ones use one snapshot per database transaction;
!  * the others use one snapshot per statement.
!  * Serializable uses Dangerous Structure Detection (DSD) locks.
!  * These macros should be used to check which one is selected.
   */
! #define IsXactIsoLevelXactSnapshotBased (XactIsoLevel >= XACT_REPEATABLE_READ)
! #define IsXactIsoLevelFullySerializable (XactIsoLevel == XACT_SERIALIZABLE)
  
  /* Xact read-only state */
  extern bool DefaultXactReadOnly;