Skip to content

Commit 8c023a2

Browse files
committed
fix: support SAMPLE and EXTEND as unquoted identifiers
Signed-off-by: 付典 <fudianchn@gmail.com>
1 parent 8d12a5d commit 8c023a2

2 files changed

Lines changed: 119 additions & 4 deletions

File tree

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

Lines changed: 9 additions & 2 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">

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

Lines changed: 110 additions & 2 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,
@@ -62,4 +73,101 @@ public void testGroupsAsIdentifier() throws JSQLParserException {
6273
assertSqlCanBeParsedAndDeparsed("SELECT a groups FROM mytable", true);
6374
assertSqlCanBeParsedAndDeparsed("CREATE TABLE mytable (groups INT)", true);
6475
}
76+
77+
// sample and extend are non-reserved in the common dialects and reach the
78+
// parser as ordinary table and column names in downstream rewriting tools.
79+
@Test
80+
public void testSampleExtendAsIdentifier() throws JSQLParserException {
81+
assertSqlCanBeParsedAndDeparsed("SELECT sample, extend FROM mytable", true);
82+
assertSqlCanBeParsedAndDeparsed("SELECT mytable.extend FROM mytable", true);
83+
assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE extend = '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);
88+
assertSqlCanBeParsedAndDeparsed("SELECT * FROM sample", true);
89+
assertSqlCanBeParsedAndDeparsed("DELETE FROM sample WHERE extend = 'x'", true);
90+
assertSqlCanBeParsedAndDeparsed("INSERT INTO sample (extend) VALUES ('x')", true);
91+
assertSqlCanBeParsedAndDeparsed("SELECT a AS sample, b AS extend FROM mytable", true);
92+
}
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+
}
65173
}

0 commit comments

Comments
 (0)