Skip to content

Commit 3289526

Browse files
committed
fix: complete SAMPLE and EXTEND identifier handling
Signed-off-by: 付典 <fudianchn@gmail.com>
1 parent fffd23b commit 3289526

2 files changed

Lines changed: 107 additions & 9 deletions

File tree

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,13 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
15331533
// AS always starts an alias
15341534
if (kind == K_AS) return true;
15351535

1536+
// SAMPLE followed by a size or BLOCK starts a sampling clause.
1537+
if (kind == K_SAMPLE && (getToken(2).kind == OPENING_BRACKET
1538+
|| getToken(2).kind == K_BLOCK || getToken(2).kind == S_LONG
1539+
|| getToken(2).kind == S_DOUBLE)) {
1540+
return false;
1541+
}
1542+
15361543
// String-literal alias: SELECT col 'myAlias'
15371544
if (kind == S_CHAR_LITERAL) return true;
15381545

@@ -2571,6 +2578,7 @@ String NonReservedWord() :
25712578
| tk=<K_EXECUTE: "EXECUTE">
25722579
| tk=<K_EXPLAIN:"EXPLAIN">
25732580
| tk=<K_EXPLICIT:"EXPLICIT">
2581+
| tk=<K_EXTEND:"EXTEND">
25742582
| tk=<K_EXTENDED:"EXTENDED">
25752583
| tk=<K_EXTRACT:"EXTRACT">
25762584
| tk=<K_EXPORT:"EXPORT">
@@ -2759,6 +2767,7 @@ String NonReservedWord() :
27592767
| tk=<K_RTRIM: "RTRIM">
27602768
| tk=<K_SAFE_CAST: "SAFE_CAST">
27612769
| tk=<K_SAFE_CONVERT: "SAFE_CONVERT">
2770+
| tk=<K_SAMPLE:"SAMPLE">
27622771
| tk=<K_SAVEPOINT: "SAVEPOINT">
27632772
| tk=<K_SCHEDULE: "SCHEDULE">
27642773
| tk=<K_SCHEMA: "SCHEMA">
@@ -2894,7 +2903,6 @@ TOKEN: /* Reserved SQL Keywords and structural tokens */
28942903
| <K_EXCEPT: "EXCEPT">
28952904
| <K_EXCLUDES: "EXCLUDES"> /* Salesforce SOQL */
28962905
| <K_EXISTS:"EXISTS">
2897-
| <K_EXTEND:"EXTEND">
28982906
| <K_FBV:"FBV">
28992907
| <K_FETCH:"FETCH">
29002908
| <K_FILE: "FILE">
@@ -2952,7 +2960,6 @@ TOKEN: /* Reserved SQL Keywords and structural tokens */
29522960
| <K_RETURNS: "RETURNS">
29532961
| <K_RETURNING: "RETURNING">
29542962
| <K_RIGHT:"RIGHT">
2955-
| <K_SAMPLE: "SAMPLE">
29562963
| <K_SCRIPT: "SCRIPT">
29572964
| <K_SELECT: ("SELECT" | "SEL")>
29582965
| <K_SEMI : "SEMI">
@@ -6863,9 +6870,7 @@ String RelObjectName() :
68636870
| tk=<K_ON> | tk=<K_OPTIMIZE> | tk=<K_ORDER> | tk=<K_PROCEDURE>
68646871
| tk=<K_PUBLIC> | tk=<K_QUALIFY> | tk=<K_RIGHT> | tk=<K_FILE>
68656872
| tk=<K_SET> | tk=<K_SOME> | tk=<K_START> | tk=<K_TABLES>
6866-
| tk=<K_TOP> | tk=<K_VALUE> | tk=<K_VALUES>
6867-
| tk=<K_SAMPLE>
6868-
| tk=<K_EXTEND> )
6873+
| tk=<K_TOP> | tk=<K_VALUE> | tk=<K_VALUES> )
68696874
)
68706875
{ return result != null ? result : tk.image; }
68716876
}

‎src/test/java/net/sf/jsqlparser/statement/KeywordsTest.java‎

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,26 @@
99
*/
1010
package net.sf.jsqlparser.statement;
1111

12+
import java.util.stream.Stream;
1213
import net.sf.jsqlparser.JSQLParserException;
14+
import net.sf.jsqlparser.expression.TernaryExpression;
15+
import net.sf.jsqlparser.expression.Function;
16+
import net.sf.jsqlparser.statement.piped.FromQuery;
17+
import net.sf.jsqlparser.statement.piped.SelectPipeOperator;
18+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
1319
import net.sf.jsqlparser.parser.ParserKeywordsUtils;
20+
import net.sf.jsqlparser.schema.Column;
21+
import net.sf.jsqlparser.schema.Table;
22+
import net.sf.jsqlparser.statement.select.PlainSelect;
23+
import net.sf.jsqlparser.statement.select.SampleClause;
24+
import net.sf.jsqlparser.statement.select.WithItem;
1425
import org.junit.jupiter.api.Test;
1526
import org.junit.jupiter.params.ParameterizedTest;
1627
import org.junit.jupiter.params.provider.MethodSource;
17-
18-
import java.util.stream.Stream;
28+
import org.junit.jupiter.params.provider.ValueSource;
1929

2030
import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
31+
import static org.junit.jupiter.api.Assertions.*;
2132

