Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: split statement did not correctly parse escaped quotes #152

Merged
merged 5 commits into from
May 23, 2022
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 @@ -70,6 +70,7 @@ public class IntermediateStatement {

private static final char STATEMENT_DELIMITER = ';';
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';

public IntermediateStatement(
OptionsMetadata options,
Expand Down Expand Up @@ -135,7 +136,7 @@ protected static ImmutableList<StatementType> determineStatementTypes(
return builder.build();
}

private static ImmutableList<String> splitStatements(String sql) {
static ImmutableList<String> splitStatements(String sql) {
// First check trivial cases with only one statement.
int firstIndexOfDelimiter = sql.indexOf(STATEMENT_DELIMITER);
if (firstIndexOfDelimiter == -1) {
Expand All @@ -148,26 +149,33 @@ private static ImmutableList<String> splitStatements(String sql) {
ImmutableList.Builder<String> builder = ImmutableList.builder();
// TODO: Fix this parsing, as it does not take all types of quotes into consideration.
boolean insideQuotes = false;
boolean currentCharacterIsEscaped = false;
char quoteChar = 0;
int index = 0;
for (int i = 0; i < sql.length(); ++i) {
// ignore escaped quotes
if (sql.charAt(i) == SINGLE_QUOTE && !currentCharacterIsEscaped) {
insideQuotes = !insideQuotes;
}
// skip semicolon inside quotes
if (sql.charAt(i) == STATEMENT_DELIMITER && !insideQuotes) {
String stmt = sql.substring(index, i).trim();
// Statements with only ';' character are empty and dropped.
if (stmt.length() > 0) {
builder.add(stmt);
if (insideQuotes) {
if (sql.charAt(i) == quoteChar) {
if (sql.length() > (i + 1) && sql.charAt(i + 1) == quoteChar) {
// This is an escaped quote. Skip one ahead.
i++;
} else {
insideQuotes = false;
}
}
} else {
if (sql.charAt(i) == SINGLE_QUOTE || sql.charAt(i) == DOUBLE_QUOTE) {
quoteChar = sql.charAt(i);
insideQuotes = true;
} else {
// skip semicolon inside quotes
if (sql.charAt(i) == STATEMENT_DELIMITER) {
String stmt = sql.substring(index, i).trim();
// Statements with only ';' character are empty and dropped.
if (stmt.length() > 0) {
builder.add(stmt);
}
index = i + 1;
}
}
index = i + 1;
}
if (currentCharacterIsEscaped) {
currentCharacterIsEscaped = false;
} else if (sql.charAt(i) == '\\') {
currentCharacterIsEscaped = true;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,10 @@ public class StatementParser {
/**
* Simple method to escape SQL. Ultimately it is preferred that a user uses PreparedStatements but
* for the case of psql emulation, we apply this to provide a simple layer of protection to the
* user. Here we simple escape the single quote if it is not currently escaped. Note that the only
* reason we are doing it manually is because StringEscapeUtils deprecated escapeSql.
* user. This method simply duplicates all single quotes (i.e. ' becomes '').
*/
public static String singleQuoteEscape(String sql) {
StringBuilder result = new StringBuilder();
boolean currentCharacterIsEscaped = false;
for (char currentCharacter : sql.toCharArray()) {
if (currentCharacterIsEscaped) {
currentCharacterIsEscaped = false;
} else if (currentCharacter == '\\') {
currentCharacterIsEscaped = true;
} else if (currentCharacter == '\'') {
result.append('\\');
}
result.append(currentCharacter);
}
return result.toString();
return sql.replace("'", "''");
}

/** Determines the (update) command that was received from the sql string. */
Expand Down
Loading