@@ -275,14 +275,9 @@ type conn struct {
275275	// tempExecOptions can be set by passing it in as an argument to ExecContext or QueryContext 
276276	// and are applied only to that statement. 
277277	tempExecOptions  * ExecOptions 
278- 	// tempTransactionOptions are temporarily set right before a read/write transaction is started. 
279- 	tempTransactionOptions  * ReadWriteTransactionOptions 
280- 	// tempReadOnlyTransactionOptions are temporarily set right before a read-only 
281- 	// transaction is started on a Spanner connection. 
282- 	tempReadOnlyTransactionOptions  * ReadOnlyTransactionOptions 
283- 	// tempBatchReadOnlyTransactionOptions are temporarily set right before a 
284- 	// batch read-only transaction is started on a Spanner connection. 
285- 	tempBatchReadOnlyTransactionOptions  * BatchReadOnlyTransactionOptions 
278+ 	// tempTransactionCloseFunc is set right before a transaction is started, and is set as the 
279+ 	// close function for that transaction. 
280+ 	tempTransactionCloseFunc  func ()
286281}
287282
288283func  (c  * conn ) UnderlyingClient () (* spanner.Client , error ) {
@@ -1011,8 +1006,10 @@ func (c *conn) options(reset bool) *ExecOptions {
10111006			TransactionTag :              c .TransactionTag (),
10121007			IsolationLevel :              toProtoIsolationLevelOrDefault (c .IsolationLevel ()),
10131008			ReadLockMode :                c .ReadLockMode (),
1009+ 			CommitPriority :              propertyCommitPriority .GetValueOrDefault (c .state ),
10141010			CommitOptions : spanner.CommitOptions {
1015- 				MaxCommitDelay : c .maxCommitDelayPointer (),
1011+ 				MaxCommitDelay :    c .maxCommitDelayPointer (),
1012+ 				ReturnCommitStats : propertyReturnCommitStats .GetValueOrDefault (c .state ),
10161013			},
10171014		},
10181015		PartitionedQueryOptions : PartitionedQueryOptions {},
@@ -1045,16 +1042,43 @@ func (c *conn) resetTransactionForRetry(ctx context.Context, errDuringCommit boo
10451042}
10461043
10471044func  (c  * conn ) withTempTransactionOptions (options  * ReadWriteTransactionOptions ) {
1048- 	c .tempTransactionOptions  =  options 
1045+ 	if  options  ==  nil  {
1046+ 		return 
1047+ 	}
1048+ 	c .tempTransactionCloseFunc  =  options .close 
1049+ 	// Start a transaction for the connection state, so we can set the transaction options 
1050+ 	// as local options in the current transaction. 
1051+ 	_  =  c .state .Begin ()
1052+ 	if  options .DisableInternalRetries  {
1053+ 		_  =  propertyRetryAbortsInternally .SetLocalValue (c .state , ! options .DisableInternalRetries )
1054+ 	}
1055+ 	if  options .TransactionOptions .BeginTransactionOption  !=  spanner .DefaultBeginTransaction  {
1056+ 		_  =  propertyBeginTransactionOption .SetLocalValue (c .state , options .TransactionOptions .BeginTransactionOption )
1057+ 	}
1058+ 	if  options .TransactionOptions .CommitOptions .MaxCommitDelay  !=  nil  {
1059+ 		_  =  propertyMaxCommitDelay .SetLocalValue (c .state , * options .TransactionOptions .CommitOptions .MaxCommitDelay )
1060+ 	}
1061+ 	if  options .TransactionOptions .CommitOptions .ReturnCommitStats  {
1062+ 		_  =  propertyReturnCommitStats .SetLocalValue (c .state , options .TransactionOptions .CommitOptions .ReturnCommitStats )
1063+ 	}
1064+ 	if  options .TransactionOptions .TransactionTag  !=  ""  {
1065+ 		_  =  propertyTransactionTag .SetLocalValue (c .state , options .TransactionOptions .TransactionTag )
1066+ 	}
1067+ 	if  options .TransactionOptions .ReadLockMode  !=  spannerpb .TransactionOptions_ReadWrite_READ_LOCK_MODE_UNSPECIFIED  {
1068+ 		_  =  propertyReadLockMode .SetLocalValue (c .state , options .TransactionOptions .ReadLockMode )
1069+ 	}
1070+ 	if  options .TransactionOptions .IsolationLevel  !=  spannerpb .TransactionOptions_ISOLATION_LEVEL_UNSPECIFIED  {
1071+ 		_  =  propertyIsolationLevel .SetLocalValue (c .state , toSqlIsolationLevelOrDefault (options .TransactionOptions .IsolationLevel ))
1072+ 	}
1073+ 	if  options .TransactionOptions .ExcludeTxnFromChangeStreams  {
1074+ 		_  =  propertyExcludeTxnFromChangeStreams .SetLocalValue (c .state , options .TransactionOptions .ExcludeTxnFromChangeStreams )
1075+ 	}
1076+ 	if  options .TransactionOptions .CommitPriority  !=  spannerpb .RequestOptions_PRIORITY_UNSPECIFIED  {
1077+ 		_  =  propertyCommitPriority .SetLocalValue (c .state , options .TransactionOptions .CommitPriority )
1078+ 	}
10491079}
10501080
10511081func  (c  * conn ) getTransactionOptions (execOptions  * ExecOptions ) ReadWriteTransactionOptions  {
1052- 	if  c .tempTransactionOptions  !=  nil  {
1053- 		defer  func () { c .tempTransactionOptions  =  nil  }()
1054- 		opts  :=  * c .tempTransactionOptions 
1055- 		opts .TransactionOptions .BeginTransactionOption  =  c .convertDefaultBeginTransactionOption (opts .TransactionOptions .BeginTransactionOption )
1056- 		return  opts 
1057- 	}
10581082	txOpts  :=  ReadWriteTransactionOptions {
10591083		TransactionOptions :     execOptions .TransactionOptions ,
10601084		DisableInternalRetries : ! c .RetryAbortsInternally (),
@@ -1075,28 +1099,39 @@ func (c *conn) getTransactionOptions(execOptions *ExecOptions) ReadWriteTransact
10751099}
10761100
10771101func  (c  * conn ) withTempReadOnlyTransactionOptions (options  * ReadOnlyTransactionOptions ) {
1078- 	c .tempReadOnlyTransactionOptions  =  options 
1102+ 	if  options  ==  nil  {
1103+ 		return 
1104+ 	}
1105+ 	c .tempTransactionCloseFunc  =  options .close 
1106+ 	// Start a transaction for the connection state, so we can set the transaction options 
1107+ 	// as local options in the current transaction. 
1108+ 	_  =  c .state .Begin ()
1109+ 	if  options .BeginTransactionOption  !=  spanner .DefaultBeginTransaction  {
1110+ 		_  =  propertyBeginTransactionOption .SetLocalValue (c .state , options .BeginTransactionOption )
1111+ 	}
1112+ 	if  options .TimestampBound .String () !=  "(strong)"  {
1113+ 		_  =  propertyReadOnlyStaleness .SetLocalValue (c .state , options .TimestampBound )
1114+ 	}
10791115}
10801116
10811117func  (c  * conn ) getReadOnlyTransactionOptions () ReadOnlyTransactionOptions  {
1082- 	if  c .tempReadOnlyTransactionOptions  !=  nil  {
1083- 		defer  func () { c .tempReadOnlyTransactionOptions  =  nil  }()
1084- 		opts  :=  * c .tempReadOnlyTransactionOptions 
1085- 		opts .BeginTransactionOption  =  c .convertDefaultBeginTransactionOption (opts .BeginTransactionOption )
1086- 		return  opts 
1087- 	}
10881118	return  ReadOnlyTransactionOptions {TimestampBound : c .ReadOnlyStaleness (), BeginTransactionOption : c .convertDefaultBeginTransactionOption (propertyBeginTransactionOption .GetValueOrDefault (c .state ))}
10891119}
10901120
10911121func  (c  * conn ) withTempBatchReadOnlyTransactionOptions (options  * BatchReadOnlyTransactionOptions ) {
1092- 	c .tempBatchReadOnlyTransactionOptions  =  options 
1122+ 	if  options  ==  nil  {
1123+ 		return 
1124+ 	}
1125+ 	c .tempTransactionCloseFunc  =  options .close 
1126+ 	// Start a transaction for the connection state, so we can set the transaction options 
1127+ 	// as local options in the current transaction. 
1128+ 	_  =  c .state .Begin ()
1129+ 	if  options .TimestampBound .String () !=  "(strong)"  {
1130+ 		_  =  propertyReadOnlyStaleness .SetLocalValue (c .state , options .TimestampBound )
1131+ 	}
10931132}
10941133
10951134func  (c  * conn ) getBatchReadOnlyTransactionOptions () BatchReadOnlyTransactionOptions  {
1096- 	if  c .tempBatchReadOnlyTransactionOptions  !=  nil  {
1097- 		defer  func () { c .tempBatchReadOnlyTransactionOptions  =  nil  }()
1098- 		return  * c .tempBatchReadOnlyTransactionOptions 
1099- 	}
11001135	return  BatchReadOnlyTransactionOptions {TimestampBound : c .ReadOnlyStaleness ()}
11011136}
11021137
@@ -1108,7 +1143,6 @@ func (c *conn) BeginReadOnlyTransaction(ctx context.Context, options *ReadOnlyTr
11081143	c .withTempReadOnlyTransactionOptions (options )
11091144	tx , err  :=  c .BeginTx (ctx , driver.TxOptions {ReadOnly : true })
11101145	if  err  !=  nil  {
1111- 		c .withTempReadOnlyTransactionOptions (nil )
11121146		return  nil , err 
11131147	}
11141148	return  tx , nil 
@@ -1122,7 +1156,6 @@ func (c *conn) BeginReadWriteTransaction(ctx context.Context, options *ReadWrite
11221156	c .withTempTransactionOptions (options )
11231157	tx , err  :=  c .BeginTx (ctx , driver.TxOptions {})
11241158	if  err  !=  nil  {
1125- 		c .withTempTransactionOptions (nil )
11261159		return  nil , err 
11271160	}
11281161	return  tx , nil 
@@ -1133,6 +1166,13 @@ func (c *conn) Begin() (driver.Tx, error) {
11331166}
11341167
11351168func  (c  * conn ) BeginTx (ctx  context.Context , driverOpts  driver.TxOptions ) (driver.Tx , error ) {
1169+ 	defer  func () {
1170+ 		c .tempTransactionCloseFunc  =  nil 
1171+ 	}()
1172+ 	return  c .beginTx (ctx , driverOpts , c .tempTransactionCloseFunc )
1173+ }
1174+ 
1175+ func  (c  * conn ) beginTx (ctx  context.Context , driverOpts  driver.TxOptions , closeFunc  func ()) (driver.Tx , error ) {
11361176	if  c .resetForRetry  {
11371177		c .resetForRetry  =  false 
11381178		return  c .tx , nil 
@@ -1141,6 +1181,10 @@ func (c *conn) BeginTx(ctx context.Context, driverOpts driver.TxOptions) (driver
11411181	defer  func () {
11421182		if  c .tx  !=  nil  {
11431183			_  =  c .state .Begin ()
1184+ 		} else  {
1185+ 			// Rollback in case the connection state transaction was started before this function 
1186+ 			// was called, for example if the caller set temporary transaction options. 
1187+ 			_  =  c .state .Rollback ()
11441188		}
11451189	}()
11461190
@@ -1180,6 +1224,9 @@ func (c *conn) BeginTx(ctx context.Context, driverOpts driver.TxOptions) (driver
11801224	if  batchReadOnly  &&  ! driverOpts .ReadOnly  {
11811225		return  nil , status .Error (codes .InvalidArgument , "levelBatchReadOnly can only be used for read-only transactions" )
11821226	}
1227+ 	if  closeFunc  ==  nil  {
1228+ 		closeFunc  =  func () {}
1229+ 	}
11831230
11841231	if  driverOpts .ReadOnly  {
11851232		var  logger  * slog.Logger 
@@ -1188,49 +1235,47 @@ func (c *conn) BeginTx(ctx context.Context, driverOpts driver.TxOptions) (driver
11881235		if  batchReadOnly  {
11891236			logger  =  c .logger .With ("tx" , "batchro" )
11901237			var  err  error 
1238+ 			// BatchReadOnly transactions (currently) do not support inline-begin. 
1239+ 			// This means that the transaction options must be supplied here, and not through a callback. 
11911240			bo , err  =  c .client .BatchReadOnlyTransaction (ctx , batchReadOnlyTxOpts .TimestampBound )
11921241			if  err  !=  nil  {
11931242				return  nil , err 
11941243			}
11951244			ro  =  & bo .ReadOnlyTransaction 
11961245		} else  {
11971246			logger  =  c .logger .With ("tx" , "ro" )
1198- 			ro  =  c .client .ReadOnlyTransaction ().WithBeginTransactionOption (readOnlyTxOpts .BeginTransactionOption ). WithTimestampBound ( readOnlyTxOpts . TimestampBound ) 
1247+ 			ro  =  c .client .ReadOnlyTransaction ().WithBeginTransactionOption (readOnlyTxOpts .BeginTransactionOption )
11991248		}
12001249		c .tx  =  & readOnlyTransaction {
12011250			roTx :   ro ,
12021251			boTx :   bo ,
12031252			logger : logger ,
12041253			close : func (result  txResult ) {
1205- 				if  batchReadOnlyTxOpts .close  !=  nil  {
1206- 					batchReadOnlyTxOpts .close ()
1207- 				}
1208- 				if  readOnlyTxOpts .close  !=  nil  {
1209- 					readOnlyTxOpts .close ()
1210- 				}
1254+ 				closeFunc ()
12111255				if  result  ==  txResultCommit  {
12121256					_  =  c .state .Commit ()
12131257				} else  {
12141258					_  =  c .state .Rollback ()
12151259				}
12161260				c .tx  =  nil 
12171261			},
1262+ 			timestampBoundCallback : func () spanner.TimestampBound  {
1263+ 				return  propertyReadOnlyStaleness .GetValueOrDefault (c .state )
1264+ 			},
12181265		}
12191266		return  c .tx , nil 
12201267	}
12211268
1269+ 	// These options are only used to determine how to start the transaction. 
1270+ 	// All other options are fetched in a callback that is called when the transaction is actually started. 
1271+ 	// That callback reads all transaction options from the connection state at that moment. This allows 
1272+ 	// applications to execute a series of statement like this: 
1273+ 	// BEGIN TRANSACTION; 
1274+ 	// SET LOCAL transaction_tag='my_tag'; 
1275+ 	// SET LOCAL commit_priority=LOW; 
1276+ 	// INSERT INTO my_table ... -- This starts the transaction with the options above included. 
12221277	opts  :=  spanner.TransactionOptions {}
1223- 	if  c .tempTransactionOptions  !=  nil  {
1224- 		opts  =  c .tempTransactionOptions .TransactionOptions 
1225- 	}
1226- 	opts .BeginTransactionOption  =  c .convertDefaultBeginTransactionOption (opts .BeginTransactionOption )
1227- 	tempCloseFunc  :=  func () {}
1228- 	if  c .tempTransactionOptions  !=  nil  &&  c .tempTransactionOptions .close  !=  nil  {
1229- 		tempCloseFunc  =  c .tempTransactionOptions .close 
1230- 	}
1231- 	if  ! disableRetryAborts  &&  c .tempTransactionOptions  !=  nil  {
1232- 		disableRetryAborts  =  c .tempTransactionOptions .DisableInternalRetries 
1233- 	}
1278+ 	opts .BeginTransactionOption  =  c .convertDefaultBeginTransactionOption (propertyBeginTransactionOption .GetValueOrDefault (c .state ))
12341279
12351280	tx , err  :=  spanner .NewReadWriteStmtBasedTransactionWithCallbackForOptions (ctx , c .client , opts , func () spanner.TransactionOptions  {
12361281		defer  func () {
@@ -1249,7 +1294,7 @@ func (c *conn) BeginTx(ctx context.Context, driverOpts driver.TxOptions) (driver
12491294		logger : logger ,
12501295		rwTx :   tx ,
12511296		close : func (result  txResult , commitResponse  * spanner.CommitResponse , commitErr  error ) {
1252- 			tempCloseFunc ()
1297+ 			closeFunc ()
12531298			c .prevTx  =  c .tx 
12541299			c .tx  =  nil 
12551300			if  commitErr  ==  nil  {
0 commit comments