diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 039278a6436..adda4c3c854 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -202,4 +202,9 @@ com/google/cloud/spanner/DatabaseClient java.lang.String getDatabaseRole() + + 7013 + com/google/cloud/spanner/connection/AbstractStatementParser + boolean checkReturningClauseInternal(java.lang.String) + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java index fb272e913e5..b0ae8863e6f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java @@ -140,6 +140,7 @@ public static class ParsedStatement { private final ClientSideStatementImpl clientSideStatement; private final Statement statement; private final String sqlWithoutComments; + private final boolean returningClause; private static ParsedStatement clientSideStatement( ClientSideStatementImpl clientSideStatement, @@ -155,11 +156,13 @@ private static ParsedStatement ddl(Statement statement, String sqlWithoutComment private static ParsedStatement query( Statement statement, String sqlWithoutComments, QueryOptions defaultQueryOptions) { return new ParsedStatement( - StatementType.QUERY, statement, sqlWithoutComments, defaultQueryOptions); + StatementType.QUERY, statement, sqlWithoutComments, defaultQueryOptions, false); } - private static ParsedStatement update(Statement statement, String sqlWithoutComments) { - return new ParsedStatement(StatementType.UPDATE, statement, sqlWithoutComments); + private static ParsedStatement update( + Statement statement, String sqlWithoutComments, boolean returningClause) { + return new ParsedStatement( + StatementType.UPDATE, statement, sqlWithoutComments, returningClause); } private static ParsedStatement unknown(Statement statement, String sqlWithoutComments) { @@ -176,23 +179,34 @@ private ParsedStatement( this.clientSideStatement = clientSideStatement; this.statement = statement; this.sqlWithoutComments = sqlWithoutComments; + this.returningClause = false; + } + + private ParsedStatement( + StatementType type, + Statement statement, + String sqlWithoutComments, + boolean returningClause) { + this(type, statement, sqlWithoutComments, null, returningClause); } private ParsedStatement(StatementType type, Statement statement, String sqlWithoutComments) { - this(type, statement, sqlWithoutComments, null); + this(type, statement, sqlWithoutComments, null, false); } private ParsedStatement( StatementType type, Statement statement, String sqlWithoutComments, - QueryOptions defaultQueryOptions) { + QueryOptions defaultQueryOptions, + boolean returningClause) { Preconditions.checkNotNull(type); Preconditions.checkNotNull(statement); this.type = type; this.clientSideStatement = null; this.statement = mergeQueryOptions(statement, defaultQueryOptions); this.sqlWithoutComments = sqlWithoutComments; + this.returningClause = returningClause; } @Override @@ -219,6 +233,12 @@ public StatementType getType() { return type; } + /** Returns whether the statement has a returning clause or not. * */ + @InternalApi + public boolean hasReturningClause() { + return this.returningClause; + } + /** * Returns true if the statement is a query that will return a {@link * com.google.cloud.spanner.ResultSet}. @@ -355,7 +375,7 @@ ParsedStatement parse(Statement statement, QueryOptions defaultQueryOptions) { } else if (isQuery(sql)) { return ParsedStatement.query(statement, sql, defaultQueryOptions); } else if (isUpdateStatement(sql)) { - return ParsedStatement.update(statement, sql); + return ParsedStatement.update(statement, sql, checkReturningClause(sql)); } else if (isDdlStatement(sql)) { return ParsedStatement.ddl(statement, sql); } @@ -460,6 +480,10 @@ private boolean statementStartsWith(String sql, Iterable checkStatements static final char SLASH = '/'; static final char ASTERISK = '*'; static final char DOLLAR = '$'; + static final char SPACE = ' '; + static final char CLOSE_PARENTHESIS = ')'; + static final char COMMA = ','; + static final char UNDERSCORE = '_'; /** * Removes comments from and trims the given sql statement using the dialect of this parser. @@ -522,4 +546,19 @@ static int countOccurrencesOf(char c, String string) { } return res; } + + /** + * Checks if the given SQL string contains a Returning clause. This method is used only in case of + * a DML statement. + * + * @param sql The sql string without comments that has to be evaluated. + * @return A boolean indicating whether the sql string has a Returning clause or not. + */ + @InternalApi + protected abstract boolean checkReturningClauseInternal(String sql); + + @InternalApi + public boolean checkReturningClause(String sql) { + return checkReturningClauseInternal(sql); + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java index 04ea893cb9c..afb3723c143 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java @@ -856,8 +856,8 @@ default RpcPriority getRPCPriority() { * state. The returned value depends on the type of statement: * *