Permalink
Browse files

fix: honor comments when replacing {fn curdate()} kind of calls

fixes #632
  • Loading branch information...
vlsi committed Aug 28, 2016
1 parent a639431 commit 2d9b313160dc5b73df2ae9001795592b37e8e8e0
@@ -967,17 +967,18 @@ static String replaceProcessing(String p_sql, boolean replaceProcessingEnabled,
// Since escape codes can only appear in SQL CODE, we keep track
// of if we enter a string or not.
int len = p_sql.length();
char[] chars = p_sql.toCharArray();
StringBuilder newsql = new StringBuilder(len);
int i = 0;
while (i < len) {
i = parseSql(p_sql, i, newsql, false, standardConformingStrings);
i = parseSql(chars, i, newsql, false, standardConformingStrings);
// We need to loop here in case we encounter invalid
// SQL, consider: SELECT a FROM t WHERE (1 > 0)) ORDER BY a
// We can't ending replacing after the extra closing paren
// because that changes a syntax error to a valid query
// that isn't what the user specified.
if (i < len) {
newsql.append(p_sql.charAt(i));
newsql.append(chars[i]);
i++;
}
}
@@ -1000,44 +1001,46 @@ static String replaceProcessing(String p_sql, boolean replaceProcessingEnabled,
* @return the position we stopped processing at
* @throws SQLException if given SQL is wrong
*/
private static int parseSql(String p_sql, int i, StringBuilder newsql, boolean stopOnComma,
private static int parseSql(char[] p_sql, int i, StringBuilder newsql, boolean stopOnComma,
boolean stdStrings) throws SQLException {
SqlParseState state = SqlParseState.IN_SQLCODE;
int len = p_sql.length();
int len = p_sql.length;
int nestedParenthesis = 0;
boolean endOfNested = false;
// because of the ++i loop
i--;
while (!endOfNested && ++i < len) {
char c = p_sql.charAt(i);
char c = p_sql[i];
switch (state) {
case IN_SQLCODE:
if (c == '$' && (i == 0 || !isIdentifierContChar(p_sql.charAt(i - 1)))) {
// start of a dollar-quoted string
int tagEnd = -1;
if (i + 1 < len) {
tagEnd = p_sql.indexOf('$', i + 1);
}
if (tagEnd != -1) {
String dollarQuoteTag = p_sql.substring(i, tagEnd + 1);
int nextPos = p_sql.indexOf(dollarQuoteTag, i + dollarQuoteTag.length());
if (nextPos > 0) {
tagEnd = nextPos + dollarQuoteTag.length();
}
}
if (tagEnd == -1) {
tagEnd = len;
}
newsql.append(p_sql, i, tagEnd); // tagEnd is excluding
i = tagEnd - 1;
if (c == '$') {
int i0 = i;
i = parseDollarQuotes(p_sql, i);
newsql.append(p_sql, i0, i - i0 + 1);
break;
} else if (c == '\'') {
// start of a string?
state = SqlParseState.IN_STRING;
int i0 = i;
i = parseSingleQuotes(p_sql, i, stdStrings);
newsql.append(p_sql, i0, i - i0 + 1);
break;
} else if (c == '"') {
// start of a identifier?
state = SqlParseState.IN_IDENTIFIER;
int i0 = i;
i = parseDoubleQuotes(p_sql, i);
newsql.append(p_sql, i0, i - i0 + 1);
break;
} else if (c == '/') {
int i0 = i;
i = parseBlockComment(p_sql, i);
newsql.append(p_sql, i0, i - i0 + 1);
break;
} else if (c == '-') {
int i0 = i;
i = parseLineComment(p_sql, i);
newsql.append(p_sql, i0, i - i0 + 1);
break;
} else if (c == '(') { // begin nested sql
nestedParenthesis++;
} else if (c == ')') { // end of nested sql
@@ -1051,8 +1054,8 @@ private static int parseSql(String p_sql, int i, StringBuilder newsql, boolean s
break;
} else if (c == '{') { // start of an escape code?
if (i + 1 < len) {
char next = p_sql.charAt(i + 1);
char nextnext = (i + 2 < len) ? p_sql.charAt(i + 2) : '\0';
char next = p_sql[i + 1];
char nextnext = (i + 2 < len) ? p_sql[i + 2] : '\0';
if (next == 'd' || next == 'D') {
state = SqlParseState.ESC_TIMEDATE;
i++;
@@ -1088,38 +1091,15 @@ private static int parseSql(String p_sql, int i, StringBuilder newsql, boolean s
newsql.append(c);
break;
case IN_STRING:
if (c == '\'') {
// end of string?
state = SqlParseState.IN_SQLCODE;
} else if (c == '\\' && !stdStrings) {
// a backslash?
state = SqlParseState.BACKSLASH;
}
newsql.append(c);
break;
case IN_IDENTIFIER:
if (c == '"') {
// end of identifier
state = SqlParseState.IN_SQLCODE;
}
newsql.append(c);
break;
case BACKSLASH:
state = SqlParseState.IN_STRING;
newsql.append(c);
break;
case ESC_FUNCTION:
// extract function name
String functionName;
int posArgs = p_sql.indexOf('(', i);
if (posArgs != -1) {
functionName = p_sql.substring(i, posArgs).trim();
int posArgs;
for (posArgs = i; posArgs < len && p_sql[posArgs] != '('; posArgs++) {
;
}
if (posArgs < len) {
functionName = new String(p_sql, i, posArgs - i).trim();
// extract arguments
i = posArgs + 1;// we start the scan after the first (
StringBuilder args = new StringBuilder();
@@ -1129,8 +1109,8 @@ private static int parseSql(String p_sql, int i, StringBuilder newsql, boolean s
}
// go to the end of the function copying anything found
i++;
while (i < len && p_sql.charAt(i) != '}') {
newsql.append(p_sql.charAt(i++));
while (i < len && p_sql[i] != '}') {
newsql.append(p_sql[i++]);
}
state = SqlParseState.IN_SQLCODE; // end of escaped function (or query)
break;
@@ -1161,12 +1141,13 @@ private static String escapeFunction(String functionName, String args, boolean s
throws SQLException {
// parse function arguments
int len = args.length();
char[] argChars = args.toCharArray();
int i = 0;
ArrayList<StringBuilder> parsedArgs = new ArrayList<StringBuilder>();
while (i < len) {
StringBuilder arg = new StringBuilder();
int lastPos = i;
i = parseSql(args, i, arg, true, stdStrings);
i = parseSql(argChars, i, arg, true, stdStrings);
if (lastPos != i) {
parsedArgs.add(arg);
}
@@ -1200,9 +1181,6 @@ private static String escapeFunction(String functionName, String args, boolean s
// Static variables for parsing SQL when replaceProcessing is true.
private enum SqlParseState {
IN_SQLCODE,
IN_STRING,
IN_IDENTIFIER,
BACKSLASH,
ESC_TIMEDATE,
ESC_FUNCTION,
ESC_OUTERJOIN,
@@ -40,6 +40,13 @@ public QuotationTest(QuoteStyle quoteStyle, String expected, String expr) {
@Parameterized.Parameters(name = "{index}: quotes(style={0}, src={1}, quoted={2})")
public static Iterable<Object[]> data() {
Collection<String> prefix = new ArrayList<String>();
// Too many prefixes make test run long
prefix.add("");
prefix.add("/*\n$\n*//* ? *//*{fn *//* now} */");
prefix.add("-- $\n");
prefix.add("--\n/* $ */");
Collection<Object[]> ids = new ArrayList<Object[]>();
Collection<String> garbageValues = new ArrayList<String>();
garbageValues.add("{fn now}");
@@ -79,7 +86,9 @@ public QuotationTest(QuoteStyle quoteStyle, String expected, String expr) {
continue;
}
String expected = unquoted;
ids.add(new Object[]{quoteStyle, expected, quoted});
for (String p : prefix) {
ids.add(new Object[]{quoteStyle, expected, p + quoted});
}
if (unquoted.length() == 1) {
char ch = unquoted.charAt(0);
if (ch >= 'a' && ch <= 'z') {
@@ -180,6 +180,20 @@ public void testPreparedFunction() throws SQLException {
assertEquals("a5", rs.getString(1));
}
public void testDollarInComment() throws SQLException {
PreparedStatement pstmt = con.prepareStatement("SELECT /* $ */ {fn curdate()}");
ResultSet rs = pstmt.executeQuery();
assertTrue(rs.next());
assertNotNull("{fn curdate()} should be not null", rs.getString(1));
}
public void testDollarInCommentTwoComments() throws SQLException {
PreparedStatement pstmt = con.prepareStatement("SELECT /* $ *//* $ */ {fn curdate()}");
ResultSet rs = pstmt.executeQuery();
assertTrue(rs.next());
assertNotNull("{fn curdate()} should be not null", rs.getString(1));
}
public void testNumericFunctions() throws SQLException {
Statement stmt = con.createStatement();

0 comments on commit 2d9b313

Please sign in to comment.