Skip to content

Commit 4a970a3

Browse files
fix: end Oracle q-quoted literals at the first closing delimiter (#2893)
1 parent efef378 commit 4a970a3

2 files changed

Lines changed: 59 additions & 5 deletions

File tree

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4913,11 +4913,13 @@ TOKEN:
49134913
(
49144914
// Backslash escapes and doubled quotes; trimmed below to the rules in effect
49154915
("'" ( "\\" ~[] | "''" | ~["'", "\\"] )* "'") | ("'" ("''" | ~["'"])* "'")
4916-
// Alternative Oracle Escape Modes
4917-
| ("q'{" (~[])* "}'")
4918-
| ("q'(" (~[])* ")'")
4919-
| ("q'[" (~[])* "]'")
4920-
| ("q''" (~[])* "''")
4916+
// Alternative Oracle Escape Modes, up to the first closing delimiter
4917+
// followed by a quote: a body cannot contain that pair, so the longest
4918+
// match stops there and cannot run on to a later literal
4919+
| ("q'{" ( ~["}"] | ("}")+ ~["}", "'"] )* ("}")+ "'")
4920+
| ("q'(" ( ~[")"] | (")")+ ~[")", "'"] )* (")")+ "'")
4921+
| ("q'[" ( ~["]"] | ("]")+ ~["]", "'"] )* ("]")+ "'")
4922+
| ("q''" ( ~["'"] | "'" ~["'"] )* "''")
49214923
// | ("q'\\" (~[])* "\\'") <--- Does not work
49224924
)
49234925
// PostgreSQL Unicode escape string with an optional UESCAPE clause, see

‎src/test/java/net/sf/jsqlparser/expression/StringValueTest.java‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
1616

1717
import java.time.Duration;
18+
import java.util.Arrays;
19+
import java.util.HashSet;
1820
import net.sf.jsqlparser.JSQLParserException;
21+
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
1922
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
2023
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
2124
import net.sf.jsqlparser.statement.Statement;
@@ -24,9 +27,13 @@
2427
import net.sf.jsqlparser.statement.create.table.ColumnOption;
2528
import net.sf.jsqlparser.statement.select.PlainSelect;
2629
import net.sf.jsqlparser.test.TestUtils;
30+
import net.sf.jsqlparser.util.TablesNamesFinder;
2731
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
2832
import org.junit.jupiter.api.Assertions;
2933
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.params.ParameterizedTest;
35+
import org.junit.jupiter.params.provider.CsvSource;
36+
import org.junit.jupiter.params.provider.ValueSource;
3037

3138
/**
3239
*
@@ -369,6 +376,51 @@ public void testOracleAlternativeQuoting() throws JSQLParserException {
369376
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
370377
}
371378

379+
@ParameterizedTest
380+
@ValueSource(strings = {"q'{a}'", "q'(a)'", "q'[a]'", "q''a''", "Q'[a]'"})
381+
void testOracleAlternativeQuotingEndsAtFirstDelimiter(String literal)
382+
throws JSQLParserException {
383+
String sqlStr =
384+
"SELECT " + literal + " FROM t WHERE id IN (SELECT id FROM secrets) AND x = "
385+
+ literal;
386+
387+
Statement statement = TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
388+
389+
assertEquals(new HashSet<>(Arrays.asList("t", "secrets")),
390+
new TablesNamesFinder<Void>().getTables(statement));
391+
assertEquals(3, CCJSqlParserUtil.parseStatements(
392+
"SELECT " + literal + " FROM t; DELETE FROM u; SELECT " + literal + " FROM t")
393+
.size());
394+
}
395+
396+
@Test
397+
void testOracleAlternativeQuotingKeepsOneLiteralPerQuote() throws JSQLParserException {
398+
PlainSelect select = (PlainSelect) TestUtils
399+
.assertSqlCanBeParsedAndDeparsed("SELECT q'[a]' || q'[b]' FROM dual", true);
400+
401+
Concat concat = (Concat) select.getSelectItem(0).getExpression();
402+
assertEquals("[a]", ((StringValue) concat.getLeftExpression()).getValue());
403+
assertEquals("[b]", ((StringValue) concat.getRightExpression()).getValue());
404+
}
405+
406+
@ParameterizedTest
407+
@CsvSource(delimiter = '|', quoteCharacter = '"', value = {
408+
"q'[a]b]'|[a]b]",
409+
"q'[a]]'|[a]]",
410+
"q'[]'|[]",
411+
"q'[]]'|[]]",
412+
"q'[it's]'|[it's]",
413+
"q'(a)b)'|(a)b)",
414+
"q'{a}}'|{a}}",
415+
"q''a'b''|'a'b'"})
416+
void testOracleAlternativeQuotingBody(String literal, String value)
417+
throws JSQLParserException {
418+
PlainSelect select = (PlainSelect) TestUtils
419+
.assertSqlCanBeParsedAndDeparsed("SELECT " + literal + " FROM dual", true);
420+
421+
assertEquals(value, ((StringValue) select.getSelectItem(0).getExpression()).getValue());
422+
}
423+
372424
@Test
373425
public void testParseInput_BYTEA() throws Exception {
374426
String sqlStr = "VALUES (X'', X'01FF', X'01 bc 2a', X'01' '02')";

0 commit comments

Comments
 (0)