@@ -243,6 +243,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
243
243
private QueryOptions queryOptions = QueryOptions .getDefaultInstance ();
244
244
private RpcPriority rpcPriority = null ;
245
245
private SavepointSupport savepointSupport = SavepointSupport .FAIL_AFTER_ROLLBACK ;
246
+ private DdlInTransactionMode ddlInTransactionMode ;
246
247
247
248
private String transactionTag ;
248
249
private String statementTag ;
@@ -271,6 +272,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
271
272
this .autocommit = options .isAutocommit ();
272
273
this .queryOptions = this .queryOptions .toBuilder ().mergeFrom (options .getQueryOptions ()).build ();
273
274
this .rpcPriority = options .getRPCPriority ();
275
+ this .ddlInTransactionMode = options .getDdlInTransactionMode ();
274
276
this .returnCommitStats = options .isReturnCommitStats ();
275
277
this .delayTransactionStartUntilFirstWrite = options .isDelayTransactionStartUntilFirstWrite ();
276
278
this .dataBoostEnabled = options .isDataBoostEnabled ();
@@ -296,6 +298,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
296
298
new StatementExecutor (options .isUseVirtualThreads (), Collections .emptyList ());
297
299
this .spannerPool = Preconditions .checkNotNull (spannerPool );
298
300
this .options = Preconditions .checkNotNull (options );
301
+ this .ddlInTransactionMode = options .getDdlInTransactionMode ();
299
302
this .spanner = spannerPool .getSpanner (options , this );
300
303
this .ddlClient = Preconditions .checkNotNull (ddlClient );
301
304
this .dbClient = Preconditions .checkNotNull (dbClient );
@@ -571,6 +574,21 @@ public RpcPriority getRPCPriority() {
571
574
return this .rpcPriority ;
572
575
}
573
576
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
+
574
592
@ Override
575
593
public void setStatementTimeout (long timeout , TimeUnit unit ) {
576
594
Preconditions .checkArgument (timeout > 0L , "Zero or negative timeout values are not allowed" );
@@ -1639,28 +1657,55 @@ private ApiFuture<long[]> internalExecuteBatchUpdateAsync(
1639
1657
}
1640
1658
1641
1659
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 );
1643
1670
}
1644
1671
1645
1672
/**
1646
1673
* Returns the current {@link UnitOfWork} of this connection, or creates a new one based on the
1647
1674
* current transaction settings of the connection and returns that.
1648
1675
*/
1649
1676
@ VisibleForTesting
1650
- UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork (boolean isInternalMetadataQuery ) {
1677
+ UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork (
1678
+ StatementType statementType , boolean isInternalMetadataQuery ) {
1651
1679
if (isInternalMetadataQuery ) {
1652
1680
// Just return a temporary single-use transaction.
1653
- return createNewUnitOfWork (true );
1681
+ return createNewUnitOfWork (/* isInternalMetadataQuery = */ true , /* forceSingleUse = */ true );
1654
1682
}
1683
+ maybeAutoCommitCurrentTransaction (statementType );
1655
1684
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 );
1657
1691
}
1658
1692
return this .currentUnitOfWork ;
1659
1693
}
1660
1694
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
+
1661
1704
@ 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 ) {
1664
1709
return SingleUseTransaction .newBuilder ()
1665
1710
.setInternalMetadataQuery (isInternalMetadataQuery )
1666
1711
.setDdlClient (ddlClient )
@@ -1741,7 +1786,7 @@ private void popUnitOfWorkFromTransactionStack() {
1741
1786
}
1742
1787
1743
1788
private ApiFuture <Void > executeDdlAsync (CallType callType , ParsedStatement ddl ) {
1744
- return getCurrentUnitOfWorkOrStartNewUnitOfWork ().executeDdlAsync (callType , ddl );
1789
+ return getOrStartDdlUnitOfWork ().executeDdlAsync (callType , ddl );
1745
1790
}
1746
1791
1747
1792
@ Override
@@ -1788,15 +1833,23 @@ public void startBatchDdl() {
1788
1833
ConnectionPreconditions .checkState (
1789
1834
!isReadOnly (), "Cannot start a DDL batch when the connection is in read-only mode" );
1790
1835
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" );
1792
1839
ConnectionPreconditions .checkState (
1793
1840
!(isAutocommit () && isInTransaction ()),
1794
1841
"Cannot start a DDL batch while in a temporary transaction" );
1795
1842
ConnectionPreconditions .checkState (
1796
1843
!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 );
1797
1849
this .batchMode = BatchMode .DDL ;
1798
1850
this .unitOfWorkType = UnitOfWorkType .DDL_BATCH ;
1799
- this .currentUnitOfWork = createNewUnitOfWork (false );
1851
+ this .currentUnitOfWork =
1852
+ createNewUnitOfWork (/* isInternalMetadataQuery = */ false , /* forceSingleUse = */ false );
1800
1853
}
1801
1854
1802
1855
@ Override
@@ -1814,7 +1867,8 @@ public void startBatchDml() {
1814
1867
// Then create the DML batch.
1815
1868
this .batchMode = BatchMode .DML ;
1816
1869
this .unitOfWorkType = UnitOfWorkType .DML_BATCH ;
1817
- this .currentUnitOfWork = createNewUnitOfWork (false );
1870
+ this .currentUnitOfWork =
1871
+ createNewUnitOfWork (/* isInternalMetadataQuery = */ false , /* forceSingleUse = */ false );
1818
1872
}
1819
1873
1820
1874
@ Override
0 commit comments