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
22 changes: 13 additions & 9 deletions core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ tableElement
;

columnDefinition
: qualifiedName type (NOT NULL)? (COMMENT string)? (WITH properties)?
: qualifiedName type (DEFAULT literal)? (NOT NULL)? (COMMENT string)? (WITH properties)?
;

likeClause
Expand Down Expand Up @@ -577,14 +577,7 @@ valueExpression
;

primaryExpression
: NULL #nullLiteral
| interval #intervalLiteral
| identifier string #typeConstructor
| DOUBLE PRECISION string #typeConstructor
| number #numericLiteral
| booleanValue #booleanLiteral
| string #stringLiteral
| BINARY_LITERAL #binaryLiteral
: literal #literals
| QUESTION_MARK #parameter
| POSITION '(' valueExpression IN valueExpression ')' #position
| '(' expression (',' expression)+ ')' #rowConstructor
Expand Down Expand Up @@ -661,6 +654,17 @@ primaryExpression
')' #jsonArray
;

literal
: interval #intervalLiteral
| identifier string #typeConstructor
| DOUBLE PRECISION string #typeConstructor
| number #numericLiteral
| booleanValue #booleanLiteral
| string #stringLiteral
| BINARY_LITERAL #binaryLiteral
| NULL #nullLiteral
;

jsonPathInvocation
: jsonValueExpression ',' path=string
(AS pathName=identifier)?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import io.trino.sql.tree.ColumnPosition;
import io.trino.sql.tree.Expression;
import io.trino.sql.tree.Identifier;
import io.trino.sql.tree.NodeRef;
import io.trino.sql.tree.Parameter;