2233
/**
2334
* Verifies that all non-reserved keywords can be used as unquoted identifiers (schema, table,
@@ -70,11 +81,93 @@ public void testSampleExtendAsIdentifier() throws JSQLParserException {
7081
assertSqlCanBeParsedAndDeparsed("SELECT sample, extend FROM mytable", true);
7182
assertSqlCanBeParsedAndDeparsed("SELECT mytable.extend FROM mytable", true);
7283
assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE extend = 'x'", true);
73-
assertSqlCanBeParsedAndDeparsed("UPDATE mytable SET sample = 1, extend = 'x' WHERE id = 1", true);
74-
assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (sample, extend) VALUES (1, 'x')", true);
84+
assertSqlCanBeParsedAndDeparsed("UPDATE mytable SET sample = 1, extend = 'x' WHERE id = 1",
85+
true);
86+
assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (sample, extend) VALUES (1, 'x')",
87+
true);
7588
assertSqlCanBeParsedAndDeparsed("SELECT * FROM sample", true);
7689
assertSqlCanBeParsedAndDeparsed("DELETE FROM sample WHERE extend = 'x'", true);
7790
assertSqlCanBeParsedAndDeparsed("INSERT INTO sample (extend) VALUES ('x')", true);
7891
assertSqlCanBeParsedAndDeparsed("SELECT a AS sample, b AS extend FROM mytable", true);
7992
}
93+
94+
@ParameterizedTest
95+
@ValueSource(strings = {"sample", "extend", "SaMpLe", "ExTeNd"})
96+
void testSampleExtendImplicitAliases(String keyword) throws JSQLParserException {
97+
PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(
98+
"SELECT a " + keyword + " FROM mytable " + keyword, true);
99+
assertEquals(keyword, select.getSelectItem(0).getAlias().getName());
100+
assertEquals(keyword, select.getFromItem().getAlias().getName());
101+
assertEquals("a", assertInstanceOf(Column.class,
102+
select.getSelectItem(0).getExpression()).getColumnName());
103+
}
104+
105+
@ParameterizedTest
106+
@ValueSource(strings = {"sample", "extend", "SaMpLe", "ExTeNd"})
107+
void testSampleExtendCte(String keyword) throws JSQLParserException {
108+
PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(
109+
"WITH " + keyword + "(x) AS (SELECT 1) SELECT x FROM " + keyword, true);
110+
WithItem<?> withItem = select.getWithItemsList().get(0);
111+
assertEquals(keyword, withItem.getAliasName());
112+
assertNotNull(withItem.getParenthesedStatement());
113+
assertNull(withItem.getExpression());
114+
assertEquals(keyword, assertInstanceOf(Table.class, select.getFromItem()).getName());
115+
assertSqlCanBeParsedAndDeparsed(
116+
"WITH RECURSIVE " + keyword + "(x) AS (SELECT 1) SELECT * FROM " + keyword, true);
117+
}
118+
119+
@ParameterizedTest
120+
@ValueSource(strings = {"sample", "extend", "SaMpLe", "ExTeNd"})
121+
void testSampleExtendExpressionContexts(String keyword) throws JSQLParserException {
122+
PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(
123+
"SELECT ALL " + keyword + " FROM mytable");
124+
assertEquals(keyword, assertInstanceOf(Column.class,
125+
select.getSelectItem(0).getExpression()).getColumnName());
126+
assertSqlCanBeParsedAndDeparsed("SELECT CASE " + keyword
127+
+ " WHEN 1 THEN " + keyword + " ELSE 0 END FROM mytable", true);
128+
assertSqlCanBeParsedAndDeparsed("UPDATE " + keyword + " SET a = 1", true);
129+
}
130+
131+
@ParameterizedTest
132+
@ValueSource(strings = {"sample", "extend", "SaMpLe", "ExTeNd"})
133+
void testSampleExtendTernaryOperand(String keyword) throws JSQLParserException {
134+
PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(
135+
"SELECT flag ? " + keyword + " : 0 FROM mytable", true);
136+
TernaryExpression ternary = assertInstanceOf(TernaryExpression.class,
137+
select.getSelectItem(0).getExpression());
138+
assertEquals(keyword, assertInstanceOf(Column.class, ternary.getThenExpression())
139+
.getColumnName());
140+
}
141+
142+
@ParameterizedTest
143+
@ValueSource(strings = {"SAMPLE (10)", "SAMPLE BLOCK (10) SEED (1)",
144+
"SAMPLE 0.1 OFFSET 1000", "USING SAMPLE 10 PERCENT (BERNOULLI)",
145+
"TABLESAMPLE SYSTEM (10)"})
146+
void testSampleClauseStaysClause(String clause) throws JSQLParserException {
147+
PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(
148+
"SELECT * FROM mytable " + clause, true);
149+
Table table = assertInstanceOf(Table.class, select.getFromItem());
150+
assertNull(table.getAlias());
151+
SampleClause sample = table.getSampleClause();
152+
assertNotNull(sample);
153+
assertEquals(" " + clause, sample.toString());
154+
}
155+
156+
@ParameterizedTest
157+
@ValueSource(strings = {"sample", "extend", "SaMpLe", "ExTeNd"})
158+
void testSampleExtendPipeExpression(String keyword) throws JSQLParserException {
159+
FromQuery query = (FromQuery) assertSqlCanBeParsedAndDeparsed(
160+
"FROM " + keyword + " |> EXTEND " + keyword + " AS x", true);
161+
SelectPipeOperator operator = assertInstanceOf(SelectPipeOperator.class,
162+
query.getPipeOperators().get(0));
163+
assertEquals("EXTEND", operator.getOperatorName());
164+
assertEquals(keyword, assertInstanceOf(Column.class,
165+
operator.getSelectItems().get(0).getExpression()).getColumnName());
166+
assertSqlCanBeParsedAndDeparsed(
167+
"FROM mytable |> EXTEND " + keyword + "(x) AS x", true);
168+
PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(
169+
"SELECT ALL " + keyword + "(x) FROM mytable");
170+
assertEquals(keyword, assertInstanceOf(Function.class,
171+
select.getSelectItem(0).getExpression()).getName());
172+
}
80173
}

0 commit comments

Comments
 (0)