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 @@ -25,6 +25,9 @@ public P withSquareBracketQuotation(boolean allowSquareBracketQuotation) {
return withFeature(Feature.allowSquareBracketQuotation, allowSquareBracketQuotation);
}

public P withAllowComplexParsing(boolean allowComplexParsing) {
return withFeature(Feature.allowComplexParsing, allowComplexParsing);
}
public P withFeature(Feature f, boolean enabled) {
getConfiguration().setValue(f, enabled);
return me();
Expand Down
40 changes: 36 additions & 4 deletions src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* @author toben
*/
public final class CCJSqlParserUtil {
public final static int ALLOWED_NESTING_DEPTH = 7;

private CCJSqlParserUtil() {
}
Expand Down Expand Up @@ -53,7 +54,9 @@ public static Statement parse(String sql) throws JSQLParserException {
* @throws JSQLParserException
*/
public static Statement parse(String sql, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
CCJSqlParser parser = newParser(sql);
boolean allowComplexParsing = getNestingDepth(sql)<=ALLOWED_NESTING_DEPTH;

CCJSqlParser parser = newParser(sql).withAllowComplexParsing(allowComplexParsing);
if (consumer != null) {
consumer.accept(parser);
}
Expand Down Expand Up @@ -110,7 +113,9 @@ public static Expression parseExpression(String expression, boolean allowPartial
}

public static Expression parseExpression(String expression, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
CCJSqlParser parser = newParser(expression);
boolean allowComplexParsing = getNestingDepth(expression)<=ALLOWED_NESTING_DEPTH;

CCJSqlParser parser = newParser(expression).withAllowComplexParsing(allowComplexParsing);
if (consumer != null) {
consumer.accept(parser);
}
Expand Down Expand Up @@ -154,7 +159,9 @@ public static Expression parseCondExpression(String condExpr, boolean allowParti
}

public static Expression parseCondExpression(String condExpr, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
CCJSqlParser parser = newParser(condExpr);
boolean allowComplexParsing = getNestingDepth(condExpr)<=ALLOWED_NESTING_DEPTH;

CCJSqlParser parser = newParser(condExpr).withAllowComplexParsing(allowComplexParsing);
if (consumer != null) {
consumer.accept(parser);
}
Expand Down Expand Up @@ -190,7 +197,9 @@ public static Statement parseStatement(CCJSqlParser parser) throws JSQLParserExc
* @return the statements parsed
*/
public static Statements parseStatements(String sqls) throws JSQLParserException {
CCJSqlParser parser = newParser(sqls);
boolean allowComplexParsing = getNestingDepth(sqls)<=ALLOWED_NESTING_DEPTH;

CCJSqlParser parser = newParser(sqls).withAllowComplexParsing(allowComplexParsing);
return parseStatements(parser);
}

Expand Down Expand Up @@ -225,5 +234,28 @@ public static void streamStatements(StatementListener listener, InputStream is,
throw new JSQLParserException(ex);
}
}

public static int getNestingDepth(String sql) {
int maxlevel=0;
int level=0;

char[] chars = sql.toCharArray();
for (char c:chars) {
switch(c) {
case '(':
level++;
break;
case ')':
if (maxlevel<level) {
maxlevel = level;
}
level--;
break;
default:
// Codazy/PMD insists in a Default statement
}
}
return maxlevel;
}

}
7 changes: 7 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,13 @@ public enum Feature {
*/
allowSquareBracketQuotation(false),

// PERFORMANCE

/**
* allows complex expression parameters or named parameters for functions
* will be switched off, when deep nesting of functions is detected
*/
allowComplexParsing(true)
;

private Object value;
Expand Down
14 changes: 12 additions & 2 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1347,7 +1347,7 @@ String RelObjectNameWithoutValue() :
| tk=<K_TABLE> | tk=<K_DATETIMELITERAL> | tk=<K_COMMIT> | tk=<K_PRECISION>
| tk=<K_INSERT> | tk=<K_INDEX> | tk=<K_PRIMARY> | tk=<K_ENABLE>
| tk=<K_TEMP> | tk=<K_TEMPORARY> | tk=<K_TO> | tk=<K_TYPE> | tk=<K_ISNULL>
| tk=<K_ZONE> | tk=<K_COLUMNS> | tk=<K_DESCRIBE> | tk=<K_FN> | tk=<K_PATH>
| tk=<K_ZONE> | tk=<K_COLUMNS> | tk=<K_DESCRIBE> | tk=<K_FN> | tk=<K_PATH>
| tk=<K_DATE_LITERAL> | tk=<K_NEXTVAL> | tk=<K_TRUE> | tk=<K_FALSE> | tk=<K_DUPLICATE>
| tk=<K_READ> | tk=<K_SCHEMA> | tk=<K_SIGNED> | tk=<K_SIZE> | tk=<K_SEQUENCE> | tk=<K_SESSION> | tk=<K_SKIP>
| tk=<K_SYNONYM>
Expand Down Expand Up @@ -3373,6 +3373,14 @@ Expression PrimaryExpression() #PrimaryExpression:

| LOOKAHEAD("(" retval=SubSelect() ")") "(" retval=SubSelect() ")"

| LOOKAHEAD({getAsBoolean(Feature.allowComplexParsing)}) "(" list = ComplexExpressionList() ")"
{
if (list.getExpressions().size() == 1) {
retval = new Parenthesis(list.getExpressions().get(0));
} else {
retval = new RowConstructor().withExprList(list);
}
}
| "(" list = SimpleExpressionList(true) ")"
{
if (list.getExpressions().size() == 1) {
Expand Down Expand Up @@ -3912,7 +3920,9 @@ Function InternalFunction(Function retval) :
|
LOOKAHEAD(3) namedExpressionList=NamedExpressionList1()
|
LOOKAHEAD(NamedExpressionListExprFirst()) namedExpressionList = NamedExpressionListExprFirst()
LOOKAHEAD(NamedExpressionListExprFirst(), { getAsBoolean(Feature.allowComplexParsing) }) namedExpressionList = NamedExpressionListExprFirst()
|
LOOKAHEAD(3, { getAsBoolean(Feature.allowComplexParsing) }) (expressionList=ComplexExpressionList() [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ])
|
LOOKAHEAD(3) (expressionList=SimpleExpressionList(true) [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ])
|
Expand Down
37 changes: 37 additions & 0 deletions src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,41 @@ public void testParseExpressionWithBracketsIssue1159_2() throws Exception {
parser -> parser.withSquareBracketQuotation(true));
assertEquals("[travel_data].[travel_id]", result.toString());
}
@Test
public void testNestingDepth() throws Exception {
assertEquals(2,
CCJSqlParserUtil.getNestingDepth("SELECT concat(concat('A','B'),'B') FROM mytbl"));
assertEquals(20, CCJSqlParserUtil.getNestingDepth(
"concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat('A','B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B'),'B') FROM mytbl"));
assertEquals(4, CCJSqlParserUtil.getNestingDepth(""
+ "-- MERGE 1\n"
+ "MERGE INTO cfe.impairment imp\n" + " USING ( WITH x AS (\n"
+ " SELECT a.id_instrument\n"
+ " , a.id_currency\n"
+ " , a.id_instrument_type\n"
+ " , b.id_portfolio\n"
+ " , c.attribute_value product_code\n"
+ " , t.valid_date\n" + " , t.ccf\n"
+ " FROM cfe.instrument a\n"
+ " INNER JOIN cfe.impairment b\n"
+ " ON a.id_instrument = b.id_instrument\n"
+ " LEFT JOIN cfe.instrument_attribute c\n"
+ " ON a.id_instrument = c.id_instrument\n"
+ " AND c.id_attribute = 'product'\n"
+ " INNER JOIN cfe.ext_ccf t\n"
+ " ON ( a.id_currency LIKE t.id_currency )\n"
+ " AND ( a.id_instrument_type LIKE t.id_instrument_type )\n"
+ " AND ( b.id_portfolio LIKE t.id_portfolio\n"
+ " OR ( b.id_portfolio IS NULL\n"
+ " AND t.id_portfolio = '%' ) )\n"
+ " AND ( c.attribute_value LIKE t.product_code\n"
+ " OR ( c.attribute_value IS NULL\n"
+ " AND t.product_code = '%' ) ) )\n"
+ "SELECT /*+ PARALLEL */ *\n" + " FROM x x1\n"
+ " WHERE x1.valid_date = ( SELECT max\n"
+ " FROM x\n"
+ " WHERE id_instrument = x1.id_instrument ) ) s\n"
+ " ON ( imp.id_instrument = s.id_instrument )\n" + "WHEN MATCHED THEN\n"
+ " UPDATE SET imp.ccf = s.ccf\n" + ";"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@
*/
public class NestedBracketsPerformanceTest {

@Test
@Test(timeout = 2000)
public void testIssue766() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat('1','2'),'3'),'4'),'5'),'6'),'7'),'8'),'9'),'10'),'11'),'12'),'13'),'14'),'15'),'16'),'17'),'18'),'19'),'20'),'21'),col1 FROM tbl t1", true);
}