import java.util.List;
import java.util.Map;
Expand All @@ -55,7 +57,9 @@
import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED;
import static io.trino.spi.StandardErrorCode.TABLE_NOT_FOUND;
import static io.trino.spi.StandardErrorCode.TYPE_NOT_FOUND;
import static io.trino.spi.connector.ConnectorCapabilities.DEFAULT_COLUMN_VALUE;
import static io.trino.spi.connector.ConnectorCapabilities.NOT_NULL_COLUMN_CONSTRAINT;
import static io.trino.sql.analyzer.ExpressionAnalyzer.analyzeDefaultColumnValue;
import static io.trino.sql.analyzer.SemanticExceptions.semanticException;
import static io.trino.sql.analyzer.TypeSignatureTranslator.toTypeSignature;
import static io.trino.type.UnknownType.UNKNOWN;
Expand Down Expand Up @@ -92,6 +96,7 @@ public ListenableFuture<Void> execute(
WarningCollector warningCollector)
{
Session session = stateMachine.getSession();
Map<NodeRef<Parameter>, Expression> parameterLookup = bindParameters(statement, parameters);
QualifiedObjectName originalTableName = createQualifiedObjectName(session, statement, statement.getName());
RedirectionAwareTableHandle redirectionAwareTableHandle = plannerContext.getMetadata().getRedirectionAwareTableHandle(session, originalTableName);
if (redirectionAwareTableHandle.tableHandle().isEmpty()) {
Expand Down Expand Up @@ -132,6 +137,9 @@ public ListenableFuture<Void> execute(
}
return immediateVoidFuture();
}
if (element.getDefaultValue().isPresent() && !plannerContext.getMetadata().getConnectorCapabilities(session, catalogHandle).contains(DEFAULT_COLUMN_VALUE)) {
throw semanticException(NOT_SUPPORTED, element, "Catalog '%s' does not support default value for column name '%s'", catalogHandle, columnName);
}
if (!element.isNullable() && !plannerContext.getMetadata().getConnectorCapabilities(session, catalogHandle).contains(NOT_NULL_COLUMN_CONSTRAINT)) {
throw semanticException(NOT_SUPPORTED, element, "Catalog '%s' does not support NOT NULL for column '%s'", catalogHandle, columnName);
}
Expand All @@ -146,12 +154,15 @@ public ListenableFuture<Void> execute(
session,
plannerContext,
accessControl,
bindParameters(statement, parameters),
parameterLookup,
true);

Type supportedType = getSupportedType(session, catalogHandle, tableMetadata.metadata().getProperties(), type);
element.getDefaultValue().ifPresent(value -> analyzeDefaultColumnValue(session, plannerContext, accessControl, parameterLookup, warningCollector, supportedType, value));
ColumnMetadata column = ColumnMetadata.builder()
.setName(columnName.getValue())
.setType(getSupportedType(session, catalogHandle, tableMetadata.metadata().getProperties(), type))
.setType(supportedType)
.setDefaultValue(element.getDefaultValue().map(Expression::toString))
.setNullable(element.isNullable())
.setComment(element.getComment())
.setProperties(columnProperties)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@
import static io.trino.spi.StandardErrorCode.TABLE_NOT_FOUND;
import static io.trino.spi.StandardErrorCode.TYPE_NOT_FOUND;
import static io.trino.spi.StandardErrorCode.UNSUPPORTED_TABLE_TYPE;
import static io.trino.spi.connector.ConnectorCapabilities.DEFAULT_COLUMN_VALUE;
import static io.trino.spi.connector.ConnectorCapabilities.NOT_NULL_COLUMN_CONSTRAINT;
import static io.trino.sql.analyzer.ExpressionAnalyzer.analyzeDefaultColumnValue;
import static io.trino.sql.analyzer.SemanticExceptions.semanticException;
import static io.trino.sql.analyzer.TypeSignatureTranslator.toTypeSignature;
import static io.trino.sql.tree.LikeClause.PropertiesOption.EXCLUDING;
Expand Down Expand Up @@ -123,11 +125,11 @@ public ListenableFuture<Void> execute(
List<Expression> parameters,
WarningCollector warningCollector)
{
return internalExecute(statement, stateMachine.getSession(), parameters, output -> stateMachine.setOutput(Optional.of(output)));
return internalExecute(statement, stateMachine.getSession(), parameters, output -> stateMachine.setOutput(Optional.of(output)), warningCollector);
}

@VisibleForTesting
ListenableFuture<Void> internalExecute(CreateTable statement, Session session, List<Expression> parameters, Consumer<Output> outputConsumer)
ListenableFuture<Void> internalExecute(CreateTable statement, Session session, List<Expression> parameters, Consumer<Output> outputConsumer, WarningCollector warningCollector)
{
checkArgument(!statement.getElements().isEmpty(), "no columns for table");

Expand Down Expand Up @@ -166,6 +168,7 @@ ListenableFuture<Void> internalExecute(CreateTable statement, Session session, L
LinkedHashMap<String, ColumnMetadata> columns = new LinkedHashMap<>();
Map<String, Object> inheritedProperties = ImmutableMap.of();
boolean includingProperties = false;
boolean supportsDefaultColumnValue = plannerContext.getMetadata().getConnectorCapabilities(session, catalogHandle).contains(DEFAULT_COLUMN_VALUE);
for (TableElement element : statement.getElements()) {
if (element instanceof ColumnDefinition column) {
if (column.getName().getParts().size() != 1) {
Expand All @@ -185,6 +188,9 @@ ListenableFuture<Void> internalExecute(CreateTable statement, Session session, L
if (columns.containsKey(name.getValue().toLowerCase(ENGLISH))) {
throw semanticException(DUPLICATE_COLUMN_NAME, column, "Column name '%s' specified more than once", name);
}
if (column.getDefaultValue().isPresent() && !supportsDefaultColumnValue) {
throw semanticException(NOT_SUPPORTED, column, "Catalog '%s' does not support default value for column name '%s'", catalogName, name);
}
if (!column.isNullable() && !plannerContext.getMetadata().getConnectorCapabilities(session, catalogHandle).contains(NOT_NULL_COLUMN_CONSTRAINT)) {
throw semanticException(NOT_SUPPORTED, column, "Catalog '%s' does not support non-null column for column name '%s'", catalogName, name);
}
Expand All @@ -198,9 +204,12 @@ ListenableFuture<Void> internalExecute(CreateTable statement, Session session, L
parameterLookup,
true);

Type supportedType = getSupportedType(session, catalogHandle, properties, type);
column.getDefaultValue().ifPresent(value -> analyzeDefaultColumnValue(session, plannerContext, accessControl, parameterLookup, warningCollector, supportedType, value));
columns.put(name.getValue().toLowerCase(ENGLISH), ColumnMetadata.builder()
.setName(name.getValue().toLowerCase(ENGLISH))
.setType(getSupportedType(session, catalogHandle, properties, type))
.setType(supportedType)
.setDefaultValue(column.getDefaultValue().map(Expression::toString))
.setNullable(column.isNullable())
.setComment(column.getComment())
.setProperties(columnProperties)
Expand Down
23 changes: 23 additions & 0 deletions core/trino-main/src/main/java/io/trino/sql/analyzer/Analysis.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ public class Analysis
private final Multiset<ColumnMaskScopeEntry> columnMaskScopes = HashMultiset.create();
private final Map<NodeRef<Table>, Map<Field, Expression>> columnMasks = new LinkedHashMap<>();

private final Map<NodeRef<Table>, Map<ColumnHandle, Expression>> defaultColumnValues = new LinkedHashMap<>();

private final Map<NodeRef<Unnest>, UnnestAnalysis> unnestAnalysis = new LinkedHashMap<>();
private Optional<Create> create = Optional.empty();
private Optional<Insert> insert = Optional.empty();
Expand Down Expand Up @@ -1187,6 +1189,19 @@ public Map<Field, Expression> getColumnMasks(Table table)
return unmodifiableMap(columnMasks.getOrDefault(NodeRef.of(table), ImmutableMap.of()));
}

public void addDefaultColumnValue(Table table, ColumnHandle column, Expression defaultValue)
{
Map<ColumnHandle, Expression> defaultValues = defaultColumnValues.computeIfAbsent(NodeRef.of(table), _ -> new LinkedHashMap<>());
checkArgument(!defaultValues.containsKey(column), "Default column value already exists for column");

defaultValues.put(column, defaultValue);
}

public Map<ColumnHandle, Expression> getDefaultColumnValues(Table table)
{
return unmodifiableMap(defaultColumnValues.getOrDefault(NodeRef.of(table), ImmutableMap.of()));
}

public List<TableInfo> getReferencedTables()
{
return tables.entrySet().stream()
Expand Down Expand Up @@ -1874,6 +1889,7 @@ public static class MergeAnalysis
private final List<List<ColumnHandle>> mergeCaseColumnHandles;
// Case number map to columns
private final Multimap<Integer, ColumnHandle> updateCaseColumnHandles;
private final Map<ColumnHandle, Expression> defaultColumnValues;
private final Set<ColumnHandle> nonNullableColumnHandles;
private final Map<ColumnHandle, Integer> columnHandleFieldNumbers;
private final RowType mergeRowType;
Expand All @@ -1890,6 +1906,7 @@ public MergeAnalysis(
List<ColumnHandle> redistributionColumnHandles,
List<List<ColumnHandle>> mergeCaseColumnHandles,
Multimap<Integer, ColumnHandle> updateCaseColumnHandles,
Map<ColumnHandle, Expression> defaultColumnValues,
Set<ColumnHandle> nonNullableColumnHandles,
Map<ColumnHandle, Integer> columnHandleFieldNumbers,
RowType mergeRowType,
Expand All @@ -1905,6 +1922,7 @@ public MergeAnalysis(
this.redistributionColumnHandles = requireNonNull(redistributionColumnHandles, "redistributionColumnHandles is null");
this.mergeCaseColumnHandles = requireNonNull(mergeCaseColumnHandles, "mergeCaseColumnHandles is null");
this.updateCaseColumnHandles = requireNonNull(updateCaseColumnHandles, "updateCaseColumnHandles is null");
this.defaultColumnValues = ImmutableMap.copyOf(defaultColumnValues);
this.nonNullableColumnHandles = requireNonNull(nonNullableColumnHandles, "nonNullableColumnHandles is null");
this.columnHandleFieldNumbers = requireNonNull(columnHandleFieldNumbers, "columnHandleFieldNumbers is null");
this.mergeRowType = requireNonNull(mergeRowType, "mergeRowType is null");
Expand Down Expand Up @@ -1945,6 +1963,11 @@ public Multimap<Integer, ColumnHandle> getUpdateCaseColumnHandles()
return updateCaseColumnHandles;
}

public Map<ColumnHandle, Expression> getDefaultColumnValues()
{
return defaultColumnValues;
}

public Set<ColumnHandle> getNonNullableColumnHandles()
{
return nonNullableColumnHandles;
Expand Down
Loading
Loading