Skip to content

Commit c7432aa

Browse files
Add EXCLUDE to the parser, ast, plan, and plan schema inferencer (#1226)
Co-authored-by: John Ed Quinn <[email protected]>
1 parent 316fe36 commit c7432aa

File tree

18 files changed

+2151
-29
lines changed

18 files changed

+2151
-29
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ Thank you to all who have contributed!
3434
- Support parsing, planning, and evaluation of Bitwise AND operator (&).
3535
- The Bitwise And Operator only works for integer operands.
3636
- The operator precedence may change based on the pending operator precedence [RFC](https://github.com/partiql/partiql-docs/issues/50).
37+
- **EXPERIMENTAL** Adds `EXCLUDE` to parser, ast, plan, and plan schema inferencer
38+
- This feature is marked as experimental until an RFC is added https://github.com/partiql/partiql-spec/issues/39
39+
- NOTE: this feature is not currently implemented in the evaluator
3740

3841
### Changed
3942

@@ -42,6 +45,7 @@ Thank you to all who have contributed!
4245
### Fixed
4346
- Fixes typing of scalar subqueries in the PartiQLSchemaInferencer. Note that usage of `SELECT *` in subqueries
4447
is not fully supported. Please make sure to handle InferenceExceptions.
48+
- Fixes schema inferencer behavior for ORDER BY
4549

4650
### Removed
4751

partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import com.amazon.ionelement.api.ionSymbol
1717
import com.amazon.ionelement.api.metaContainerOf
1818
import org.partiql.ast.AstNode
1919
import org.partiql.ast.DatetimeField
20+
import org.partiql.ast.Exclude
2021
import org.partiql.ast.Expr
2122
import org.partiql.ast.From
2223
import org.partiql.ast.GraphMatch
@@ -665,14 +666,15 @@ private class AstTranslator(val metas: Map<String, MetaContainer>) : AstBaseVisi
665666
}
666667
val project = visitSelect(node.select, ctx)
667668
val from = visitFrom(node.from, ctx)
669+
val exclude = node.exclude?.let { visitExclude(it, ctx) }
668670
val fromLet = node.let?.let { visitLet(it, ctx) }
669671
val where = node.where?.let { visitExpr(it, ctx) }
670672
val groupBy = node.groupBy?.let { visitGroupBy(it, ctx) }
671673
val having = node.having?.let { visitExpr(it, ctx) }
672674
val orderBy = node.orderBy?.let { visitOrderBy(it, ctx) }
673675
val limit = node.limit?.let { visitExpr(it, ctx) }
674676
val offset = node.offset?.let { visitExpr(it, ctx) }
675-
select(setq, project, from, fromLet, where, groupBy, having, orderBy, limit, offset, metas)
677+
select(setq, project, exclude, from, fromLet, where, groupBy, having, orderBy, limit, offset, metas)
676678
}
677679

