Skip to content

Commit 2a57836

Browse files
Add string coalescing support to DeltaSqlParser (#5865)
1 parent 1204398 commit 2a57836

File tree

3 files changed

+69
-22
lines changed

3 files changed

+69
-22
lines changed

spark/src/main/antlr4/io/delta/sql/parser/DeltaSqlBase.g4

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ singleStatement
7272

7373
// If you add keywords here that should not be reserved, add them to 'nonReserved' list.
7474
statement
75-
: VACUUM (path=STRING | table=qualifiedName)
75+
: VACUUM (path=stringLit | table=qualifiedName)
7676
vacuumModifiers #vacuumTable
77-
| (DESC | DESCRIBE) DETAIL (path=STRING | table=qualifiedName) #describeDeltaDetail
77+
| (DESC | DESCRIBE) DETAIL (path=stringLit | table=qualifiedName) #describeDeltaDetail
7878
| GENERATE modeName=identifier FOR TABLE table=qualifiedName #generate
79-
| (DESC | DESCRIBE) HISTORY (path=STRING | table=qualifiedName)
79+
| (DESC | DESCRIBE) HISTORY (path=stringLit | table=qualifiedName)
8080
(LIMIT limit=INTEGER_VALUE)? #describeDeltaHistory
8181
| CONVERT TO DELTA table=qualifiedName
8282
(NO STATISTICS)? (PARTITIONED BY '(' colTypeList ')')? #convert
@@ -92,7 +92,7 @@ statement
9292
(clusterBySpec | CLUSTER BY NONE) #alterTableClusterBy
9393
| ALTER TABLE table=qualifiedName
9494
(ALTER | CHANGE) COLUMN? column=qualifiedName SYNC IDENTITY #alterTableSyncIdentity
95-
| OPTIMIZE (path=STRING | table=qualifiedName) FULL?
95+
| OPTIMIZE (path=stringLit | table=qualifiedName) FULL?
9696
(WHERE partitionPredicate=predicateToken)?
9797
(zorderSpec)? #optimizeTable
9898
| REORG TABLE table=qualifiedName
@@ -164,11 +164,15 @@ featureNameValue
164164
| stringLit
165165
;
166166

167-
stringLit
167+
singleStringLit
168168
: STRING
169169
| DOUBLEQUOTED_STRING
170170
;
171171

172+
stringLit
173+
: singleStringLit+
174+
;
175+
172176
booleanValue
173177
: TRUE | FALSE
174178
;
@@ -188,7 +192,7 @@ colTypeList
188192
;
189193

190194
colType
191-
: colName=identifier dataType (NOT NULL)? (COMMENT STRING)?
195+
: colName=identifier dataType (NOT NULL)? (COMMENT comment=stringLit)?
192196
;
193197

194198
dataType

spark/src/main/scala/io/delta/sql/parser/DeltaSqlParser.scala

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ class DeltaSqlAstBuilder extends DeltaSqlBaseBaseVisitor[AnyRef] {
217217
*/
218218
override def visitPropertyKey(key: PropertyKeyContext): String = {
219219
if (key.stringLit() != null) {
220-
string(visitStringLit(key.stringLit()))
220+
visitStringLit(key.stringLit())
221221
} else {
222222
key.getText
223223
}
@@ -233,24 +233,24 @@ class DeltaSqlAstBuilder extends DeltaSqlBaseBaseVisitor[AnyRef] {
233233
} else if (value.identifier != null) {
234234
value.identifier.getText
235235
} else if (value.value != null) {
236-
string(visitStringLit(value.value))
236+
visitStringLit(value.value)
237237
} else if (value.booleanValue != null) {
238238
value.getText.toLowerCase(Locale.ROOT)
239239
} else {
240240
value.getText
241241
}
242242
}
243243

244-
override def visitStringLit(ctx: StringLitContext): Token = {
245-
if (ctx != null) {
246-
if (ctx.STRING != null) {
247-
ctx.STRING.getSymbol
244+
override def visitStringLit(ctx: StringLitContext): String = {
245+
if (ctx == null) return null
246+
ctx.singleStringLit().asScala.map { singleCtx =>
247+
val token = if (singleCtx.STRING != null) {
248+
singleCtx.STRING.getSymbol
248249
} else {
249-
ctx.DOUBLEQUOTED_STRING.getSymbol
250+
singleCtx.DOUBLEQUOTED_STRING.getSymbol
250251
}
251-
} else {
252-
null
253-
}
252+
string(token)
253+
}.mkString
254254
}
255255

256256
/**
@@ -311,7 +311,7 @@ class DeltaSqlAstBuilder extends DeltaSqlBaseBaseVisitor[AnyRef] {
311311
isReplace,
312312
isCreate,
313313
tablePropertyOverrides,
314-
Option(ctx.location).map(s => string(visitStringLit(s))))
314+
Option(ctx.location).map(visitStringLit))
315315
}
316316

317317
/**
@@ -336,7 +336,7 @@ class DeltaSqlAstBuilder extends DeltaSqlBaseBaseVisitor[AnyRef] {
336336
}
337337
}
338338
VacuumTableCommand(
339-
path = Option(ctx.path).map(string),
339+
path = Option(ctx.path).map(visitStringLit),
340340
table = Option(ctx.table).map(visitTableIdentifier),
341341
inventoryTable = ctx.vacuumModifiers().inventory().asScala.headOption.collect {
342342
case i if i.inventoryTable != null => visitTableIdentifier(i.inventoryTable)
@@ -389,7 +389,7 @@ class DeltaSqlAstBuilder extends DeltaSqlBaseBaseVisitor[AnyRef] {
389389
}
390390
val interleaveBy = Option(ctx.zorderSpec).map(visitZorderSpec).getOrElse(Seq.empty)
391391
OptimizeTableCommand(
392-
Option(ctx.path).map(string),
392+
Option(ctx.path).map(visitStringLit),
393393
Option(ctx.table).map(visitTableIdentifier),
394394
Option(ctx.partitionPredicate).map(extractRawText(_)).toSeq,
395395
DeltaOptimizeContext(isFull = ctx.FULL != null))(interleaveBy)
@@ -437,15 +437,15 @@ class DeltaSqlAstBuilder extends DeltaSqlBaseBaseVisitor[AnyRef] {
437437
override def visitDescribeDeltaDetail(
438438
ctx: DescribeDeltaDetailContext): LogicalPlan = withOrigin(ctx) {
439439
DescribeDeltaDetailCommand(
440-
Option(ctx.path).map(string),
440+
Option(ctx.path).map(visitStringLit),
441441
Option(ctx.table).map(visitTableIdentifier),
442442
Map.empty)
443443
}
444444

445445
override def visitDescribeDeltaHistory(
446446
ctx: DescribeDeltaHistoryContext): LogicalPlan = withOrigin(ctx) {
447447
DescribeDeltaHistory(
448-
Option(ctx.path).map(string),
448+
Option(ctx.path).map(visitStringLit),
449449
Option(ctx.table).map(visitTableIdentifier),
450450
Option(ctx.limit).map(_.getText.toInt))
451451
}
@@ -602,7 +602,7 @@ class DeltaSqlAstBuilder extends DeltaSqlBaseBaseVisitor[AnyRef] {
602602
*/
603603
override def visitFeatureNameValue(featureNameValue: FeatureNameValueContext): String = {
604604
if (featureNameValue.stringLit() != null) {
605-
string(visitStringLit(featureNameValue.stringLit()))
605+
visitStringLit(featureNameValue.stringLit())
606606
} else {
607607
featureNameValue.getText
608608
}

spark/src/test/scala/io/delta/sql/parser/DeltaSqlParserSuite.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,4 +544,47 @@ class DeltaSqlParserSuite extends SparkFunSuite with SQLHelper {
544544
}
545545
}
546546
}
547+
548+
test("string coalescing") {
549+
val parser = new DeltaSqlParser(new SparkSqlParser())
550+
551+
val pathToTable = "/path/to/table"
552+
val partedPathes = Seq(
553+
"'/path/to/table'",
554+
"'/path/to' '/table'",
555+
"'/path' '/to' '/table'"
556+
)
557+
558+
partedPathes.foreach { path =>
559+
// CLONE LOCATION
560+
val cloneCmd = parser.parsePlan(
561+
s"CREATE TABLE t1 SHALLOW CLONE source LOCATION $path")
562+
assert(cloneCmd.asInstanceOf[CloneTableStatement].targetLocation === Some(pathToTable))
563+
564+
// OPTIMIZE
565+
val optimizeCmd = parser.parsePlan(s"OPTIMIZE $path")
566+
assert(optimizeCmd ===
567+
OptimizeTableCommand(Some(pathToTable), None, Nil)(Nil))
568+
569+
// DESCRIBE HISTORY
570+
var describeHistoryCmd = parser.parsePlan(s"DESCRIBE HISTORY $path")
571+
assert(describeHistoryCmd.asInstanceOf[DescribeDeltaHistory].child ===
572+
UnresolvedPathBasedDeltaTable(pathToTable, Map.empty, DescribeDeltaHistory.COMMAND_NAME))
573+
574+
// DESCRIBE DETAIL
575+
val describeDetailCmd = parser.parsePlan(s"DESCRIBE DETAIL $path")
576+
assert(describeDetailCmd ===
577+
DescribeDeltaDetailCommand(
578+
UnresolvedPathBasedTable(pathToTable, Map.empty, DescribeDeltaDetailCommand.CMD_NAME),
579+
Map.empty))
580+
581+
// VACUUM
582+
val vacuumCmd = parser.parsePlan(s"VACUUM $path")
583+
assert(vacuumCmd ===
584+
VacuumTableCommand(
585+
UnresolvedPathBasedDeltaTable(pathToTable, Map.empty, "VACUUM"),
586+
None, None, None, false, None))
587+
}
588+
589+
}
547590
}

0 commit comments

Comments
 (0)