Skip to content

Commit

Permalink
Adds separate plan node for PartiQL bag ops; fixes parsing of non-SFW…
Browse files Browse the repository at this point in the history
… exprs; add/update tests
  • Loading branch information
alancai98 committed Jul 12, 2024
1 parent af7bc0b commit 0bd0572
Show file tree
Hide file tree
Showing 18 changed files with 1,125 additions and 389 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ unconstrained which is not SQL-conformant and is causing issues in integrating w
INTEGER an alias for INT4 which is the internal type name. In a later release, we will make INTEGER the default 32-bit
integer with INT/INT4/INTEGER4 being aliases per other systems. This change only applies to
org.partiql.parser.PartiQLParser, not the org.partiql.lang.syntax.PartiQLParser.
- **Breaking change**: partiql-plan: adds a set quantifier field to set operators `UNION`, `INTERSECT` and `EXCEPT`
- **Breaking change**: partiql-plan: adds a set quantifier field to SQL set operators `UNION`, `INTERSECT`, and `EXCEPT`
- partiql-plan: adds a dedicated Rex node for PartiQL bag operators `UNION`, `INTERSECT`, and `EXCEPT`
- partiql-planner: Adds typing support for set operators
- partiql-parser: parses non-SFW expressions to be PartiQL `OUTER` bag operators

### Deprecated