678680
/**
@@ -750,6 +752,48 @@ private class AstTranslator(val metas: Map<String, MetaContainer>) : AstBaseVisi
750752
join(type, lhs, rhs, condition, metas)
751753
}
752754

755+
override fun visitExclude(node: Exclude, ctx: Ctx): PartiqlAst.ExcludeOp = translate(node) { metas ->
756+
val excludeExprs = node.exprs.translate<PartiqlAst.ExcludeExpr>(ctx)
757+
excludeOp(excludeExprs, metas)
758+
}
759+
760+
override fun visitExcludeExcludeExpr(node: Exclude.ExcludeExpr, ctx: Ctx) = translate(node) { metas ->
761+
val root = visitIdentifierSymbol(node.root, ctx)
762+
val steps = node.steps.translate<PartiqlAst.ExcludeStep>(ctx)
763+
excludeExpr(root = root, steps = steps, metas)
764+
}
765+
766+
override fun visitExcludeStep(node: Exclude.Step, ctx: Ctx) =
767+
super.visitExcludeStep(node, ctx) as PartiqlAst.ExcludeStep
768+
769+
override fun visitExcludeStepExcludeTupleAttr(node: Exclude.Step.ExcludeTupleAttr, ctx: Ctx) = translate(node) { metas ->
770+
val attr = node.symbol.symbol
771+
val case = node.symbol.caseSensitivity.toLegacyCaseSensitivity()
772+
excludeTupleAttr(identifier(attr, case), metas)
773+
}
774+
775+
override fun visitExcludeStepExcludeCollectionIndex(
776+
node: Exclude.Step.ExcludeCollectionIndex,
777+
ctx: Ctx
778+
) = translate(node) { metas ->
779+
val index = node.index.toLong()
780+
excludeCollectionIndex(index, metas)
781+
}
782+
783+
override fun visitExcludeStepExcludeTupleWildcard(
784+
node: Exclude.Step.ExcludeTupleWildcard,
785+
ctx: Ctx
786+
) = translate(node) { metas ->
787+
excludeTupleWildcard(metas)
788+
}
789+
790+
override fun visitExcludeStepExcludeCollectionWildcard(
791+
node: Exclude.Step.ExcludeCollectionWildcard,
792+
ctx: Ctx
793+
) = translate(node) { metas ->
794+
excludeCollectionWildcard(metas)
795+
}
796+
753797
override fun visitLet(node: Let, ctx: Ctx) = translate(node) { metas ->
754798
val bindings = node.bindings.translate<PartiqlAst.LetBinding>(ctx)
755799
let(bindings, metas)

partiql-ast/src/main/pig/partiql.ion

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ may then be further optimized by selecting better implementations of each operat
167167
(select
168168
(setq (? set_quantifier))
169169
(project projection)
170+
(exclude_clause (? exclude_op))
170171
(from from_source)
171172
(from_let (? let))
172173
(where (? expr))
@@ -197,7 +198,7 @@ may then be further optimized by selecting better implementations of each operat
197198
(sum path_step
198199
// `someRoot[<expr>]`, or `someRoot.someField` which is equivalent to `someRoot['someField']`.
199200
(path_expr index::expr case::case_sensitivity)
200-
// `someRoot[*]`]
201+
// `someRoot[*]`
201202
(path_wildcard)
202203
// `someRoot.*`
203204
(path_unpivot))
@@ -220,6 +221,22 @@ may then be further optimized by selecting better implementations of each operat
220221
// For `<expr> [AS <id>]`
221222
(project_expr expr::expr as_alias::(? symbol)))
222223

224+
// EXCLUDE clause
225+
(product exclude_op exprs::(* exclude_expr 1))
226+
227+
(product exclude_expr root::identifier steps::(* exclude_step 1))
228+
229+
(sum exclude_step
230+
// `someRoot.someField` case sensitivity depends on if `someField` is quoted
231+
// `someRoot[<string literal>] is equivalent to `someRoot."<string literal>"` (case-sensitive)
232+
(exclude_tuple_attr attr::identifier)
233+
// `someRoot[<int literal>]`
234+
(exclude_collection_index index::int)
235+
// `someRoot[*]`]
236+
(exclude_tuple_wildcard)
237+
// `someRoot.*`
238+
(exclude_collection_wildcard))
239+
223240
// A list of LET bindings
224241
(product let let_bindings::(* let_binding 1))
225242

partiql-ast/src/main/resources/partiql_ast.ion

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ expr::[
494494
// The PartiQL `<sfw>` query expression, think SQL `<query specification>`
495495
s_f_w::{
496496
select: select, // oneof SELECT / SELECT VALUE / PIVOT
497+
exclude: optional::exclude,
497498
from: from,
498499
let: optional::let,
499500
where: optional::expr,
@@ -561,6 +562,22 @@ select::[
561562
},
562563
]
563564

565+
exclude::{
566+
exprs: list::[exclude_expr],
567+
_: [
568+
exclude_expr::{
569+
root: '.identifier.symbol',
570+
steps: list::[step],
571+
},
572+
step::[
573+
exclude_tuple_attr::{ symbol: '.identifier.symbol' },
574+
exclude_collection_index::{ index: int },
575+
exclude_tuple_wildcard::{},
576+
exclude_collection_wildcard::{},
577+
]
578+
]
579+
}
580+
564581
// PartiQL FROM Clause Variants — https://partiql.org/dql/from.html
565582
from::[
566583

partiql-lang/src/main/kotlin/org/partiql/lang/eval/visitors/GroupByPathExpressionVisitorTransform.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ class GroupByPathExpressionVisitorTransform(
102102
val projection = currentAndUnshadowedTransformer.transformExprSelect_project(node)
103103

104104
// The scope of the expressions in the FROM clause is the same as that of the parent scope.
105+
val exclude = unshadowedTransformer.transformExprSelect_excludeClause(node)
105106
val from = this.transformExprSelect_from(node)
106107
val fromLet = unshadowedTransformer.transformExprSelect_fromLet(node)
107108
val where = unshadowedTransformer.transformExprSelect_where(node)
@@ -116,6 +117,7 @@ class GroupByPathExpressionVisitorTransform(
116117
PartiqlAst.Expr.Select(
117118
setq = node.setq,
118119
project = projection,
120+
excludeClause = exclude,
119121
from = from,
120122
fromLet = fromLet,
121123
where = where,

partiql-lang/src/main/kotlin/org/partiql/lang/eval/visitors/VisitorTransformBase.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ abstract class VisitorTransformBase : PartiqlAst.VisitorTransform() {
3636
* infinite recursion.
3737
*/
3838
fun transformExprSelectEvaluationOrder(node: PartiqlAst.Expr.Select): PartiqlAst.Expr {
39+
val exclude = transformExprSelect_excludeClause(node)
3940
val from = transformExprSelect_from(node)
4041
val fromLet = transformExprSelect_fromLet(node)
4142
val where = transformExprSelect_where(node)
@@ -51,6 +52,7 @@ abstract class VisitorTransformBase : PartiqlAst.VisitorTransform() {
5152
PartiqlAst.Expr.Select(
5253
setq = setq,
5354
project = project,
55+
excludeClause = exclude,
5456
from = from,
5557
fromLet = fromLet,
5658
where = where,

partiql-lang/src/main/kotlin/org/partiql/lang/planner/PlanningProblemDetails.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ sealed class PlanningProblemDetails(
7777
"Please use the `INSERT INTO <table> << <expr>, ... >>` form instead."
7878
}
7979
)
80+
81+
data class UnresolvedExcludeExprRoot(val root: String) :
82+
PlanningProblemDetails(
83+
ProblemSeverity.ERROR,
84+
{ "Exclude expression given an unresolvable root '$root'" }
85+
)
8086
}
8187

8288
private fun quotationHint(caseSensitive: Boolean) =

0 commit comments

Comments
 (0)