Skip to content

Commit 62d258f

Browse files
authored
Merge 18e9708 into 941a760
2 parents 941a760 + 18e9708 commit 62d258f

File tree

75 files changed

+1516
-700
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1516
-700
lines changed

.github/workflows/conformance-report.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
# Run the conformance tests and save to an Ion file.
2828
- name: gradle test of the conformance tests (can fail) and save to Ion file
2929
continue-on-error: true
30-
run: gradle :test:partiql-tests-runner:test --tests "*ConformanceTestsReportRunner" -PconformanceReport
30+
run: gradle :test:partiql-tests-runner:test --tests "*ConformanceTestReport" -PconformanceReport
3131
# Upload conformance report for future viewing and comparison with future runs.
3232
- name: Upload `conformance_test_results.ion`
3333
uses: actions/upload-artifact@v3
@@ -86,7 +86,7 @@ jobs:
8686
continue-on-error: true
8787
run: |
8888
cd ${{ github.event.pull_request.base.sha }}
89-
gradle :test:partiql-tests-runner:test --tests "*ConformanceTestsReportRunner" -PconformanceReport
89+
gradle :test:partiql-tests-runner:test --tests "*ConformanceTestReport" -PconformanceReport
9090
- name: (If download of target branch conformance report fails) Move conformance test report of target branch to ./artifact directory
9191
if: ${{ steps.download-report.outcome == 'failure' }}
9292
continue-on-error: true

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Thank you to all who have contributed!
3636
- **EXPERIMENTAL** Evaluation of `EXCLUDE` in the `EvaluatingCompiler`
3737
- This is currently marked as experimental until the RFC is approved https://github.com/partiql/partiql-lang/issues/27
3838
- This will be added to the `PhysicalPlanCompiler` in an upcoming release
39+
- **EXPERIMENTAL**: Adds support for EXCLUDE in the default SqlDialect.
3940

4041
### Changed
4142
- StaticTypeInferencer and PlanTyper will not raise an error when an expression is inferred to `NULL` or `unionOf(NULL, MISSING)`. In these cases the StaticTypeInferencer and PlanTyper will still raise the Problem Code `ExpressionAlwaysReturnsNullOrMissing` but the severity of the problem has been changed to warning. In the case an expression always returns `MISSING`, problem code `ExpressionAlwaysReturnsMissing` will be raised, which will have problem severity of error.
@@ -51,6 +52,7 @@ Thank you to all who have contributed!
5152

5253
### Fixed
5354
- Fixes the CLI hanging on invalid queries. See issue #1230.
55+
- Fixes Timestamp Type parsing issue. Previously Timestamp Type would get parsed to a Time type.
5456

5557
### Removed
5658
- **Breaking** Removed IR factory in favor of static top-level functions. Change `Ast.foo()`

lib/isl/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ val elements: List<AnyElement> = loadAllElements("type::{ name: foo }")
4545
val parsedSchemaModel: IonSchemaModel.Model = parseSchema(elements)
4646
```
4747

48-
Alternatively, users can parse `ion-schema-kotlin` [Schema](https://github.com/amzn/ion-schema-kotlin/blob/master/src/com/amazon/ionschema/Schema.kt#L36)
48+
Alternatively, users can parse `ion-schema-kotlin` [Schema](https://github.com/amzn/ion-schema-kotlin/blob/master/src/com/amazon/ionschema/Namespace.kt#L36)
4949
by first converting to `IonElement`:
5050
```Kotlin
5151
val schema = ... // some ion-schema-kotlin Schema object

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,7 @@ private class AstTranslator(val metas: Map<String, MetaContainer>) : AstBaseVisi
13331333
translate(node) { metas -> timestampType(node.precision?.toLong(), metas) }
13341334

13351335
override fun visitTypeTimestampWithTz(node: Type.TimestampWithTz, ctx: Ctx) =
1336-
throw IllegalArgumentException("TIMESTAMP [WITH TIMEZONE] type not supported")
1336+
translate(node) { metas -> timestampWithTimeZoneType(node.precision?.toLong(), metas) }
13371337