@Test
@Test(timeout = 2000)
public void testIssue766_2() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT concat(concat(concat('1', '2'), '3'), '4'), col1 FROM tbl t1");
}

@Test
@Test(timeout = 2000)
public void testIssue235() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT CASE WHEN ( CASE WHEN ( CASE WHEN ( CASE WHEN ( 1 ) THEN 0 END ) THEN 0 END ) THEN 0 END ) THEN 0 END FROM a", true);
}
Expand Down Expand Up @@ -116,7 +116,7 @@ public void testIssue856() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(sql);
}

@Test
@Test(timeout = 2000)
public void testRecursiveBracketExpressionIssue1019() {
assertEquals("IF(1=1, 1, 2)", buildRecursiveBracketExpression("IF(1=1, $1, 2)", "1", 0));
assertEquals("IF(1=1, IF(1=1, 1, 2), 2)", buildRecursiveBracketExpression("IF(1=1, $1, 2)", "1", 1));
Expand All @@ -128,12 +128,12 @@ public void testRecursiveBracketExpressionIssue1019_2() throws JSQLParserExcepti
doIncreaseOfParseTimeTesting("IF(1=1, $1, 2)", "1", 10);
}

@Test
@Test(timeout = 2000)
public void testIssue1013() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT ((((((((((((((((tblA)))))))))))))))) FROM mytable");
}

