|
18 | 18 | import static org.junit.jupiter.api.Assertions.assertTrue; |
19 | 19 |
|
20 | 20 | import java.time.Duration; |
| 21 | +import java.util.ArrayList; |
21 | 22 | import java.util.Arrays; |
22 | 23 | import java.util.List; |
23 | 24 | import java.util.Set; |
24 | 25 | import net.sf.jsqlparser.JSQLParserException; |
| 26 | +import net.sf.jsqlparser.expression.Alias; |
| 27 | +import net.sf.jsqlparser.expression.LongValue; |
25 | 28 | import net.sf.jsqlparser.expression.OracleHint; |
26 | 29 | import net.sf.jsqlparser.parser.CCJSqlParserUtil; |
27 | 30 | import net.sf.jsqlparser.schema.Column; |
28 | 31 | import net.sf.jsqlparser.schema.Table; |
29 | 32 | import net.sf.jsqlparser.statement.DescribeStatement; |
30 | 33 | import net.sf.jsqlparser.statement.Statement; |
31 | 34 | import net.sf.jsqlparser.statement.comment.Comment; |
| 35 | +import net.sf.jsqlparser.statement.select.AllColumns; |
| 36 | +import net.sf.jsqlparser.statement.select.Join; |
| 37 | +import net.sf.jsqlparser.statement.select.ParenthesedSelect; |
32 | 38 | import net.sf.jsqlparser.statement.select.PlainSelect; |
| 39 | +import net.sf.jsqlparser.statement.select.SelectItem; |
| 40 | +import net.sf.jsqlparser.statement.select.WithItem; |
33 | 41 | import net.sf.jsqlparser.test.TestUtils; |
34 | 42 | import org.junit.jupiter.api.Test; |
35 | 43 |
|
@@ -1065,30 +1073,31 @@ void testUpdateOrderByLimitSubquery() throws JSQLParserException { |
1065 | 1073 | } |
1066 | 1074 |
|
1067 | 1075 | @Test |
1068 | | - void testManyDerivedNamesDoNotScaleQuadratically() throws JSQLParserException { |
1069 | | - // Each aliased WITH item is remembered in otherItemNames and every plain table |
1070 | | - // reference used to be matched against it with a linear ArrayList.contains, so a |
1071 | | - // statement with n CTE aliases and n tables cost O(n^2) to walk. |
| 1076 | + void testManyDerivedNamesDoNotScaleQuadratically() { |
| 1077 | + // Every plain table reference used to be matched against the CTE aliases with |
| 1078 | + // ArrayList.contains, so n aliases and n tables cost O(n^2) to walk. |
1072 | 1079 | final int n = 30000; |
1073 | | - final StringBuilder sb = new StringBuilder("WITH "); |
| 1080 | + final PlainSelect statement = new PlainSelect() |
| 1081 | + .withSelectItems(new SelectItem<>(new AllColumns())) |
| 1082 | + .withFromItem(new Table("t0")); |
| 1083 | + final List<WithItem<?>> withItems = new ArrayList<>(n); |
| 1084 | + final List<Join> joins = new ArrayList<>(n - 1); |
1074 | 1085 | for (int i = 0; i < n; i++) { |
| 1086 | + PlainSelect body = |
| 1087 | + new PlainSelect().withSelectItems(new SelectItem<>(new LongValue(1))); |
| 1088 | + withItems.add(new WithItem<>(new ParenthesedSelect().withSelect(body), |
| 1089 | + new Alias("a" + i))); |
1075 | 1090 | if (i > 0) { |
1076 | | - sb.append(", "); |
| 1091 | + joins.add(new Join().withSimple(true).withRightItem(new Table("t" + i))); |
1077 | 1092 | } |
1078 | | - sb.append("a").append(i).append(" AS (SELECT 1)"); |
1079 | | - } |
1080 | | - sb.append(" SELECT * FROM "); |
1081 | | - for (int i = 0; i < n; i++) { |
1082 | | - if (i > 0) { |
1083 | | - sb.append(", "); |
1084 | | - } |
1085 | | - sb.append("t").append(i); |
1086 | 1093 | } |
| 1094 | + statement.setWithItemsList(withItems); |
| 1095 | + statement.setJoins(joins); |
1087 | 1096 |
|
1088 | | - // Parsing is linear and left outside the timed region; only the table walk is measured. |
1089 | | - final Statement statement = CCJSqlParserUtil.parse(sb.toString()); |
| 1097 | + // Build the AST directly: this tests table traversal, and parsing a large SQL |
| 1098 | + // fixture can hit the parser's independent timeout before traversal even starts. |
1090 | 1099 | final Set<String> tables = assertTimeoutPreemptively(Duration.ofSeconds(2), |
1091 | | - () -> new TablesNamesFinder<>().getTables(statement)); |
| 1100 | + () -> new TablesNamesFinder<>().getTables((Statement) statement)); |
1092 | 1101 | assertEquals(n, tables.size()); |
1093 | 1102 | } |
1094 | 1103 |
|
|
0 commit comments