@@ -243,6 +243,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
243243 private QueryOptions queryOptions = QueryOptions .getDefaultInstance ();
244244 private RpcPriority rpcPriority = null ;
245245 private SavepointSupport savepointSupport = SavepointSupport .FAIL_AFTER_ROLLBACK ;
246+ private DdlInTransactionMode ddlInTransactionMode ;
246247
247248 private String transactionTag ;
248249 private String statementTag ;
@@ -271,6 +272,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
271272 this .autocommit = options .isAutocommit ();
272273 this .queryOptions = this .queryOptions .toBuilder ().mergeFrom (options .getQueryOptions ()).build ();
273274 this .rpcPriority = options .getRPCPriority ();
275+ this .ddlInTransactionMode = options .getDdlInTransactionMode ();
274276 this .returnCommitStats = options .isReturnCommitStats ();
275277 this .delayTransactionStartUntilFirstWrite = options .isDelayTransactionStartUntilFirstWrite ();
276278 this .dataBoostEnabled = options .isDataBoostEnabled ();
@@ -296,6 +298,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
296298 new StatementExecutor (options .isUseVirtualThreads (), Collections .emptyList ());
297299 this .spannerPool = Preconditions .checkNotNull (spannerPool );
298300 this .options = Preconditions .checkNotNull (options );
301+ this .ddlInTransactionMode = options .getDdlInTransactionMode ();
299302 this .spanner = spannerPool .getSpanner (options , this );
300303 this .ddlClient = Preconditions .checkNotNull (ddlClient );
301304 this .dbClient = Preconditions .checkNotNull (dbClient );
@@ -571,6 +574,21 @@ public RpcPriority getRPCPriority() {
571574 return this .rpcPriority ;
572575 }
573576
577+ @ Override
578+ public DdlInTransactionMode getDdlInTransactionMode () {
579+ return this .ddlInTransactionMode ;
580+ }
581+
582+ @ Override
583+ public void setDdlInTransactionMode (DdlInTransactionMode ddlInTransactionMode ) {
584+ ConnectionPreconditions .checkState (!isClosed (), CLOSED_ERROR_MSG );
585+ ConnectionPreconditions .checkState (
586+ !isBatchActive (), "Cannot set DdlInTransactionMode while in a batch" );
587+ ConnectionPreconditions .checkState (
588+ !isTransactionStarted (), "Cannot set DdlInTransactionMode while a transaction is active" );
589+ this .ddlInTransactionMode = Preconditions .checkNotNull (ddlInTransactionMode );
590+ }
591+
574592 @ Override
575593 public void setStatementTimeout (long timeout , TimeUnit unit ) {
576594 Preconditions .checkArgument (timeout > 0L , "Zero or negative timeout values are not allowed" );
@@ -1639,28 +1657,55 @@ private ApiFuture<long[]> internalExecuteBatchUpdateAsync(
16391657 }
16401658
16411659 private UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork () {
1642- return getCurrentUnitOfWorkOrStartNewUnitOfWork (false );
1660+ return getCurrentUnitOfWorkOrStartNewUnitOfWork (StatementType .UNKNOWN , false );
1661+ }
1662+
1663+ @ VisibleForTesting
1664+ UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork (boolean isInternalMetadataQuery ) {
1665+ return getCurrentUnitOfWorkOrStartNewUnitOfWork (StatementType .UNKNOWN , isInternalMetadataQuery );
1666+ }
1667+
1668+ private UnitOfWork getOrStartDdlUnitOfWork () {
1669+ return getCurrentUnitOfWorkOrStartNewUnitOfWork (StatementType .DDL , false );
16431670 }
16441671
16451672 /**
16461673 * Returns the current {@link UnitOfWork} of this connection, or creates a new one based on the
16471674 * current transaction settings of the connection and returns that.
16481675 */
16491676 @ VisibleForTesting
1650- UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork (boolean isInternalMetadataQuery ) {
1677+ UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork (
1678+ StatementType statementType , boolean isInternalMetadataQuery ) {
16511679 if (isInternalMetadataQuery ) {
16521680 // Just return a temporary single-use transaction.
1653- return createNewUnitOfWork (true );
1681+ return createNewUnitOfWork (/* isInternalMetadataQuery = */ true , /* forceSingleUse = */ true );
16541682 }
1683+ maybeAutoCommitCurrentTransaction (statementType );
16551684 if (this .currentUnitOfWork == null || !this .currentUnitOfWork .isActive ()) {
1656- this .currentUnitOfWork = createNewUnitOfWork (false );
1685+ this .currentUnitOfWork =
1686+ createNewUnitOfWork (
1687+ /* isInternalMetadataQuery = */ false ,
1688+ /* forceSingleUse = */ statementType == StatementType .DDL
1689+ && this .ddlInTransactionMode != DdlInTransactionMode .FAIL
1690+ && !this .transactionBeginMarked );
16571691 }
16581692 return this .currentUnitOfWork ;
16591693 }
16601694
1695+ void maybeAutoCommitCurrentTransaction (StatementType statementType ) {
1696+ if (this .currentUnitOfWork instanceof ReadWriteTransaction
1697+ && this .currentUnitOfWork .isActive ()
1698+ && statementType == StatementType .DDL
1699+ && this .ddlInTransactionMode == DdlInTransactionMode .AUTO_COMMIT_TRANSACTION ) {
1700+ commit ();
1701+ }
1702+ }
1703+
16611704 @ VisibleForTesting
1662- UnitOfWork createNewUnitOfWork (boolean isInternalMetadataQuery ) {
1663- if (isInternalMetadataQuery || (isAutocommit () && !isInTransaction () && !isInBatch ())) {
1705+ UnitOfWork createNewUnitOfWork (boolean isInternalMetadataQuery , boolean forceSingleUse ) {
1706+ if (isInternalMetadataQuery
1707+ || (isAutocommit () && !isInTransaction () && !isInBatch ())
1708+ || forceSingleUse ) {
16641709 return SingleUseTransaction .newBuilder ()
16651710 .setInternalMetadataQuery (isInternalMetadataQuery )
16661711 .setDdlClient (ddlClient )
@@ -1741,7 +1786,7 @@ private void popUnitOfWorkFromTransactionStack() {
17411786 }
17421787
17431788 private ApiFuture <Void > executeDdlAsync (CallType callType , ParsedStatement ddl ) {
1744- return getCurrentUnitOfWorkOrStartNewUnitOfWork ().executeDdlAsync (callType , ddl );
1789+ return getOrStartDdlUnitOfWork ().executeDdlAsync (callType , ddl );
17451790 }
17461791
17471792 @ Override
@@ -1788,15 +1833,23 @@ public void startBatchDdl() {
17881833 ConnectionPreconditions .checkState (
17891834 !isReadOnly (), "Cannot start a DDL batch when the connection is in read-only mode" );
17901835 ConnectionPreconditions .checkState (
1791- !isTransactionStarted (), "Cannot start a DDL batch while a transaction is active" );
1836+ !isTransactionStarted ()
1837+ || getDdlInTransactionMode () == DdlInTransactionMode .AUTO_COMMIT_TRANSACTION ,
1838+ "Cannot start a DDL batch while a transaction is active" );
17921839 ConnectionPreconditions .checkState (
17931840 !(isAutocommit () && isInTransaction ()),
17941841 "Cannot start a DDL batch while in a temporary transaction" );
17951842 ConnectionPreconditions .checkState (
17961843 !transactionBeginMarked , "Cannot start a DDL batch when a transaction has begun" );
1844+ ConnectionPreconditions .checkState (
1845+ isAutocommit () || getDdlInTransactionMode () != DdlInTransactionMode .FAIL ,
1846+ "Cannot start a DDL batch when autocommit=false and ddlInTransactionMode=FAIL" );
1847+
1848+ maybeAutoCommitCurrentTransaction (StatementType .DDL );
17971849 this .batchMode = BatchMode .DDL ;
17981850 this .unitOfWorkType = UnitOfWorkType .DDL_BATCH ;
1799- this .currentUnitOfWork = createNewUnitOfWork (false );
1851+ this .currentUnitOfWork =
1852+ createNewUnitOfWork (/* isInternalMetadataQuery = */ false , /* forceSingleUse = */ false );
18001853 }
18011854
18021855 @ Override
@@ -1814,7 +1867,8 @@ public void startBatchDml() {
18141867 // Then create the DML batch.
18151868 this .batchMode = BatchMode .DML ;
18161869 this .unitOfWorkType = UnitOfWorkType .DML_BATCH ;
1817- this .currentUnitOfWork = createNewUnitOfWork (false );
1870+ this .currentUnitOfWork =
1871+ createNewUnitOfWork (/* isInternalMetadataQuery = */ false , /* forceSingleUse = */ false );
18181872 }
18191873
18201874 @ Override
0 commit comments