13381338
override fun visitTypeInterval(node: Type.Interval, ctx: Ctx) =
13391339
throw IllegalArgumentException("INTERVAL type not supported")

partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.partiql.ast.sql
22

33
import org.partiql.ast.AstNode
4+
import org.partiql.ast.Exclude
45
import org.partiql.ast.Expr
56
import org.partiql.ast.From
67
import org.partiql.ast.GroupBy
@@ -82,6 +83,38 @@ public abstract class SqlDialect : AstBaseVisitor<SqlBlock, SqlBlock>() {
8283
return head concat r(path)
8384
}
8485

86+
override fun visitExclude(node: Exclude, head: SqlBlock): SqlBlock {
87+
var h = head
88+
h = h concat " EXCLUDE "
89+
h = h concat list(start = null, end = null) { node.exprs }
90+
return h
91+
}
92+
93+
override fun visitExcludeExcludeExpr(node: Exclude.ExcludeExpr, head: SqlBlock): SqlBlock {
94+
var h = head
95+
h = h concat visitIdentifierSymbol(node.root, SqlBlock.Nil)
96+
h = h concat list(delimiter = null, start = null, end = null) { node.steps }
97+
return h
98+
}
99+
100+
override fun visitExcludeStepExcludeCollectionIndex(node: Exclude.Step.ExcludeCollectionIndex, head: SqlBlock): SqlBlock {
101+
return head concat r("[${node.index}]")
102+
}
103+
104+
override fun visitExcludeStepExcludeTupleWildcard(node: Exclude.Step.ExcludeTupleWildcard, head: SqlBlock): SqlBlock {
105+
return head concat r(".*")
106+
}
107+
108+
override fun visitExcludeStepExcludeTupleAttr(node: Exclude.Step.ExcludeTupleAttr, head: SqlBlock): SqlBlock {
109+
var h = head concat r(".")
110+
h = h concat visitIdentifierSymbol(node.symbol, SqlBlock.Nil)
111+
return h
112+
}
113+
114+
override fun visitExcludeStepExcludeCollectionWildcard(node: Exclude.Step.ExcludeCollectionWildcard, head: SqlBlock): SqlBlock {
115+
return head concat r("[*]")
116+
}
117+
85118
// cannot write path step outside the context of a path as we don't want it to reflow
86119
override fun visitPathStep(node: Path.Step, head: SqlBlock) = error("path step cannot be written directly")
87120

