From ad9521da2d9a2a01f1851bba0703f52908985404 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Wed, 5 Jul 2023 09:21:59 -0700 Subject: [PATCH 1/3] [Sprout] Fixes rewriter for null valued collections (#1143) --- .../target/kotlin/poems/KotlinUtilsPoem.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/sprout/src/main/kotlin/org/partiql/sprout/generator/target/kotlin/poems/KotlinUtilsPoem.kt b/lib/sprout/src/main/kotlin/org/partiql/sprout/generator/target/kotlin/poems/KotlinUtilsPoem.kt index f0a8f0ebd3..9a5cf15e54 100644 --- a/lib/sprout/src/main/kotlin/org/partiql/sprout/generator/target/kotlin/poems/KotlinUtilsPoem.kt +++ b/lib/sprout/src/main/kotlin/org/partiql/sprout/generator/target/kotlin/poems/KotlinUtilsPoem.kt @@ -39,6 +39,7 @@ class KotlinUtilsPoem(symbols: KotlinSymbols) : KotlinPoem(symbols) { private val suppressUnused = AnnotationSpec.builder(Suppress::class) .useSiteTarget(AnnotationSpec.UseSiteTarget.FILE) .addMember("%S", "UNUSED_PARAMETER") + .addMember("%S", "UNUSED_VARIABLE") .build() // Not taking a dep on builder or visitor poems, as this is temporary @@ -107,8 +108,8 @@ class KotlinUtilsPoem(symbols: KotlinSymbols) : KotlinPoem(symbols) { // Node N -> visitN(node.n, ctx) as N // Node N? -> node.n?.let { visitN(it, ctx) as N } // Collection -> rewrite(node, ctx, ::method) - // Collection? -> node.n?.let { rewrite(it, ctx, ::method) } - // Collection? -> node.n?.let { rewrite(it, ctx, ::method) } + // Collection? -> node.n?.let { rewrite(it, ctx, ::method) } + // Collection? -> node.n?.let { rewrite(it, ctx, ::method) } // Collection -> rewrite(node, ctx, ::method) private fun KotlinNodeSpec.Product.rewriter(): FunSpec { @@ -139,17 +140,25 @@ class KotlinUtilsPoem(symbols: KotlinSymbols) : KotlinPoem(symbols) { (ref is TypeRef.List && isNode(ref.type)) -> { // Collections val method = (ref.type as TypeRef.Path).visitMethodName() + val helper = when (ref.type.nullable) { + true -> "_visitListNull" + else -> "_visitList" + } when (ref.nullable) { - true -> addStatement("val $name = $child?.let { _visitListNull(it, ctx, ::$method) }") - false -> addStatement("val $name = _visitList($child, ctx, ::$method)") + true -> addStatement("val $name = $child?.let { $helper(it, ctx, ::$method) }") + false -> addStatement("val $name = $helper($child, ctx, ::$method)") } } (ref is TypeRef.Set && isNode(ref.type)) -> { // Collections val method = (ref.type as TypeRef.Path).visitMethodName() + val helper = when (ref.type.nullable) { + true -> "_visitSetNull" + else -> "_visitSet" + } when (ref.nullable) { - true -> addStatement("val $name = $child?.let { _visitSetNull(it, ctx, ::$method) }") - false -> addStatement("val $name = _visitSet($child, ctx, ::$method)") + true -> addStatement("val $name = $child?.let { $helper(it, ctx, ::$method) }") + false -> addStatement("val $name = $helper($child, ctx, ::$method)") } } else -> { From 8a3c3de3f8d7998a8885ae1fb6bdb0f9e27b58bb Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Tue, 11 Jul 2023 14:28:42 -0700 Subject: [PATCH 2/3] Fixes Nullable PartiQLCollectionValue (#1148) --- .../kotlin/org/partiql/value/PartiQLValue.kt | 18 ++++--- .../value/impl/PartiQLValueNullableImpl.kt | 53 ++++--------------- .../value/io/PartiQLValueTextWriter.kt | 28 +++++----- .../value/util/PartiQLValueBaseVisitor.kt | 8 ++- .../value/io/PartiQLValueTextWriterTest.kt | 21 ++++++++ 5 files changed, 60 insertions(+), 68 deletions(-) diff --git a/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt b/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt index 81383915c6..c5d5e23e19 100644 --- a/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt +++ b/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt @@ -465,11 +465,10 @@ public sealed interface NullableScalarValue : PartiQLValue { } @PartiQLValueExperimental -public sealed interface NullableCollectionValue : PartiQLValue, Collection { +public sealed interface NullableCollectionValue : PartiQLValue { + public fun isNull(): Boolean - public override val size: Int - - public val elements: Collection? + public fun promote(): CollectionValue override fun copy(annotations: Annotations): NullableCollectionValue @@ -787,6 +786,8 @@ public abstract class NullableBagValue : NullableCollectionVal override val type: PartiQLValueType = PartiQLValueType.NULLABLE_BAG + abstract override fun promote(): BagValue + abstract override fun copy(annotations: Annotations): NullableBagValue abstract override fun withAnnotations(annotations: Annotations): NullableBagValue @@ -799,6 +800,8 @@ public abstract class NullableListValue : NullableCollectionVa override val type: PartiQLValueType = PartiQLValueType.NULLABLE_LIST + abstract override fun promote(): ListValue + abstract override fun copy(annotations: Annotations): NullableListValue abstract override fun withAnnotations(annotations: Annotations): NullableListValue @@ -811,6 +814,8 @@ public abstract class NullableSexpValue : NullableCollectionVa override val type: PartiQLValueType = PartiQLValueType.NULLABLE_SEXP + abstract override fun promote(): SexpValue + abstract override fun copy(annotations: Annotations): NullableSexpValue abstract override fun withAnnotations(annotations: Annotations): NullableSexpValue @@ -819,9 +824,10 @@ public abstract class NullableSexpValue : NullableCollectionVa } @PartiQLValueExperimental -public abstract class NullableStructValue : PartiQLValue, Collection> { +public abstract class NullableStructValue : PartiQLValue { + public abstract fun isNull(): Boolean - public abstract val fields: List>? + public abstract fun promote(): StructValue override val type: PartiQLValueType = PartiQLValueType.NULLABLE_STRUCT diff --git a/partiql-types/src/main/kotlin/org/partiql/value/impl/PartiQLValueNullableImpl.kt b/partiql-types/src/main/kotlin/org/partiql/value/impl/PartiQLValueNullableImpl.kt index d905af3514..d3c9af6601 100644 --- a/partiql-types/src/main/kotlin/org/partiql/value/impl/PartiQLValueNullableImpl.kt +++ b/partiql-types/src/main/kotlin/org/partiql/value/impl/PartiQLValueNullableImpl.kt @@ -17,9 +17,9 @@ package org.partiql.value.impl import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentList import org.partiql.value.Annotations +import org.partiql.value.BagValue import org.partiql.value.MissingValue import org.partiql.value.NullValue import org.partiql.value.NullableBagValue @@ -398,18 +398,9 @@ internal data class NullableBagValueImpl( private val delegate: PersistentList?, override val annotations: PersistentList, ) : NullableBagValue() { + override fun isNull(): Boolean = delegate == null - override fun contains(element: T) = delegate!!.contains(element) - - override fun containsAll(elements: Collection) = delegate!!.containsAll(elements) - - override fun isEmpty() = delegate!!.isEmpty() - - override fun iterator() = delegate!!.iterator() - - override val size = delegate!!.size - - override val elements = delegate!!.toImmutableList() + override fun promote(): BagValue = BagValueImpl(delegate!!, annotations) override fun copy(annotations: Annotations) = NullableBagValueImpl(delegate, annotations.toPersistentList()) @@ -424,18 +415,9 @@ internal data class NullableListValueImpl( private val delegate: PersistentList?, override val annotations: PersistentList, ) : NullableListValue() { + override fun isNull() = delegate == null - override fun contains(element: T) = delegate!!.contains(element) - - override fun containsAll(elements: Collection) = delegate!!.containsAll(elements) - - override fun isEmpty() = delegate!!.isEmpty() - - override fun iterator() = delegate!!.iterator() - - override val size = delegate!!.size - - override val elements = delegate!!.toImmutableList() + override fun promote() = ListValueImpl(delegate!!, annotations) override fun copy(annotations: Annotations) = NullableListValueImpl(delegate, annotations.toPersistentList()) @@ -451,17 +433,9 @@ internal data class NullableSexpValueImpl( override val annotations: PersistentList, ) : NullableSexpValue() { - override fun contains(element: T) = delegate!!.contains(element) + override fun isNull(): Boolean = delegate == null - override fun containsAll(elements: Collection) = delegate!!.containsAll(elements) - - override fun isEmpty() = delegate!!.isEmpty() - - override fun iterator() = delegate!!.iterator() - - override val size = delegate!!.size - - override val elements = delegate!!.toImmutableList() + override fun promote() = SexpValueImpl(delegate!!, annotations) override fun copy(annotations: Annotations) = NullableSexpValueImpl(delegate, annotations.toPersistentList()) @@ -476,18 +450,9 @@ internal data class NullableStructValueImpl( private val values: PersistentList>?, override val annotations: PersistentList, ) : NullableStructValue() { + override fun isNull(): Boolean = values == null - override val fields = values!!.toImmutableList() - - override val size = values!!.size - - override fun isEmpty() = values!!.isEmpty() - - override fun iterator(): Iterator> = values!!.iterator() - - override fun containsAll(elements: Collection>) = values!!.containsAll(elements) - - override fun contains(element: Pair) = values!!.contains(element) + override fun promote() = StructValueImpl(values!!, annotations) override fun copy(annotations: Annotations) = NullableStructValueImpl(values, annotations.toPersistentList()) diff --git a/partiql-types/src/main/kotlin/org/partiql/value/io/PartiQLValueTextWriter.kt b/partiql-types/src/main/kotlin/org/partiql/value/io/PartiQLValueTextWriter.kt index 802bf9a6ea..0457c78c76 100644 --- a/partiql-types/src/main/kotlin/org/partiql/value/io/PartiQLValueTextWriter.kt +++ b/partiql-types/src/main/kotlin/org/partiql/value/io/PartiQLValueTextWriter.kt @@ -51,7 +51,6 @@ import org.partiql.value.SexpValue import org.partiql.value.StringValue import org.partiql.value.StructValue import org.partiql.value.SymbolValue -import org.partiql.value.bagValue import org.partiql.value.boolValue import org.partiql.value.charValue import org.partiql.value.decimalValue @@ -62,10 +61,7 @@ import org.partiql.value.int32Value import org.partiql.value.int64Value import org.partiql.value.int8Value import org.partiql.value.intValue -import org.partiql.value.listValue -import org.partiql.value.sexpValue import org.partiql.value.stringValue -import org.partiql.value.structValue import org.partiql.value.symbolValue import org.partiql.value.util.PartiQLValueBaseVisitor import java.io.PrintStream @@ -295,24 +291,24 @@ internal class PartiQLValueTextWriter( else -> visitSymbol(symbolValue(v.value!!, v.annotations), ctx) } - override fun visitNullableBag(v: NullableBagValue<*>, ctx: Format?) = when (v.elements) { - null -> "null" - else -> visitBag(bagValue(v.elements!!.toList(), v.annotations), ctx) + override fun visitNullableBag(v: NullableBagValue<*>, ctx: Format?) = when (v.isNull()) { + true -> "null" + else -> visitBag(v.promote(), ctx) } - override fun visitNullableList(v: NullableListValue<*>, ctx: Format?) = when (v.elements) { - null -> "null" - else -> visitList(listValue(v.elements!!.toList(), v.annotations), ctx) + override fun visitNullableList(v: NullableListValue<*>, ctx: Format?) = when (v.isNull()) { + true -> "null" + else -> visitList(v.promote(), ctx) } - override fun visitNullableSexp(v: NullableSexpValue<*>, ctx: Format?) = when (v.elements) { - null -> "null" - else -> visitSexp(sexpValue(v.elements!!.toList(), v.annotations), ctx) + override fun visitNullableSexp(v: NullableSexpValue<*>, ctx: Format?) = when (v.isNull()) { + true -> "null" + else -> visitSexp(v.promote(), ctx) } - override fun visitNullableStruct(v: NullableStructValue<*>, ctx: Format?) = when (v.fields) { - null -> "null" - else -> visitStruct(structValue(v.fields!!.toList(), v.annotations), ctx) + override fun visitNullableStruct(v: NullableStructValue<*>, ctx: Format?) = when (v.isNull()) { + true -> "null" + else -> visitStruct(v.promote(), ctx) } } } diff --git a/partiql-types/src/main/kotlin/org/partiql/value/util/PartiQLValueBaseVisitor.kt b/partiql-types/src/main/kotlin/org/partiql/value/util/PartiQLValueBaseVisitor.kt index b3459c0737..f6fdab837f 100644 --- a/partiql-types/src/main/kotlin/org/partiql/value/util/PartiQLValueBaseVisitor.kt +++ b/partiql-types/src/main/kotlin/org/partiql/value/util/PartiQLValueBaseVisitor.kt @@ -87,10 +87,14 @@ public abstract class PartiQLValueBaseVisitor : PartiQLValueVisitor v.fields.forEach { it.second.accept(this, ctx) } } is NullableCollectionValue<*> -> { - v.elements?.forEach { it.accept(this, ctx) } + if (!v.isNull()) { + v.promote().elements.forEach { it.accept(this, ctx) } + } } is NullableStructValue<*> -> { - v.fields?.forEach { it.second.accept(this, ctx) } + if (!v.isNull()) { + v.promote().fields.forEach { it.second.accept(this, ctx) } + } } else -> {} } diff --git a/partiql-types/src/test/kotlin/org/partiql/value/io/PartiQLValueTextWriterTest.kt b/partiql-types/src/test/kotlin/org/partiql/value/io/PartiQLValueTextWriterTest.kt index 13404fd72d..3b54305e79 100644 --- a/partiql-types/src/test/kotlin/org/partiql/value/io/PartiQLValueTextWriterTest.kt +++ b/partiql-types/src/test/kotlin/org/partiql/value/io/PartiQLValueTextWriterTest.kt @@ -24,6 +24,7 @@ import org.partiql.value.intValue import org.partiql.value.listValue import org.partiql.value.missingValue import org.partiql.value.nullValue +import org.partiql.value.nullableBagValue import org.partiql.value.nullableBoolValue import org.partiql.value.nullableCharValue import org.partiql.value.nullableDecimalValue @@ -34,7 +35,10 @@ import org.partiql.value.nullableInt32Value import org.partiql.value.nullableInt64Value import org.partiql.value.nullableInt8Value import org.partiql.value.nullableIntValue +import org.partiql.value.nullableListValue +import org.partiql.value.nullableSexpValue import org.partiql.value.nullableStringValue +import org.partiql.value.nullableStructValue import org.partiql.value.nullableSymbolValue import org.partiql.value.sexpValue import org.partiql.value.stringValue @@ -375,6 +379,23 @@ class PartiQLValueTextWriterTest { ), expected = "(1 2 3)", ), + // nullable collections + case( + value = nullableBagValue(null), + expected = "null", + ), + case( + value = nullableListValue(null), + expected = "null", + ), + case( + value = nullableSexpValue(null), + expected = "null", + ), + case( + value = nullableStructValue(null), + expected = "null", + ), ) @JvmStatic From 44e8a8d10738577fe0c54e300202b067037a9040 Mon Sep 17 00:00:00 2001 From: yliuuuu <107505258+yliuuuu@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:21:15 -0700 Subject: [PATCH 3/3] Timestamp Data Model (#1121) * Date time data model --- CHANGELOG.md | 3 + partiql-ast/src/main/pig/partiql.ion | 19 +- .../org/partiql/lang/errors/ErrorCode.kt | 13 + .../partiql/lang/eval/EvaluatingCompiler.kt | 1 + .../eval/physical/PhysicalPlanCompilerImpl.kt | 1 + .../SubqueryCoercionVisitorTransform.kt | 1 + .../lang/prettyprint/ASTPrettyPrinter.kt | 1 + .../lang/prettyprint/QueryPrettyPrinter.kt | 1 + .../lang/syntax/impl/PartiQLPigVisitor.kt | 548 +++-- .../partiql/lang/syntax/util/DateTimeUtils.kt | 76 + .../types/PartiqlPhysicalTypeExtensions.kt | 2 + .../lang/syntax/PartiQLParserDateTimeTests.kt | 1871 +++++++++++------ partiql-parser/src/main/antlr/PartiQL.g4 | 5 +- .../main/kotlin/org/partiql/value/PartiQL.kt | 66 +- .../kotlin/org/partiql/value/PartiQLValue.kt | 55 +- .../org/partiql/value/datetime/DateTime.kt | 560 +++++ .../value/datetime/DateTimeComparator.kt | 84 + .../value/datetime/DateTimeException.kt | 23 + .../datetime/DateTimePrecisionChanger.kt | 109 + .../partiql/value/datetime/DateTimeUtil.kt | 52 + .../partiql/value/datetime/DateTimeValue.kt | 155 ++ .../org/partiql/value/datetime/TimeZone.kt | 64 + .../datetime/impl/LocalTimeHighPrecision.kt | 70 + .../datetime/impl/LocalTimeLowPrecision.kt | 88 + .../impl/LocalTimestampHighPrecision.kt | 113 + .../impl/LocalTimestampLowPrecision.kt | 96 + .../datetime/impl/OffsetTimeHighPrecision.kt | 89 + .../datetime/impl/OffsetTimeLowPrecision.kt | 115 + .../impl/OffsetTimestampHighPrecision.kt | 217 ++ .../impl/OffsetTimestampLowPrecision.kt | 212 ++ .../partiql/value/datetime/impl/SqlDate.kt | 70 + .../partiql/value/impl/PartiQLValueImpl.kt | 23 +- .../value/impl/PartiQLValueNullableImpl.kt | 23 +- .../org/partiql/datetime/TimestampTest.kt | 915 ++++++++ 34 files changed, 4838 insertions(+), 903 deletions(-) create mode 100644 partiql-lang/src/main/kotlin/org/partiql/lang/syntax/util/DateTimeUtils.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/DateTime.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/DateTimeComparator.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/DateTimeException.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/DateTimePrecisionChanger.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/DateTimeUtil.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/DateTimeValue.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/TimeZone.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/LocalTimeHighPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/LocalTimeLowPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/LocalTimestampHighPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/LocalTimestampLowPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/OffsetTimeHighPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/OffsetTimeLowPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/OffsetTimestampHighPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/OffsetTimestampLowPrecision.kt create mode 100644 partiql-types/src/main/kotlin/org/partiql/value/datetime/impl/SqlDate.kt create mode 100644 partiql-types/src/test/kotlin/org/partiql/datetime/TimestampTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 1172bba0a1..bed6f1c485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ Thank you to all who have contributed! ### Added - Adds `org.partiql.value` (experimental) package for reading/writing PartiQL values +- Adds PartiQL's Timestamp Data Model. +- Adds support for Timestamp constructor call in Parser. ### Changed @@ -45,6 +47,7 @@ Thank you to all who have contributed! ### Contributors Thank you to all who have contributed! - @howero +- @yliuuuu - @ ## [0.12.0] - 2023-06-14 diff --git a/partiql-ast/src/main/pig/partiql.ion b/partiql-ast/src/main/pig/partiql.ion index 10a3803a61..8734632406 100644 --- a/partiql-ast/src/main/pig/partiql.ion +++ b/partiql-ast/src/main/pig/partiql.ion @@ -140,6 +140,7 @@ may then be further optimized by selecting better implementations of each operat // Constructors for DateTime types (date year::int month::int day::int) (lit_time value::time_value) + (timestamp value::timestamp_value) // Bag operators (bag_op op::bag_op_type quantifier::set_quantifier operands::(* expr 2)) @@ -178,6 +179,19 @@ may then be further optimized by selecting better implementations of each operat // Time (product time_value hour::int minute::int second::int nano::int precision::int with_time_zone::bool tz_minutes::(? int)) + // Timestamp + (product timestamp_value year::int month::int day::int hour::int minute::int second::ion timezone::(? timezone) precision::(? int) ) + + (sum timezone + // Unknown time zone -00:00 + (unknown_timezone) + // UTC offset ex: -01:30 -> tz_hour = -1, tz_minutes = -30, total offset minutes = -90 + // tz_hour is in [-23, 23], timezone minutes is in [-59,59] + // we only need total offset minutes to model this property + (utc_offset offset_minutes::int) + // TODO: Timezone ID not support yet. Ex: US/Pacific + ) + // A "step" within a path expression; that is the components of the expression following the root. (sum path_step // `someRoot[]`, or `someRoot.someField` which is equivalent to `someRoot['someField']`. @@ -535,9 +549,6 @@ may then be further optimized by selecting better implementations of each operat // `NUMERIC[( [, int])]`. Elements are precision then scale. (numeric_type precision::(? int) scale::(? int)) - // `TIMESTAMP` - (timestamp_type) - // `CHAR()` (character_type length::(? int)) @@ -557,6 +568,8 @@ may then be further optimized by selecting better implementations of each operat // Note: This logic is implemented in SqlParser. (time_type precision::(? int)) (time_with_time_zone_type precision::(? int)) + (timestamp_type precision::(? int)) + (timestamp_with_time_zone_type precision::(? int)) (struct_type) (tuple_type) diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/errors/ErrorCode.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/errors/ErrorCode.kt index 3d043897ce..91a1e6720e 100644 --- a/partiql-lang/src/main/kotlin/org/partiql/lang/errors/ErrorCode.kt +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/errors/ErrorCode.kt @@ -143,6 +143,13 @@ enum class ErrorCode( "invalid precision used for TIME type" ), + // TODO: We should combine this with the above + PARSE_INVALID_PRECISION_FOR_TIMESTAMP( + ErrorCategory.PARSER, + LOC_TOKEN, + "invalid precision used for TIMESTAMP type" + ), + PARSE_INVALID_DATE_STRING( ErrorCategory.PARSER, LOC_TOKEN, @@ -155,6 +162,12 @@ enum class ErrorCode( "expected time string to be of the format HH:MM:SS[.dddd...][+|-HH:MM]" ), + PARSE_INVALID_DATETIME_STRING( + ErrorCategory.PARSER, + LOC_TOKEN, + "Invalid timestamp string" + ), + PARSE_INVALID_TRIM_SPEC( ErrorCategory.PARSER, LOC_TOKEN, diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/eval/EvaluatingCompiler.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/eval/EvaluatingCompiler.kt index dd495f1e71..ca336d85ad 100644 --- a/partiql-lang/src/main/kotlin/org/partiql/lang/eval/EvaluatingCompiler.kt +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/eval/EvaluatingCompiler.kt @@ -464,6 +464,7 @@ internal class EvaluatingCompiler( is PartiqlAst.Expr.GraphMatch -> compileGraphMatch(expr, metas) is PartiqlAst.Expr.CallWindow -> TODO("Evaluating Compiler doesn't support window function") + is PartiqlAst.Expr.Timestamp -> TODO() } } diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/eval/physical/PhysicalPlanCompilerImpl.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/eval/physical/PhysicalPlanCompilerImpl.kt index 0db694dd14..32eedd435e 100644 --- a/partiql-lang/src/main/kotlin/org/partiql/lang/eval/physical/PhysicalPlanCompilerImpl.kt +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/eval/physical/PhysicalPlanCompilerImpl.kt @@ -279,6 +279,7 @@ internal class PhysicalPlanCompilerImpl( is PartiqlPhysical.Expr.BindingsToValues -> compileBindingsToValues(expr) is PartiqlPhysical.Expr.Pivot -> compilePivot(expr, metas) is PartiqlPhysical.Expr.GraphMatch -> TODO("Physical compilation of GraphMatch expression") + is PartiqlPhysical.Expr.Timestamp -> TODO() } } diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/eval/visitors/SubqueryCoercionVisitorTransform.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/eval/visitors/SubqueryCoercionVisitorTransform.kt index beb8fa1d14..969f5da980 100644 --- a/partiql-lang/src/main/kotlin/org/partiql/lang/eval/visitors/SubqueryCoercionVisitorTransform.kt +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/eval/visitors/SubqueryCoercionVisitorTransform.kt @@ -101,6 +101,7 @@ class SubqueryCoercionVisitorTransform : VisitorTransformBase() { is PartiqlAst.Expr.CanLosslessCast -> n is PartiqlAst.Expr.NullIf -> n is PartiqlAst.Expr.Coalesce -> n + is PartiqlAst.Expr.Timestamp -> TODO() } } diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/ASTPrettyPrinter.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/ASTPrettyPrinter.kt index c2276105b5..87657084aa 100644 --- a/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/ASTPrettyPrinter.kt +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/ASTPrettyPrinter.kt @@ -577,6 +577,7 @@ class ASTPrettyPrinter { ) is PartiqlAst.Expr.GraphMatch -> TODO("Unsupported GraphMatch AST node") is PartiqlAst.Expr.CallWindow -> TODO("PrettyPrinter doesn't support Window Function yet.") + is PartiqlAst.Expr.Timestamp -> TODO() } private fun toRecursionTreeList(nodes: List, attrOfParent: String? = null): List = diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/QueryPrettyPrinter.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/QueryPrettyPrinter.kt index e66024154e..788954e78a 100644 --- a/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/QueryPrettyPrinter.kt +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/prettyprint/QueryPrettyPrinter.kt @@ -328,6 +328,7 @@ class QueryPrettyPrinter { is PartiqlAst.Expr.CallWindow -> TODO() is PartiqlAst.Expr.GraphMatch -> TODO() is PartiqlAst.Expr.SessionAttribute -> writeSessionAttribute(node, sb) + is PartiqlAst.Expr.Timestamp -> TODO() } } diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/syntax/impl/PartiQLPigVisitor.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/syntax/impl/PartiQLPigVisitor.kt index a505a8bc4a..ac74588c37 100644 --- a/partiql-lang/src/main/kotlin/org/partiql/lang/syntax/impl/PartiQLPigVisitor.kt +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/syntax/impl/PartiQLPigVisitor.kt @@ -54,6 +54,7 @@ import org.partiql.lang.errors.PropertyValueMap import org.partiql.lang.eval.EvaluationException import org.partiql.lang.eval.time.MAX_PRECISION_FOR_TIME import org.partiql.lang.syntax.ParserException +import org.partiql.lang.syntax.util.DateTimeUtils import org.partiql.lang.types.CustomType import org.partiql.lang.util.DATE_PATTERN_REGEX import org.partiql.lang.util.bigDecimalOf @@ -64,6 +65,8 @@ import org.partiql.lang.util.unaryMinus import org.partiql.parser.antlr.PartiQLBaseVisitor import org.partiql.parser.antlr.PartiQLParser import org.partiql.pig.runtime.SymbolPrimitive +import org.partiql.value.datetime.DateTimeException +import org.partiql.value.datetime.TimeZone import java.math.BigInteger import java.time.LocalDate import java.time.LocalTime @@ -77,7 +80,10 @@ import kotlin.reflect.cast * Extends ANTLR's generated [PartiQLBaseVisitor] to visit an ANTLR ParseTree and convert it into a PartiQL AST. This * class uses the [PartiqlAst.PartiqlAstNode] to represent all nodes within the new AST. */ -internal class PartiQLPigVisitor(val customTypes: List = listOf(), private val parameterIndexes: Map = mapOf()) : +internal class PartiQLPigVisitor( + val customTypes: List = listOf(), + private val parameterIndexes: Map = mapOf() +) : PartiQLBaseVisitor() { companion object { @@ -124,8 +130,13 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p throw option.param.error("Unknown EXPLAIN parameter.", ErrorCode.PARSE_UNEXPECTED_TOKEN, cause = ex) } when (parameter) { - ExplainParameters.TYPE -> { type = parameter.getCompliantString(type, option.value) } - ExplainParameters.FORMAT -> { format = parameter.getCompliantString(format, option.value) } + ExplainParameters.TYPE -> { + type = parameter.getCompliantString(type, option.value) + } + + ExplainParameters.FORMAT -> { + format = parameter.getCompliantString(format, option.value) + } } } explain( @@ -155,7 +166,13 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p override fun visitSymbolPrimitive(ctx: PartiQLParser.SymbolPrimitiveContext) = PartiqlAst.build { val metas = ctx.ident.getSourceMetaContainer() when (ctx.ident.type) { - PartiQLParser.IDENTIFIER_QUOTED -> id(ctx.IDENTIFIER_QUOTED().getStringValue(), caseSensitive(), unqualified(), metas) + PartiQLParser.IDENTIFIER_QUOTED -> id( + ctx.IDENTIFIER_QUOTED().getStringValue(), + caseSensitive(), + unqualified(), + metas + ) + PartiQLParser.IDENTIFIER -> id(ctx.IDENTIFIER().getStringValue(), caseInsensitive(), unqualified(), metas) else -> throw ParserException("Invalid symbol reference.", ErrorCode.PARSE_INVALID_QUERY) } @@ -280,7 +297,13 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p val from = visit(ctx.fromClauseSimple(), PartiqlAst.FromSource::class) val where = visitOrNull(ctx.whereClause(), PartiqlAst.Expr::class) val returning = visitOrNull(ctx.returningClause(), PartiqlAst.ReturningExpr::class) - dml(dmlOpList(delete(ctx.DELETE().getSourceMetaContainer()), metas = ctx.DELETE().getSourceMetaContainer()), from, where, returning, ctx.DELETE().getSourceMetaContainer()) + dml( + dmlOpList(delete(ctx.DELETE().getSourceMetaContainer()), metas = ctx.DELETE().getSourceMetaContainer()), + from, + where, + returning, + ctx.DELETE().getSourceMetaContainer() + ) } override fun visitInsertStatementLegacy(ctx: PartiQLParser.InsertStatementLegacyContext) = PartiqlAst.build { @@ -331,7 +354,13 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p val returning = visitOrNull(ctx.returningClause(), PartiqlAst.ReturningExpr::class) dml( dmlOpList( - insertValue(target, visit(ctx.value, PartiqlAst.Expr::class), index = index, onConflict = onConflictLegacy, ctx.INSERT().getSourceMetaContainer()), + insertValue( + target, + visit(ctx.value, PartiqlAst.Expr::class), + index = index, + onConflict = onConflictLegacy, + ctx.INSERT().getSourceMetaContainer() + ), metas = metas ), returning = returning, @@ -367,7 +396,11 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p } override fun visitOnConflictLegacy(ctx: PartiQLParser.OnConflictLegacyContext) = PartiqlAst.build { - onConflict(expr = visitExpr(ctx.expr()), conflictAction = doNothing(), metas = ctx.ON().getSourceMetaContainer()) + onConflict( + expr = visitExpr(ctx.expr()), + conflictAction = doNothing(), + metas = ctx.ON().getSourceMetaContainer() + ) } override fun visitConflictAction(ctx: PartiQLParser.ConflictActionContext) = PartiqlAst.build { @@ -412,7 +445,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p pathExpr(visitSymbolPrimitive(ctx.symbolPrimitive()), caseSensitive()) } - override fun visitPathSimpleDotSymbol(ctx: PartiQLParser.PathSimpleDotSymbolContext) = getSymbolPathExpr(ctx.symbolPrimitive()) + override fun visitPathSimpleDotSymbol(ctx: PartiQLParser.PathSimpleDotSymbolContext) = + getSymbolPathExpr(ctx.symbolPrimitive()) override fun visitSetCommand(ctx: PartiQLParser.SetCommandContext) = PartiqlAst.build { val assignments = visitOrEmpty(ctx.setAssignment(), PartiqlAst.DmlOp.Set::class) @@ -424,7 +458,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p set(assignment(visitPathSimple(ctx.pathSimple()), visitExpr(ctx.expr()))) } - override fun visitUpdateClause(ctx: PartiQLParser.UpdateClauseContext) = visit(ctx.tableBaseReference(), PartiqlAst.FromSource::class) + override fun visitUpdateClause(ctx: PartiQLParser.UpdateClauseContext) = + visit(ctx.tableBaseReference(), PartiqlAst.FromSource::class) /** * @@ -472,18 +507,20 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p * */ - override fun visitSetQuantifierStrategy(ctx: PartiQLParser.SetQuantifierStrategyContext?): PartiqlAst.SetQuantifier? = when { - ctx == null -> null - ctx.DISTINCT() != null -> PartiqlAst.SetQuantifier.Distinct() - ctx.ALL() != null -> PartiqlAst.SetQuantifier.All() - else -> null - } + override fun visitSetQuantifierStrategy(ctx: PartiQLParser.SetQuantifierStrategyContext?): PartiqlAst.SetQuantifier? = + when { + ctx == null -> null + ctx.DISTINCT() != null -> PartiqlAst.SetQuantifier.Distinct() + ctx.ALL() != null -> PartiqlAst.SetQuantifier.All() + else -> null + } override fun visitSelectAll(ctx: PartiQLParser.SelectAllContext) = PartiqlAst.build { projectStar(ctx.ASTERISK().getSourceMetaContainer()) } - override fun visitSelectItems(ctx: PartiQLParser.SelectItemsContext) = convertProjectionItems(ctx.projectionItems(), ctx.SELECT().getSourceMetaContainer()) + override fun visitSelectItems(ctx: PartiQLParser.SelectItemsContext) = + convertProjectionItems(ctx.projectionItems(), ctx.SELECT().getSourceMetaContainer()) override fun visitSelectPivot(ctx: PartiQLParser.SelectPivotContext) = PartiqlAst.build { projectPivot(visitExpr(ctx.at), visitExpr(ctx.pivot)) @@ -506,7 +543,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p * */ - override fun visitLimitClause(ctx: PartiQLParser.LimitClauseContext): PartiqlAst.Expr = visit(ctx.arg, PartiqlAst.Expr::class) + override fun visitLimitClause(ctx: PartiQLParser.LimitClauseContext): PartiqlAst.Expr = + visit(ctx.arg, PartiqlAst.Expr::class) override fun visitExpr(ctx: PartiQLParser.ExprContext): PartiqlAst.Expr { checkThreadInterrupted() @@ -517,7 +555,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p override fun visitWhereClause(ctx: PartiQLParser.WhereClauseContext) = visitExpr(ctx.arg) - override fun visitWhereClauseSelect(ctx: PartiQLParser.WhereClauseSelectContext) = visit(ctx.arg, PartiqlAst.Expr::class) + override fun visitWhereClauseSelect(ctx: PartiQLParser.WhereClauseSelectContext) = + visit(ctx.arg, PartiqlAst.Expr::class) override fun visitHavingClause(ctx: PartiQLParser.HavingClauseContext) = visit(ctx.arg, PartiqlAst.Expr::class) @@ -672,7 +711,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p graphMatchPattern_(parts = parts, restrictor = restrictor, variable = variable) } - override fun visitPatternPathVariable(ctx: PartiQLParser.PatternPathVariableContext) = visitSymbolPrimitive(ctx.symbolPrimitive()) + override fun visitPatternPathVariable(ctx: PartiQLParser.PatternPathVariableContext) = + visitSymbolPrimitive(ctx.symbolPrimitive()) override fun visitSelectorBasic(ctx: PartiQLParser.SelectorBasicContext) = PartiqlAst.build { val metas = ctx.mod.getSourceMetaContainer() @@ -700,7 +740,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p } } - override fun visitPatternPartLabel(ctx: PartiQLParser.PatternPartLabelContext) = visitSymbolPrimitive(ctx.symbolPrimitive()) + override fun visitPatternPartLabel(ctx: PartiQLParser.PatternPartLabelContext) = + visitSymbolPrimitive(ctx.symbolPrimitive()) override fun visitPattern(ctx: PartiQLParser.PatternContext) = PartiqlAst.build { val restrictor = visitOrNull(ctx.restrictor, PartiqlAst.GraphMatchRestrictor::class) @@ -709,7 +750,13 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p val quantifier = visitOrNull(ctx.quantifier, PartiqlAst.GraphMatchQuantifier::class) val parts = visitOrEmpty(ctx.graphPart(), PartiqlAst.GraphMatchPatternPart::class) pattern( - graphMatchPattern_(parts = parts, variable = variable, restrictor = restrictor, quantifier = quantifier, prefilter = prefilter) + graphMatchPattern_( + parts = parts, + variable = variable, + restrictor = restrictor, + quantifier = quantifier, + prefilter = prefilter + ) ) } @@ -730,7 +777,12 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p val variable = visitOrNull(ctx.symbolPrimitive(), PartiqlAst.Expr.Id::class)?.name val prefilter = visitOrNull(ctx.whereClause(), PartiqlAst.Expr::class) val label = visitOrNull(ctx.patternPartLabel(), PartiqlAst.Expr.Id::class)?.name - edge_(direction = placeholderDirection, variable = variable, prefilter = prefilter, label = listOfNotNull(label)) + edge_( + direction = placeholderDirection, + variable = variable, + prefilter = prefilter, + label = listOfNotNull(label) + ) } override fun visitEdgeSpecLeft(ctx: PartiQLParser.EdgeSpecLeftContext) = PartiqlAst.build { @@ -748,10 +800,11 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p edge.copy(direction = edgeLeftOrRight()) } - override fun visitEdgeSpecUndirectedBidirectional(ctx: PartiQLParser.EdgeSpecUndirectedBidirectionalContext) = PartiqlAst.build { - val edge = visitEdgeSpec(ctx.edgeSpec()) - edge.copy(direction = edgeLeftOrUndirectedOrRight()) - } + override fun visitEdgeSpecUndirectedBidirectional(ctx: PartiQLParser.EdgeSpecUndirectedBidirectionalContext) = + PartiqlAst.build { + val edge = visitEdgeSpec(ctx.edgeSpec()) + edge.copy(direction = edgeLeftOrUndirectedOrRight()) + } override fun visitEdgeSpecUndirected(ctx: PartiQLParser.EdgeSpecUndirectedContext) = PartiqlAst.build { val edge = visitEdgeSpec(ctx.edgeSpec()) @@ -813,31 +866,68 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p * */ - override fun visitFromClause(ctx: PartiQLParser.FromClauseContext) = visit(ctx.tableReference(), PartiqlAst.FromSource::class) + override fun visitFromClause(ctx: PartiQLParser.FromClauseContext) = + visit(ctx.tableReference(), PartiqlAst.FromSource::class) override fun visitTableBaseRefClauses(ctx: PartiQLParser.TableBaseRefClausesContext) = PartiqlAst.build { val expr = visit(ctx.source, PartiqlAst.Expr::class) - val (asAlias, atAlias, byAlias) = visitNullableItems(listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), PartiqlAst.Expr.Id::class) - scan_(expr, asAlias = asAlias.toPigSymbolPrimitive(), byAlias = byAlias.toPigSymbolPrimitive(), atAlias = atAlias.toPigSymbolPrimitive(), metas = expr.metas) + val (asAlias, atAlias, byAlias) = visitNullableItems( + listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), + PartiqlAst.Expr.Id::class + ) + scan_( + expr, + asAlias = asAlias.toPigSymbolPrimitive(), + byAlias = byAlias.toPigSymbolPrimitive(), + atAlias = atAlias.toPigSymbolPrimitive(), + metas = expr.metas + ) } override fun visitTableBaseRefMatch(ctx: PartiQLParser.TableBaseRefMatchContext) = PartiqlAst.build { val expr = visit(ctx.source, PartiqlAst.Expr::class) - val (asAlias, atAlias, byAlias) = visitNullableItems(listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), PartiqlAst.Expr.Id::class) - scan_(expr, asAlias = asAlias.toPigSymbolPrimitive(), byAlias = byAlias.toPigSymbolPrimitive(), atAlias = atAlias.toPigSymbolPrimitive(), metas = expr.metas) + val (asAlias, atAlias, byAlias) = visitNullableItems( + listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), + PartiqlAst.Expr.Id::class + ) + scan_( + expr, + asAlias = asAlias.toPigSymbolPrimitive(), + byAlias = byAlias.toPigSymbolPrimitive(), + atAlias = atAlias.toPigSymbolPrimitive(), + metas = expr.metas + ) } override fun visitFromClauseSimpleExplicit(ctx: PartiQLParser.FromClauseSimpleExplicitContext) = PartiqlAst.build { val expr = visitPathSimple(ctx.pathSimple()) - val (asAlias, atAlias, byAlias) = visitNullableItems(listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), PartiqlAst.Expr.Id::class) - scan_(expr, asAlias = asAlias.toPigSymbolPrimitive(), byAlias = byAlias.toPigSymbolPrimitive(), atAlias = atAlias.toPigSymbolPrimitive(), metas = expr.metas) + val (asAlias, atAlias, byAlias) = visitNullableItems( + listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), + PartiqlAst.Expr.Id::class + ) + scan_( + expr, + asAlias = asAlias.toPigSymbolPrimitive(), + byAlias = byAlias.toPigSymbolPrimitive(), + atAlias = atAlias.toPigSymbolPrimitive(), + metas = expr.metas + ) } override fun visitTableUnpivot(ctx: PartiQLParser.TableUnpivotContext) = PartiqlAst.build { val expr = visitExpr(ctx.expr()) val metas = ctx.UNPIVOT().getSourceMetaContainer() - val (asAlias, atAlias, byAlias) = visitNullableItems(listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), PartiqlAst.Expr.Id::class) - unpivot_(expr, asAlias = asAlias.toPigSymbolPrimitive(), atAlias = atAlias.toPigSymbolPrimitive(), byAlias = byAlias.toPigSymbolPrimitive(), metas) + val (asAlias, atAlias, byAlias) = visitNullableItems( + listOf(ctx.asIdent(), ctx.atIdent(), ctx.byIdent()), + PartiqlAst.Expr.Id::class + ) + unpivot_( + expr, + asAlias = asAlias.toPigSymbolPrimitive(), + atAlias = atAlias.toPigSymbolPrimitive(), + byAlias = byAlias.toPigSymbolPrimitive(), + metas + ) } override fun visitTableCrossJoin(ctx: PartiQLParser.TableCrossJoinContext) = PartiqlAst.build { @@ -868,7 +958,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p scan_(path, name, metas = path.metas) } - override fun visitTableWrapped(ctx: PartiQLParser.TableWrappedContext): PartiqlAst.PartiqlAstNode = visit(ctx.tableReference()) + override fun visitTableWrapped(ctx: PartiQLParser.TableWrappedContext): PartiqlAst.PartiqlAstNode = + visit(ctx.tableReference()) override fun visitJoinSpec(ctx: PartiQLParser.JoinSpecContext) = visitExpr(ctx.expr()) @@ -885,7 +976,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p } } - override fun visitJoinRhsTableJoined(ctx: PartiQLParser.JoinRhsTableJoinedContext) = visit(ctx.tableReference(), PartiqlAst.FromSource::class) + override fun visitJoinRhsTableJoined(ctx: PartiQLParser.JoinRhsTableJoinedContext) = + visit(ctx.tableReference(), PartiqlAst.FromSource::class) /** * SIMPLE EXPRESSIONS @@ -897,13 +989,17 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p override fun visitNot(ctx: PartiQLParser.NotContext) = visitUnaryOperation(ctx.rhs, ctx.op, null) - override fun visitMathOp00(ctx: PartiQLParser.MathOp00Context): PartiqlAst.PartiqlAstNode = visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op, ctx.parent) + override fun visitMathOp00(ctx: PartiQLParser.MathOp00Context): PartiqlAst.PartiqlAstNode = + visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op, ctx.parent) - override fun visitMathOp01(ctx: PartiQLParser.MathOp01Context): PartiqlAst.PartiqlAstNode = visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op, ctx.parent) + override fun visitMathOp01(ctx: PartiQLParser.MathOp01Context): PartiqlAst.PartiqlAstNode = + visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op, ctx.parent) - override fun visitMathOp02(ctx: PartiQLParser.MathOp02Context): PartiqlAst.PartiqlAstNode = visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op, ctx.parent) + override fun visitMathOp02(ctx: PartiQLParser.MathOp02Context): PartiqlAst.PartiqlAstNode = + visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op, ctx.parent) - override fun visitValueExpr(ctx: PartiQLParser.ValueExprContext) = visitUnaryOperation(ctx.rhs, ctx.sign, ctx.parent) + override fun visitValueExpr(ctx: PartiQLParser.ValueExprContext) = + visitUnaryOperation(ctx.rhs, ctx.sign, ctx.parent) /** * @@ -911,7 +1007,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p * */ - override fun visitPredicateComparison(ctx: PartiQLParser.PredicateComparisonContext) = visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op) + override fun visitPredicateComparison(ctx: PartiQLParser.PredicateComparisonContext) = + visitBinaryOperation(ctx.lhs, ctx.rhs, ctx.op) /** * Note: This predicate can take a wrapped expression on the RHS, and it will wrap it in a LIST. However, if the @@ -963,21 +1060,24 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p * */ - override fun visitExprTermWrappedQuery(ctx: PartiQLParser.ExprTermWrappedQueryContext) = visit(ctx.expr(), PartiqlAst.Expr::class) + override fun visitExprTermWrappedQuery(ctx: PartiQLParser.ExprTermWrappedQueryContext) = + visit(ctx.expr(), PartiqlAst.Expr::class) - override fun visitVariableIdentifier(ctx: PartiQLParser.VariableIdentifierContext): PartiqlAst.PartiqlAstNode = PartiqlAst.build { - val metas = ctx.ident.getSourceMetaContainer() - val qualifier = if (ctx.qualifier == null) unqualified() else localsFirst() - val sensitivity = if (ctx.ident.type == PartiQLParser.IDENTIFIER) caseInsensitive() else caseSensitive() - id(ctx.ident.getStringValue(), sensitivity, qualifier, metas) - } + override fun visitVariableIdentifier(ctx: PartiQLParser.VariableIdentifierContext): PartiqlAst.PartiqlAstNode = + PartiqlAst.build { + val metas = ctx.ident.getSourceMetaContainer() + val qualifier = if (ctx.qualifier == null) unqualified() else localsFirst() + val sensitivity = if (ctx.ident.type == PartiQLParser.IDENTIFIER) caseInsensitive() else caseSensitive() + id(ctx.ident.getStringValue(), sensitivity, qualifier, metas) + } - override fun visitVariableKeyword(ctx: PartiQLParser.VariableKeywordContext): PartiqlAst.PartiqlAstNode = PartiqlAst.build { - val keyword = ctx.nonReservedKeywords().start.text - val metas = ctx.start.getSourceMetaContainer() - val qualifier = ctx.qualifier?.let { localsFirst() } ?: unqualified() - id(keyword, caseInsensitive(), qualifier, metas) - } + override fun visitVariableKeyword(ctx: PartiQLParser.VariableKeywordContext): PartiqlAst.PartiqlAstNode = + PartiqlAst.build { + val keyword = ctx.nonReservedKeywords().start.text + val metas = ctx.start.getSourceMetaContainer() + val qualifier = ctx.qualifier?.let { localsFirst() } ?: unqualified() + id(keyword, caseInsensitive(), qualifier, metas) + } override fun visitParameter(ctx: PartiQLParser.ParameterContext) = PartiqlAst.build { val parameterIndex = parameterIndexes[ctx.QUESTION_MARK().symbol.tokenIndex] @@ -1150,7 +1250,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p override fun visitExtract(ctx: PartiQLParser.ExtractContext) = PartiqlAst.build { if (DateTimePart.safeValueOf(ctx.IDENTIFIER().text) == null) { - throw ctx.IDENTIFIER().err("Expected one of: ${DateTimePart.values()}", ErrorCode.PARSE_EXPECTED_DATE_TIME_PART) + throw ctx.IDENTIFIER() + .err("Expected one of: ${DateTimePart.values()}", ErrorCode.PARSE_EXPECTED_DATE_TIME_PART) } val datetimePart = lit(ionSymbol(ctx.IDENTIFIER().text)) val timeExpr = visit(ctx.rhs, PartiqlAst.Expr::class) @@ -1176,9 +1277,11 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p if (isTrimSpec) ctx.mod.toSymbol() to null else null to id(possibleModText!!, caseInsensitive(), unqualified(), ctx.mod.getSourceMetaContainer()) } + ctx.mod == null && ctx.sub != null -> { null to visitExpr(ctx.sub) } + ctx.mod != null && ctx.sub != null -> { if (isTrimSpec) ctx.mod.toSymbol() to visitExpr(ctx.sub) // todo we need to decide if it should be an evaluator error or a parser error @@ -1192,6 +1295,7 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p ) } } + else -> null to null } @@ -1231,12 +1335,16 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p val metas = ctx.func.getSourceMetaContainer() callWindow(ctx.func.text.lowercase(), over, args, metas) } + override fun visitOver(ctx: PartiQLParser.OverContext) = PartiqlAst.build { - val windowPartitionList = if (ctx.windowPartitionList() != null) visitWindowPartitionList(ctx.windowPartitionList()) else null - val windowSortSpecList = if (ctx.windowSortSpecList() != null) visitWindowSortSpecList(ctx.windowSortSpecList()) else null + val windowPartitionList = + if (ctx.windowPartitionList() != null) visitWindowPartitionList(ctx.windowPartitionList()) else null + val windowSortSpecList = + if (ctx.windowSortSpecList() != null) visitWindowSortSpecList(ctx.windowSortSpecList()) else null val metas = ctx.OVER().getSourceMetaContainer() over(windowPartitionList, windowSortSpecList, metas) } + override fun visitWindowPartitionList(ctx: PartiQLParser.WindowPartitionListContext) = PartiqlAst.build { val args = visitOrEmpty(ctx.expr(), PartiqlAst.Expr::class) val metas = ctx.PARTITION().getSourceMetaContainer() @@ -1318,7 +1426,8 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p override fun visitLiteralDate(ctx: PartiQLParser.LiteralDateContext) = PartiqlAst.build { val dateString = ctx.LITERAL_STRING().getStringValue() if (DATE_PATTERN_REGEX.matches(dateString).not()) { - throw ctx.LITERAL_STRING().err("Expected DATE string to be of the format yyyy-MM-dd", ErrorCode.PARSE_INVALID_DATE_STRING) + throw ctx.LITERAL_STRING() + .err("Expected DATE string to be of the format yyyy-MM-dd", ErrorCode.PARSE_INVALID_DATE_STRING) } try { LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE) @@ -1339,6 +1448,14 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p } } + override fun visitLiteralTimestamp(ctx: PartiQLParser.LiteralTimestampContext): PartiqlAst.PartiqlAstNode { + val (timestamp, precision) = getTimestampStringAndPrecision(ctx.LITERAL_STRING(), ctx.LITERAL_INTEGER()) + return when (ctx.WITH()) { + null -> getTimestampDynamic(timestamp, precision, ctx.LITERAL_STRING()) + else -> getTimestampWithTimezone(timestamp, precision, ctx.LITERAL_STRING()) + } + } + override fun visitTuple(ctx: PartiQLParser.TupleContext) = PartiqlAst.build { val pairs = visitOrEmpty(ctx.pair(), PartiqlAst.ExprPair::class) val metas = ctx.BRACE_LEFT().getSourceMetaContainer() @@ -1375,7 +1492,6 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p PartiQLParser.BIGINT -> integer8Type(metas) PartiQLParser.REAL -> realType(metas) PartiQLParser.DOUBLE -> doublePrecisionType(metas) - PartiQLParser.TIMESTAMP -> timestampType(metas) PartiQLParser.CHAR -> characterType(metas = metas) PartiQLParser.CHARACTER -> characterType(metas = metas) PartiQLParser.MISSING -> missingType(metas) @@ -1431,8 +1547,15 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p if (precision != null && (precision < 0 || precision > MAX_PRECISION_FOR_TIME)) { throw ctx.precision.err("Unsupported precision", ErrorCode.PARSE_INVALID_PRECISION_FOR_TIME) } - if (ctx.WITH() == null) return@build timeType(precision) - timeWithTimeZoneType(precision) + val hasTimeZone = ctx.WITH() != null + when (ctx.datatype.type) { + PartiQLParser.TIME -> if (hasTimeZone) timeWithTimeZoneType(precision) else timeType(precision) + PartiQLParser.TIMESTAMP -> if (hasTimeZone) timestampWithTimeZoneType(precision) else timestampType( + precision + ) + + else -> throw ParserException("Unknown datatype", ErrorCode.PARSE_UNEXPECTED_TOKEN, PropertyValueMap()) + } } override fun visitTypeCustom(ctx: PartiQLParser.TypeCustomContext) = PartiqlAst.build { @@ -1451,17 +1574,34 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p */ override fun visitTerminal(node: TerminalNode?): PartiqlAst.PartiqlAstNode = super.visitTerminal(node) - override fun shouldVisitNextChild(node: RuleNode?, currentResult: PartiqlAst.PartiqlAstNode?) = super.shouldVisitNextChild(node, currentResult) + override fun shouldVisitNextChild(node: RuleNode?, currentResult: PartiqlAst.PartiqlAstNode?) = + super.shouldVisitNextChild(node, currentResult) + override fun visitErrorNode(node: ErrorNode?): PartiqlAst.PartiqlAstNode = super.visitErrorNode(node) override fun visitChildren(node: RuleNode?): PartiqlAst.PartiqlAstNode = super.visitChildren(node) - override fun visitExprPrimaryBase(ctx: PartiQLParser.ExprPrimaryBaseContext?): PartiqlAst.PartiqlAstNode = super.visitExprPrimaryBase(ctx) - override fun visitExprTermBase(ctx: PartiQLParser.ExprTermBaseContext?): PartiqlAst.PartiqlAstNode = super.visitExprTermBase(ctx) - override fun visitCollection(ctx: PartiQLParser.CollectionContext?): PartiqlAst.PartiqlAstNode = super.visitCollection(ctx) - override fun visitPredicateBase(ctx: PartiQLParser.PredicateBaseContext?): PartiqlAst.PartiqlAstNode = super.visitPredicateBase(ctx) - override fun visitTableNonJoin(ctx: PartiQLParser.TableNonJoinContext?): PartiqlAst.PartiqlAstNode = super.visitTableNonJoin(ctx) - override fun visitTableRefBase(ctx: PartiQLParser.TableRefBaseContext?): PartiqlAst.PartiqlAstNode = super.visitTableRefBase(ctx) - override fun visitJoinRhsBase(ctx: PartiQLParser.JoinRhsBaseContext?): PartiqlAst.PartiqlAstNode = super.visitJoinRhsBase(ctx) - override fun visitConflictTarget(ctx: PartiQLParser.ConflictTargetContext?): PartiqlAst.PartiqlAstNode = super.visitConflictTarget(ctx) + override fun visitExprPrimaryBase(ctx: PartiQLParser.ExprPrimaryBaseContext?): PartiqlAst.PartiqlAstNode = + super.visitExprPrimaryBase(ctx) + + override fun visitExprTermBase(ctx: PartiQLParser.ExprTermBaseContext?): PartiqlAst.PartiqlAstNode = + super.visitExprTermBase(ctx) + + override fun visitCollection(ctx: PartiQLParser.CollectionContext?): PartiqlAst.PartiqlAstNode = + super.visitCollection(ctx) + + override fun visitPredicateBase(ctx: PartiQLParser.PredicateBaseContext?): PartiqlAst.PartiqlAstNode = + super.visitPredicateBase(ctx) + + override fun visitTableNonJoin(ctx: PartiQLParser.TableNonJoinContext?): PartiqlAst.PartiqlAstNode = + super.visitTableNonJoin(ctx) + + override fun visitTableRefBase(ctx: PartiQLParser.TableRefBaseContext?): PartiqlAst.PartiqlAstNode = + super.visitTableRefBase(ctx) + + override fun visitJoinRhsBase(ctx: PartiQLParser.JoinRhsBaseContext?): PartiqlAst.PartiqlAstNode = + super.visitJoinRhsBase(ctx) + + override fun visitConflictTarget(ctx: PartiQLParser.ConflictTargetContext?): PartiqlAst.PartiqlAstNode = + super.visitConflictTarget(ctx) /** * @@ -1469,22 +1609,28 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p * */ - private fun visitOrEmpty(ctx: List?, clazz: KClass): List = when { - ctx.isNullOrEmpty() -> emptyList() - else -> ctx.map { clazz.cast(visit(it)) } - } + private fun visitOrEmpty(ctx: List?, clazz: KClass): List = + when { + ctx.isNullOrEmpty() -> emptyList() + else -> ctx.map { clazz.cast(visit(it)) } + } - private fun visitNullableItems(ctx: List?, clazz: KClass): List = when { + private fun visitNullableItems( + ctx: List?, + clazz: KClass + ): List = when { ctx.isNullOrEmpty() -> emptyList() else -> ctx.map { visitOrNull(it, clazz) } } - private fun visitOrNull(ctx: ParserRuleContext?, clazz: KClass): T? = when (ctx) { - null -> null - else -> clazz.cast(visit(ctx)) - } + private fun visitOrNull(ctx: ParserRuleContext?, clazz: KClass): T? = + when (ctx) { + null -> null + else -> clazz.cast(visit(ctx)) + } - private fun visit(ctx: ParserRuleContext, clazz: KClass): T = clazz.cast(visit(ctx)) + private fun visit(ctx: ParserRuleContext, clazz: KClass): T = + clazz.cast(visit(ctx)) private fun TerminalNode?.getSourceMetaContainer(): MetaContainer { if (this == null) return emptyMetaContainer() @@ -1505,7 +1651,12 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p return SourceLocationMeta(this.line.toLong(), this.charPositionInLine.toLong() + 1, length.toLong()) } - private fun visitBinaryOperation(lhs: ParserRuleContext?, rhs: ParserRuleContext?, op: Token?, parent: ParserRuleContext? = null) = PartiqlAst.build { + private fun visitBinaryOperation( + lhs: ParserRuleContext?, + rhs: ParserRuleContext?, + op: Token?, + parent: ParserRuleContext? = null + ) = PartiqlAst.build { if (parent != null) return@build visit(parent, PartiqlAst.Expr::class) val args = visitOrEmpty(listOf(lhs!!, rhs!!), PartiqlAst.Expr::class) val metas = op.getSourceMetaContainer() @@ -1528,47 +1679,54 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p } } - private fun visitUnaryOperation(operand: ParserRuleContext?, op: Token?, parent: ParserRuleContext? = null) = PartiqlAst.build { - if (parent != null) return@build visit(parent, PartiqlAst.Expr::class) - val arg = visit(operand!!, PartiqlAst.Expr::class) - val metas = op.getSourceMetaContainer() - when (op!!.type) { - PartiQLParser.PLUS -> { - when { - arg !is PartiqlAst.Expr.Lit -> pos(arg, metas) - arg.value is IntElement -> arg - arg.value is FloatElement -> arg - arg.value is DecimalElement -> arg - else -> pos(arg, metas) + private fun visitUnaryOperation(operand: ParserRuleContext?, op: Token?, parent: ParserRuleContext? = null) = + PartiqlAst.build { + if (parent != null) return@build visit(parent, PartiqlAst.Expr::class) + val arg = visit(operand!!, PartiqlAst.Expr::class) + val metas = op.getSourceMetaContainer() + when (op!!.type) { + PartiQLParser.PLUS -> { + when { + arg !is PartiqlAst.Expr.Lit -> pos(arg, metas) + arg.value is IntElement -> arg + arg.value is FloatElement -> arg + arg.value is DecimalElement -> arg + else -> pos(arg, metas) + } } - } - PartiQLParser.MINUS -> { - when { - arg !is PartiqlAst.Expr.Lit -> neg(arg, metas) - arg.value is IntElement -> { - val intValue = when (arg.value.integerSize) { - IntElementSize.LONG -> ionInt(-arg.value.longValue) - IntElementSize.BIG_INTEGER -> when (arg.value.bigIntegerValue) { - Long.MAX_VALUE.toBigInteger() + (1L).toBigInteger() -> ionInt(Long.MIN_VALUE) - else -> ionInt(arg.value.bigIntegerValue * BigInteger.valueOf(-1L)) + + PartiQLParser.MINUS -> { + when { + arg !is PartiqlAst.Expr.Lit -> neg(arg, metas) + arg.value is IntElement -> { + val intValue = when (arg.value.integerSize) { + IntElementSize.LONG -> ionInt(-arg.value.longValue) + IntElementSize.BIG_INTEGER -> when (arg.value.bigIntegerValue) { + Long.MAX_VALUE.toBigInteger() + (1L).toBigInteger() -> ionInt(Long.MIN_VALUE) + else -> ionInt(arg.value.bigIntegerValue * BigInteger.valueOf(-1L)) + } } + arg.copy(value = intValue.asAnyElement()) } - arg.copy(value = intValue.asAnyElement()) + + arg.value is FloatElement -> arg.copy(value = ionFloat(-(arg.value.doubleValue)).asAnyElement()) + arg.value is DecimalElement -> arg.copy(value = ionDecimal(-(arg.value.decimalValue)).asAnyElement()) + else -> neg(arg, metas) } - arg.value is FloatElement -> arg.copy(value = ionFloat(-(arg.value.doubleValue)).asAnyElement()) - arg.value is DecimalElement -> arg.copy(value = ionDecimal(-(arg.value.decimalValue)).asAnyElement()) - else -> neg(arg, metas) } + + PartiQLParser.NOT -> not(arg, metas) + else -> throw ParserException("Unknown unary operator", ErrorCode.PARSE_INVALID_QUERY) } - PartiQLParser.NOT -> not(arg, metas) - else -> throw ParserException("Unknown unary operator", ErrorCode.PARSE_INVALID_QUERY) } - } private fun PartiQLParser.SymbolPrimitiveContext.getSourceMetaContainer() = when (this.ident.type) { PartiQLParser.IDENTIFIER -> this.IDENTIFIER().getSourceMetaContainer() PartiQLParser.IDENTIFIER_QUOTED -> this.IDENTIFIER_QUOTED().getSourceMetaContainer() - else -> throw ParserException("Unable to get identifier's source meta-container.", ErrorCode.PARSE_INVALID_QUERY) + else -> throw ParserException( + "Unable to get identifier's source meta-container.", + ErrorCode.PARSE_INVALID_QUERY + ) } private fun PartiqlAst.Expr.getStringValue(token: Token? = null): String = when (this) { @@ -1578,9 +1736,13 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p is SymbolElement -> this.value.symbolValue.lowercase() is StringElement -> this.value.stringValue.lowercase() else -> - this.value.stringValueOrNull ?: throw token.err("Unable to pass the string value", ErrorCode.PARSE_UNEXPECTED_TOKEN) + this.value.stringValueOrNull ?: throw token.err( + "Unable to pass the string value", + ErrorCode.PARSE_UNEXPECTED_TOKEN + ) } } + else -> throw token.err("Unable to get value", ErrorCode.PARSE_UNEXPECTED_TOKEN) } @@ -1612,6 +1774,7 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p cause = e ) } + else -> integerNode.text.toInteger().toLong() } if (precision < 0 || precision > MAX_PRECISION_FOR_TIME) { @@ -1624,24 +1787,31 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p * Parses a [timeString] using [OffsetTime] and converts to a [PartiqlAst.Expr.LitTime]. If unable to parse, parses * using [getLocalTime]. */ - private fun getOffsetTime(timeString: String, precision: Long, stringNode: TerminalNode, timeNode: TerminalNode) = PartiqlAst.build { - try { - val time: OffsetTime = OffsetTime.parse(timeString) - litTime( - timeValue( - time.hour.toLong(), time.minute.toLong(), time.second.toLong(), time.nano.toLong(), - precision, true, (time.offset.totalSeconds / 60).toLong() + private fun getOffsetTime(timeString: String, precision: Long, stringNode: TerminalNode, timeNode: TerminalNode) = + PartiqlAst.build { + try { + val time: OffsetTime = OffsetTime.parse(timeString) + litTime( + timeValue( + time.hour.toLong(), time.minute.toLong(), time.second.toLong(), time.nano.toLong(), + precision, true, (time.offset.totalSeconds / 60).toLong() + ) ) - ) - } catch (e: DateTimeParseException) { - getLocalTime(timeString, true, precision, stringNode, timeNode) + } catch (e: DateTimeParseException) { + getLocalTime(timeString, true, precision, stringNode, timeNode) + } } - } /** * Parses a [timeString] using [LocalTime] and converts to a [PartiqlAst.Expr.LitTime] */ - private fun getLocalTime(timeString: String, withTimeZone: Boolean, precision: Long, stringNode: TerminalNode, timeNode: TerminalNode) = PartiqlAst.build { + private fun getLocalTime( + timeString: String, + withTimeZone: Boolean, + precision: Long, + stringNode: TerminalNode, + timeNode: TerminalNode + ) = PartiqlAst.build { val time: LocalTime val formatter = when (withTimeZone) { false -> DateTimeFormatter.ISO_TIME @@ -1662,15 +1832,87 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p ) } + private fun getTimestampStringAndPrecision( + stringNode: TerminalNode, + integerNode: TerminalNode? + ): Pair { + val timestampString = stringNode.getStringValue() + val precision = when (integerNode) { + null -> return timestampString to null + else -> integerNode.text.toInteger().toLong() + } + if (precision < 0) { + throw integerNode.err("Precision out of bounds", ErrorCode.PARSE_INVALID_PRECISION_FOR_TIMESTAMP) + } + return timestampString to precision + } + + /** + * Parse Timestamp based on the existence of Time zone + */ + private fun getTimestampDynamic( + timestampString: String, + precision: Long?, + node: TerminalNode + ) = PartiqlAst.build { + val timestamp = + try { + DateTimeUtils.parseTimestamp(timestampString) + } catch (e: DateTimeException) { + throw node.err("Invalid Date Time Literal", ErrorCode.PARSE_INVALID_DATETIME_STRING, cause = e) + } + val timeZone = timestamp.timeZone?.let { getTimeZone(it) } + timestamp( + timestampValue( + timestamp.year.toLong(), timestamp.month.toLong(), timestamp.day.toLong(), + timestamp.hour.toLong(), timestamp.minute.toLong(), ionDecimal(Decimal.valueOf(timestamp.decimalSecond)), + timeZone, precision + ) + ) + } + + private fun getTimestampWithTimezone( + timestampString: String, + precision: Long?, + node: TerminalNode + ) = PartiqlAst.build { + val timestamp = try { + DateTimeUtils.parseTimestamp(timestampString) + } catch (e: DateTimeException) { + throw node.err("Invalid Date Time Literal", ErrorCode.PARSE_INVALID_DATETIME_STRING, cause = e) + } + if (timestamp.timeZone == null) + throw node.err( + "Invalid Date Time Literal, expect Time Zone for Type Timestamp With Time Zone", + ErrorCode.PARSE_INVALID_DATETIME_STRING + ) + val timeZone = timestamp.timeZone?.let { getTimeZone(it) } + timestamp( + timestampValue( + timestamp.year.toLong(), timestamp.month.toLong(), timestamp.day.toLong(), + timestamp.hour.toLong(), timestamp.minute.toLong(), ionDecimal(Decimal.valueOf(timestamp.decimalSecond)), + timeZone, precision + ) + ) + } + + private fun getTimeZone(timeZone: TimeZone) = PartiqlAst.build { + when (timeZone) { + TimeZone.UnknownTimeZone -> unknownTimezone() + is TimeZone.UtcOffset -> utcOffset(timeZone.totalOffsetMinutes.toLong()) + } + } + private fun convertSymbolPrimitive(sym: PartiQLParser.SymbolPrimitiveContext?): SymbolPrimitive? = when (sym) { null -> null else -> SymbolPrimitive(sym.getString(), sym.getSourceMetaContainer()) } - private fun convertProjectionItems(ctx: PartiQLParser.ProjectionItemsContext, metas: MetaContainer) = PartiqlAst.build { - val projections = visitOrEmpty(ctx.projectionItem(), PartiqlAst.ProjectItem::class) - projectList(projections, metas) - } + private fun convertProjectionItems(ctx: PartiQLParser.ProjectionItemsContext, metas: MetaContainer) = + PartiqlAst.build { + val projections = visitOrEmpty(ctx.projectionItem(), PartiqlAst.ProjectItem::class) + projectList(projections, metas) + } private fun PartiQLParser.SelectClauseContext.getMetas(): MetaContainer = when (this) { is PartiQLParser.SelectAllContext -> this.SELECT().getSourceMetaContainer() @@ -1734,7 +1976,11 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p when { path.steps.last() is PartiqlAst.PathStep.PathUnpivot && steps.isEmpty() -> projectAll(path.root, path.metas) - path.steps.last() is PartiqlAst.PathStep.PathUnpivot -> projectAll(path(path.root, steps, path.metas), path.metas) + path.steps.last() is PartiqlAst.PathStep.PathUnpivot -> projectAll( + path(path.root, steps, path.metas), + path.metas + ) + else -> projectExpr_(path, asAlias = alias, path.metas) } } @@ -1749,14 +1995,15 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p else -> throw this.err("Unsupported token for grabbing string value.", ErrorCode.PARSE_INVALID_QUERY) } - private fun getStrategy(strategy: PartiQLParser.SetQuantifierStrategyContext?, default: PartiqlAst.SetQuantifier) = PartiqlAst.build { - when { - strategy == null -> default - strategy.DISTINCT() != null -> distinct() - strategy.ALL() != null -> all() - else -> default + private fun getStrategy(strategy: PartiQLParser.SetQuantifierStrategyContext?, default: PartiqlAst.SetQuantifier) = + PartiqlAst.build { + when { + strategy == null -> default + strategy.DISTINCT() != null -> distinct() + strategy.ALL() != null -> all() + else -> default + } } - } private fun getStrategy(strategy: PartiQLParser.SetQuantifierStrategyContext?): PartiqlAst.SetQuantifier? { return when { @@ -1790,10 +2037,12 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p lit(ionString(ctx.IDENTIFIER_QUOTED().getStringValue())), caseSensitive(), metas = ctx.IDENTIFIER_QUOTED().getSourceMetaContainer() ) + ctx.IDENTIFIER() != null -> pathExpr( lit(ionString(ctx.IDENTIFIER().text)), caseInsensitive(), metas = ctx.IDENTIFIER().getSourceMetaContainer() ) + else -> throw ParserException("Unable to get symbol's text.", ErrorCode.PARSE_INVALID_QUERY) } } @@ -1821,7 +2070,10 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p if (value !is IntElement) throw token.err("Expected an integer value.", ErrorCode.PARSE_MALFORMED_PARSE_TREE) if (value.integerSize == IntElementSize.BIG_INTEGER || value.longValue > Int.MAX_VALUE || value.longValue < Int.MIN_VALUE) - throw token.err("Type parameter exceeded maximum value", ErrorCode.PARSE_TYPE_PARAMETER_EXCEEDED_MAXIMUM_VALUE) + throw token.err( + "Type parameter exceeded maximum value", + ErrorCode.PARSE_TYPE_PARAMETER_EXCEEDED_MAXIMUM_VALUE + ) } private enum class ExplainParameters { @@ -1830,10 +2082,24 @@ internal class PartiQLPigVisitor(val customTypes: List = listOf(), p fun getCompliantString(target: String?, input: Token): String = when (target) { null -> input.text!! - else -> throw input.error("Cannot set EXPLAIN parameter ${this.name} multiple times.", ErrorCode.PARSE_UNEXPECTED_TOKEN) + else -> throw input.error( + "Cannot set EXPLAIN parameter ${this.name} multiple times.", + ErrorCode.PARSE_UNEXPECTED_TOKEN + ) } } - private fun TerminalNode?.err(msg: String, code: ErrorCode, ctx: PropertyValueMap = PropertyValueMap(), cause: Throwable? = null) = this.error(msg, code, ctx, cause) - private fun Token?.err(msg: String, code: ErrorCode, ctx: PropertyValueMap = PropertyValueMap(), cause: Throwable? = null) = this.error(msg, code, ctx, cause) + private fun TerminalNode?.err( + msg: String, + code: ErrorCode, + ctx: PropertyValueMap = PropertyValueMap(), + cause: Throwable? = null + ) = this.error(msg, code, ctx, cause) + + private fun Token?.err( + msg: String, + code: ErrorCode, + ctx: PropertyValueMap = PropertyValueMap(), + cause: Throwable? = null + ) = this.error(msg, code, ctx, cause) } diff --git a/partiql-lang/src/main/kotlin/org/partiql/lang/syntax/util/DateTimeUtils.kt b/partiql-lang/src/main/kotlin/org/partiql/lang/syntax/util/DateTimeUtils.kt new file mode 100644 index 0000000000..ec349d24c0 --- /dev/null +++ b/partiql-lang/src/main/kotlin/org/partiql/lang/syntax/util/DateTimeUtils.kt @@ -0,0 +1,76 @@ +package org.partiql.lang.syntax.util + +import org.partiql.value.datetime.Date +import org.partiql.value.datetime.DateTimeException +import org.partiql.value.datetime.DateTimeValue +import org.partiql.value.datetime.Time +import org.partiql.value.datetime.TimeZone +import org.partiql.value.datetime.Timestamp +import java.math.BigDecimal +import java.util.regex.Matcher +import java.util.regex.Pattern + +internal object DateTimeUtils { + private val DATE_PATTERN: Pattern = Pattern.compile("(?\\d{4,})-(?\\d{2,})-(?\\d{2,})") + private val TIME_PATTERN: Pattern = Pattern.compile("(?\\d{2,}):(?\\d{2,}):(?\\d{2,})(?:\\.(?\\d+))?\\s*(?([+-]\\d\\d:\\d\\d)|(?[Zz]))?") + private val SQL_TIMESTAMP_DATE_TIME_DELIMITER = "\\s+".toRegex() + private val RFC8889_TIMESTAMP_DATE_TIME_DELIMITER = "[Tt]".toRegex() + private val TIMESTAMP_PATTERN = "(?$DATE_PATTERN)($SQL_TIMESTAMP_DATE_TIME_DELIMITER|$RFC8889_TIMESTAMP_DATE_TIME_DELIMITER)(?