@@ -853,6 +853,9 @@ public StatementResult execute(Statement statement) {
853
853
case QUERY :
854
854
return StatementResultImpl .of (internalExecuteQuery (parsedStatement , AnalyzeMode .NONE ));
855
855
case UPDATE :
856
+ if (parsedStatement .hasReturningClause ()) {
857
+ return StatementResultImpl .of (internalExecuteQuery (parsedStatement , AnalyzeMode .NONE ));
858
+ }
856
859
return StatementResultImpl .of (get (internalExecuteUpdateAsync (parsedStatement )));
857
860
case DDL :
858
861
get (executeDdlAsync (parsedStatement ));
@@ -881,6 +884,10 @@ public AsyncStatementResult executeAsync(Statement statement) {
881
884
return AsyncStatementResultImpl .of (
882
885
internalExecuteQueryAsync (parsedStatement , AnalyzeMode .NONE ));
883
886
case UPDATE :
887
+ if (parsedStatement .hasReturningClause ()) {
888
+ return AsyncStatementResultImpl .of (
889
+ internalExecuteQueryAsync (parsedStatement , AnalyzeMode .NONE ));
890
+ }
884
891
return AsyncStatementResultImpl .of (internalExecuteUpdateAsync (parsedStatement ));
885
892
case DDL :
886
893
return AsyncStatementResultImpl .noResult (executeDdlAsync (parsedStatement ));
@@ -918,7 +925,7 @@ private ResultSet parseAndExecuteQuery(
918
925
Preconditions .checkNotNull (analyzeMode );
919
926
ConnectionPreconditions .checkState (!isClosed (), CLOSED_ERROR_MSG );
920
927
ParsedStatement parsedStatement = getStatementParser ().parse (query , this .queryOptions );
921
- if (parsedStatement .isQuery ()) {
928
+ if (parsedStatement .isQuery () || parsedStatement . isUpdate () ) {
922
929
switch (parsedStatement .getType ()) {
923
930
case CLIENT_SIDE :
924
931
return parsedStatement
@@ -928,22 +935,36 @@ private ResultSet parseAndExecuteQuery(
928
935
case QUERY :
929
936
return internalExecuteQuery (parsedStatement , analyzeMode , options );
930
937
case UPDATE :
938
+ if (parsedStatement .hasReturningClause ()) {
939
+ // Cannot execute DML statement with returning clause in read-only mode or in
940
+ // READ_ONLY_TRANSACTION transaction mode.
941
+ if (this .isReadOnly ()
942
+ || (this .isInTransaction ()
943
+ && this .getTransactionMode () == TransactionMode .READ_ONLY_TRANSACTION )) {
944
+ throw SpannerExceptionFactory .newSpannerException (
945
+ ErrorCode .FAILED_PRECONDITION ,
946
+ "DML statement with returning clause cannot be executed in read-only mode: "
947
+ + parsedStatement .getSqlWithoutComments ());
948
+ }
949
+ return internalExecuteQuery (parsedStatement , analyzeMode , options );
950
+ }
931
951
case DDL :
932
952
case UNKNOWN :
933
953
default :
934
954
}
935
955
}
936
956
throw SpannerExceptionFactory .newSpannerException (
937
957
ErrorCode .INVALID_ARGUMENT ,
938
- "Statement is not a query: " + parsedStatement .getSqlWithoutComments ());
958
+ "Statement is not a query or DML with returning clause: "
959
+ + parsedStatement .getSqlWithoutComments ());
939
960
}
940
961
941
962
private AsyncResultSet parseAndExecuteQueryAsync (
942
963
Statement query , AnalyzeMode analyzeMode , QueryOption ... options ) {
943
964
Preconditions .checkNotNull (query );
944
965
ConnectionPreconditions .checkState (!isClosed (), CLOSED_ERROR_MSG );
945
966
ParsedStatement parsedStatement = getStatementParser ().parse (query , this .queryOptions );
946
- if (parsedStatement .isQuery ()) {
967
+ if (parsedStatement .isQuery () || parsedStatement . isUpdate () ) {
947
968
switch (parsedStatement .getType ()) {
948
969
case CLIENT_SIDE :
949
970
return ResultSets .toAsyncResultSet (
@@ -956,14 +977,28 @@ private AsyncResultSet parseAndExecuteQueryAsync(
956
977
case QUERY :
957
978
return internalExecuteQueryAsync (parsedStatement , analyzeMode , options );
958
979
case UPDATE :
980
+ if (parsedStatement .hasReturningClause ()) {
981
+ // Cannot execute DML statement with returning clause in read-only mode or in
982
+ // READ_ONLY_TRANSACTION transaction mode.
983
+ if (this .isReadOnly ()
984
+ || (this .isInTransaction ()
985
+ && this .getTransactionMode () == TransactionMode .READ_ONLY_TRANSACTION )) {
986
+ throw SpannerExceptionFactory .newSpannerException (
987
+ ErrorCode .FAILED_PRECONDITION ,
988
+ "DML statement with returning clause cannot be executed in read-only mode: "
989
+ + parsedStatement .getSqlWithoutComments ());
990
+ }
991
+ return internalExecuteQueryAsync (parsedStatement , analyzeMode , options );
992
+ }
959
993
case DDL :
960
994
case UNKNOWN :
961
995
default :
962
996
}
963
997
}
964
998
throw SpannerExceptionFactory .newSpannerException (
965
999
ErrorCode .INVALID_ARGUMENT ,
966
- "Statement is not a query: " + parsedStatement .getSqlWithoutComments ());
1000
+ "Statement is not a query or DML with returning clause: "
1001
+ + parsedStatement .getSqlWithoutComments ());
967
1002
}
968
1003
969
1004
@ Override
@@ -974,6 +1009,13 @@ public long executeUpdate(Statement update) {
974
1009
if (parsedStatement .isUpdate ()) {
975
1010
switch (parsedStatement .getType ()) {
976
1011
case UPDATE :
1012
+ if (parsedStatement .hasReturningClause ()) {
1013
+ throw SpannerExceptionFactory .newSpannerException (
1014
+ ErrorCode .FAILED_PRECONDITION ,
1015
+ "DML statement with returning clause cannot be executed using executeUpdate: "
1016
+ + parsedStatement .getSqlWithoutComments ()
1017
+ + ". Please use executeQuery instead." );
1018
+ }
977
1019
return get (internalExecuteUpdateAsync (parsedStatement ));
978
1020
case CLIENT_SIDE :
979
1021
case QUERY :
@@ -995,6 +1037,13 @@ public ApiFuture<Long> executeUpdateAsync(Statement update) {
995
1037
if (parsedStatement .isUpdate ()) {
996
1038
switch (parsedStatement .getType ()) {
997
1039
case UPDATE :
1040
+ if (parsedStatement .hasReturningClause ()) {
1041
+ throw SpannerExceptionFactory .newSpannerException (
1042
+ ErrorCode .FAILED_PRECONDITION ,
1043
+ "DML statement with returning clause cannot be executed using executeUpdateAsync: "
1044
+ + parsedStatement .getSqlWithoutComments ()
1045
+ + ". Please use executeQueryAsync instead." );
1046
+ }
998
1047
return internalExecuteUpdateAsync (parsedStatement );
999
1048
case CLIENT_SIDE :
1000
1049
case QUERY :
@@ -1141,8 +1190,9 @@ private ResultSet internalExecuteQuery(
1141
1190
final QueryOption ... options ) {
1142
1191
Preconditions .checkArgument (
1143
1192
statement .getType () == StatementType .QUERY
1144
- || (statement .getType () == StatementType .UPDATE && analyzeMode != AnalyzeMode .NONE ),
1145
- "Statement must either be a query or a DML mode with analyzeMode!=NONE" );
1193
+ || (statement .getType () == StatementType .UPDATE
1194
+ && (analyzeMode != AnalyzeMode .NONE || statement .hasReturningClause ())),
1195
+ "Statement must either be a query or a DML mode with analyzeMode!=NONE or returning clause" );
1146
1196
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork ();
1147
1197
return get (
1148
1198
transaction .executeQueryAsync (
@@ -1154,7 +1204,9 @@ private AsyncResultSet internalExecuteQueryAsync(
1154
1204
final AnalyzeMode analyzeMode ,
1155
1205
final QueryOption ... options ) {
1156
1206
Preconditions .checkArgument (
1157
- statement .getType () == StatementType .QUERY , "Statement must be a query" );
1207
+ (statement .getType () == StatementType .QUERY )
1208
+ || (statement .getType () == StatementType .UPDATE && statement .hasReturningClause ()),
1209
+ "Statement must be a query or DML with returning clause." );
1158
1210
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork ();
1159
1211
return ResultSets .toAsyncResultSet (
1160
1212
transaction .executeQueryAsync (
0 commit comments