Skip to content
1 change: 1 addition & 0 deletions docs/sql-ref-ansi-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ Below is a list of all the keywords in Spark SQL.
|DATABASES|non-reserved|non-reserved|non-reserved|
|DAY|non-reserved|non-reserved|non-reserved|
|DBPROPERTIES|non-reserved|non-reserved|non-reserved|
|DEFAULT|non-reserved|non-reserved|non-reserved|
|DEFINED|non-reserved|non-reserved|non-reserved|
|DELETE|non-reserved|non-reserved|reserved|
|DELIMITED|non-reserved|non-reserved|non-reserved|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ statement
(RESTRICT | CASCADE)? #dropNamespace
| SHOW namespaces ((FROM | IN) multipartIdentifier)?
(LIKE? pattern=STRING)? #showNamespaces
| createTableHeader ('(' colTypeList ')')? tableProvider?
| createTableHeader ('(' createOrReplaceTableColTypeList ')')? tableProvider?
createTableClauses
(AS? query)? #createTable
| CREATE TABLE (IF NOT EXISTS)? target=tableIdentifier
Expand All @@ -146,7 +146,7 @@ statement
createFileFormat |
locationSpec |
(TBLPROPERTIES tableProps=propertyList))* #createTableLike
| replaceTableHeader ('(' colTypeList ')')? tableProvider?
| replaceTableHeader ('(' createOrReplaceTableColTypeList ')')? tableProvider?
createTableClauses
(AS? query)? #replaceTable
| ANALYZE TABLE multipartIdentifier partitionSpec? COMPUTE STATISTICS
Expand Down Expand Up @@ -961,7 +961,11 @@ qualifiedColTypeWithPositionList
;

qualifiedColTypeWithPosition
: name=multipartIdentifier dataType (NOT NULL)? commentSpec? colPosition?
: name=multipartIdentifier dataType (NOT NULL)? defaultExpression? commentSpec? colPosition?
;

defaultExpression
: DEFAULT expression
;

colTypeList
Expand All @@ -972,6 +976,14 @@ colType
: colName=errorCapturingIdentifier dataType (NOT NULL)? commentSpec?
;

createOrReplaceTableColTypeList
: createOrReplaceTableColType (',' createOrReplaceTableColType)*
;

createOrReplaceTableColType
: colName=errorCapturingIdentifier dataType (NOT NULL)? defaultExpression? commentSpec?
;

complexColTypeList
: complexColType (',' complexColType)*
;
Expand Down Expand Up @@ -1078,6 +1090,8 @@ alterColumnAction
| commentSpec
| colPosition
| setOrDrop=(SET | DROP) NOT NULL
| SET defaultExpression
Comment thread
dtenedor marked this conversation as resolved.
| dropDefault=DROP DEFAULT
Comment thread
dtenedor marked this conversation as resolved.
;


Expand Down Expand Up @@ -1132,6 +1146,7 @@ ansiNonReserved
| DATABASES
| DAY
| DBPROPERTIES
| DEFAULT
| DEFINED
| DELETE
| DELIMITED
Expand Down Expand Up @@ -1379,6 +1394,7 @@ nonReserved
| DATABASES
| DAY
| DBPROPERTIES
| DEFAULT
| DEFINED
| DELETE
| DELIMITED
Expand Down Expand Up @@ -1645,6 +1661,7 @@ DATA: 'DATA';
DATABASE: 'DATABASE';
DATABASES: 'DATABASES';
DBPROPERTIES: 'DBPROPERTIES';
DEFAULT: 'DEFAULT';
DEFINED: 'DEFINED';
DELETE: 'DELETE';
DELIMITED: 'DELIMITED';
Expand Down Expand Up @@ -1974,3 +1991,4 @@ WS
UNRECOGNIZED
: .
;

Comment thread
HyukjinKwon marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -2730,6 +2730,13 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
StructType(Option(ctx).toSeq.flatMap(visitColTypeList))
}

/**
* Create top level table schema.
*/
protected def createSchema(ctx: CreateOrReplaceTableColTypeListContext): StructType = {
StructType(Option(ctx).toSeq.flatMap(visitCreateOrReplaceTableColTypeList))
}

/**
* Create a [[StructType]] from a number of column definitions.
*/
Expand All @@ -2756,6 +2763,41 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
metadata = builder.build())
}

/**
* Create a [[StructType]] from a number of CREATE TABLE column definitions.
*/
override def visitCreateOrReplaceTableColTypeList(
ctx: CreateOrReplaceTableColTypeListContext): Seq[StructField] = withOrigin(ctx) {
ctx.createOrReplaceTableColType().asScala.map(visitCreateOrReplaceTableColType).toSeq
}