@Test
@Test(timeout = 2000)
public void testIssue1013_2() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT * FROM ((((((((((((((((tblA))))))))))))))))");
}
Expand All @@ -143,7 +143,7 @@ public void testIssue1013_3() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT * FROM (((tblA)))");
}

@Test
@Test(timeout = 2000)
public void testIssue1013_4() throws JSQLParserException {
String s = "tblA";
for (int i = 1; i < 100; i++) {
Expand All @@ -161,13 +161,13 @@ public void testIssue1013_4() throws JSQLParserException {
*
* @throws JSQLParserException
*/
@Test
@Test(timeout = 2000)
public void testIncreaseOfParseTime() throws JSQLParserException {
doIncreaseOfParseTimeTesting("concat($1,'B')", "'A'", 20);
doIncreaseOfParseTimeTesting("concat($1,'B')", "'A'", 50);
}

private void doIncreaseOfParseTimeTesting(String template, String finalExpression, int maxDepth) throws JSQLParserException {
long oldDurationTime = 1000;
long oldDurationTime = 2000;
int countProblematic = 0;
for (int i = 0; i < maxDepth; i++) {
String sql = "SELECT " + buildRecursiveBracketExpression(template, finalExpression, i) + " FROM mytbl";
Expand Down Expand Up @@ -202,4 +202,16 @@ private String buildRecursiveBracketExpression(String template, String finalExpr
}
return template.replace("$1", buildRecursiveBracketExpression(template, finalExpression, depth - 1));
}
}

@Test(timeout = 2000)
public void testIssue1103() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(
"SELECT\n" + "ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(\n"
+ "ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(\n"
+ "ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(\n"
+ "ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(ROUND(0\n" + ",0),0),0),0),0),0),0),0)\n"
+ ",0),0),0),0),0),0),0),0)\n" + ",0),0),0),0),0),0),0),0)\n"
+ ",0),0),0),0),0),0),0),0)",
true);
}
}
13 changes: 11 additions & 2 deletions src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4544,12 +4544,21 @@ public void testKeywordCostsIssue1185() throws JSQLParserException {
}

@Test
public void testKeywordCostsIssue1135() throws JSQLParserException {
public void testFunctionWithComplexParameters_Issue1190() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT to_char(a = '3') FROM dual", true);
}

@Test
public void testConditionsWithExtraBrackets_Issue1194() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT (col IS NULL) FROM tbl", true);
}

public void testWithValueListWithExtraBrackets1135() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))) select day, value from sample_data", true);
}

@Test
public void testKeywordCostsIssue1135_2() throws JSQLParserException {
public void testWithValueListWithOutExtraBrackets1135() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("with sample_data(day, value) as (values (0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16)) select day, value from sample_data", true);
}

Expand Down