@@ -332,16 +332,22 @@ func (c *conn) showConnectionVariable(identifier parser.Identifier) (any, bool,
332332 return c .state .GetValue (extension , name )
333333}
334334
335- func (c * conn ) setConnectionVariable (identifier parser.Identifier , value string , local bool , transaction bool ) error {
335+ func (c * conn ) setConnectionVariable (identifier parser.Identifier , value string , local bool , transaction bool , statementScoped bool ) error {
336336 if transaction && ! local {
337337 // When transaction == true, then local must also be true.
338338 // We should never hit this condition, as this is an indication of a bug in the driver code.
339339 return status .Errorf (codes .FailedPrecondition , "transaction properties must be set as a local value" )
340340 }
341+ if statementScoped && local {
342+ return status .Errorf (codes .FailedPrecondition , "cannot specify both statementScoped and local" )
343+ }
341344 extension , name , err := toExtensionAndName (identifier )
342345 if err != nil {
343346 return err
344347 }
348+ if statementScoped {
349+ return c .state .SetStatementScopedValue (extension , name , value )
350+ }
345351 if local {
346352 return c .state .SetLocalValue (extension , name , value , transaction )
347353 }
@@ -568,7 +574,10 @@ func (c *conn) startBatchDDL() (driver.Result, error) {
568574}
569575
570576func (c * conn ) startBatchDML (automatic bool ) (driver.Result , error ) {
571- execOptions := c .options ( /*reset = */ true )
577+ execOptions , err := c .options ( /*reset = */ true )
578+ if err != nil {
579+ return nil , err
580+ }
572581
573582 if c .inTransaction () {
574583 return c .tx .StartBatchDML (execOptions .QueryOptions , automatic )
@@ -779,6 +788,10 @@ func (c *conn) CheckNamedValue(value *driver.NamedValue) error {
779788 c .tempExecOptions = & execOptions
780789 return driver .ErrRemoveArgument
781790 }
791+ if execOptions , ok := value .Value .(* ExecOptions ); ok {
792+ c .tempExecOptions = execOptions
793+ return driver .ErrRemoveArgument
794+ }
782795
783796 if checkIsValidType (value .Value ) {
784797 return nil
@@ -823,7 +836,10 @@ func (c *conn) Prepare(query string) (driver.Stmt, error) {
823836}
824837
825838func (c * conn ) PrepareContext (_ context.Context , query string ) (driver.Stmt , error ) {
826- execOptions := c .options ( /* reset = */ true )
839+ execOptions , err := c .options ( /* reset = */ true )
840+ if err != nil {
841+ return nil , err
842+ }
827843 parsedSQL , args , err := c .parser .ParseParameters (query )
828844 if err != nil {
829845 return nil , err
@@ -894,13 +910,37 @@ func (c *conn) transactionDeadline() (time.Time, bool, error) {
894910 return deadline , hasDeadline , nil
895911}
896912
913+ func (c * conn ) applyStatementScopedValues (execOptions * ExecOptions ) (cleanup func (), returnedErr error ) {
914+ if execOptions == nil {
915+ return func () {}, nil
916+ }
917+
918+ defer func () {
919+ if returnedErr != nil {
920+ // Clear any statement values that might have been set if we return an error, as that also means that we
921+ // are not returning a cleanup function.
922+ c .state .ClearStatementScopedValues ()
923+ }
924+ }()
925+ values := execOptions .PropertyValues
926+ for _ , value := range values {
927+ if err := c .setConnectionVariable (value .Identifier , value .Value /*local=*/ , false /*transaction=*/ , false /*statementScoped=*/ , true ); err != nil {
928+ return func () {}, err
929+ }
930+ }
931+ return c .state .ClearStatementScopedValues , nil
932+ }
933+
897934func (c * conn ) QueryContext (ctx context.Context , query string , args []driver.NamedValue ) (driver.Rows , error ) {
898935 // Execute client side statement if it is one.
899936 clientStmt , err := c .parser .ParseClientSideStatement (query )
900937 if err != nil {
901938 return nil , err
902939 }
903- execOptions := c .options ( /* reset = */ clientStmt == nil )
940+ execOptions , err := c .options ( /* reset = */ clientStmt == nil )
941+ if err != nil {
942+ return nil , err
943+ }
904944 if clientStmt != nil {
905945 execStmt , err := createExecutableStatement (clientStmt )
906946 if err != nil {
@@ -924,6 +964,7 @@ func (c *conn) queryContext(ctx context.Context, query string, execOptions *Exec
924964 }()
925965 // Clear the commit timestamp of this connection before we execute the query.
926966 c .clearCommitResponse ()
967+
927968 // Check if the execution options contains an instruction to execute
928969 // a specific partition of a PartitionedQuery.
929970 if pq := execOptions .PartitionedQueryOptions .ExecutePartition .PartitionedQuery ; pq != nil {
@@ -1040,7 +1081,10 @@ func (c *conn) ExecContext(ctx context.Context, query string, args []driver.Name
10401081 if err != nil {
10411082 return nil , err
10421083 }
1043- execOptions := c .options ( /*reset = */ stmt == nil )
1084+ execOptions , err := c .options ( /*reset = */ stmt == nil )
1085+ if err != nil {
1086+ return nil , err
1087+ }
10441088 if stmt != nil {
10451089 execStmt , err := createExecutableStatement (stmt )
10461090 if err != nil {
@@ -1119,7 +1163,7 @@ func (c *conn) execContext(ctx context.Context, query string, execOptions *ExecO
11191163}
11201164
11211165// options returns and optionally resets the ExecOptions for the next statement.
1122- func (c * conn ) options (reset bool ) * ExecOptions {
1166+ func (c * conn ) options (reset bool ) ( * ExecOptions , error ) {
11231167 if reset {
11241168 defer func () {
11251169 // Only reset the transaction tag if there is no active transaction on the connection.
@@ -1130,11 +1174,24 @@ func (c *conn) options(reset bool) *ExecOptions {
11301174 c .tempExecOptions = nil
11311175 }()
11321176 }
1177+ // TODO: Refactor this to only use connection state as the (temporary) storage, and remove the tempExecOptions field
1178+ if c .tempExecOptions != nil {
1179+ cleanup , err := c .applyStatementScopedValues (c .tempExecOptions )
1180+ if err != nil {
1181+ return nil , err
1182+ }
1183+ defer cleanup ()
1184+ }
1185+
1186+ // TODO: Refactor this to work 'the other way around'. That is:
1187+ // The ExecOptions that are given for a statement should update the connection state.
1188+ // The statement execution should read the state from the connection state.
11331189 effectiveOptions := & ExecOptions {
11341190 AutocommitDMLMode : c .AutocommitDMLMode (),
11351191 DecodeToNativeArrays : c .DecodeToNativeArrays (),
11361192 QueryOptions : spanner.QueryOptions {
11371193 RequestTag : c .StatementTag (),
1194+ Priority : propertyRpcPriority .GetValueOrDefault (c .state ),
11381195 },
11391196 TransactionOptions : spanner.TransactionOptions {
11401197 ExcludeTxnFromChangeStreams : c .ExcludeTxnFromChangeStreams (),
@@ -1152,7 +1209,7 @@ func (c *conn) options(reset bool) *ExecOptions {
11521209 if c .tempExecOptions != nil {
11531210 effectiveOptions .merge (c .tempExecOptions )
11541211 }
1155- return effectiveOptions
1212+ return effectiveOptions , nil
11561213}
11571214
11581215func (c * conn ) Close () error {
@@ -1442,7 +1499,11 @@ func (c *conn) activateTransaction() (contextTransaction, error) {
14421499 // Reset the transaction_tag after starting the transaction.
14431500 _ = propertyTransactionTag .ResetValue (c .state , connectionstate .ContextUser )
14441501 }()
1445- return c .effectiveTransactionOptions (spannerpb .TransactionOptions_ISOLATION_LEVEL_UNSPECIFIED , c .options ( /*reset=*/ true ))
1502+ execOptions , err := c .options ( /*reset=*/ true )
1503+ if err != nil {
1504+ execOptions = & ExecOptions {}
1505+ }
1506+ return c .effectiveTransactionOptions (spannerpb .TransactionOptions_ISOLATION_LEVEL_UNSPECIFIED , execOptions )
14461507 })
14471508 if err != nil {
14481509 cancel ()
0 commit comments