/**
* Create a top level [[StructField]] from a CREATE TABLE column definition.
*/
override def visitCreateOrReplaceTableColType(
ctx: CreateOrReplaceTableColTypeContext): StructField = withOrigin(ctx) {
import ctx._

val builder = new MetadataBuilder
// Add comment to metadata
Option(commentSpec()).map(visitCommentSpec).foreach {
builder.putString("comment", _)
}

// Process the 'DEFAULT expression' clause in the column definition, if any.
val name: String = colName.getText
val defaultExpr = Option(ctx.defaultExpression()).map(visitDefaultExpression)
if (defaultExpr != None) {
Comment thread
dtenedor marked this conversation as resolved.
Outdated
throw new ParseException(defaultColumnNotImplementedYetError, ctx)
Comment thread
dtenedor marked this conversation as resolved.
Outdated
}

StructField(
name = name,
dataType = typedVisit[DataType](ctx.dataType),
nullable = NULL == null,
metadata = builder.build())
}

/**
* Create a [[StructType]] from a sequence of [[StructField]]s.
*/
Expand Down Expand Up @@ -3459,7 +3501,8 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
override def visitCreateTable(ctx: CreateTableContext): LogicalPlan = withOrigin(ctx) {
Comment thread
dtenedor marked this conversation as resolved.
val (table, temp, ifNotExists, external) = visitCreateTableHeader(ctx.createTableHeader)

val columns = Option(ctx.colTypeList()).map(visitColTypeList).getOrElse(Nil)
val columns = Option(ctx.createOrReplaceTableColTypeList())
.map(visitCreateOrReplaceTableColTypeList).getOrElse(Nil)
val provider = Option(ctx.tableProvider).map(_.multipartIdentifier.getText)
val (partTransforms, partCols, bucketSpec, properties, options, location, comment, serdeInfo) =
visitCreateTableClauses(ctx.createTableClauses())
Expand Down Expand Up @@ -3538,7 +3581,8 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
val orCreate = ctx.replaceTableHeader().CREATE() != null
val (partTransforms, partCols, bucketSpec, properties, options, location, comment, serdeInfo) =
visitCreateTableClauses(ctx.createTableClauses())
val columns = Option(ctx.colTypeList()).map(visitColTypeList).getOrElse(Nil)
val columns = Option(ctx.createOrReplaceTableColTypeList())
.map(visitCreateOrReplaceTableColTypeList).getOrElse(Nil)
val provider = Option(ctx.tableProvider).map(_.multipartIdentifier.getText)

if (provider.isDefined && serdeInfo.isDefined) {
Expand Down Expand Up @@ -3651,12 +3695,20 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
}
}

private def defaultColumnNotImplementedYetError = {
"Support for DEFAULT column values is not implemented yet"
}

/**
* Parse new column info from ADD COLUMN into a QualifiedColType.
*/
override def visitQualifiedColTypeWithPosition(
ctx: QualifiedColTypeWithPositionContext): QualifiedColType = withOrigin(ctx) {
val name = typedVisit[Seq[String]](ctx.name)
val defaultExpr = Option(ctx.defaultExpression()).map(visitDefaultExpression)
if (defaultExpr != None) {
Comment thread
dtenedor marked this conversation as resolved.
Outdated
throw new ParseException(defaultColumnNotImplementedYetError, ctx)
}
QualifiedColType(
path = if (name.length > 1) Some(UnresolvedFieldName(name.init)) else None,
colName = name.last,
Expand Down Expand Up @@ -3745,6 +3797,12 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
} else {
None
}
if (action.defaultExpression != null) {
throw new ParseException(defaultColumnNotImplementedYetError, ctx)
}
if (action.dropDefault != null) {
throw new ParseException(defaultColumnNotImplementedYetError, ctx)
}

assert(Seq(dataType, nullable, comment, position).count(_.nonEmpty) == 1)

Expand Down Expand Up @@ -3813,6 +3871,9 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
throw QueryParsingErrors.operationInHiveStyleCommandUnsupportedError(
"Replacing with a nested column", "REPLACE COLUMNS", ctx)
}
if (Option(colType.defaultExpression()).map(visitDefaultExpression) != None) {
Comment thread
dtenedor marked this conversation as resolved.
Outdated
throw new ParseException(defaultColumnNotImplementedYetError, ctx)
}
col
}.toSeq
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,7 @@ class DDLParserSuite extends AnalysisTest {
Assignment(UnresolvedAttribute("target.col2"), UnresolvedAttribute("source.col2")))))))
}


