[#3430] JDBC escape syntax is not correctly rendered from plain SQL when plain SQL contains newlines

This commit is contained in:
Lukas Eder 2014-07-22 11:12:24 +02:00
parent 5d51daac63
commit 524155e53a
2 changed files with 74 additions and 8 deletions

View File

@ -51,6 +51,7 @@ import static org.jooq.impl.DSL.fieldByName;
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.one;
import static org.jooq.impl.DSL.param;
import static org.jooq.impl.DSL.table;
import static org.jooq.impl.DSL.tableByName;
@ -673,7 +674,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
}
public void testPlainSQLAndJDBCEscapeSyntax() throws Exception {
Record result = create()
Record r1 = create()
.select(
field("{d '2014-01-01'}", Date.class).as("a"),
field("{t '19:00:00'}", Time.class).as("b"),
@ -681,8 +682,18 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
)
.fetchOne();
assertEquals(Date.valueOf("2014-01-01"), result.getValue(0));
assertEquals(Time.valueOf("19:00:00"), result.getValue(1));
assertEquals(Timestamp.valueOf("2014-01-01 19:00:00"), result.getValue(2));
assertEquals(Date.valueOf("2014-01-01"), r1.getValue(0));
assertEquals(Time.valueOf("19:00:00"), r1.getValue(1));
assertEquals(Timestamp.valueOf("2014-01-01 19:00:00"), r1.getValue(2));
// [#3430] Don't let newline characters break the parsing of JDBC escape syntax:
Record r2 = create()
.select(one().as("one"))
.where("{d '2014-01-01'} < {d '2014-01-02'}\n"
+ "and {t '00:00:00'} < {t '01:00:00'}\n"
+ "and {ts '2014-01-01 00:00:00'} < {ts '2014-01-02 00:00:00'}")
.fetchOne();
assertEquals(1, r2.getValue(0));
}
}

View File

@ -314,9 +314,24 @@ final class Utils {
private static final Pattern PLUS_PATTERN = Pattern.compile("\\+(-+)(?=\\+)");
/**
* A pattern for the JDBC escape syntax
* All characters that are matched by Java's interpretation of \s.
* <p>
* For a more accurate set of whitespaces, refer to
* http://stackoverflow.com/a/4731164/521799. In the event of SQL
* processing, it is probably safe to ignore most of those alternative
* Unicode whitespaces.
*/
private static final Pattern JDBC_ESCAPE_PATTERN = Pattern.compile("\\{(fn|d|t|ts)\\b.*");
private static final String WHITESPACE = " \t\n\u000B\f\r";
/**
* Acceptable prefixes for JDBC escape syntax.
*/
private static final String[] JDBC_ESCAPE_PREFIXES = {
"{fn ",
"{d ",
"{t ",
"{ts "
};
// ------------------------------------------------------------------------
// XXX: Record constructors and related methods
@ -1213,7 +1228,7 @@ final class Utils {
else if (sqlChars[i] == '{') {
// [#1461] Be careful not to match any JDBC escape syntax
if (JDBC_ESCAPE_PATTERN.matcher(sql.substring(i)).matches()) {
if (peekAny(sqlChars, i, JDBC_ESCAPE_PREFIXES, true)) {
render.sql(sqlChars[i]);
}
@ -1257,13 +1272,38 @@ final class Utils {
* @param peek The string to peek for
*/
static final boolean peek(char[] sqlChars, int index, String peek) {
return peek(sqlChars, index, peek, false);
}
/**
* Peek for a string at a given <code>index</code> of a <code>char[]</code>
*
* @param sqlChars The char array to peek into
* @param index The index within the char array to peek for a string
* @param peek The string to peek for
* @param anyWhitespace A whitespace character in <code>peekAny</code>
* represents "any" whitespace character as defined in
* {@link #WHITESPACE}, or in Java Regex "\s".
*/
static final boolean peek(char[] sqlChars, int index, String peek, boolean anyWhitespace) {
char[] peekArray = peek.toCharArray();
peekArrayLoop:
for (int i = 0; i < peekArray.length; i++) {
if (index + i >= sqlChars.length) {
return false;
}
if (sqlChars[index + i] != peekArray[i]) {
// [#3430] In some cases, we don't care about the type of whitespace.
if (anyWhitespace && peekArray[i] == ' ') {
for (int j = 0; j < WHITESPACE.length(); j++) {
if (sqlChars[index + i] == WHITESPACE.charAt(j)) {
continue peekArrayLoop;
}
}
}
return false;
}
}
@ -1279,8 +1319,23 @@ final class Utils {
* @param peekAny The strings to peek for
*/
static final boolean peekAny(char[] sqlChars, int index, String[] peekAny) {
return peekAny(sqlChars, index, peekAny, false);
}
/**
* Peek for several strings at a given <code>index</code> of a
* <code>char[]</code>
*
* @param sqlChars The char array to peek into
* @param index The index within the char array to peek for a string
* @param peekAny The strings to peek for
* @param anyWhitespace A whitespace character in <code>peekAny</code>
* represents "any" whitespace character as defined in
* {@link #WHITESPACE}, or in Java Regex "\s".
*/
static final boolean peekAny(char[] sqlChars, int index, String[] peekAny, boolean anyWhitespace) {
for (String peek : peekAny)
if (peek(sqlChars, index, peek))
if (peek(sqlChars, index, peek, anyWhitespace))
return true;
return false;