diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/normalize/NormalizeFromSource.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/normalize/NormalizeFromSource.kt index 081321f46d..8c12807ef6 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/normalize/NormalizeFromSource.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/normalize/NormalizeFromSource.kt @@ -52,9 +52,19 @@ internal object NormalizeFromSource : AstPass { override fun visitFromValue(node: From.Value, ctx: Int): From { val expr = visitExpr(node.expr, ctx) as Expr - val asAlias = node.asAlias ?: expr.toBinder(ctx) - return if (expr !== node.expr || asAlias !== node.asAlias) { - node.copy(expr = expr, asAlias = asAlias) + var i = ctx + var asAlias = node.asAlias + var atAlias = node.atAlias + // derive AS alias + if (asAlias == null) { + asAlias = expr.toBinder(i++) + } + // derive AT binder + if (atAlias == null && node.type == From.Value.Type.UNPIVOT) { + atAlias = expr.toBinder(i++) + } + return if (expr !== node.expr || asAlias !== node.asAlias || atAlias !== node.atAlias) { + node.copy(expr = expr, asAlias = asAlias, atAlias = atAlias) } else { node } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt index ad535339bf..98d5dc2eab 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt @@ -159,17 +159,13 @@ internal class PlanTyper(private val env: Env) { * TODO handle NULL|STRUCT type */ override fun visitRelOpUnpivot(node: Rel.Op.Unpivot, ctx: Rel.Type?): Rel { - // descend, with GLOBAL resolution strategy val rex = node.rex.type(emptyList(), outer, Scope.GLOBAL) - // key type, always a string. val kType = STRING - - // value type, possibly coerced. - val vType = rex.type.allTypes.map { type -> + val vTypes = rex.type.allTypes.map { type -> when (type) { is StructType -> { - if (type.contentClosed || type.constraints.contains(TupleConstraint.Open(false))) { + if ((type.contentClosed || type.constraints.contains(TupleConstraint.Open(false))) && type.fields.isNotEmpty()) { unionOf(type.fields.map { it.value }.toSet()).flatten() } else { ANY @@ -177,9 +173,8 @@ internal class PlanTyper(private val env: Env) { } else -> type } - }.let { - unionOf(it.toSet()).flatten() } + val vType = unionOf(vTypes.toSet()).flatten() // rewrite val type = ctx!!.copyWithSchema(listOf(kType, vType)) diff --git a/test/partiql-tests-runner/build.gradle.kts b/test/partiql-tests-runner/build.gradle.kts index 9fa06449da..6f9cb78b67 100644 --- a/test/partiql-tests-runner/build.gradle.kts +++ b/test/partiql-tests-runner/build.gradle.kts @@ -64,8 +64,8 @@ val generateTestReport by tasks.registering(Test::class) { environment(Env.PARTIQL_EQUIV, file("$tests/eval-equiv/").absolutePath) environment("conformanceReportDir", reportDir) include("org/partiql/runner/ConformanceTestEval.class", "org/partiql/runner/ConformanceTestLegacy.class") - if (project.hasProperty("Engine")) { - val engine = property("Engine")!! as String + if (project.hasProperty("engine")) { + val engine = project.property("engine")!! as String if (engine.toLowerCase() == "legacy") { exclude("org/partiql/runner/ConformanceTestEval.class") } else if (engine.toLowerCase() == "eval") {