Comment thread
dtenedor marked this conversation as resolved.
Outdated
test("merge into table: using subquery") {
parseCompare(
"""
Expand Down Expand Up @@ -2235,4 +2236,57 @@ class DDLParserSuite extends AnalysisTest {
comparePlans(parsePlan(timestampTypeSql), insertPartitionPlan(timestamp))
comparePlans(parsePlan(binaryTypeSql), insertPartitionPlan(binaryStr))
}

test("SPARK-38335: Implement support for DEFAULT values for columns in tables") {
Comment thread
dtenedor marked this conversation as resolved.
Outdated
// The following commands will support DEFAULT columns, but this has not been implemented yet.
for (sql <- Seq(
"ALTER TABLE t1 ADD COLUMN x int NOT NULL DEFAULT 42",
"ALTER TABLE t1 ALTER COLUMN a.b.c SET DEFAULT 42",
"ALTER TABLE t1 ALTER COLUMN a.b.c DROP DEFAULT",
"ALTER TABLE t1 REPLACE COLUMNS (x STRING DEFAULT 42)",
"CREATE TABLE my_tab(a INT COMMENT 'test', b STRING NOT NULL DEFAULT \"abc\") USING parquet",
"REPLACE TABLE my_tab(a INT COMMENT 'test', b STRING NOT NULL DEFAULT \"xyz\") USING parquet"
)) {
val exc = intercept[ParseException] {
parsePlan(sql);
}
assert(exc.getMessage.contains("Support for DEFAULT column values is not implemented yet"));
}
// In each of the following cases, the DEFAULT reference parses as an unresolved attribute
// reference. We can handle these cases after the parsing stage, at later phases of analysis.
comparePlans(parsePlan("VALUES (1, 2, DEFAULT) AS val"),
SubqueryAlias("val",
UnresolvedInlineTable(Seq("col1", "col2", "col3"), Seq(Seq(Literal(1), Literal(2),
UnresolvedAttribute("DEFAULT"))))))
comparePlans(parsePlan(
"INSERT INTO t PARTITION(part = date'2019-01-02') VALUES ('a', DEFAULT)"),
InsertIntoStatement(
UnresolvedRelation(Seq("t")),
Map("part" -> Some("2019-01-02")),
userSpecifiedCols = Seq.empty[String],
query = UnresolvedInlineTable(Seq("col1", "col2"), Seq(Seq(Literal("a"),
UnresolvedAttribute("DEFAULT")))),
overwrite = false, ifPartitionNotExists = false))
parseCompare(
"""
|MERGE INTO testcat1.ns1.ns2.tbl AS target
|USING testcat2.ns1.ns2.tbl AS source
|ON target.col1 = source.col1
|WHEN MATCHED AND (target.col2='delete') THEN DELETE
|WHEN MATCHED AND (target.col2='update') THEN UPDATE SET target.col2 = DEFAULT
|WHEN NOT MATCHED AND (target.col2='insert')
|THEN INSERT (target.col1, target.col2) VALUES (source.col1, DEFAULT)
""".stripMargin,
MergeIntoTable(
SubqueryAlias("target", UnresolvedRelation(Seq("testcat1", "ns1", "ns2", "tbl"))),
SubqueryAlias("source", UnresolvedRelation(Seq("testcat2", "ns1", "ns2", "tbl"))),
EqualTo(UnresolvedAttribute("target.col1"), UnresolvedAttribute("source.col1")),
Seq(DeleteAction(Some(EqualTo(UnresolvedAttribute("target.col2"), Literal("delete")))),
UpdateAction(Some(EqualTo(UnresolvedAttribute("target.col2"), Literal("update"))),
Seq(Assignment(UnresolvedAttribute("target.col2"),
UnresolvedAttribute("DEFAULT"))))),
Seq(InsertAction(Some(EqualTo(UnresolvedAttribute("target.col2"), Literal("insert"))),
Seq(Assignment(UnresolvedAttribute("target.col1"), UnresolvedAttribute("source.col1")),
Assignment(UnresolvedAttribute("target.col2"), UnresolvedAttribute("DEFAULT")))))))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ class SparkSqlAstBuilder extends AstBuilder {
val (_, _, _, _, options, location, _, _) = visitCreateTableClauses(ctx.createTableClauses())
val provider = Option(ctx.tableProvider).map(_.multipartIdentifier.getText).getOrElse(
throw QueryParsingErrors.createTempTableNotSpecifyProviderError(ctx))
val schema = Option(ctx.colTypeList()).map(createSchema)
val schema = Option(ctx.createOrReplaceTableColTypeList()).map(createSchema)

logWarning(s"CREATE TEMPORARY TABLE ... USING ... is deprecated, please use " +
"CREATE TEMPORARY VIEW ... USING ... instead")
Expand Down