Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ String removeCommentsAndTrimInternal(String sql) {
startQuote = 0;
}
} else if (c == '\\') {
lastCharWasEscapeChar = true;
lastCharWasEscapeChar = !lastCharWasEscapeChar;
} else {
lastCharWasEscapeChar = false;
}
Expand Down Expand Up @@ -294,7 +294,7 @@ protected boolean checkReturningClauseInternal(String rawSql) {
startQuote = 0;
}
} else if (c == '\\') {
lastCharWasEscapeChar = true;
lastCharWasEscapeChar = !lastCharWasEscapeChar;
} else {
lastCharWasEscapeChar = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@

package com.google.cloud.spanner.connection;

import static com.google.cloud.spanner.ErrorCode.INVALID_ARGUMENT;
import static com.google.cloud.spanner.connection.StatementParserTest.assertUnclosedLiteral;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.connection.StatementParserTest.CommentInjector;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -39,6 +45,55 @@ static String skip(String sql, int currentIndex) {
return sql.substring(currentIndex, position);
}

@Test
public void testRemoveCommentsAndTrim() {
AbstractStatementParser parser =
AbstractStatementParser.getInstance(Dialect.GOOGLE_STANDARD_SQL);

// Statements that should parse correctly
String[] validStatements =
new String[] {
"SELECT '\\\\'", // SELECT '\\' (escaped backslash, followed by quote)
"SELECT '\\''", // SELECT '\'' (escaped quote, followed by an actual closing quote)
"SELECT '\\\\\\\\'" // SELECT '\\\\' (two escaped backslashes)
};
for (String sql : validStatements) {
assertEquals(sql, parser.removeCommentsAndTrim(sql));
}

// Statements that contain an unclosed literal because the final quote is
// escaped
String[] invalidStatements =
new String[] {
"SELECT '\\'" // SELECT '\' (escaped closing quote)
};

for (String sql : invalidStatements) {
try {
parser.removeCommentsAndTrim(sql);
fail("Expected SpannerException for unclosed literal: " + sql);
} catch (SpannerException e) {
assertEquals(INVALID_ARGUMENT, e.getErrorCode());
}
}
}

@Test
public void testReturningClauseWithBackslashes() {
AbstractStatementParser parser =
AbstractStatementParser.getInstance(Dialect.GOOGLE_STANDARD_SQL);

// Valid returning clause, double backslash in string literal should be handled
// correctly.
String sqlWithReturning = "INSERT INTO my_table (value) VALUES ('foo \\\\ bar') THEN RETURN id";
assertTrue(parser.parse(Statement.of(sqlWithReturning)).hasReturningClause());

// No returning clause, `then return` is inside a string literal with a double
// backslash.
String sqlWithoutReturning = "INSERT INTO my_table (value) VALUES ('then \\\\ return')";
assertFalse(parser.parse(Statement.of(sqlWithoutReturning)).hasReturningClause());
}

@Test
public void testSkip() {
assertEquals("", skip(""));
Expand Down
Loading