Skip to content

Commit 1d7c3c9

Browse files
authored
Merge 61de632 into 3db0221
2 parents 3db0221 + 61de632 commit 1d7c3c9

File tree

22 files changed

+1731
-1158
lines changed

22 files changed

+1731
-1158
lines changed

buildSrc/src/main/kotlin/partiql.conventions.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ java {
5050
tasks.test {
5151
useJUnitPlatform() // Enable JUnit5
5252
jvmArgs.addAll(listOf("-Duser.language=en", "-Duser.country=US"))
53+
jvmArgs.add("-Djunit.jupiter.execution.timeout.mode=disabled_on_debug")
5354
maxHeapSize = "4g"
5455
testLogging {
5556
events.add(TestLogEvent.FAILED)

partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinNestedLoop.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ internal abstract class RelJoinNestedLoop : RelPeeking() {
6363
toReturn = join(result.isTrue(), lhsRecord!!, rhsRecord)
6464
}
6565
// Move the pointer to the next row for the RHS
66-
if (toReturn == null) rhsRecord = rhs.next()
66+
if (toReturn == null) rhsRecord = if (rhs.hasNext()) rhs.next() else null
6767
}
6868
while (toReturn == null)
6969
return toReturn

partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelOffset.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ internal class RelOffset(
3333

3434
override fun hasNext(): Boolean {
3535
if (!init) {
36-
for (record in input) {
36+
while (input.hasNext()) {
3737
if (_seen >= _offset) {
3838
break
3939
}
4040
_seen = _seen.add(BigInteger.ONE)
41+
input.next()
4142
}
4243
init = true
4344
}

partiql-plan/src/main/resources/partiql_plan.ion

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ rex::{
195195

196196
err::{
197197
message: string,
198+
causes: list::['.rex.op']
199+
},
200+
201+
missing::{
202+
message: string,
203+
causes: list::['.rex.op']
198204
},
199205
],
200206
}

partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlanner.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public interface PartiQLPlanner {
5050
public val catalogs: Map<String, ConnectorMetadata> = emptyMap(),
5151
public val instant: Instant = Instant.now(),
5252
)
53-
5453
public companion object {
5554

5655
@JvmStatic

partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlannerBuilder.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
package org.partiql.planner
22

3+
import org.partiql.planner.internal.PartiQLPlannerDefault
4+
import org.partiql.planner.internal.PlannerFlag
35
import org.partiql.spi.connector.ConnectorMetadata
46

57
/**
68
* PartiQLPlannerBuilder is used to programmatically construct a [PartiQLPlanner] implementation.
79
*
810
* Usage:
911
* PartiQLPlanner.builder()
12+
* .signalMode()
1013
* .addPass(myPass)
1114
* .build()
1215
*/
1316
public class PartiQLPlannerBuilder {
1417

18+
private val flags: MutableSet<PlannerFlag> = mutableSetOf()
19+
1520
private val passes: MutableList<PartiQLPlannerPass> = mutableListOf()
1621

1722
/**
1823
* Build the builder, return an implementation of a [PartiQLPlanner].
1924
*
2025
* @return
2126
*/
22-
public fun build(): PartiQLPlanner = PartiQLPlannerDefault(passes)
27+
public fun build(): PartiQLPlanner = PartiQLPlannerDefault(passes, flags)
2328

2429
/**
2530
* Java style method for adding a planner pass to this planner builder.
@@ -41,6 +46,13 @@ public class PartiQLPlannerBuilder {
4146
this.passes.addAll(passes)
4247
}
4348

49+
/**
50+
* Java style method for setting the planner to signal mode
51+
*/
52+
public fun signalMode(): PartiQLPlannerBuilder = this.apply {
53+
this.flags.add(PlannerFlag.SIGNAL_MODE)
54+
}
55+
4456
/**
4557
* Java style method for assigning a Catalog name to [ConnectorMetadata].
4658
*

partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,22 @@ internal class Env(private val session: PartiQLPlanner.Session) {
9393
// Invoke FnResolver to determine if we made a match
9494
val variants = item.handle.entity.getVariants()
9595
val match = FnResolver.resolve(variants, args.map { it.type })
96+
// If Type mismatch, then we return a missingOp whose trace is all possible candidates.
9697
if (match == null) {
97-
// unable to make a match, consider returning helpful error messages given the item.variants.
98-
return null
98+
val candidates = variants.map { fnSignature ->
99+
rexOpCallDynamicCandidate(
100+
fn = refFn(
101+
item.catalog,
102+
path = item.handle.path.steps,
103+
signature = fnSignature
104+
),
105+
coercions = emptyList()
106+
)
107+
}
108+
return ProblemGenerator.missingRex(
109+
rexOpCallDynamic(args, candidates, false),
110+
ProblemGenerator.incompatibleTypesForOp(args.map { it.type }, path.normalized.joinToString("."))
111+
)
99112
}
100113
return when (match) {
101114
is FnMatch.Dynamic -> {

partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlannerDefault.kt renamed to partiql-planner/src/main/kotlin/org/partiql/planner/internal/PartiQLPlannerDefault.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
package org.partiql.planner
1+
package org.partiql.planner.internal
22

33
import org.partiql.ast.Statement
44
import org.partiql.ast.normalize.normalize
55
import org.partiql.errors.ProblemCallback
6-
import org.partiql.planner.internal.Env
6+
import org.partiql.planner.PartiQLPlanner
7+
import org.partiql.planner.PartiQLPlannerPass
78
import org.partiql.planner.internal.transforms.AstToPlan
89
import org.partiql.planner.internal.transforms.PlanTransform
910
import org.partiql.planner.internal.typer.PlanTyper
@@ -13,6 +14,7 @@ import org.partiql.planner.internal.typer.PlanTyper
1314
*/
1415
internal class PartiQLPlannerDefault(
1516
private val passes: List<PartiQLPlannerPass>,
17+
private val flags: Set<PlannerFlag>
1618
) : PartiQLPlanner {
1719

1820
override fun plan(
@@ -31,12 +33,12 @@ internal class PartiQLPlannerDefault(
3133
val root = AstToPlan.apply(ast, env)
3234

3335
// 3. Resolve variables
34-
val typer = PlanTyper(env, onProblem)
36+
val typer = PlanTyper(env)
3537
val typed = typer.resolve(root)
3638
val internal = org.partiql.planner.internal.ir.PartiQLPlan(typed)
3739

3840
// 4. Assert plan has been resolved — translating to public API
39-
var plan = PlanTransform.transform(internal, onProblem)
41+
var plan = PlanTransform(flags).transform(internal, onProblem)
4042

4143
// 5. Apply all passes
4244
for (pass in passes) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.partiql.planner.internal
2+
3+
internal enum class PlannerFlag {
4+
/**
5+
* Determine the planner behavior upon encounter an operation that always returns MISSING.
6+
*
7+
* If this flag is included:
8+
*
9+
* The problematic operation will be tracked in problem callback as a error.
10+
*
11+
* The result plan will turn the problematic operation into an error node.
12+
*
13+
* Otherwise:
14+
*
15+
* The problematic operation will be tracked in problem callback as a missing.
16+
*
17+
* The result plan will turn the problematic operation into a missing node.
18+
*/
19+
SIGNAL_MODE
20+
}

partiql-planner/src/main/kotlin/org/partiql/planner/Errors.kt renamed to partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlanningProblemDetails.kt

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.partiql.planner
1+
package org.partiql.planner.internal
22

33
import org.partiql.errors.ProblemDetails
44
import org.partiql.errors.ProblemSeverity
@@ -13,21 +13,45 @@ import org.partiql.types.StaticType
1313
* This information can be used to generate end-user readable error messages and is also easy to assert
1414
* equivalence in unit tests.
1515
*/
16-
public sealed class PlanningProblemDetails(
16+
internal open class PlanningProblemDetails(
1717
override val severity: ProblemSeverity,
18-
public val messageFormatter: () -> String,
18+
val messageFormatter: () -> String,
1919
) : ProblemDetails {
2020

21+
companion object {
22+
private fun quotationHint(caseSensitive: Boolean) =
23+
if (caseSensitive) {
24+
// Individuals that are new to SQL often try to use double quotes for string literals.
25+
// Let's help them out a bit.
26+
" Hint: did you intend to use single-quotes (') here? Remember that double-quotes (\") denote " +
27+
"quoted identifiers and single-quotes denote strings."
28+
} else {
29+
""
30+
}
31+
32+
private fun Identifier.sql(): String = when (this) {
33+
is Identifier.Qualified -> this.sql()
34+
is Identifier.Symbol -> this.sql()
35+
}
36+
37+
private fun Identifier.Qualified.sql(): String = root.sql() + "." + steps.joinToString(".") { it.sql() }
38+
39+
private fun Identifier.Symbol.sql(): String = when (caseSensitivity) {
40+
Identifier.CaseSensitivity.SENSITIVE -> "\"$symbol\""
41+
Identifier.CaseSensitivity.INSENSITIVE -> symbol
42+
}
43+
}
44+
2145
override fun toString(): String = message
2246
override val message: String get() = messageFormatter()
2347

24-
public data class ParseError(val parseErrorMessage: String) :
48+
data class ParseError(val parseErrorMessage: String) :
2549
PlanningProblemDetails(ProblemSeverity.ERROR, { parseErrorMessage })
2650

27-
public data class CompileError(val errorMessage: String) :
51+
data class CompileError(val errorMessage: String) :
2852
PlanningProblemDetails(ProblemSeverity.ERROR, { errorMessage })
2953

30-
public data class UndefinedVariable(val id: BindingPath) :
54+
data class UndefinedVariable(val id: BindingPath) :
3155
PlanningProblemDetails(
3256
ProblemSeverity.ERROR,
3357
{
@@ -37,7 +61,7 @@ public sealed class PlanningProblemDetails(
3761
}
3862
)
3963

40-
public data class UndefinedDmlTarget(val variableName: String, val caseSensitive: Boolean) :
64+
data class UndefinedDmlTarget(val variableName: String, val caseSensitive: Boolean) :
4165
PlanningProblemDetails(
4266
ProblemSeverity.ERROR,
4367
{
@@ -47,25 +71,25 @@ public sealed class PlanningProblemDetails(
4771
}
4872
)
4973

50-
public data class VariablePreviouslyDefined(val variableName: String) :
74+
data class VariablePreviouslyDefined(val variableName: String) :
5175
PlanningProblemDetails(
5276
ProblemSeverity.ERROR,
5377
{ "The variable '$variableName' was previously defined." }
5478
)
5579

56-
public data class UnimplementedFeature(val featureName: String) :
80+
data class UnimplementedFeature(val featureName: String) :
5781
PlanningProblemDetails(
5882
ProblemSeverity.ERROR,
5983
{ "The syntax at this location is valid but utilizes unimplemented PartiQL feature '$featureName'" }
6084
)
6185

62-
public object InvalidDmlTarget :
86+
object InvalidDmlTarget :
6387
PlanningProblemDetails(
6488
ProblemSeverity.ERROR,
6589
{ "Expression is not a valid DML target. Hint: only table names are allowed here." }
6690
)
6791

68-
public object InsertValueDisallowed :
92+
object InsertValueDisallowed :
6993
PlanningProblemDetails(
7094
ProblemSeverity.ERROR,
7195
{
@@ -74,7 +98,7 @@ public sealed class PlanningProblemDetails(
7498
}
7599
)
76100

77-
public object InsertValuesDisallowed :
101+
object InsertValuesDisallowed :
78102
PlanningProblemDetails(
79103
ProblemSeverity.ERROR,
80104
{
@@ -83,27 +107,32 @@ public sealed class PlanningProblemDetails(
83107
}
84108
)
85109

86-
public data class UnexpectedType(
110+
data class UnexpectedType(
87111
val actualType: StaticType,
88112
val expectedTypes: Set<StaticType>,
89113
) : PlanningProblemDetails(ProblemSeverity.ERROR, {
90114
"Unexpected type $actualType, expected one of ${expectedTypes.joinToString()}"
91115
})
92116

93-
public data class UnknownFunction(
117+
data class UnknownFunction(
94118
val identifier: String,
95119
val args: List<StaticType>,
96120
) : PlanningProblemDetails(ProblemSeverity.ERROR, {
97121
val types = args.joinToString { "<${it.toString().lowercase()}>" }
98122
"Unknown function `$identifier($types)"
99123
})
100124

101-
public object ExpressionAlwaysReturnsNullOrMissing : PlanningProblemDetails(
125+
data class ExpressionAlwaysReturnsMissing(val reason: String? = null) : PlanningProblemDetails(
126+
severity = ProblemSeverity.ERROR,
127+
messageFormatter = { "Expression always returns null or missing: caused by $reason" }
128+
)
129+
130+
object ExpressionAlwaysReturnsNullOrMissing : PlanningProblemDetails(
102131
severity = ProblemSeverity.ERROR,
103132
messageFormatter = { "Expression always returns null or missing." }
104133
)
105134

106-
public data class InvalidArgumentTypeForFunction(
135+
data class InvalidArgumentTypeForFunction(
107136
val functionName: String,
108137
val expectedType: StaticType,
109138
val actualType: StaticType,
@@ -113,7 +142,7 @@ public sealed class PlanningProblemDetails(
113142
messageFormatter = { "Invalid argument type for $functionName. Expected $expectedType but got $actualType" }
114143
)
115144

116-
public data class IncompatibleTypesForOp(
145+
data class IncompatibleTypesForOp(
117146
val actualTypes: List<StaticType>,
118147
val operator: String,
119148
) :
@@ -122,31 +151,9 @@ public sealed class PlanningProblemDetails(
122151
messageFormatter = { "${actualTypes.joinToString()} is/are incompatible data types for the '$operator' operator." }
123152
)
124153

125-
public data class UnresolvedExcludeExprRoot(val root: String) :
154+
data class UnresolvedExcludeExprRoot(val root: String) :
126155
PlanningProblemDetails(
127156
ProblemSeverity.ERROR,
128157
{ "Exclude expression given an unresolvable root '$root'" }
129158
)
130159
}
131-
132-
private fun quotationHint(caseSensitive: Boolean) =
133-
if (caseSensitive) {
134-
// Individuals that are new to SQL often try to use double quotes for string literals.
135-
// Let's help them out a bit.
136-
" Hint: did you intend to use single-quotes (') here? Remember that double-quotes (\") denote " +
137-
"quoted identifiers and single-quotes denote strings."
138-
} else {
139-
""
140-
}
141-
142-
private fun Identifier.sql(): String = when (this) {
143-
is Identifier.Qualified -> this.sql()
144-
is Identifier.Symbol -> this.sql()
145-
}
146-
147-
private fun Identifier.Qualified.sql(): String = root.sql() + "." + steps.joinToString(".") { it.sql() }
148-
149-
private fun Identifier.Symbol.sql(): String = when (caseSensitivity) {
150-
Identifier.CaseSensitivity.SENSITIVE -> "\"$symbol\""
151-
Identifier.CaseSensitivity.INSENSITIVE -> symbol
152-
}

0 commit comments

Comments
 (0)