Skip to content

Commit 6d597a2

Browse files
hayssamsclaude
andcommitted
Support CREATE [SNAPSHOT] TABLE ... CLONE
BigQuery snapshots and clones a table with CREATE [SNAPSHOT] TABLE target CLONE source [FOR SYSTEM_TIME AS OF ...]. The clone source is kept on CreateTable and rendered by both toString() and CreateTableDeParser. SNAPSHOT itself already parsed as a create option. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
1 parent 6cecf6b commit 6d597a2

4 files changed

Lines changed: 58 additions & 0 deletions

File tree

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class CreateTable implements Statement {
3636
private List<TableElement> tableElements;
3737
private Select select;
3838
private Table likeTable;
39+
private Table cloneTable;
3940
private ColDataType ofType;
4041
private boolean selectParenthesis;
4142
private boolean ifNotExists = false;
@@ -236,6 +237,22 @@ public void setLikeTable(Table likeTable, boolean parenthesis) {
236237
this.selectParenthesis = parenthesis;
237238
}
238239

240+
/**
241+
* BigQuery's {@code CREATE [SNAPSHOT] TABLE target CLONE source [FOR SYSTEM_TIME AS OF ...]}.
242+
*/
243+
public Table getCloneTable() {
244+
return cloneTable;
245+
}
246+
247+
public void setCloneTable(Table cloneTable) {
248+
this.cloneTable = cloneTable;
249+
}
250+
251+
public CreateTable withCloneTable(Table cloneTable) {
252+
setCloneTable(cloneTable);
253+
return this;
254+
}
255+
239256
public boolean isIfNotExists() {
240257
return ifNotExists;
241258
}
@@ -330,6 +347,9 @@ private void appendCreateClause(StringBuilder b) {
330347
if (partitionOf != null) {
331348
b.append(" PARTITION OF ").append(partitionOf);
332349
}
350+
if (cloneTable != null) {
351+
b.append(" CLONE ").append(cloneTable);
352+
}
333353
}
334354

335355
private void appendColumnDefinitions(StringBuilder b) {

src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public void deParse(CreateTable createTable) {
6262
builder.append(" PARTITION OF ")
6363
.append(createTable.getPartitionOf().getFullyQualifiedName());
6464
}
65+
if (createTable.getCloneTable() != null) {
66+
builder.append(" CLONE ").append(createTable.getCloneTable());
67+
}
6568

6669
if (createTable.getColumns() != null && !createTable.getColumns().isEmpty()) {
6770
builder.append(" (");

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,7 @@ String NonReservedWord() :
19751975
| tk=<K_CLOSE:"CLOSE">
19761976
| tk=<K_CLOUD:"CLOUD">
19771977
| tk=<K_COALESCE:"COALESCE">
1978+
| tk=<K_CLONE: "CLONE">
19781979
| tk=<K_COLLATE:"COLLATE">
19791980
| tk=<K_COLUMN:"COLUMN">
19801981
| tk=<K_COLUMNS:"COLUMNS">
@@ -13556,6 +13557,12 @@ CreateTable CreateTable(boolean isUsingOrReplace):
1355613557
<K_TABLE>
1355713558
[ LOOKAHEAD(2) <K_IF> <K_NOT> <K_EXISTS> { createTable.setIfNotExists(true); }]
1355813559
table=Table()
13560+
[
13561+
// BigQuery: CREATE [SNAPSHOT] TABLE target CLONE source [FOR SYSTEM_TIME AS OF ...]
13562+
LOOKAHEAD(2) <K_CLONE> likeTable=Table()
13563+
[ LOOKAHEAD(2) tableColumn=TimeTravelAfterAlias() { likeTable.setTimeTravel(tableColumn); } ]
13564+
{ createTable.setCloneTable(likeTable); likeTable = null; }
13565+
]
1355913566
[ <K_OF> ofType=ColDataType() { createTable.setOfType(ofType); } ]
1356013567
[ LOOKAHEAD(2) <K_PARTITION> <K_OF> partitionOfTable=Table()
1356113568
{ createTable.setPartitionOf(partitionOfTable); } ]

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,32 @@ void testLoadDataTablesNamesFinder() throws JSQLParserException {
315315
new net.sf.jsqlparser.util.TablesNamesFinder<Void>()
316316
.getTableList(net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(sqlStr)));
317317
}
318+
319+
@Test
320+
void testCreateSnapshotTableClone() throws JSQLParserException {
321+
String sqlStr = "CREATE SNAPSHOT TABLE ds.snap CLONE ds.t";
322+
CreateTable createTable =
323+
(CreateTable) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
324+
325+
Assertions.assertEquals("ds.t", createTable.getCloneTable().getFullyQualifiedName());
326+
}
327+
328+
@Test
329+
void testCreateSnapshotTableCloneForSystemTimeAsOf() throws JSQLParserException {
330+
String sqlStr = "CREATE SNAPSHOT TABLE IF NOT EXISTS ds.snap CLONE ds.t "
331+
+ "FOR SYSTEM_TIME AS OF CURRENT_TIMESTAMP()";
332+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
333+
}
334+
335+
@Test
336+
void testCreateTableCloneWithOptions() throws JSQLParserException {
337+
String sqlStr = "CREATE TABLE ds.copy CLONE ds.t OPTIONS (description = 'x')";
338+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
339+
}
340+
341+
@Test
342+
void testCloneRemainsUsableAsIdentifier() throws JSQLParserException {
343+
String sqlStr = "SELECT clone FROM t";
344+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
345+
}
318346
}

0 commit comments

Comments
 (0)