@@ -550,6 +583,8 @@ public abstract class SqlDialect : AstBaseVisitor<SqlBlock, SqlBlock>() {
550583
var h = head
551584
// SELECT
552585
h = visit(node.select, h)
586+
// EXCLUDE
587+
h = node.exclude?.let { visit(it, h) } ?: h
553588
// FROM
554589
h = visit(node.from, h concat r(" FROM "))
555590
// LET

partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ class SqlDialectTest {
134134
@Execution(ExecutionMode.CONCURRENT)
135135
fun testSelectClause(case: Case) = case.assert()
136136

137+
@ParameterizedTest(name = "EXCLUDE Clause #{index}")
138+
@MethodSource("excludeClauseCases")
139+
@Execution(ExecutionMode.CONCURRENT)
140+
fun testExcludeClause(case: Case) = case.assert()
141+
137142
@ParameterizedTest(name = "FROM Clause #{index}")
138143
@MethodSource("fromClauseCases")
139144
@Execution(ExecutionMode.CONCURRENT)
@@ -1058,6 +1063,94 @@ class SqlDialectTest {
10581063
},
10591064
)
10601065

1066+
@JvmStatic
1067+
fun excludeClauseCases() = listOf(
1068+
expect("SELECT a EXCLUDE t.a FROM T") {
1069+
exprSFW {
1070+
select = select("a")
1071+
from = fromValue {
1072+
expr = v("T")
1073+
type = From.Value.Type.SCAN
1074+
}
1075+
exclude = exclude {
1076+
exprs += excludeExcludeExpr {
1077+
root = id("t", Identifier.CaseSensitivity.INSENSITIVE)
1078+
steps += excludeStepExcludeTupleAttr {
1079+
symbol = id("a", Identifier.CaseSensitivity.INSENSITIVE)
1080+
}
1081+
}
1082+
}
1083+
}
1084+
},
1085+
expect("SELECT a EXCLUDE a.b, c.d, e.f, g.h FROM T") {
1086+
exprSFW {
1087+
select = select("a")
1088+
from = fromValue {
1089+
expr = v("T")
1090+
type = From.Value.Type.SCAN
1091+
}
1092+
exclude = exclude {
1093+
exprs += excludeExcludeExpr {
1094+
root = id("a", Identifier.CaseSensitivity.INSENSITIVE)
1095+
steps += insensitiveExcludeTupleAttr("b")
1096+
}
1097+
exprs += excludeExcludeExpr {
1098+
root = id("c", Identifier.CaseSensitivity.INSENSITIVE)
1099+
steps += insensitiveExcludeTupleAttr("d")
1100+
}
1101+
exprs += excludeExcludeExpr {
1102+
root = id("e", Identifier.CaseSensitivity.INSENSITIVE)
1103+
steps += insensitiveExcludeTupleAttr("f")
1104+
}
1105+
exprs += excludeExcludeExpr {
1106+
root = id("g", Identifier.CaseSensitivity.INSENSITIVE)
1107+
steps += insensitiveExcludeTupleAttr("h")
1108+
}
1109+
}
1110+
}
1111+
},
1112+
expect("SELECT a EXCLUDE t.a.\"b\".*[*].c, \"s\"[0].d.\"e\"[*].f.* FROM T") {
1113+
exprSFW {
1114+
select = select("a")
1115+
from = fromValue {
1116+
expr = v("T")
1117+
type = From.Value.Type.SCAN
1118+
}
1119+
exclude = exclude {
1120+
exprs += excludeExcludeExpr {
1121+
root = id("t", Identifier.CaseSensitivity.INSENSITIVE)
1122+
steps += mutableListOf(
1123+
insensitiveExcludeTupleAttr("a"),
1124+
sensitiveExcludeTupleAttr("b"),
1125+
excludeStepExcludeTupleWildcard(),
1126+
excludeStepExcludeCollectionWildcard(),
1127+
insensitiveExcludeTupleAttr("c"),
1128+
)
1129+
}
1130+
exprs += excludeExcludeExpr {
1131+
root = id("s", Identifier.CaseSensitivity.SENSITIVE)
1132+
steps += mutableListOf(
1133+
excludeStepExcludeCollectionIndex(0),
1134+
insensitiveExcludeTupleAttr("d"),
1135+
sensitiveExcludeTupleAttr("e"),
1136+
excludeStepExcludeCollectionWildcard(),
1137+
insensitiveExcludeTupleAttr("f"),
1138+
excludeStepExcludeTupleWildcard(),
1139+
)
1140+
}
1141+
}
1142+
}
1143+
},
1144+
)
1145+
1146+
private fun AstBuilder.insensitiveExcludeTupleAttr(str: String) = excludeStepExcludeTupleAttr {
1147+
symbol = id(str, Identifier.CaseSensitivity.INSENSITIVE)
1148+
}
1149+
1150+
private fun AstBuilder.sensitiveExcludeTupleAttr(str: String) = excludeStepExcludeTupleAttr {
1151+
symbol = id(str, Identifier.CaseSensitivity.SENSITIVE)
1152+
}
1153+
10611154
@JvmStatic
10621155
fun fromClauseCases() = listOf(
10631156
expect("SELECT a FROM T") {

partiql-cli/src/main/kotlin/org/partiql/cli/utils/ServiceLoaderUtil.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,12 @@ class ServiceLoaderUtil {
114114
listOf()
115115
}
116116
return plugins.flatMap { plugin -> plugin.getFunctions() }
117+
.filterIsInstance<PartiQLFunction.Scalar>()
117118
.map { partiqlFunc -> PartiQLtoExprFunction(partiqlFunc) }
118119
}
119120

120121
@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class)
121-
private fun PartiQLtoExprFunction(customFunction: PartiQLFunction): ExprFunction {
122+
private fun PartiQLtoExprFunction(customFunction: PartiQLFunction.Scalar): ExprFunction {
122123
val name = customFunction.signature.name
123124
val parameters = customFunction.signature.parameters.map { it.type }
124125
val returnType = customFunction.signature.returns
@@ -130,8 +131,8 @@ class ServiceLoaderUtil {
130131
)
131132

132133
override fun callWithRequired(session: EvaluationSession, required: List<ExprValue>): ExprValue {
133-
val partiQLArguments = required.mapIndexed { i, expr -> ExprToPartiQLValue(expr, parameters[i]) }
134-
val partiQLResult = customFunction.invoke(session.toConnectorSession(), partiQLArguments)
134+
val partiQLArguments = required.mapIndexed { i, expr -> ExprToPartiQLValue(expr, parameters[i]) }.toTypedArray()
135+
val partiQLResult = customFunction.invoke(partiQLArguments)
135136
return PartiQLtoExprValue(partiQLResult)
136137
}
137138
}

partiql-cli/src/test/kotlin/org/partiql/cli/utils/PowTest.kt renamed to partiql-cli/src/test/kotlin/org/partiql/cli/functions/PowTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package org.partiql.cli.functions
33

44
import org.junit.jupiter.api.Assertions.assertEquals
5+
import org.junit.jupiter.api.Disabled
56
import org.junit.jupiter.api.Test
67
import org.partiql.cli.makeCliAndGetResult
78
import org.partiql.cli.pipeline.AbstractPipeline
@@ -16,6 +17,7 @@ import java.nio.file.Paths
1617
*
1718
* @constructor Creates an instance of `PowTest`.
1819
*/
20+
@Disabled
1921
class PowTest {
2022

2123
val pluginPath = Paths.get(System.getProperty("testingPluginDirectory"))

partiql-cli/src/test/kotlin/org/partiql/cli/utils/TrimLeadTest.kt renamed to partiql-cli/src/test/kotlin/org/partiql/cli/functions/TrimLeadTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package org.partiql.cli.functions
33

44
import org.junit.jupiter.api.Assertions.assertEquals
5+
import org.junit.jupiter.api.Disabled
56
import org.junit.jupiter.api.Test
67
import org.partiql.cli.makeCliAndGetResult
78
import org.partiql.cli.pipeline.AbstractPipeline
@@ -16,6 +17,7 @@ import java.nio.file.Paths
1617
*
1718
* @constructor Creates an instance of `TrimLeadTest`.
1819
*/
20+
@Disabled
1921
class TrimLeadTest {
2022

2123
val pluginPath = Paths.get(System.getProperty("testingPluginDirectory"))

partiql-cli/src/test/kotlin/org/partiql/cli/utils/ServiceLoaderUtilTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11

22
import org.junit.jupiter.api.Assertions.assertTrue
3+
import org.junit.jupiter.api.Disabled
34
import org.junit.jupiter.api.Test
45
import org.partiql.cli.utils.ServiceLoaderUtil
56
import org.partiql.lang.eval.ExprFunction
67
import java.nio.file.Paths
78

9+
@Disabled
810
class ServiceLoaderUtilTest {
911
@Test
1012
fun `loadPlugins loads the correct plugins`() {
Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.partiql.eval
22

3-
import org.partiql.eval.impl.PartiQLEngineDefault
3+
import org.partiql.eval.internal.Compiler
4+
import org.partiql.eval.internal.Record
45
import org.partiql.plan.PartiQLPlan
56
import org.partiql.spi.Plugin
67
import org.partiql.value.PartiQLValue
@@ -23,37 +24,51 @@ import org.partiql.value.PartiQLValueExperimental
2324
*/
2425
public interface PartiQLEngine {
2526

26-
public fun execute(plan: PartiQLPlan): Result
27+
public fun prepare(plan: PartiQLPlan): PartiQLStatement<*>
2728

28-
public enum class Implementation {
29-
DEFAULT
30-
}
31-
32-
public sealed interface Result {
33-
public data class Success @OptIn(PartiQLValueExperimental::class) constructor(
34-
val output: PartiQLValue
35-
) : Result
29+
public fun execute(statement: PartiQLStatement<*>): PartiQLResult
3630

37-
public data class Error @OptIn(PartiQLValueExperimental::class) constructor(
38-
val output: PartiQLValue
39-
) : Result
31+
companion object {
32+
@JvmStatic
33+
@JvmOverloads
34+
fun default(plugins: List<Plugin> = emptyList()) = Builder().plugins(plugins).build()
4035
}
4136

4237
public class Builder {
4338

4439
private var plugins: List<Plugin> = emptyList()
45-
private var implementation: Implementation = Implementation.DEFAULT
4640

47-
public fun withPlugins(plugins: List<Plugin>): Builder = this.apply {
41+
public fun plugins(plugins: List<Plugin>): Builder = this.apply {
4842
this.plugins = plugins
4943
}
5044

51-
public fun withImplementation(impl: Implementation): Builder = this.apply {
52-
this.implementation = impl
45+
public fun build(): PartiQLEngine = Default(plugins)
46+
}
47+
48+
private class Default(private val plugins: List<Plugin>) : PartiQLEngine {
49+
50+
@OptIn(PartiQLValueExperimental::class)
51+
override fun prepare(plan: PartiQLPlan): PartiQLStatement<*> {
52+
// Close over the expression.
53+
// Right now we are assuming we only have a query statement hence a value statement.
54+
val expression = Compiler.compile(plan)
55+
return object : PartiQLStatement.Query {
56+
override fun execute(): PartiQLValue {
57+
return expression.eval(Record.empty)
58+
}
59+
}
5360
}
5461

55-
public fun build(): PartiQLEngine = when (this.implementation) {
56-
Implementation.DEFAULT -> PartiQLEngineDefault()
62+
@OptIn(PartiQLValueExperimental::class)
63+
override fun execute(statement: PartiQLStatement<*>): PartiQLResult {
64+
return when (statement) {
65+
is PartiQLStatement.Query -> try {
66+
val value = statement.execute()
67+
PartiQLResult.Value(value)
68+
} catch (ex: Exception) {
69+
PartiQLResult.Error(ex)
70+
}
71+
}
5772
}
5873
}
5974
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.partiql.eval
2+
3+
import org.partiql.value.PartiQLValue
4+
import org.partiql.value.PartiQLValueExperimental
5+
6+
public sealed interface PartiQLResult {
7+
8+
@OptIn(PartiQLValueExperimental::class)
9+
public data class Value(public val value: PartiQLValue) : PartiQLResult
10+
11+
public data class Error(public val cause: Throwable) : PartiQLResult
12+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.partiql.eval
2+
3+
import org.partiql.value.PartiQLValue
4+
import org.partiql.value.PartiQLValueExperimental
5+
6+
/**
7+
* Represents a compiled PartiQL Plan ready for execution.
8+
*/
9+
sealed interface PartiQLStatement<T> {
10+
11+
public fun execute(): T
12+
13+
@OptIn(PartiQLValueExperimental::class)
14+
interface Query : PartiQLStatement<PartiQLValue>
15+
}

0 commit comments

Comments
 (0)