Skip to content

Commit 27a2785

Browse files
hayssamsclaude
andcommitted
Support BigQuery PARTITION BY expression in CREATE TABLE
BigQuery partitions by a bare expression rather than a partitioning method: PARTITION BY d, PARTITION BY DATE(ts), PARTITION BY RANGE_BUCKET(id, GENERATE_ARRAY(0, 100, 10)). Adds TablePartitioning.Type.EXPRESSION, which renders the expression without a method keyword or parentheses. The MySQL HASH/KEY/RANGE/LIST forms keep priority. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
1 parent aaac0c3 commit 27a2785

3 files changed

Lines changed: 63 additions & 4 deletions

File tree

src/main/java/net/sf/jsqlparser/statement/create/table/TablePartitioning.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@
2727
public class TablePartitioning implements Serializable {
2828

2929
public enum Type {
30-
HASH, KEY, RANGE, LIST
30+
HASH, KEY, RANGE, LIST,
31+
/**
32+
* BigQuery partitions by a bare expression, without a partitioning method keyword:
33+
* {@code PARTITION BY DATE(ts)}.
34+
*/
35+
EXPRESSION
3136
}
3237

3338
private Type type;
@@ -285,6 +290,10 @@ public void appendTo(StringBuilder builder, Consumer<Expression> expressionPrint
285290
}
286291

287292
private void appendMethod(StringBuilder builder, Consumer<Expression> expressionPrinter) {
293+
if (type == Type.EXPRESSION) {
294+
expressionPrinter.accept(expression);
295+
return;
296+
}
288297
if (linear) {
289298
builder.append("LINEAR ");
290299
}

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14895,6 +14895,7 @@ TablePartitioning CreateTablePartitioning():
1489514895
{
1489614896
TablePartitioning partitioning = null;
1489714897
TablePartitioning subPartitioning = null;
14898+
Expression expression = null;
1489814899
ExpressionList<Expression> expressionList = null;
1489914900
ExpressionList<Column> columns = null;
1490014901
List<PartitionDefinition> partitionDefinitions = null;
@@ -14905,9 +14906,11 @@ TablePartitioning CreateTablePartitioning():
1490514906
{
1490614907
<K_PARTITION> <K_BY>
1490714908
(
14908-
partitioning=HashOrKeyPartitioning()
14909+
// explicit lookaheads: the method keywords win over the BigQuery
14910+
// bare-expression alternative below, which they share a prefix with
14911+
LOOKAHEAD(2) partitioning=HashOrKeyPartitioning()
1490914912
|
14910-
<K_RANGE> { partitioning = new TablePartitioning(TablePartitioning.Type.RANGE); }
14913+
LOOKAHEAD(2) <K_RANGE> { partitioning = new TablePartitioning(TablePartitioning.Type.RANGE); }
1491114914
(
1491214915
"(" expressionList=SimpleExpressionList() ")" {
1491314916
if (expressionList.size() == 1) {
@@ -14924,7 +14927,7 @@ TablePartitioning CreateTablePartitioning():
1492414927
}
1492514928
)
1492614929
|
14927-
<K_LIST> { partitioning = new TablePartitioning(TablePartitioning.Type.LIST); }
14930+
LOOKAHEAD(2) <K_LIST> { partitioning = new TablePartitioning(TablePartitioning.Type.LIST); }
1492814931
(
1492914932
"(" expressionList=SimpleExpressionList() ")" {
1493014933
if (expressionList.size() == 1) {
@@ -14940,6 +14943,17 @@ TablePartitioning CreateTablePartitioning():
1494014943
partitioning.setColumns(columns);
1494114944
}
1494214945
)
14946+
|
14947+
// BigQuery partitions by a bare expression: PARTITION BY d, PARTITION BY DATE(ts),
14948+
// PARTITION BY RANGE_BUCKET(id, GENERATE_ARRAY(0, 100, 10))
14949+
LOOKAHEAD({ getToken(1).kind != K_HASH && getToken(1).kind != K_KEY
14950+
&& getToken(1).kind != K_LINEAR && getToken(1).kind != K_RANGE
14951+
&& getToken(1).kind != K_LIST })
14952+
expression=SimpleExpression()
14953+
{
14954+
partitioning = new TablePartitioning(TablePartitioning.Type.EXPRESSION);
14955+
partitioning.setExpression(expression);
14956+
}
1494314957
)
1494414958
[ LOOKAHEAD({ getToken(1).kind == K_PARTITIONS })
1494514959
<K_PARTITIONS> tk=<S_LONG> { partitioning.setPartitions(Long.valueOf(tk.image)); } ]

src/test/java/net/sf/jsqlparser/statement/select/BigQueryTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import net.sf.jsqlparser.JSQLParserException;
1313
import net.sf.jsqlparser.expression.ArrayExpression;
14+
import net.sf.jsqlparser.statement.create.table.CreateTable;
15+
import net.sf.jsqlparser.statement.create.table.TablePartitioning;
1416
import net.sf.jsqlparser.test.TestUtils;
1517
import org.junit.jupiter.api.Assertions;
1618
import org.junit.jupiter.api.Disabled;
@@ -178,4 +180,38 @@ void testAggregateFunctionOrderByWithoutNullHandling() throws JSQLParserExceptio
178180
String sqlStr = "SELECT ARRAY_AGG(x ORDER BY x) FROM t";
179181
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
180182
}
183+
184+
@Test
185+
void testCreateTablePartitionByExpression() throws JSQLParserException {
186+
String sqlStr = "CREATE TABLE ds.t (id INT64, ts TIMESTAMP) PARTITION BY DATE(ts)";
187+
CreateTable createTable = (CreateTable) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
188+
189+
Assertions.assertEquals(TablePartitioning.Type.EXPRESSION,
190+
createTable.getPartitioning().getType());
191+
Assertions.assertEquals("DATE(ts)",
192+
createTable.getPartitioning().getExpression().toString());
193+
}
194+
195+
@Test
196+
void testCreateTablePartitionByColumnWithOptions() throws JSQLParserException {
197+
String sqlStr =
198+
"CREATE TABLE ds.t (id INT64, d DATE) PARTITION BY d OPTIONS (description = 'x')";
199+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
200+
}
201+
202+
@Test
203+
void testCreateTableAsSelectPartitionByAndClusterBy() throws JSQLParserException {
204+
String sqlStr = "CREATE TABLE ds.t PARTITION BY d CLUSTER BY id "
205+
+ "AS SELECT 1 AS id, CURRENT_DATE() AS d";
206+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
207+
}
208+
209+
@Test
210+
void testCreateTablePartitionByHashStillParses() throws JSQLParserException {
211+
String sqlStr = "CREATE TABLE t (id INT) PARTITION BY HASH (id)";
212+
CreateTable createTable = (CreateTable) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
213+
214+
Assertions.assertEquals(TablePartitioning.Type.HASH,
215+
createTable.getPartitioning().getType());
216+
}
181217
}

0 commit comments

Comments
 (0)