Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class CreateTable implements Statement {
private Table cloneTable;
private ColDataType ofType;
private boolean selectParenthesis;
private boolean useAsKeyword = true;
private boolean ifNotExists = false;
private boolean orReplace = false;
private TablePartitioning partitioning;
Expand Down Expand Up @@ -195,6 +196,29 @@ public Select getSelect() {
return select;
}

public boolean isUseAsKeyword() {
return useAsKeyword;
}

public void setUseAsKeyword(boolean useAsKeyword) {
this.useAsKeyword = useAsKeyword;
}

public StringBuilder appendSelectTo(StringBuilder builder,
java.util.function.Consumer<Select> selectRenderer) {
if (select != null) {
builder.append(useAsKeyword ? " AS " : " ");
if (selectParenthesis) {
builder.append("(");
}
selectRenderer.accept(select);
if (selectParenthesis) {
builder.append(")");
}
}
return builder;
}

public void setSelect(Select select, boolean parenthesis) {
this.select = select;
this.selectParenthesis = parenthesis;
Expand Down Expand Up @@ -388,16 +412,7 @@ private void appendTableProperties(StringBuilder b) {
if (rowMovement != null) {
b.append(" ").append(rowMovement.getMode()).append(" ROW MOVEMENT");
}
if (select != null) {
b.append(" AS ");
if (selectParenthesis) {
b.append("(");
}
b.append(select);
if (selectParenthesis) {
b.append(")");
}
}
appendSelectTo(b, b::append);
if (likeTable != null) {
b.append(" LIKE ");
if (selectParenthesis) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import net.sf.jsqlparser.statement.create.table.Index;
import net.sf.jsqlparser.statement.create.table.TableElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;

public class CreateTableDeParser extends AbstractDeParser<CreateTable> {

Expand Down Expand Up @@ -129,17 +128,7 @@ public void deParse(CreateTable createTable) {
builder.append(' ').append(createTable.getRowMovement().getMode().toString())
.append(" ROW MOVEMENT");
}
if (createTable.getSelect() != null) {
builder.append(" AS ");
if (createTable.isSelectParenthesis()) {
builder.append("(");
}
Select sel = createTable.getSelect();
sel.accept(this.statementDeParser, null);
if (createTable.isSelectParenthesis()) {
builder.append(")");
}
}
createTable.appendSelectTo(builder, select -> select.accept(this.statementDeParser, null));
if (createTable.getTrailingLikeTable() != null) {
builder.append(" LIKE ");
if (createTable.isSelectParenthesis()) {
Expand Down
11 changes: 8 additions & 3 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -14941,7 +14941,12 @@ CreateTable CreateTable(boolean isUsingOrReplace):
// see https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7002.htm#i2126725
// table properties , all these are optional
[ rowMovement = RowMovement() { createTable.setRowMovement(rowMovement); }]
[ <K_AS> select = Select() { createTable.setSelect(select, false); }]
[ LOOKAHEAD(2)
{ createTable.setUseAsKeyword(false); }
[ <K_AS> { createTable.setUseAsKeyword(true); } ]
( LOOKAHEAD(<K_TABLE>) select=TableStatement() | select=Select() )
{ createTable.setSelect(select, false); }
]
[
<K_LIKE> ( LOOKAHEAD("(" Table() ")") "(" likeTable=Table() { createTable.setLikeTable(likeTable, true); } ")"
| likeTable=Table() { createTable.setLikeTable(likeTable, false); } )
Expand Down Expand Up @@ -14999,10 +15004,10 @@ ColumnDefinition CreateTableColumnDefinition(boolean typed):
void CreateTableOptions(List<TableOption> options):
{ TableOption option; }
{
[ LOOKAHEAD(2, { getToken(1).kind != K_AS
[ LOOKAHEAD(2, { getToken(1).kind != K_AS && getToken(1).kind != K_TABLE
&& !(getToken(1).kind == K_PARTITION && getToken(2).kind == K_BY) })
option=CreateTableOption() { options.add(option); }
( LOOKAHEAD(2, { getToken(1).kind != K_AS
( LOOKAHEAD(2, { getToken(1).kind != K_AS && getToken(1).kind != K_TABLE
&& !(getToken(1).kind == K_PARTITION && getToken(2).kind == K_BY)
&& !(getToken(1).kind == K_COMMA
&& "INTERLEAVE".equalsIgnoreCase(getToken(2).image)) })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.create;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.select.TableStatement;
import net.sf.jsqlparser.util.TablesNamesFinder;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class MySqlCreateTableSourceTest {
@ParameterizedTest
@ValueSource(strings = {"CREATE TABLE t AS TABLE db.s", "CREATE TABLE t TABLE db.s",
"CREATE TABLE t (id BIGINT) TABLE db.s",
"CREATE TEMPORARY TABLE IF NOT EXISTS t ENGINE=InnoDB TABLE `db`.`s`",
"CREATE TABLE t (extra INT) ENGINE=InnoDB, COMMENT='source' AS TABLE s ORDER BY id DESC LIMIT 2 OFFSET 1",
"CREATE TABLE t AS SELECT 1 AS id", "CREATE TABLE t SELECT 1 AS id",
"CREATE TABLE t AS (SELECT 1 AS id)"})
void roundTripsQuerySources(String sql) throws Exception {
CreateTable table = parse(sql);
assertNotNull(table.getSelect());
if (sql.contains("TABLE db.") || sql.contains("TABLE `db`")) {
assertInstanceOf(TableStatement.class, table.getSelect());
}
assertRoundTrip(table);
}

@Test
void retainsSourceStructureAndAsChoiceAfterMutation() throws Exception {
CreateTable table = parse("CREATE TABLE target TABLE old.source");
assertFalse(table.isUseAsKeyword());
TableStatement source = assertInstanceOf(TableStatement.class, table.getSelect());
source.getTable().setSchemaName("newdb");
assertThat(new TablesNamesFinder().getTables(table))
.containsExactlyInAnyOrder("target", "newdb.source");
assertEquals("CREATE TABLE target TABLE newdb.source", table.toString());
table.setUseAsKeyword(true);
assertEquals("CREATE TABLE target AS TABLE newdb.source", table.toString());
assertRoundTrip(table);
assertTrue(new CreateTable().isUseAsKeyword());
}

@ParameterizedTest
@ValueSource(strings = {"CREATE TABLE t AS TABLE", "CREATE TABLE t TABLE",
"CREATE TABLE t ENGINE=InnoDB, TABLE s", "CREATE TABLE t AS"})
void rejectsMissingSourcesAndDanglingOptionSeparators(String sql) {
assertThrows(JSQLParserException.class, () -> parse(sql));
}

private static CreateTable parse(String sql) throws JSQLParserException {
return (CreateTable) CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.MYSQL));
}

private static void assertRoundTrip(CreateTable table) throws Exception {
StringBuilder output = new StringBuilder();
table.accept(new StatementDeParser(output), null);
assertEquals(table.toString(), output.toString());
CreateTable reparsed = parse(output.toString());
assertEquals(table.toString(), reparsed.toString());
assertEquals(table.isUseAsKeyword(), reparsed.isUseAsKeyword());
}
}
Loading