Skip to content

Support CREATE OR REPLACE in parser #17050

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 5, 2023
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 @@ -81,6 +81,9 @@
import static io.trino.sql.analyzer.TypeSignatureTranslator.toTypeSignature;
import static io.trino.sql.tree.LikeClause.PropertiesOption.EXCLUDING;
import static io.trino.sql.tree.LikeClause.PropertiesOption.INCLUDING;
import static io.trino.sql.tree.SaveMode.FAIL;
import static io.trino.sql.tree.SaveMode.IGNORE;
import static io.trino.sql.tree.SaveMode.REPLACE;
import static io.trino.type.UnknownType.UNKNOWN;
import static java.util.Locale.ENGLISH;
import static java.util.Objects.requireNonNull;
Expand Down Expand Up @@ -126,6 +129,10 @@ public ListenableFuture<Void> execute(
ListenableFuture<Void> internalExecute(CreateTable statement, Session session, List<Expression> parameters, Consumer<Output> outputConsumer)
{
checkArgument(!statement.getElements().isEmpty(), "no columns for table");
// TODO: Remove when engine is supporting table replacement
if (statement.getSaveMode() == REPLACE) {
throw semanticException(NOT_SUPPORTED, statement, "Replace table is not supported");
}

Map<NodeRef<Parameter>, Expression> parameterLookup = bindParameters(statement, parameters);
QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName());
Expand All @@ -140,7 +147,7 @@ ListenableFuture<Void> internalExecute(CreateTable statement, Session session, L
throw e;
}
if (tableHandle.isPresent()) {
if (!statement.isNotExists()) {
if (statement.getSaveMode() == FAIL) {
throw semanticException(TABLE_ALREADY_EXISTS, statement, "Table '%s' already exists", tableName);
}
return immediateVoidFuture();
Expand Down Expand Up @@ -282,11 +289,11 @@ else if (element instanceof LikeClause likeClause) {
Map<String, Object> finalProperties = combineProperties(specifiedPropertyKeys, properties, inheritedProperties);
ConnectorTableMetadata tableMetadata = new ConnectorTableMetadata(tableName.asSchemaTableName(), ImmutableList.copyOf(columns.values()), finalProperties, statement.getComment());
try {
plannerContext.getMetadata().createTable(session, catalogName, tableMetadata, statement.isNotExists());
plannerContext.getMetadata().createTable(session, catalogName, tableMetadata, statement.getSaveMode() == IGNORE);
}
catch (TrinoException e) {
// connectors are not required to handle the ignoreExisting flag
if (!e.getErrorCode().equals(ALREADY_EXISTS.toErrorCode()) || !statement.isNotExists()) {
if (!e.getErrorCode().equals(ALREADY_EXISTS.toErrorCode()) || statement.getSaveMode() == FAIL) {
throw e;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@
import static io.trino.sql.tree.Join.Type.INNER;
import static io.trino.sql.tree.Join.Type.LEFT;
import static io.trino.sql.tree.Join.Type.RIGHT;
import static io.trino.sql.tree.SaveMode.IGNORE;
import static io.trino.sql.tree.SaveMode.REPLACE;
import static io.trino.sql.util.AstUtils.preOrder;
import static io.trino.type.UnknownType.UNKNOWN;
import static io.trino.util.MoreLists.mappedCopy;
Expand Down Expand Up @@ -866,12 +868,17 @@ protected Scope visitAnalyze(Analyze node, Optional<Scope> scope)
@Override
protected Scope visitCreateTableAsSelect(CreateTableAsSelect node, Optional<Scope> scope)
{
// TODO: Remove when engine is supporting table replacement
if (node.getSaveMode() == REPLACE) {
throw semanticException(NOT_SUPPORTED, node, "Replace table is not supported");
}

// turn this into a query that has a new table writer node on top.
QualifiedObjectName targetTable = createQualifiedObjectName(session, node, node.getName());

Optional<TableHandle> targetTableHandle = metadata.getTableHandle(session, targetTable);
if (targetTableHandle.isPresent()) {
if (node.isNotExists()) {
if (node.getSaveMode() == IGNORE) {
analysis.setCreate(new Analysis.Create(
Optional.of(targetTable),
Optional.empty(),
Expand Down Expand Up @@ -1071,6 +1078,11 @@ protected Scope visitSetSchemaAuthorization(SetSchemaAuthorization node, Optiona
@Override
protected Scope visitCreateTable(CreateTable node, Optional<Scope> scope)
{
// TODO: Remove when engine is supporting table replacement
if (node.getSaveMode() == REPLACE) {
throw semanticException(NOT_SUPPORTED, node, "Replace table is not supported");
}

validateProperties(node.getProperties(), scope);
return createAndAssignScope(node, scope);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
import static io.trino.sql.tree.CreateView.Security.DEFINER;
import static io.trino.sql.tree.CreateView.Security.INVOKER;
import static io.trino.sql.tree.LogicalExpression.and;
import static io.trino.sql.tree.SaveMode.FAIL;
import static io.trino.sql.tree.ShowCreate.Type.MATERIALIZED_VIEW;
import static io.trino.sql.tree.ShowCreate.Type.SCHEMA;
import static io.trino.sql.tree.ShowCreate.Type.TABLE;
Expand Down Expand Up @@ -687,7 +688,7 @@ protected Node visitShowCreate(ShowCreate node, Void context)
CreateTable createTable = new CreateTable(
QualifiedName.of(targetTableName.getCatalogName(), targetTableName.getSchemaName(), targetTableName.getObjectName()),
columns,
false,
FAIL,
propertyNodes,
connectorTableMetadata.getComment());
return singleValueQuery("Create Table", formatSql(createTable).trim());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import static io.trino.SessionTestUtils.TEST_SESSION;
import static io.trino.execution.querystats.PlanOptimizersStatsCollector.createPlanOptimizersStatsCollector;
import static io.trino.metadata.MetadataManager.createTestMetadataManager;
import static io.trino.sql.tree.SaveMode.FAIL;
import static io.trino.testing.TestingEventListenerManager.emptyEventListenerManager;
import static io.trino.transaction.InMemoryTransactionManager.createTestTransactionManager;
import static java.util.concurrent.Executors.newCachedThreadPool;
Expand Down Expand Up @@ -130,7 +131,7 @@ public void testSubmittedForDispatchedQuery()
new ConnectorCatalogServiceProvider<>("function provider", new NoConnectorServicesProvider(), ConnectorServices::getFunctionProvider),
new GlobalFunctionCatalog()),
new QueryMonitorConfig());
CreateTable createTable = new CreateTable(QualifiedName.of("table"), ImmutableList.of(), false, ImmutableList.of(), Optional.empty());
CreateTable createTable = new CreateTable(QualifiedName.of("table"), ImmutableList.of(), FAIL, ImmutableList.of(), Optional.empty());
QueryPreparer.PreparedQuery preparedQuery = new QueryPreparer.PreparedQuery(createTable, ImmutableList.of(), Optional.empty());
DataDefinitionExecution.DataDefinitionExecutionFactory dataDefinitionExecutionFactory = new DataDefinitionExecution.DataDefinitionExecutionFactory(
ImmutableMap.<Class<? extends Statement>, DataDefinitionTask<?>>of(CreateTable.class, new TestCreateTableTask()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
import static io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType;
import static io.trino.sql.planner.TestingPlannerContext.plannerContextBuilder;
import static io.trino.sql.tree.LikeClause.PropertiesOption.INCLUDING;
import static io.trino.sql.tree.SaveMode.FAIL;
import static io.trino.sql.tree.SaveMode.IGNORE;
import static io.trino.testing.TestingAccessControlManager.TestingPrivilegeType.SELECT_COLUMN;
import static io.trino.testing.TestingAccessControlManager.TestingPrivilegeType.SHOW_CREATE_TABLE;
import static io.trino.testing.TestingAccessControlManager.privilege;
Expand Down Expand Up @@ -157,7 +159,7 @@ public void testCreateTableNotExistsTrue()
{
CreateTable statement = new CreateTable(QualifiedName.of("test_table"),
ImmutableList.of(new ColumnDefinition(identifier("a"), toSqlType(BIGINT), true, emptyList(), Optional.empty())),
true,
IGNORE,
ImmutableList.of(),
Optional.empty());

Expand All @@ -171,7 +173,7 @@ public void testCreateTableNotExistsFalse()
{
CreateTable statement = new CreateTable(QualifiedName.of("test_table"),
ImmutableList.of(new ColumnDefinition(identifier("a"), toSqlType(BIGINT), true, emptyList(), Optional.empty())),
false,
FAIL,
ImmutableList.of(),
Optional.empty());

Expand All @@ -188,7 +190,7 @@ public void testCreateTableWithMaterializedViewPropertyFails()
{
CreateTable statement = new CreateTable(QualifiedName.of("test_table"),
ImmutableList.of(new ColumnDefinition(identifier("a"), toSqlType(BIGINT), true, emptyList(), Optional.empty())),
false,
FAIL,
ImmutableList.of(new Property(new Identifier("foo"), new StringLiteral("bar"))),
Optional.empty());

Expand All @@ -208,7 +210,7 @@ public void testCreateWithNotNullColumns()
new ColumnDefinition(identifier("a"), toSqlType(DATE), true, emptyList(), Optional.empty()),
new ColumnDefinition(identifier("b"), toSqlType(VARCHAR), false, emptyList(), Optional.empty()),
new ColumnDefinition(identifier("c"), toSqlType(VARBINARY), false, emptyList(), Optional.empty()));
CreateTable statement = new CreateTable(QualifiedName.of("test_table"), inputColumns, true, ImmutableList.of(), Optional.empty());
CreateTable statement = new CreateTable(QualifiedName.of("test_table"), inputColumns, IGNORE, ImmutableList.of(), Optional.empty());

CreateTableTask createTableTask = new CreateTableTask(plannerContext, new AllowAllAccessControl(), columnPropertyManager, tablePropertyManager);
getFutureValue(createTableTask.internalExecute(statement, testSession, emptyList(), output -> {}));
Expand Down Expand Up @@ -239,7 +241,7 @@ public void testCreateWithUnsupportedConnectorThrowsWhenNotNull()
CreateTable statement = new CreateTable(
QualifiedName.of("test_table"),
inputColumns,
true,
IGNORE,
ImmutableList.of(),
Optional.empty());

Expand Down Expand Up @@ -341,7 +343,7 @@ private static CreateTable getCreateLikeStatement(QualifiedName name, boolean in
return new CreateTable(
name,
List.of(new LikeClause(QualifiedName.of(PARENT_TABLE.getTable().getTableName()), includingProperties ? Optional.of(INCLUDING) : Optional.empty())),
true,
IGNORE,
ImmutableList.of(),
Optional.empty());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ statement
| DROP SCHEMA (IF EXISTS)? qualifiedName (CASCADE | RESTRICT)? #dropSchema
| ALTER SCHEMA qualifiedName RENAME TO identifier #renameSchema
| ALTER SCHEMA qualifiedName SET AUTHORIZATION principal #setSchemaAuthorization
| CREATE TABLE (IF NOT EXISTS)? qualifiedName columnAliases?
| CREATE (OR REPLACE)? TABLE (IF NOT EXISTS)? qualifiedName
columnAliases?
(COMMENT string)?
(WITH properties)? AS (query | '('query')')
(WITH (NO)? DATA)? #createTableAsSelect
| CREATE TABLE (IF NOT EXISTS)? qualifiedName
| CREATE (OR REPLACE)? TABLE (IF NOT EXISTS)? qualifiedName
'(' tableElement (',' tableElement)* ')'
(COMMENT string)?
(WITH properties)? #createTable
Expand Down
18 changes: 14 additions & 4 deletions core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@
import static io.trino.sql.ExpressionFormatter.formatStringLiteral;
import static io.trino.sql.ExpressionFormatter.formatWindowSpecification;
import static io.trino.sql.RowPatternFormatter.formatPattern;
import static io.trino.sql.tree.SaveMode.IGNORE;
import static io.trino.sql.tree.SaveMode.REPLACE;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;

Expand Down Expand Up @@ -1523,8 +1525,12 @@ protected Void visitSetSchemaAuthorization(SetSchemaAuthorization node, Integer
@Override
protected Void visitCreateTableAsSelect(CreateTableAsSelect node, Integer indent)
{
builder.append("CREATE TABLE ");
if (node.isNotExists()) {
builder.append("CREATE ");
if (node.getSaveMode() == REPLACE) {
builder.append("OR REPLACE ");
}
builder.append("TABLE ");
if (node.getSaveMode() == IGNORE) {
builder.append("IF NOT EXISTS ");
}
builder.append(formatName(node.getName()));
Expand Down Expand Up @@ -1553,8 +1559,12 @@ protected Void visitCreateTableAsSelect(CreateTableAsSelect node, Integer indent
@Override
protected Void visitCreateTable(CreateTable node, Integer indent)
{
builder.append("CREATE TABLE ");
if (node.isNotExists()) {
builder.append("CREATE ");
if (node.getSaveMode() == REPLACE) {
builder.append("OR REPLACE ");
}
builder.append("TABLE ");
if (node.getSaveMode() == IGNORE) {
builder.append("IF NOT EXISTS ");
}
String tableName = formatName(node.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
import io.trino.sql.tree.RowDataType;
import io.trino.sql.tree.RowPattern;
import io.trino.sql.tree.SampledRelation;
import io.trino.sql.tree.SaveMode;
import io.trino.sql.tree.SearchedCaseExpression;
import io.trino.sql.tree.Select;
import io.trino.sql.tree.SelectItem;
Expand Down Expand Up @@ -285,6 +286,7 @@
import java.util.Optional;
import java.util.function.Function;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Iterables.getOnlyElement;
import static io.trino.sql.parser.SqlBaseParser.TIME;
Expand Down Expand Up @@ -318,6 +320,9 @@
import static io.trino.sql.tree.PatternSearchMode.Mode.SEEK;
import static io.trino.sql.tree.ProcessingMode.Mode.FINAL;
import static io.trino.sql.tree.ProcessingMode.Mode.RUNNING;
import static io.trino.sql.tree.SaveMode.FAIL;
import static io.trino.sql.tree.SaveMode.IGNORE;
import static io.trino.sql.tree.SaveMode.REPLACE;
import static io.trino.sql.tree.SkipTo.skipPastLastRow;
import static io.trino.sql.tree.SkipTo.skipToFirst;
import static io.trino.sql.tree.SkipTo.skipToLast;
Expand Down Expand Up @@ -470,6 +475,23 @@ public Node visitSetSchemaAuthorization(SqlBaseParser.SetSchemaAuthorizationCont
getPrincipalSpecification(context.principal()));
}

private static SaveMode toSaveMode(TerminalNode replace, TerminalNode exists)
{
boolean isReplace = replace != null;
boolean isNotExists = exists != null;
checkArgument(!(isReplace && isNotExists), "'OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together");

if (isReplace) {
return REPLACE;
}

if (isNotExists) {
return IGNORE;
}

return FAIL;
}

@Override
public Node visitCreateTableAsSelect(SqlBaseParser.CreateTableAsSelectContext context)
{
Expand All @@ -488,11 +510,15 @@ public Node visitCreateTableAsSelect(SqlBaseParser.CreateTableAsSelectContext co
properties = visit(context.properties().propertyAssignments().property(), Property.class);
}

if (context.REPLACE() != null && context.EXISTS() != null) {
throw parseError("'OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together", context);
}

return new CreateTableAsSelect(
getLocation(context),
getQualifiedName(context.qualifiedName()),
(Query) visit(context.query()),
context.EXISTS() != null,
toSaveMode(context.REPLACE(), context.EXISTS()),
properties,
context.NO() == null,
columnAliases,
Expand All @@ -510,11 +536,14 @@ public Node visitCreateTable(SqlBaseParser.CreateTableContext context)
if (context.properties() != null) {
properties = visit(context.properties().propertyAssignments().property(), Property.class);
}
if (context.REPLACE() != null && context.EXISTS() != null) {
throw parseError("'OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together", context);
}
return new CreateTable(
getLocation(context),
getQualifiedName(context.qualifiedName()),
visit(context.tableElement(), TableElement.class),
context.EXISTS() != null,
toSaveMode(context.REPLACE(), context.EXISTS()),
properties,
comment);
}
Expand Down
Loading