From 084c49fdb6848721c91dafe4a210fd72527e29e3 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Thu, 25 Jul 2024 15:55:11 -0700 Subject: [PATCH] Adds grammar for boolean test and absent predicates --- partiql-ast/api/partiql-ast.api | 225 +++++++++++++++--- .../org/partiql/ast/helpers/ToLegacyAst.kt | 28 ++- .../kotlin/org/partiql/ast/sql/SqlDialect.kt | 32 ++- .../ast/sql/internal/InternalSqlDialect.kt | 28 ++- .../src/main/resources/partiql_ast.ion | 29 ++- .../partiql/ast/helpers/ToLegacyAstTest.kt | 21 +- .../org/partiql/ast/sql/SqlDialectTest.kt | 21 +- .../eval/internal/operator/Operator.kt | 2 - .../eval/internal/operator/rex/ExprIs.kt | 0 .../internal/operator/rex/ExprIsMissing.kt | 21 ++ .../eval/internal/operator/rex/ExprIsNull.kt | 0 .../lang/syntax/impl/PartiQLPigVisitor.kt | 8 +- .../src/main/antlr/PartiQLParser.g4 | 12 +- .../parser/internal/PartiQLParserDefault.kt | 32 ++- .../org/partiql/planner/internal/SqlTypes.kt | 215 +++++++++++++++++ .../internal/transforms/NormalizeSelect.kt | 17 +- .../internal/transforms/RexConverter.kt | 84 +++---- .../partiql/spi/connector/sql/SqlBuiltins.kt | 7 +- .../spi/connector/sql/builtins/FnIsFalse.kt | 38 +++ .../spi/connector/sql/builtins/FnIsTrue.kt | 38 +++ 20 files changed, 734 insertions(+), 124 deletions(-) create mode 100644 partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprIs.kt create mode 100644 partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprIsMissing.kt create mode 100644 partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprIsNull.kt create mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlTypes.kt create mode 100644 partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsFalse.kt create mode 100644 partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsTrue.kt diff --git a/partiql-ast/api/partiql-ast.api b/partiql-ast/api/partiql-ast.api index ce7f25c0e..c0caa09ce 100644 --- a/partiql-ast/api/partiql-ast.api +++ b/partiql-ast/api/partiql-ast.api @@ -30,7 +30,11 @@ public final class org/partiql/ast/Ast { public static final fun exprExtract (Lorg/partiql/ast/DatetimeField;Lorg/partiql/ast/Expr;)Lorg/partiql/ast/Expr$Extract; public static final fun exprInCollection (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$InCollection; public static final fun exprIon (Lcom/amazon/ionelement/api/IonElement;)Lorg/partiql/ast/Expr$Ion; - public static final fun exprIsType (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsType; + public static final fun exprIsFalse (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsFalse; + public static final fun exprIsMissing (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsMissing; + public static final fun exprIsNull (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsNull; + public static final fun exprIsTrue (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsTrue; + public static final fun exprIsUnknown (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsUnknown; public static final fun exprLike (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$Like; public static final fun exprLit (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/ast/Expr$Lit; public static final fun exprMatch (Lorg/partiql/ast/Expr;Lorg/partiql/ast/GraphMatch;)Lorg/partiql/ast/Expr$Match; @@ -877,27 +881,109 @@ public final class org/partiql/ast/Expr$Ion$Companion { public final fun builder ()Lorg/partiql/ast/builder/ExprIonBuilder; } -public final class org/partiql/ast/Expr$IsType : org/partiql/ast/Expr { - public static final field Companion Lorg/partiql/ast/Expr$IsType$Companion; +public final class org/partiql/ast/Expr$IsFalse : org/partiql/ast/Expr { + public static final field Companion Lorg/partiql/ast/Expr$IsFalse$Companion; public final field not Ljava/lang/Boolean; - public final field type Lorg/partiql/ast/Type; public final field value Lorg/partiql/ast/Expr; - public fun (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;)V + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V public fun accept (Lorg/partiql/ast/visitor/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; - public static final fun builder ()Lorg/partiql/ast/builder/ExprIsTypeBuilder; + public static final fun builder ()Lorg/partiql/ast/builder/ExprIsFalseBuilder; public final fun component1 ()Lorg/partiql/ast/Expr; - public final fun component2 ()Lorg/partiql/ast/Type; - public final fun component3 ()Ljava/lang/Boolean; - public final fun copy (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsType; - public static synthetic fun copy$default (Lorg/partiql/ast/Expr$IsType;Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsType; + public final fun component2 ()Ljava/lang/Boolean; + public final fun copy (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsFalse; + public static synthetic fun copy$default (Lorg/partiql/ast/Expr$IsFalse;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsFalse; public fun equals (Ljava/lang/Object;)Z public fun getChildren ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } -public final class org/partiql/ast/Expr$IsType$Companion { - public final fun builder ()Lorg/partiql/ast/builder/ExprIsTypeBuilder; +public final class org/partiql/ast/Expr$IsFalse$Companion { + public final fun builder ()Lorg/partiql/ast/builder/ExprIsFalseBuilder; +} + +public final class org/partiql/ast/Expr$IsMissing : org/partiql/ast/Expr { + public static final field Companion Lorg/partiql/ast/Expr$IsMissing$Companion; + public final field not Ljava/lang/Boolean; + public final field value Lorg/partiql/ast/Expr; + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public fun accept (Lorg/partiql/ast/visitor/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/ast/builder/ExprIsMissingBuilder; + public final fun component1 ()Lorg/partiql/ast/Expr; + public final fun component2 ()Ljava/lang/Boolean; + public final fun copy (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsMissing; + public static synthetic fun copy$default (Lorg/partiql/ast/Expr$IsMissing;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsMissing; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/ast/Expr$IsMissing$Companion { + public final fun builder ()Lorg/partiql/ast/builder/ExprIsMissingBuilder; +} + +public final class org/partiql/ast/Expr$IsNull : org/partiql/ast/Expr { + public static final field Companion Lorg/partiql/ast/Expr$IsNull$Companion; + public final field not Ljava/lang/Boolean; + public final field value Lorg/partiql/ast/Expr; + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public fun accept (Lorg/partiql/ast/visitor/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/ast/builder/ExprIsNullBuilder; + public final fun component1 ()Lorg/partiql/ast/Expr; + public final fun component2 ()Ljava/lang/Boolean; + public final fun copy (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsNull; + public static synthetic fun copy$default (Lorg/partiql/ast/Expr$IsNull;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsNull; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/ast/Expr$IsNull$Companion { + public final fun builder ()Lorg/partiql/ast/builder/ExprIsNullBuilder; +} + +public final class org/partiql/ast/Expr$IsTrue : org/partiql/ast/Expr { + public static final field Companion Lorg/partiql/ast/Expr$IsTrue$Companion; + public final field not Ljava/lang/Boolean; + public final field value Lorg/partiql/ast/Expr; + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public fun accept (Lorg/partiql/ast/visitor/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/ast/builder/ExprIsTrueBuilder; + public final fun component1 ()Lorg/partiql/ast/Expr; + public final fun component2 ()Ljava/lang/Boolean; + public final fun copy (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsTrue; + public static synthetic fun copy$default (Lorg/partiql/ast/Expr$IsTrue;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsTrue; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/ast/Expr$IsTrue$Companion { + public final fun builder ()Lorg/partiql/ast/builder/ExprIsTrueBuilder; +} + +public final class org/partiql/ast/Expr$IsUnknown : org/partiql/ast/Expr { + public static final field Companion Lorg/partiql/ast/Expr$IsUnknown$Companion; + public final field not Ljava/lang/Boolean; + public final field value Lorg/partiql/ast/Expr; + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public fun accept (Lorg/partiql/ast/visitor/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/ast/builder/ExprIsUnknownBuilder; + public final fun component1 ()Lorg/partiql/ast/Expr; + public final fun component2 ()Ljava/lang/Boolean; + public final fun copy (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)Lorg/partiql/ast/Expr$IsUnknown; + public static synthetic fun copy$default (Lorg/partiql/ast/Expr$IsUnknown;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsUnknown; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/ast/Expr$IsUnknown$Companion { + public final fun builder ()Lorg/partiql/ast/builder/ExprIsUnknownBuilder; } public final class org/partiql/ast/Expr$Like : org/partiql/ast/Expr { @@ -4052,8 +4138,16 @@ public final class org/partiql/ast/builder/AstBuilder { public static synthetic fun exprInCollection$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$InCollection; public final fun exprIon (Lcom/amazon/ionelement/api/IonElement;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$Ion; public static synthetic fun exprIon$default (Lorg/partiql/ast/builder/AstBuilder;Lcom/amazon/ionelement/api/IonElement;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$Ion; - public final fun exprIsType (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$IsType; - public static synthetic fun exprIsType$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsType; + public final fun exprIsFalse (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$IsFalse; + public static synthetic fun exprIsFalse$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsFalse; + public final fun exprIsMissing (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$IsMissing; + public static synthetic fun exprIsMissing$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsMissing; + public final fun exprIsNull (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$IsNull; + public static synthetic fun exprIsNull$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsNull; + public final fun exprIsTrue (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$IsTrue; + public static synthetic fun exprIsTrue$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsTrue; + public final fun exprIsUnknown (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$IsUnknown; + public static synthetic fun exprIsUnknown$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$IsUnknown; public final fun exprLike (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$Like; public static synthetic fun exprLike$default (Lorg/partiql/ast/builder/AstBuilder;Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Lorg/partiql/ast/Expr;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/ast/Expr$Like; public final fun exprLit (Lorg/partiql/value/PartiQLValue;Lkotlin/jvm/functions/Function1;)Lorg/partiql/ast/Expr$Lit; @@ -4720,20 +4814,69 @@ public final class org/partiql/ast/builder/ExprIonBuilder { public final fun value (Lcom/amazon/ionelement/api/IonElement;)Lorg/partiql/ast/builder/ExprIonBuilder; } -public final class org/partiql/ast/builder/ExprIsTypeBuilder { +public final class org/partiql/ast/builder/ExprIsFalseBuilder { public fun ()V - public fun (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;)V - public synthetic fun (Lorg/partiql/ast/Expr;Lorg/partiql/ast/Type;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun build ()Lorg/partiql/ast/Expr$IsType; + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public synthetic fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/ast/Expr$IsFalse; public final fun getNot ()Ljava/lang/Boolean; - public final fun getType ()Lorg/partiql/ast/Type; public final fun getValue ()Lorg/partiql/ast/Expr; - public final fun not (Ljava/lang/Boolean;)Lorg/partiql/ast/builder/ExprIsTypeBuilder; + public final fun not (Ljava/lang/Boolean;)Lorg/partiql/ast/builder/ExprIsFalseBuilder; + public final fun setNot (Ljava/lang/Boolean;)V + public final fun setValue (Lorg/partiql/ast/Expr;)V + public final fun value (Lorg/partiql/ast/Expr;)Lorg/partiql/ast/builder/ExprIsFalseBuilder; +} + +public final class org/partiql/ast/builder/ExprIsMissingBuilder { + public fun ()V + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public synthetic fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/ast/Expr$IsMissing; + public final fun getNot ()Ljava/lang/Boolean; + public final fun getValue ()Lorg/partiql/ast/Expr; + public final fun not (Ljava/lang/Boolean;)Lorg/partiql/ast/builder/ExprIsMissingBuilder; + public final fun setNot (Ljava/lang/Boolean;)V + public final fun setValue (Lorg/partiql/ast/Expr;)V + public final fun value (Lorg/partiql/ast/Expr;)Lorg/partiql/ast/builder/ExprIsMissingBuilder; +} + +public final class org/partiql/ast/builder/ExprIsNullBuilder { + public fun ()V + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public synthetic fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/ast/Expr$IsNull; + public final fun getNot ()Ljava/lang/Boolean; + public final fun getValue ()Lorg/partiql/ast/Expr; + public final fun not (Ljava/lang/Boolean;)Lorg/partiql/ast/builder/ExprIsNullBuilder; + public final fun setNot (Ljava/lang/Boolean;)V + public final fun setValue (Lorg/partiql/ast/Expr;)V + public final fun value (Lorg/partiql/ast/Expr;)Lorg/partiql/ast/builder/ExprIsNullBuilder; +} + +public final class org/partiql/ast/builder/ExprIsTrueBuilder { + public fun ()V + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public synthetic fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/ast/Expr$IsTrue; + public final fun getNot ()Ljava/lang/Boolean; + public final fun getValue ()Lorg/partiql/ast/Expr; + public final fun not (Ljava/lang/Boolean;)Lorg/partiql/ast/builder/ExprIsTrueBuilder; + public final fun setNot (Ljava/lang/Boolean;)V + public final fun setValue (Lorg/partiql/ast/Expr;)V + public final fun value (Lorg/partiql/ast/Expr;)Lorg/partiql/ast/builder/ExprIsTrueBuilder; +} + +public final class org/partiql/ast/builder/ExprIsUnknownBuilder { + public fun ()V + public fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;)V + public synthetic fun (Lorg/partiql/ast/Expr;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/ast/Expr$IsUnknown; + public final fun getNot ()Ljava/lang/Boolean; + public final fun getValue ()Lorg/partiql/ast/Expr; + public final fun not (Ljava/lang/Boolean;)Lorg/partiql/ast/builder/ExprIsUnknownBuilder; public final fun setNot (Ljava/lang/Boolean;)V - public final fun setType (Lorg/partiql/ast/Type;)V public final fun setValue (Lorg/partiql/ast/Expr;)V - public final fun type (Lorg/partiql/ast/Type;)Lorg/partiql/ast/builder/ExprIsTypeBuilder; - public final fun value (Lorg/partiql/ast/Expr;)Lorg/partiql/ast/builder/ExprIsTypeBuilder; + public final fun value (Lorg/partiql/ast/Expr;)Lorg/partiql/ast/builder/ExprIsUnknownBuilder; } public final class org/partiql/ast/builder/ExprLikeBuilder { @@ -6442,8 +6585,16 @@ public abstract class org/partiql/ast/sql/SqlDialect : org/partiql/ast/visitor/A public fun visitExprInCollection (Lorg/partiql/ast/Expr$InCollection;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprIon (Lorg/partiql/ast/Expr$Ion;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprIon (Lorg/partiql/ast/Expr$Ion;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; - public synthetic fun visitExprIsType (Lorg/partiql/ast/Expr$IsType;Ljava/lang/Object;)Ljava/lang/Object; - public fun visitExprIsType (Lorg/partiql/ast/Expr$IsType;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprIsFalse (Lorg/partiql/ast/Expr$IsFalse;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsFalse (Lorg/partiql/ast/Expr$IsFalse;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprIsMissing (Lorg/partiql/ast/Expr$IsMissing;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsMissing (Lorg/partiql/ast/Expr$IsMissing;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprIsNull (Lorg/partiql/ast/Expr$IsNull;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsNull (Lorg/partiql/ast/Expr$IsNull;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprIsTrue (Lorg/partiql/ast/Expr$IsTrue;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsTrue (Lorg/partiql/ast/Expr$IsTrue;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprIsUnknown (Lorg/partiql/ast/Expr$IsUnknown;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsUnknown (Lorg/partiql/ast/Expr$IsUnknown;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprLike (Lorg/partiql/ast/Expr$Like;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprLike (Lorg/partiql/ast/Expr$Like;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprLit (Lorg/partiql/ast/Expr$Lit;Ljava/lang/Object;)Ljava/lang/Object; @@ -6719,8 +6870,16 @@ public abstract class org/partiql/ast/util/AstRewriter : org/partiql/ast/visitor public fun visitExprInCollection (Lorg/partiql/ast/Expr$InCollection;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprIon (Lorg/partiql/ast/Expr$Ion;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprIon (Lorg/partiql/ast/Expr$Ion;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; - public synthetic fun visitExprIsType (Lorg/partiql/ast/Expr$IsType;Ljava/lang/Object;)Ljava/lang/Object; - public fun visitExprIsType (Lorg/partiql/ast/Expr$IsType;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprIsFalse (Lorg/partiql/ast/Expr$IsFalse;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsFalse (Lorg/partiql/ast/Expr$IsFalse;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprIsMissing (Lorg/partiql/ast/Expr$IsMissing;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsMissing (Lorg/partiql/ast/Expr$IsMissing;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprIsNull (Lorg/partiql/ast/Expr$IsNull;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsNull (Lorg/partiql/ast/Expr$IsNull;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprIsTrue (Lorg/partiql/ast/Expr$IsTrue;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsTrue (Lorg/partiql/ast/Expr$IsTrue;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprIsUnknown (Lorg/partiql/ast/Expr$IsUnknown;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsUnknown (Lorg/partiql/ast/Expr$IsUnknown;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprLike (Lorg/partiql/ast/Expr$Like;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprLike (Lorg/partiql/ast/Expr$Like;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprLit (Lorg/partiql/ast/Expr$Lit;Ljava/lang/Object;)Ljava/lang/Object; @@ -7037,7 +7196,11 @@ public abstract class org/partiql/ast/visitor/AstBaseVisitor : org/partiql/ast/v public fun visitExprExtract (Lorg/partiql/ast/Expr$Extract;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprInCollection (Lorg/partiql/ast/Expr$InCollection;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprIon (Lorg/partiql/ast/Expr$Ion;Ljava/lang/Object;)Ljava/lang/Object; - public fun visitExprIsType (Lorg/partiql/ast/Expr$IsType;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsFalse (Lorg/partiql/ast/Expr$IsFalse;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsMissing (Lorg/partiql/ast/Expr$IsMissing;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsNull (Lorg/partiql/ast/Expr$IsNull;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsTrue (Lorg/partiql/ast/Expr$IsTrue;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprIsUnknown (Lorg/partiql/ast/Expr$IsUnknown;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprLike (Lorg/partiql/ast/Expr$Like;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprLit (Lorg/partiql/ast/Expr$Lit;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprMatch (Lorg/partiql/ast/Expr$Match;Ljava/lang/Object;)Ljava/lang/Object; @@ -7232,7 +7395,11 @@ public abstract interface class org/partiql/ast/visitor/AstVisitor { public abstract fun visitExprExtract (Lorg/partiql/ast/Expr$Extract;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitExprInCollection (Lorg/partiql/ast/Expr$InCollection;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitExprIon (Lorg/partiql/ast/Expr$Ion;Ljava/lang/Object;)Ljava/lang/Object; - public abstract fun visitExprIsType (Lorg/partiql/ast/Expr$IsType;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitExprIsFalse (Lorg/partiql/ast/Expr$IsFalse;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitExprIsMissing (Lorg/partiql/ast/Expr$IsMissing;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitExprIsNull (Lorg/partiql/ast/Expr$IsNull;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitExprIsTrue (Lorg/partiql/ast/Expr$IsTrue;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitExprIsUnknown (Lorg/partiql/ast/Expr$IsUnknown;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitExprLike (Lorg/partiql/ast/Expr$Like;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitExprLit (Lorg/partiql/ast/Expr$Lit;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitExprMatch (Lorg/partiql/ast/Expr$Match;Ljava/lang/Object;)Ljava/lang/Object; diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt index cd25a2fde..9ab2ccf77 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt @@ -520,16 +520,38 @@ private class AstTranslator(val metas: Map) : AstBaseVisi } } - override fun visitExprIsType(node: Expr.IsType, ctx: Ctx) = translate(node) { metas -> + override fun visitExprIsNull(node: Expr.IsNull, ctx: Ctx) = translate(node) { metas -> val value = visitExpr(node.value, ctx) - val type = visitType(node.type, ctx) - if (node.not != null && node.not!!) { + val type = nullType() + if (node.not != null && node.not) { not(isType(value, type), metas) } else { isType(value, type, metas) } } + override fun visitExprIsMissing(node: Expr.IsMissing, ctx: Ctx) = translate(node) { metas -> + val value = visitExpr(node.value, ctx) + val type = missingType() + if (node.not != null && node.not) { + not(isType(value, type), metas) + } else { + isType(value, type, metas) + } + } + + override fun visitExprIsTrue(node: Expr.IsTrue, ctx: Ctx): PartiqlAst.PartiqlAstNode { + error("IS [ NOT ] TRUE is not supported in the legacy AST") + } + + override fun visitExprIsFalse(node: Expr.IsFalse, ctx: Ctx): PartiqlAst.PartiqlAstNode { + error("IS [ NOT ] FALSE is not supported in the legacy AST") + } + + override fun visitExprIsUnknown(node: Expr.IsUnknown, ctx: Ctx): PartiqlAst.PartiqlAstNode { + error("IS [ NOT ] UNKNOWN is not supported in the legacy AST") + } + override fun visitExprCase(node: Expr.Case, ctx: Ctx) = translate(node) { metas -> val cases = exprPairList(node.branches.translate(ctx)) val condition = visitOrNull(node.expr, ctx) diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt index 0b3b18043..c2e6bb9a9 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt @@ -376,12 +376,32 @@ public abstract class SqlDialect : AstBaseVisitor() { return h } - override fun visitExprIsType(node: Expr.IsType, head: SqlBlock): SqlBlock { - var h = head - h = visitExprWrapped(node.value, h) - h = h concat if (node.not == true) r(" IS NOT ") else r(" IS ") - h = visitType(node.type, h) - return h + override fun visitExprIsNull(node: Expr.IsNull, head: SqlBlock): SqlBlock { + return visitExprIsType(node.value, node.not, "NULL", head) + } + + override fun visitExprIsMissing(node: Expr.IsMissing, head: SqlBlock): SqlBlock { + return visitExprIsType(node.value, node.not, "MISSING", head) + } + + override fun visitExprIsTrue(node: Expr.IsTrue, head: SqlBlock): SqlBlock { + return visitExprIsType(node.value, node.not, "TRUE", head) + } + + override fun visitExprIsFalse(node: Expr.IsFalse, head: SqlBlock): SqlBlock { + return visitExprIsType(node.value, node.not, "FALSE", head) + } + + override fun visitExprIsUnknown(node: Expr.IsUnknown, head: SqlBlock): SqlBlock { + return visitExprIsType(node.value, node.not, "UNKNOWN", head) + } + + private fun visitExprIsType(value: Expr, not: Boolean?, rhs: String, head: SqlBlock): SqlBlock { + var t = head + t = visitExprWrapped(value, t) + t = t concat if (not == true) " IS NOT " else " IS " + t = t concat rhs + return t } override fun visitExprCase(node: Expr.Case, head: SqlBlock): SqlBlock { diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/sql/internal/InternalSqlDialect.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/sql/internal/InternalSqlDialect.kt index 97917bb9a..c878f23d7 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/sql/internal/InternalSqlDialect.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/sql/internal/InternalSqlDialect.kt @@ -404,11 +404,31 @@ internal abstract class InternalSqlDialect : AstBaseVisitor nullType() + PartiQLParser.MISSING -> missingType() + else -> error("Unexpected value for absent predicate IS [NULL|MISSING]") + } val isType = isType(lhs, rhs, ctx.IS().getSourceMetaContainer()) if (ctx.NOT() == null) return@build isType not(isType, ctx.NOT().getSourceMetaContainer() + metaContainerOf(LegacyLogicalNotMeta.instance)) diff --git a/partiql-parser/src/main/antlr/PartiQLParser.g4 b/partiql-parser/src/main/antlr/PartiQLParser.g4 index 54be03c23..98d63f5c8 100644 --- a/partiql-parser/src/main/antlr/PartiQLParser.g4 +++ b/partiql-parser/src/main/antlr/PartiQLParser.g4 @@ -552,7 +552,7 @@ joinType * 4. Addition, Subtraction (ex: a + b) * 5. Other operators (ex: a || b, a & b) * 6. Predicates (ex: a LIKE b, a < b, a IN b, a = b) - * 7. IS true/false. Not yet implemented in PartiQL, but defined in SQL-92. (ex: a IS TRUE) + * 7. IS TRUE|FALSE|UNKNOWN * 8. NOT (ex: NOT a) * 8. AND (ex: a AND b) * 9. OR (ex: a OR b) @@ -596,12 +596,18 @@ exprAnd exprNot : op=NOT rhs=exprNot # Not - | parent=exprPredicate # ExprNotBase + | parent=exprTest # ExprNotBase + ; + +exprTest + : exprTest IS NOT? value=(TRUE|FALSE|UNKNOWN) # Test + | parent=exprPredicate # ExprTestBase ; exprPredicate : lhs=exprPredicate op=comparisonOp rhs=mathOp00 # PredicateComparison - | lhs=exprPredicate IS NOT? type # PredicateIs + | lhs=exprPredicate IS NOT? absent=(NULL|MISSING) # PredicateAbsent + | lhs=exprPredicate IS NOT? type # PredicateType | lhs=exprPredicate NOT? IN PAREN_LEFT expr PAREN_RIGHT # PredicateIn | lhs=exprPredicate NOT? IN rhs=mathOp00 # PredicateIn | lhs=exprPredicate NOT? LIKE rhs=mathOp00 ( ESCAPE escape=expr )? # PredicateLike diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index ab893442d..5e56d08f1 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -84,7 +84,11 @@ import org.partiql.ast.exprDateDiff import org.partiql.ast.exprExtract import org.partiql.ast.exprInCollection import org.partiql.ast.exprIon -import org.partiql.ast.exprIsType +import org.partiql.ast.exprIsFalse +import org.partiql.ast.exprIsMissing +import org.partiql.ast.exprIsNull +import org.partiql.ast.exprIsTrue +import org.partiql.ast.exprIsUnknown import org.partiql.ast.exprLike import org.partiql.ast.exprLit import org.partiql.ast.exprMatch @@ -1583,6 +1587,17 @@ internal class PartiQLParserDefault : PartiQLParser { exprNot(expr) } + override fun visitTest(ctx: GeneratedParser.TestContext) = translate(ctx) { + val expr = visit(ctx.exprTest()) as Expr + val not: Boolean = ctx.NOT() != null + when (ctx.value.type) { + GeneratedParser.TRUE -> exprIsTrue(expr, not) + GeneratedParser.FALSE -> exprIsFalse(expr, not) + GeneratedParser.UNKNOWN -> exprIsUnknown(expr, not) + else -> throw error(ctx, "Unexpected value for boolean test IS [TRUE|FALSE|UNKNOWN]") + } + } + private fun checkForInvalidTokens(op: ParserRuleContext) { val start = op.start.tokenIndex val stop = op.stop.tokenIndex @@ -1668,11 +1683,22 @@ internal class PartiQLParserDefault : PartiQLParser { exprInCollection(lhs, rhs, not) } - override fun visitPredicateIs(ctx: GeneratedParser.PredicateIsContext) = translate(ctx) { + override fun visitPredicateAbsent(ctx: GeneratedParser.PredicateAbsentContext) = translate(ctx) { + val value = visitAs(ctx.lhs) + val not = ctx.NOT() != null + when (ctx.absent.type) { + GeneratedParser.NULL -> exprIsNull(value, not) + GeneratedParser.MISSING -> exprIsMissing(value, not) + else -> throw error(ctx, "Unexpected value for absent predicate IS [NULL|MISSING]") + } + } + + override fun visitPredicateType(ctx: GeneratedParser.PredicateTypeContext) = translate(ctx) { val value = visitAs(ctx.lhs) val type = visitAs(ctx.type()).also { isValidTypeParameterOrThrow(it, ctx.type()) } val not = ctx.NOT() != null - exprIsType(value, type, not) + TODO() + // exprIsType(value, type, not) } override fun visitPredicateBetween(ctx: GeneratedParser.PredicateBetweenContext) = translate(ctx) { diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlTypes.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlTypes.kt new file mode 100644 index 000000000..a4d6d002a --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlTypes.kt @@ -0,0 +1,215 @@ +package org.partiql.planner.internal + +import org.partiql.ast.Type +import org.partiql.types.Field +import org.partiql.types.PType + +/** + * A factory for single-source of truth for type creations — DO NOT CREATE PTYPE DIRECTLY. + * + * This allows us to raise an interface if we need custom type factories; for now just use defaults with static methods. + */ +internal object SqlTypes { + + private const val MAX_SIZE = Int.MAX_VALUE + + // + // DYNAMIC + // + + @JvmStatic + fun dynamic(): PType = PType.typeDynamic() + + // + // BOOLEAN + // + + @JvmStatic + fun bool(): PType = PType.typeBool() + + // + // NUMERIC + // + + @JvmStatic + fun tinyint(): PType = PType.typeTinyInt() + + @JvmStatic + fun smallint(): PType = PType.typeSmallInt() + + @JvmStatic + fun int(): PType = PType.typeInt() + + @JvmStatic + fun bigint(): PType = PType.typeBigInt() + + /** + * NUMERIC represents an integer with arbitrary precision. It is equivalent to Ion’s integer type, and is conformant to SQL-99s rules for the NUMERIC type. In SQL-99, if a scale is omitted then we choose zero — and if a precision is omitted then the precision is implementation defined. For PartiQL, we define this precision to be inf — aka arbitrary precision. + * + * @param precision Defaults to inf. + * @param scale Defaults to 0. + * @return + */ + @JvmStatic + fun numeric(precision: Int? = null, scale: Int? = null): PType { + if (scale != null && precision == null) { + error("Precision can never be null while scale is specified.") + } + return when { + precision != null && scale != null -> PType.typeDecimal(precision, scale) + precision != null -> PType.typeDecimal(precision, 0) + else -> PType.typeIntArbitrary() + } + } + + /** + * DECIMAL represents an exact numeric type with arbitrary precision and arbitrary scale. It is equivalent to Ion’s decimal type. For a DECIMAL with no given scale we choose inf rather than the SQL prescribed 0 (zero). Here we diverge from SQL-99 for Ion compatibility. Finally, SQL defines decimals as having precision equal to or greater than the given precision. Like other systems, we truncate extraneous precision so that NUMERIC(p,s) is equivalent to DECIMAL(p,s). The only difference between them is the default scale when it’s not specified — we follow SQL-99 for NUMERIC, and we follow Postgres for DECIMAL. + * + * @param precision Defaults to inf. + * @param scale Defaults to 0 when precision is given, otherwise inf. + * @return + */ + @JvmStatic + fun decimal(precision: Int? = null, scale: Int? = null): PType { + if (scale != null && precision == null) { + error("Precision can never be null while scale is specified.") + } + return when { + precision != null && scale != null -> PType.typeDecimal(precision, scale) + precision != null -> PType.typeDecimal(precision, 0) + else -> PType.typeDecimalArbitrary() + } + } + + @JvmStatic + fun real(): PType = PType.typeReal() + + @JvmStatic + fun double(): PType = PType.typeDoublePrecision() + + // + // CHARACTER STRINGS + // + + @JvmStatic + fun char(length: Int? = null): PType = PType.typeChar(length ?: 1) + + @JvmStatic + fun varchar(length: Int? = null): PType = PType.typeVarChar(length ?: MAX_SIZE) + + @JvmStatic + fun string(): PType = PType.typeString() + + @JvmStatic + fun clob(length: Int? = null) = PType.typeClob(length ?: MAX_SIZE) + + // + // BIT STRINGS + // + + @JvmStatic + fun blob(length: Int? = null) = PType.typeBlob(length ?: MAX_SIZE) + + // + // DATETIME + // + + @JvmStatic + fun date(): PType = PType.typeDate() + + @JvmStatic + fun time(precision: Int? = null): PType = PType.typeTimeWithoutTZ(precision ?: 6) + + @JvmStatic + fun timez(precision: Int? = null): PType = PType.typeTimeWithTZ(precision ?: 6) + + @JvmStatic + fun timestamp(precision: Int? = null): PType = PType.typeTimeWithoutTZ(precision ?: 6) + + @JvmStatic + fun timestampz(precision: Int? = null): PType = PType.typeTimestampWithTZ(precision ?: 6) + + // + // COLLECTIONS + // + + @JvmStatic + fun array(element: PType? = null, size: Int? = null): PType { + if (size != null) { + error("Fixed-length ARRAY [N] is not supported.") + } + return when (element) { + null -> PType.typeList() + else -> PType.typeList(element) + } + } + + @JvmStatic + fun bag(element: PType? = null, size: Int? = null): PType { + if (size != null) { + error("Fixed-length BAG [N] is not supported.") + } + return when (element) { + null -> PType.typeBag() + else -> PType.typeBag(element) + } + } + + // + // STRUCTURAL + // + + @JvmStatic + fun struct(): PType = PType.typeStruct() + + @JvmStatic + fun row(fields: List): PType = PType.typeRow(fields) + + /** + * Create PType from the AST type. + */ + @JvmStatic + fun from(type: Type): PType = when (type) { + is Type.NullType -> error("Casting to NULL is not supported.") + is Type.Missing -> error("Casting to MISSING is not supported.") + is Type.Bool -> bool() + is Type.Tinyint -> tinyint() + is Type.Smallint, is Type.Int2 -> smallint() + is Type.Int4, is Type.Int -> int() + is Type.Bigint, is Type.Int8 -> bigint() + is Type.Numeric -> numeric(type.precision, type.scale) + is Type.Decimal -> decimal(type.precision, type.scale) + is Type.Real -> real() + is Type.Float32 -> real() + is Type.Float64 -> double() + is Type.Char -> char(type.length) + is Type.Varchar -> varchar(type.length) + is Type.String -> string() + is Type.Symbol -> { + // TODO will we continue supporting symbol? + PType.typeSymbol() + } + is Type.Bit -> error("BIT is not supported yet.") + is Type.BitVarying -> error("BIT VARYING is not supported yet.") + is Type.ByteString -> error("BINARY is not supported yet.") + is Type.Blob -> blob(type.length) + is Type.Clob -> clob(type.length) + is Type.Date -> date() + is Type.Time -> time(type.precision) + is Type.TimeWithTz -> timez(type.precision) + is Type.Timestamp -> timestamp(type.precision) + is Type.TimestampWithTz -> timestampz(type.precision) + is Type.Interval -> error("INTERVAL is not supported yet.") + is Type.Bag -> bag() + is Type.Sexp -> { + // TODO will we continue supporting s-expression? + PType.typeSexp() + } + is Type.Any -> dynamic() + is Type.List -> array() + is Type.Array -> array(type.type?.let { from(it) }) + is Type.Tuple -> struct() + is Type.Struct -> struct() + is Type.Custom -> TODO("Custom type not supported ") + } +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt index cd2649dd6..e0f80a466 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt @@ -22,7 +22,6 @@ import org.partiql.ast.Select import org.partiql.ast.exprCall import org.partiql.ast.exprCase import org.partiql.ast.exprCaseBranch -import org.partiql.ast.exprIsType import org.partiql.ast.exprLit import org.partiql.ast.exprStruct import org.partiql.ast.exprStructField @@ -32,7 +31,6 @@ import org.partiql.ast.identifierSymbol import org.partiql.ast.selectProject import org.partiql.ast.selectProjectItemExpression import org.partiql.ast.selectValue -import org.partiql.ast.typeStruct import org.partiql.ast.util.AstRewriter import org.partiql.value.PartiQLValueExperimental import org.partiql.value.stringValue @@ -173,7 +171,10 @@ internal object NormalizeSelect { } } - override fun visitSelectProjectItemExpression(node: Select.Project.Item.Expression, ctx: () -> Int): Select.Project.Item.Expression { + override fun visitSelectProjectItemExpression( + node: Select.Project.Item.Expression, + ctx: () -> Int, + ): Select.Project.Item.Expression { val expr = visitExpr(node.expr, newCtx()) as Expr val alias = when (node.asAlias) { null -> expr.toBinder(ctx) @@ -266,7 +267,8 @@ internal object NormalizeSelect { @OptIn(PartiQLValueExperimental::class) private fun visitSelectProjectWithoutProjectAll(node: Select.Project): Select.Value { val structFields = node.items.map { item -> - val itemExpr = item as? Select.Project.Item.Expression ?: error("Expected the projection to be an expression.") + val itemExpr = + item as? Select.Project.Item.Expression ?: error("Expected the projection to be an expression.") exprStructField( name = exprLit(stringValue(itemExpr.asAlias?.symbol!!)), value = item.expr @@ -280,12 +282,15 @@ internal object NormalizeSelect { ) } - @OptIn(PartiQLValueExperimental::class) private fun buildCaseWhenStruct(expr: Expr, index: Int): Expr.Case = exprCase( expr = null, branches = listOf( exprCaseBranch( - condition = exprIsType(expr, typeStruct(emptyList()), null), + condition = exprCall( + function = id("is_struct"), + args = listOf(expr), + setq = null, + ), expr = expr ) ), diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt index 26e6c90a7..505fa2dcb 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt @@ -621,60 +621,52 @@ internal object RexConverter { return rex(type, call) } - /** - * IS ? - */ - override fun visitExprIsType(node: Expr.IsType, ctx: Env): Rex { - val type = BOOL - // arg + override fun visitExprIsNull(node: Expr.IsNull, ctx: Env): Rex { val arg0 = visitExprCoerce(node.value, ctx) + var call = call("is_null", arg0) + if (node.not == true) { + call = negate(call) + } + return rex(BOOL, call) + } + + override fun visitExprIsMissing(node: Expr.IsMissing, ctx: Env): Rex { + val arg0 = visitExprCoerce(node.value, ctx) + var call = call("is_missing", arg0) + if (node.not == true) { + call = negate(call) + } + return rex(BOOL, call) + } - var call = when (val targetType = node.type) { - is Type.NullType -> call("is_null", arg0) - is Type.Missing -> call("is_missing", arg0) - is Type.Bool -> call("is_bool", arg0) - is Type.Tinyint -> call("is_int8", arg0) - is Type.Smallint, is Type.Int2 -> call("is_int16", arg0) - is Type.Int4 -> call("is_int32", arg0) - is Type.Bigint, is Type.Int8 -> call("is_int64", arg0) - is Type.Int -> call("is_int", arg0) - is Type.Real -> call("is_real", arg0) - is Type.Float32 -> call("is_float32", arg0) - is Type.Float64 -> call("is_float64", arg0) - is Type.Decimal -> call("is_decimal", targetType.precision.toRex(), targetType.scale.toRex(), arg0) - is Type.Numeric -> call("is_numeric", targetType.precision.toRex(), targetType.scale.toRex(), arg0) - is Type.Char -> call("is_char", targetType.length.toRex(), arg0) - is Type.Varchar -> call("is_varchar", targetType.length.toRex(), arg0) - is Type.String -> call("is_string", targetType.length.toRex(), arg0) - is Type.Symbol -> call("is_symbol", arg0) - is Type.Bit -> call("is_bit", arg0) - is Type.BitVarying -> call("is_bitVarying", arg0) - is Type.ByteString -> call("is_byteString", arg0) - is Type.Blob -> call("is_blob", arg0) - is Type.Clob -> call("is_clob", arg0) - is Type.Date -> call("is_date", arg0) - is Type.Time -> call("is_time", arg0) - // TODO: DO we want to seperate with time zone vs without time zone into two different type in the plan? - // leave the parameterized type out for now until the above is answered - is Type.TimeWithTz -> call("is_timeWithTz", arg0) - is Type.Timestamp -> call("is_timestamp", arg0) - is Type.TimestampWithTz -> call("is_timestampWithTz", arg0) - is Type.Interval -> call("is_interval", arg0) - is Type.Bag -> call("is_bag", arg0) - is Type.Sexp -> call("is_sexp", arg0) - is Type.Any -> call("is_any", arg0) - is Type.Custom -> call("is_custom", arg0) - is Type.List -> call("is_list", arg0) - is Type.Tuple -> call("is_tuple", arg0) - // Note that for is function, the parser will reject parameterized list/struct - is Type.Array -> call("is_list", arg0) - is Type.Struct -> call("is_struct", arg0) + override fun visitExprIsTrue(node: Expr.IsTrue, ctx: Env): Rex { + val arg0 = visitExprCoerce(node.value, ctx) + var call = call("is_true", arg0) + if (node.not == true) { + call = negate(call) } + return rex(BOOL, call) + } + override fun visitExprIsFalse(node: Expr.IsFalse, ctx: Env): Rex { + val arg0 = visitExprCoerce(node.value, ctx) + var call = call("is_false", arg0) if (node.not == true) { call = negate(call) } + return rex(BOOL, call) + } + /** + * SQL-99 does not make a distinction between the null boolean and the unknown truth value + */ + override fun visitExprIsUnknown(node: Expr.IsUnknown, ctx: Env): Rex { + val type = BOOL + val arg0 = visitExprCoerce(node.value, ctx) + var call = call("is_null", arg0) + if (node.not == true) { + call = negate(call) + } return rex(type, call) } diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt index bc13d3b52..226ef2056 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt @@ -272,11 +272,8 @@ internal object SqlBuiltins { Fn_IS_STRING__INT32_ANY__BOOL, Fn_IS_STRING__ANY__BOOL, Fn_IS_STRUCT__ANY__BOOL, - Fn_IS_SYMBOL__ANY__BOOL, - Fn_IS_TIME__BOOL_INT32_ANY__BOOL, - Fn_IS_TIME__ANY__BOOL, - Fn_IS_TIMESTAMP__BOOL_INT32_ANY__BOOL, - Fn_IS_TIMESTAMP__ANY__BOOL, + Fn_IS_TRUE__ANY__BOOL, + Fn_IS_FALSE__ANY__BOOL, Fn_LIKE__STRING_STRING__BOOL, Fn_LIKE__CLOB_CLOB__BOOL, Fn_LIKE__SYMBOL_SYMBOL__BOOL, diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsFalse.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsFalse.kt new file mode 100644 index 000000000..46f774874 --- /dev/null +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsFalse.kt @@ -0,0 +1,38 @@ +// ktlint-disable filename +@file:Suppress("ClassName") + +package org.partiql.spi.connector.sql.builtins + +import org.partiql.spi.fn.Fn +import org.partiql.spi.fn.FnExperimental +import org.partiql.spi.fn.FnParameter +import org.partiql.spi.fn.FnSignature +import org.partiql.value.BoolValue +import org.partiql.value.PartiQLValue +import org.partiql.value.PartiQLValueExperimental +import org.partiql.value.PartiQLValueType.ANY +import org.partiql.value.PartiQLValueType.BOOL +import org.partiql.value.boolValue +import org.partiql.value.check + +@OptIn(PartiQLValueExperimental::class, FnExperimental::class) +internal object Fn_IS_FALSE__ANY__BOOL : Fn { + + override val signature = FnSignature( + name = "is_false", + returns = BOOL, + parameters = listOf(FnParameter("value", ANY)), + isNullable = false, + isNullCall = false, + isMissable = false, + isMissingCall = false, + ) + + override fun invoke(args: Array): PartiQLValue { + val arg = args[0] + if (arg.type != BOOL) { + return boolValue(false) + } + return boolValue(arg.check().value == true) + } +} diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsTrue.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsTrue.kt new file mode 100644 index 000000000..b1672b689 --- /dev/null +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsTrue.kt @@ -0,0 +1,38 @@ +// ktlint-disable filename +@file:Suppress("ClassName") + +package org.partiql.spi.connector.sql.builtins + +import org.partiql.spi.fn.Fn +import org.partiql.spi.fn.FnExperimental +import org.partiql.spi.fn.FnParameter +import org.partiql.spi.fn.FnSignature +import org.partiql.value.BoolValue +import org.partiql.value.PartiQLValue +import org.partiql.value.PartiQLValueExperimental +import org.partiql.value.PartiQLValueType.ANY +import org.partiql.value.PartiQLValueType.BOOL +import org.partiql.value.boolValue +import org.partiql.value.check + +@OptIn(PartiQLValueExperimental::class, FnExperimental::class) +internal object Fn_IS_TRUE__ANY__BOOL : Fn { + + override val signature = FnSignature( + name = "is_true", + returns = BOOL, + parameters = listOf(FnParameter("value", ANY)), + isNullable = false, + isNullCall = false, + isMissable = false, + isMissingCall = false, + ) + + override fun invoke(args: Array): PartiQLValue { + val arg = args[0] + if (arg.type != BOOL) { + return boolValue(false) + } + return boolValue(arg.check().value == true) + } +}