Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
db13658
initial parser support
allisonport-db Jul 23, 2022
29d11ec
add sql conf
allisonport-db Aug 3, 2022
3c19862
table provider support
allisonport-db Nov 28, 2022
ebc5ed9
refactor
allisonport-db Nov 28, 2022
02f5783
check for replace statements
allisonport-db Nov 28, 2022
08a5f09
Merge remote-tracking branch 'apache/master' into parser-support
allisonport-db Nov 28, 2022
f9cfd6b
fix updates from master
allisonport-db Nov 28, 2022
4eab27d
update some docs
allisonport-db Nov 30, 2022
8c80d23
fix errors
allisonport-db Nov 30, 2022
57e5c1a
fix formatting in error classes
allisonport-db Nov 30, 2022
f9999ef
remove feature flag
allisonport-db Dec 2, 2022
cbb512a
update error message
allisonport-db Dec 2, 2022
7155d48
respond to comments
allisonport-db Dec 15, 2022
ca838fd
Merge remote-tracking branch 'apache/master' into parser-support
allisonport-db Dec 15, 2022
6cf13af
update to have TableCatalogCapabilities
allisonport-db Jan 9, 2023
a0eb80e
finish up todos
allisonport-db Jan 11, 2023
ed41d31
clarifying comment
allisonport-db Jan 11, 2023
7d9f8e8
Merge remote-tracking branch 'apache/master' into parser-support
allisonport-db Jan 11, 2023
2905db1
update to parse expression to v2 and then convert to sql + added tests
allisonport-db Feb 7, 2023
564da54
Merge remote-tracking branch 'apache/master' into parser-support
allisonport-db Feb 7, 2023
bb684cd
fix error class order and update class doc
allisonport-db Feb 10, 2023
61b801e
Merge remote-tracking branch 'apache/master' into parser-support-updated
allisonport-db Feb 21, 2023
e9b28df
implement with new v2 column framework
allisonport-db Feb 23, 2023
53c9dbd
improve some docs
allisonport-db Feb 23, 2023
301db0d
respond to some comments
allisonport-db Feb 23, 2023
c8dce5f
respond to comments
allisonport-db Feb 24, 2023
c016690
block subquery expressions
allisonport-db Feb 24, 2023
5b0eaad
can't reference other generated columns
allisonport-db Feb 24, 2023
f01a0b4
respond to some comments
allisonport-db Feb 24, 2023
9ff8fa8
Merge remote-tracking branch 'apache/master' into parser-support-updated
allisonport-db Feb 24, 2023
09d437d
respond to comments
allisonport-db Feb 24, 2023
3fb71ed
fix github actions
allisonport-db Feb 25, 2023
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
10 changes: 10 additions & 0 deletions core/src/main/resources/error/error-classes.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,11 @@
],
"sqlState" : "42809"
},
"GENERATED_COLUMN_WITH_DEFAULT_VALUE" : {
"message" : [
"A column cannot have both a default value and a generation expression but column <colName> has default value: (<defaultValue>) and generation expression: (<genExpr>)."
]
},
"GRAPHITE_SINK_INVALID_PROTOCOL" : {
"message" : [
"Invalid Graphite protocol: <protocol>."
Expand Down Expand Up @@ -1607,6 +1612,11 @@
},
"sqlState" : "0A000"
},
"UNSUPPORTED_EXPRESSION_GENERATED_COLUMN" : {
"message" : [
"Cannot create generated column <fieldName> with generation expression <expressionStr> because <reason>."
]
},
"UNSUPPORTED_EXPR_FOR_OPERATOR" : {
"message" : [
"A query operator contains one or more unsupported expressions. Consider to rewrite it to avoid window functions, aggregate functions, and generator functions in the WHERE clause.",
Expand Down
2 changes: 2 additions & 0 deletions docs/sql-ref-ansi-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ Below is a list of all the keywords in Spark SQL.
|AFTER|non-reserved|non-reserved|non-reserved|
|ALL|reserved|non-reserved|reserved|
|ALTER|non-reserved|non-reserved|reserved|
|ALWAYS|non-reserved|non-reserved|non-reserved|
|ANALYZE|non-reserved|non-reserved|non-reserved|
|AND|reserved|non-reserved|reserved|
|ANTI|non-reserved|strict-non-reserved|non-reserved|
Expand Down Expand Up @@ -451,6 +452,7 @@ Below is a list of all the keywords in Spark SQL.
|FULL|reserved|strict-non-reserved|reserved|
|FUNCTION|non-reserved|non-reserved|reserved|
|FUNCTIONS|non-reserved|non-reserved|non-reserved|
|GENERATED|non-reserved|non-reserved|non-reserved|
|GLOBAL|non-reserved|non-reserved|reserved|
|GRANT|reserved|non-reserved|reserved|
|GROUP|reserved|non-reserved|reserved|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ ADD: 'ADD';
AFTER: 'AFTER';
ALL: 'ALL';
ALTER: 'ALTER';
ALWAYS: 'ALWAYS';
ANALYZE: 'ANALYZE';
AND: 'AND';
ANTI: 'ANTI';
Expand Down Expand Up @@ -189,6 +190,7 @@ FROM: 'FROM';
FULL: 'FULL';
FUNCTION: 'FUNCTION';
FUNCTIONS: 'FUNCTIONS';
GENERATED: 'GENERATED';
GLOBAL: 'GLOBAL';
GRANT: 'GRANT';
GROUP: 'GROUP';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1035,9 +1035,14 @@ createOrReplaceTableColType
colDefinitionOption
: NOT NULL
| defaultExpression
| generationExpression
| commentSpec
;

generationExpression
: GENERATED ALWAYS AS LEFT_PAREN expression RIGHT_PAREN
;

complexColTypeList
: complexColType (COMMA complexColType)*
;
Expand Down Expand Up @@ -1183,6 +1188,7 @@ ansiNonReserved
: ADD
| AFTER
| ALTER
| ALWAYS
| ANALYZE
| ANTI
| ANY_VALUE
Expand Down Expand Up @@ -1252,6 +1258,7 @@ ansiNonReserved
| FORMATTED
| FUNCTION
| FUNCTIONS
| GENERATED
| GLOBAL
| GROUPING
| HOUR
Expand Down Expand Up @@ -1441,6 +1448,7 @@ nonReserved
| AFTER
| ALL
| ALTER
| ALWAYS
| ANALYZE
| AND
| ANY
Expand Down Expand Up @@ -1535,6 +1543,7 @@ nonReserved
| FROM
| FUNCTION
| FUNCTIONS
| GENERATED
| GLOBAL
| GRANT
| GROUP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
* {@link TableCatalog#createTable(Identifier, Column[], Transform[], Map)}, and report it in
* {@link Table#columns()} by calling the static {@code create} functions of this interface to
* create it.
* <p>
* A column cannot have both a default value and a generation expression.
Copy link
Member

@dongjoon-hyun dongjoon-hyun Feb 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question. Do we have a proper error message for this case?
Never mind. I found GENERATED_COLUMN_WITH_DEFAULT_VALUE.

*/
@Evolving
public interface Column {
Expand All @@ -42,7 +44,16 @@ static Column create(String name, DataType dataType) {
}

static Column create(String name, DataType dataType, boolean nullable) {
return create(name, dataType, nullable, null, null, null);
return create(name, dataType, nullable, null, null);
}

static Column create(
String name,
DataType dataType,
boolean nullable,
String comment,
String metadataInJSON) {
return new ColumnImpl(name, dataType, nullable, comment, null, null, metadataInJSON);
}

static Column create(
Expand All @@ -52,7 +63,18 @@ static Column create(
String comment,
ColumnDefaultValue defaultValue,
String metadataInJSON) {
return new ColumnImpl(name, dataType, nullable, comment, defaultValue, metadataInJSON);
return new ColumnImpl(name, dataType, nullable, comment, defaultValue, null, metadataInJSON);
}

static Column create(
String name,
DataType dataType,
boolean nullable,
String comment,
String generationExpression,
String metadataInJSON) {
return new ColumnImpl(name, dataType, nullable, comment, null,
generationExpression, metadataInJSON);
}

/**
Expand Down Expand Up @@ -82,6 +104,15 @@ static Column create(
@Nullable
ColumnDefaultValue defaultValue();

/**
* Returns the generation expression of this table column. Null means no generation expression.
* <p>
* The generation expression is stored as spark SQL dialect. It is up to the data source to verify
* expression compatibility and reject writes as necessary.
*/
@Nullable
String generationExpression();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added it as just a string here as it seems we don't need any additional information


/**
* Returns the column metadata in JSON format.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import org.apache.spark.sql.errors.QueryCompilationErrors;
import org.apache.spark.sql.types.StructType;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

/**
* Catalog methods for working with Tables.
Expand Down Expand Up @@ -78,6 +80,11 @@ public interface TableCatalog extends CatalogPlugin {
*/
String OPTION_PREFIX = "option.";

/**
* @return the set of capabilities for this TableCatalog
*/
default Set<TableCatalogCapability> capabilities() { return Collections.emptySet(); }

/**
* List the tables in a namespace from the catalog.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.spark.sql.connector.catalog;

import org.apache.spark.annotation.Evolving;

/**
* Capabilities that can be provided by a {@link TableCatalog} implementation.
* <p>
* TableCatalogs use {@link TableCatalog#capabilities()} to return a set of capabilities. Each
* capability signals to Spark that the catalog supports a feature identified by the capability.
* For example, returning {@link #SUPPORTS_CREATE_TABLE_WITH_GENERATED_COLUMNS} allows Spark to
* accept {@code GENERATED ALWAYS AS} expressions in {@code CREATE TABLE} statements.
*
* @since 3.4.0
*/
@Evolving
public enum TableCatalogCapability {

/**
* Signals that the TableCatalog supports defining generated columns upon table creation in SQL.
* <p>
* Without this capability, any create/replace table statements with a generated column defined
* in the table schema will throw an exception during analysis.
* <p>
* A generated column is defined with syntax: {@code colName colType GENERATED ALWAYS AS (expr)}
* <p>
* Generation expression are included in the column definition for APIs like
* {@link TableCatalog#createTable}.
* See {@link Column#generationExpression()}.
*/
SUPPORTS_CREATE_TABLE_WITH_GENERATED_COLUMNS
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import org.apache.spark.sql.catalyst.parser.SqlBaseParser._
import org.apache.spark.sql.catalyst.plans._
import org.apache.spark.sql.catalyst.plans.logical._
import org.apache.spark.sql.catalyst.trees.CurrentOrigin
import org.apache.spark.sql.catalyst.util.{CharVarcharUtils, DateTimeUtils, IntervalUtils, ResolveDefaultColumns}
import org.apache.spark.sql.catalyst.util.{CharVarcharUtils, DateTimeUtils, GeneratedColumn, IntervalUtils, ResolveDefaultColumns}
import org.apache.spark.sql.catalyst.util.DateTimeUtils.{convertSpecialDate, convertSpecialTimestamp, convertSpecialTimestampNTZ, getZoneId, stringToDate, stringToTimestamp, stringToTimestampWithoutTimeZone}
import org.apache.spark.sql.connector.catalog.{CatalogV2Util, SupportsNamespaces, TableCatalog}
import org.apache.spark.sql.connector.catalog.TableChange.ColumnPosition
Expand Down Expand Up @@ -3002,6 +3002,7 @@ class AstBuilder extends SqlBaseParserBaseVisitor[AnyRef] with SQLConfHelper wit
// Check that no duplicates exist among any CREATE TABLE column options specified.
var nullable = true
var defaultExpression: Option[DefaultExpressionContext] = None
var generationExpression: Option[GenerationExpressionContext] = None
var commentSpec: Option[CommentSpecContext] = None
ctx.colDefinitionOption().asScala.foreach { option =>
if (option.NULL != null) {
Expand All @@ -3018,6 +3019,13 @@ class AstBuilder extends SqlBaseParserBaseVisitor[AnyRef] with SQLConfHelper wit
}
defaultExpression = Some(expr)
}
Option(option.generationExpression()).foreach { expr =>
if (generationExpression.isDefined) {
throw QueryParsingErrors.duplicateCreateTableColumnOption(
option, colName.getText, "GENERATED ALWAYS AS")
}
generationExpression = Some(expr)
}
Option(option.commentSpec()).foreach { spec =>
if (commentSpec.isDefined) {
throw QueryParsingErrors.duplicateCreateTableColumnOption(
Expand All @@ -3042,6 +3050,11 @@ class AstBuilder extends SqlBaseParserBaseVisitor[AnyRef] with SQLConfHelper wit
throw QueryParsingErrors.defaultColumnNotEnabledError(ctx)
}
}
// Add the 'GENERATED ALWAYS AS expression' clause in the column definition, if any, to the
// column metadata.
generationExpression.map(visitGenerationExpression).foreach { field =>
builder.putString(GeneratedColumn.GENERATION_EXPRESSION_METADATA_KEY, field)
}

val name: String = colName.getText

Expand Down Expand Up @@ -3100,11 +3113,7 @@ class AstBuilder extends SqlBaseParserBaseVisitor[AnyRef] with SQLConfHelper wit
string(visitStringLit(ctx.stringLit))
}

/**
* Create a default string.
*/
override def visitDefaultExpression(ctx: DefaultExpressionContext): String = withOrigin(ctx) {
val exprCtx = ctx.expression()
private def verifyAndGetExpression(exprCtx: ExpressionContext): String = {
// Make sure it can be converted to Catalyst expressions.
expression(exprCtx)
// Extract the raw expression text so that we can save the user provided text. We don't
Expand All @@ -3116,6 +3125,22 @@ class AstBuilder extends SqlBaseParserBaseVisitor[AnyRef] with SQLConfHelper wit
exprCtx.getStart.getInputStream.getText(new Interval(start, end))
}

/**
* Create a default string.
*/
override def visitDefaultExpression(ctx: DefaultExpressionContext): String =
withOrigin(ctx) {
verifyAndGetExpression(ctx.expression())
}

/**
* Create a generation expression string.
*/
override def visitGenerationExpression(ctx: GenerationExpressionContext): String =
withOrigin(ctx) {
verifyAndGetExpression(ctx.expression())
}

/**
* Create an optional comment string.
*/
Expand Down
Loading