Expand Down
2 changes: 1 addition & 1 deletion partiql-ast/src/main/resources/partiql_ast.ion
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ expr::[
where: optional::expr,
group_by: optional::group_by,
having: optional::expr,
set_op: optional::{
set_op: optional::{ // TODO modeling of `set_op` needs updated to support left-associative set ops https://github.com/partiql/partiql-lang-kotlin/issues/1507
type: '.set_op',
operand: '.expr.s_f_w',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,14 @@ class QueryPrettyPrinter {
is PartiqlAst.Expr.Or -> writeNAryOperator("OR", node.operands, sb, level)
is PartiqlAst.Expr.InCollection -> writeNAryOperator("IN", node.operands, sb, level)
is PartiqlAst.Expr.BagOp -> {
var name = node.op.javaClass.simpleName.toUpperCase().replace("_", " ")
var name = when (node.op) {
is PartiqlAst.BagOpType.Except -> "EXCEPT"
is PartiqlAst.BagOpType.Intersect -> "INTERSECT"
is PartiqlAst.BagOpType.Union -> "UNION"
is PartiqlAst.BagOpType.OuterExcept -> "OUTER EXCEPT"
is PartiqlAst.BagOpType.OuterIntersect -> "OUTER INTERSECT"
is PartiqlAst.BagOpType.OuterUnion -> "OUTER UNION"
}
if (node.quantifier is PartiqlAst.SetQuantifier.All) {
name += " ALL"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -752,12 +752,33 @@ internal class PartiQLPigVisitor(
*
*/

/**
* Verifies if all of the [args] are
* 1. [PartiqlAst.Expr.Select] or
* 2. [PartiqlAst.Expr.BagOp] and is a SQL Set op (i.e. not an `OUTER` bag op)
*/
private fun argsAreSFW(args: List<PartiqlAst.Expr>): Boolean {
return args.all { arg ->
arg is PartiqlAst.Expr.Select || (arg is PartiqlAst.Expr.BagOp && isOuter(arg.op))
}
}

private fun isOuter(op: PartiqlAst.BagOpType): Boolean {
return op is PartiqlAst.BagOpType.OuterUnion || op is PartiqlAst.BagOpType.OuterExcept || op is PartiqlAst.BagOpType.OuterIntersect
}

override fun visitIntersect(ctx: PartiQLParser.IntersectContext) = PartiqlAst.build {
val lhs = visit(ctx.lhs) as PartiqlAst.Expr
val rhs = visit(ctx.rhs) as PartiqlAst.Expr
val quantifier = if (ctx.ALL() != null) all() else distinct()
val (intersect, metas) = when (ctx.OUTER()) {
null -> intersect() to ctx.INTERSECT().getSourceMetaContainer()
null -> {
if (argsAreSFW(listOf(lhs, rhs))) {
intersect() to ctx.INTERSECT().getSourceMetaContainer()
} else {
outerIntersect() to ctx.OUTER().getSourceMetaContainer()
}
}
else -> outerIntersect() to ctx.OUTER().getSourceMetaContainer()
}
bagOp(intersect, quantifier, listOf(lhs, rhs), metas)
Expand All @@ -768,7 +789,13 @@ internal class PartiQLPigVisitor(
val rhs = visit(ctx.rhs) as PartiqlAst.Expr
val quantifier = if (ctx.ALL() != null) all() else distinct()
val (except, metas) = when (ctx.OUTER()) {
null -> except() to ctx.EXCEPT().getSourceMetaContainer()
null -> {
if (argsAreSFW(listOf(lhs, rhs))) {
except() to ctx.EXCEPT().getSourceMetaContainer()
} else {
outerExcept() to ctx.OUTER().getSourceMetaContainer()
}
}
else -> outerExcept() to ctx.OUTER().getSourceMetaContainer()
}
bagOp(except, quantifier, listOf(lhs, rhs), metas)
Expand All @@ -779,7 +806,13 @@ internal class PartiQLPigVisitor(
val rhs = visit(ctx.rhs) as PartiqlAst.Expr
val quantifier = if (ctx.ALL() != null) all() else distinct()
val (union, metas) = when (ctx.OUTER()) {
null -> union() to ctx.UNION().getSourceMetaContainer()
null -> {
if (argsAreSFW(listOf(lhs, rhs))) {
union() to ctx.UNION().getSourceMetaContainer()
} else {
outerUnion() to ctx.OUTER().getSourceMetaContainer()
}
}
else -> outerUnion() to ctx.OUTER().getSourceMetaContainer()
}
bagOp(union, quantifier, listOf(lhs, rhs), metas)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ class ASTPrettyPrinterTest {
checkPrettyPrintAst(
"a UNION b",
"""
Union
OuterUnion
Id a (case_insensitive) (unqualified)
Id b (case_insensitive) (unqualified)
""".trimIndent()
Expand All @@ -551,7 +551,7 @@ class ASTPrettyPrinterTest {
checkPrettyPrintAst(
"a EXCEPT b",
"""
Except
OuterExcept
Id a (case_insensitive) (unqualified)
Id b (case_insensitive) (unqualified)
""".trimIndent()
Expand All @@ -563,7 +563,7 @@ class ASTPrettyPrinterTest {
checkPrettyPrintAst(
"a INTERSECT b",
"""
Intersect
OuterIntersect
Id a (case_insensitive) (unqualified)
Id b (case_insensitive) (unqualified)
""".trimIndent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,22 +465,22 @@ class QueryPrettyPrinterTest {

@Test
fun union1() {
checkPrettyPrintQuery("a UNION b", "a UNION b")
checkPrettyPrintQuery("a UNION b", "a OUTER UNION b")
}

@Test
fun union2() {
checkPrettyPrintQuery("a UNION ALL b", "a UNION ALL b")
checkPrettyPrintQuery("a UNION ALL b", "a OUTER UNION ALL b")
}

@Test
fun except() {
checkPrettyPrintQuery("a EXCEPT b", "a EXCEPT b")
checkPrettyPrintQuery("a EXCEPT b", "a OUTER EXCEPT b")
}

@Test
fun intersect() {
checkPrettyPrintQuery("a INTERSECT b", "a INTERSECT b")
checkPrettyPrintQuery("a INTERSECT b", "a OUTER INTERSECT b")
}

@Test
Expand Down Expand Up @@ -822,7 +822,7 @@ class QueryPrettyPrinterTest {
checkPrettyPrintQuery(
"(SELECT a FROM b) UNION (SELECT c FROM d) UNION (SELECT e FROM f)",
"""
((SELECT a FROM b) UNION (SELECT c FROM d)) UNION (SELECT e FROM f)
((SELECT a FROM b) UNION (SELECT c FROM d)) OUTER UNION (SELECT e FROM f)
""".trimIndent()
)
}
Expand All @@ -832,7 +832,7 @@ class QueryPrettyPrinterTest {
checkPrettyPrintQuery(
"(SELECT a FROM b) UNION c",
"""
(SELECT a FROM b) UNION c
(SELECT a FROM b) OUTER UNION c
""".trimIndent()
)
}
Expand Down Expand Up @@ -873,7 +873,7 @@ class QueryPrettyPrinterTest {
"CASE (SELECT name FROM t) WHEN (SELECT a FROM b) UNION c THEN 1 WHEN (SELECT c FROM d) THEN 2 ELSE (SELECT e FROM f) END",
"""
CASE (SELECT name FROM t)
WHEN (SELECT a FROM b) UNION c THEN 1
WHEN (SELECT a FROM b) OUTER UNION c THEN 1
WHEN (SELECT c FROM d) THEN 2
ELSE (SELECT e FROM f)
END
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class PartiQLParserMatchTest : PartiQLParserTestBase() {
"(MyGraph MATCH (x)) UNION SELECT * FROM tbl1"
) {
bagOp(
op = union(),
op = outerUnion(), // TODO decide if graph match set op maps to PartiQL or SQL
quantifier = distinct(),
operands = listOf(
astMygraphMatchAllNodes,
Expand Down Expand Up @@ -142,7 +142,7 @@ class PartiQLParserMatchTest : PartiQLParserTestBase() {
"SELECT * FROM tbl1 UNION (MyGraph MATCH (x))"
) {
bagOp(
op = union(),
op = outerUnion(), // TODO decide if graph match set op maps to PartiQL or SQL
quantifier = distinct(),
operands = listOf(
astSelectStarFromTbl1,
Expand Down
Loading

0 comments on commit 0bd0572

Please sign in to comment.