diff --git a/partiql-ast/api/partiql-ast.api b/partiql-ast/api/partiql-ast.api index 06f809ca2..aaf87c904 100644 --- a/partiql-ast/api/partiql-ast.api +++ b/partiql-ast/api/partiql-ast.api @@ -40,6 +40,7 @@ public final class org/partiql/ast/Ast { public static final fun exprArray (Ljava/util/List;)Lorg/partiql/ast/expr/ExprArray; public static final fun exprBag (Ljava/util/List;)Lorg/partiql/ast/expr/ExprBag; public static final fun exprBetween (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Z)Lorg/partiql/ast/expr/ExprBetween; + public static final fun exprBoolTest (Lorg/partiql/ast/expr/Expr;ZLorg/partiql/ast/expr/TruthValue;)Lorg/partiql/ast/expr/ExprBoolTest; public static final fun exprCall (Lorg/partiql/ast/IdentifierChain;Ljava/util/List;)Lorg/partiql/ast/expr/ExprCall; public static final fun exprCall (Lorg/partiql/ast/IdentifierChain;Ljava/util/List;Lorg/partiql/ast/SetQuantifier;)Lorg/partiql/ast/expr/ExprCall; public static synthetic fun exprCall$default (Lorg/partiql/ast/IdentifierChain;Ljava/util/List;Lorg/partiql/ast/SetQuantifier;ILjava/lang/Object;)Lorg/partiql/ast/expr/ExprCall; @@ -58,8 +59,10 @@ public final class org/partiql/ast/Ast { public static synthetic fun exprLike$default (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;ZILjava/lang/Object;)Lorg/partiql/ast/expr/ExprLike; public static final fun exprLit (Lorg/partiql/ast/Literal;)Lorg/partiql/ast/expr/ExprLit; public static final fun exprMatch (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/graph/GraphMatch;)Lorg/partiql/ast/expr/ExprMatch; + public static final fun exprMissingPredicate (Lorg/partiql/ast/expr/Expr;Z)Lorg/partiql/ast/expr/ExprMissingPredicate; public static final fun exprNot (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNot; public static final fun exprNullIf (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNullIf; + public static final fun exprNullPredicate (Lorg/partiql/ast/expr/Expr;Z)Lorg/partiql/ast/expr/ExprNullPredicate; public static final fun exprOperator (Ljava/lang/String;Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprOperator; public static final fun exprOperator (Ljava/lang/String;Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprOperator; public static synthetic fun exprOperator$default (Ljava/lang/String;Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;ILjava/lang/Object;)Lorg/partiql/ast/expr/ExprOperator; @@ -284,6 +287,8 @@ public abstract class org/partiql/ast/AstRewriter : org/partiql/ast/AstVisitor { public fun visitExprBag (Lorg/partiql/ast/expr/ExprBag;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprBetween (Lorg/partiql/ast/expr/ExprBetween;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprBetween (Lorg/partiql/ast/expr/ExprBetween;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprBoolTest (Lorg/partiql/ast/expr/ExprBoolTest;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprBoolTest (Lorg/partiql/ast/expr/ExprBoolTest;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprCall (Lorg/partiql/ast/expr/ExprCall;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprCall (Lorg/partiql/ast/expr/ExprCall;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprCase (Lorg/partiql/ast/expr/ExprCase;Ljava/lang/Object;)Ljava/lang/Object; @@ -306,10 +311,14 @@ public abstract class org/partiql/ast/AstRewriter : org/partiql/ast/AstVisitor { public fun visitExprLit (Lorg/partiql/ast/expr/ExprLit;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprMatch (Lorg/partiql/ast/expr/ExprMatch;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprMatch (Lorg/partiql/ast/expr/ExprMatch;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprMissingPredicate (Lorg/partiql/ast/expr/ExprMissingPredicate;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprMissingPredicate (Lorg/partiql/ast/expr/ExprMissingPredicate;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprNot (Lorg/partiql/ast/expr/ExprNot;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprNot (Lorg/partiql/ast/expr/ExprNot;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprNullIf (Lorg/partiql/ast/expr/ExprNullIf;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprNullIf (Lorg/partiql/ast/expr/ExprNullIf;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitExprNullPredicate (Lorg/partiql/ast/expr/ExprNullPredicate;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprNullPredicate (Lorg/partiql/ast/expr/ExprNullPredicate;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprOperator (Lorg/partiql/ast/expr/ExprOperator;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprOperator (Lorg/partiql/ast/expr/ExprOperator;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitExprOr (Lorg/partiql/ast/expr/ExprOr;Ljava/lang/Object;)Ljava/lang/Object; @@ -471,6 +480,7 @@ public abstract class org/partiql/ast/AstVisitor { public fun visitExprArray (Lorg/partiql/ast/expr/ExprArray;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprBag (Lorg/partiql/ast/expr/ExprBag;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprBetween (Lorg/partiql/ast/expr/ExprBetween;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprBoolTest (Lorg/partiql/ast/expr/ExprBoolTest;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprCall (Lorg/partiql/ast/expr/ExprCall;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprCase (Lorg/partiql/ast/expr/ExprCase;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprCaseBranch (Lorg/partiql/ast/expr/ExprCase$Branch;Ljava/lang/Object;)Ljava/lang/Object; @@ -482,8 +492,10 @@ public abstract class org/partiql/ast/AstVisitor { public fun visitExprLike (Lorg/partiql/ast/expr/ExprLike;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprLit (Lorg/partiql/ast/expr/ExprLit;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprMatch (Lorg/partiql/ast/expr/ExprMatch;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprMissingPredicate (Lorg/partiql/ast/expr/ExprMissingPredicate;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprNot (Lorg/partiql/ast/expr/ExprNot;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprNullIf (Lorg/partiql/ast/expr/ExprNullIf;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprNullPredicate (Lorg/partiql/ast/expr/ExprNullPredicate;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprOperator (Lorg/partiql/ast/expr/ExprOperator;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprOr (Lorg/partiql/ast/expr/ExprOr;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprOverlay (Lorg/partiql/ast/expr/ExprOverlay;Ljava/lang/Object;)Ljava/lang/Object; @@ -607,8 +619,6 @@ public class org/partiql/ast/DataType : org/partiql/ast/AstEnum { public static final field INTEGER8 I public static final field INTERVAL I public static final field LIST I - public static final field MISSING I - public static final field NULL I public static final field NUMERIC I public static final field REAL I public static final field SEXP I @@ -673,8 +683,6 @@ public class org/partiql/ast/DataType : org/partiql/ast/AstEnum { public static fun INTEGER8 ()Lorg/partiql/ast/DataType; public static fun INTERVAL ()Lorg/partiql/ast/DataType; public static fun LIST ()Lorg/partiql/ast/DataType; - public static fun MISSING ()Lorg/partiql/ast/DataType; - public static fun NULL ()Lorg/partiql/ast/DataType; public static fun NUMERIC ()Lorg/partiql/ast/DataType; public static fun NUMERIC (I)Lorg/partiql/ast/DataType; public static fun NUMERIC (II)Lorg/partiql/ast/DataType; @@ -2070,6 +2078,27 @@ public class org/partiql/ast/expr/ExprBetween$Builder { public fun value (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprBetween$Builder; } +public class org/partiql/ast/expr/ExprBoolTest : org/partiql/ast/expr/Expr { + public final field not Z + public final field truthValue Lorg/partiql/ast/expr/TruthValue; + public final field value Lorg/partiql/ast/expr/Expr; + public fun (Lorg/partiql/ast/expr/Expr;ZLorg/partiql/ast/expr/TruthValue;)V + public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static fun builder ()Lorg/partiql/ast/expr/ExprBoolTest$Builder; + protected fun canEqual (Ljava/lang/Object;)Z + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I +} + +public class org/partiql/ast/expr/ExprBoolTest$Builder { + public fun build ()Lorg/partiql/ast/expr/ExprBoolTest; + public fun not (Z)Lorg/partiql/ast/expr/ExprBoolTest$Builder; + public fun toString ()Ljava/lang/String; + public fun truthValue (Lorg/partiql/ast/expr/TruthValue;)Lorg/partiql/ast/expr/ExprBoolTest$Builder; + public fun value (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprBoolTest$Builder; +} + public class org/partiql/ast/expr/ExprCall : org/partiql/ast/expr/Expr { public final field args Ljava/util/List; public final field function Lorg/partiql/ast/IdentifierChain; @@ -2280,6 +2309,25 @@ public class org/partiql/ast/expr/ExprMatch$Builder { public fun toString ()Ljava/lang/String; } +public class org/partiql/ast/expr/ExprMissingPredicate : org/partiql/ast/expr/Expr { + public final field not Z + public final field value Lorg/partiql/ast/expr/Expr; + public fun (Lorg/partiql/ast/expr/Expr;Z)V + public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static fun builder ()Lorg/partiql/ast/expr/ExprMissingPredicate$Builder; + protected fun canEqual (Ljava/lang/Object;)Z + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I +} + +public class org/partiql/ast/expr/ExprMissingPredicate$Builder { + public fun build ()Lorg/partiql/ast/expr/ExprMissingPredicate; + public fun not (Z)Lorg/partiql/ast/expr/ExprMissingPredicate$Builder; + public fun toString ()Ljava/lang/String; + public fun value (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprMissingPredicate$Builder; +} + public class org/partiql/ast/expr/ExprNot : org/partiql/ast/expr/Expr { public final field value Lorg/partiql/ast/expr/Expr; public fun (Lorg/partiql/ast/expr/Expr;)V @@ -2316,6 +2364,25 @@ public class org/partiql/ast/expr/ExprNullIf$Builder { public fun v2 (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNullIf$Builder; } +public class org/partiql/ast/expr/ExprNullPredicate : org/partiql/ast/expr/Expr { + public final field not Z + public final field value Lorg/partiql/ast/expr/Expr; + public fun (Lorg/partiql/ast/expr/Expr;Z)V + public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static fun builder ()Lorg/partiql/ast/expr/ExprNullPredicate$Builder; + protected fun canEqual (Ljava/lang/Object;)Z + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I +} + +public class org/partiql/ast/expr/ExprNullPredicate$Builder { + public fun build ()Lorg/partiql/ast/expr/ExprNullPredicate; + public fun not (Z)Lorg/partiql/ast/expr/ExprNullPredicate$Builder; + public fun toString ()Ljava/lang/String; + public fun value (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNullPredicate$Builder; +} + public class org/partiql/ast/expr/ExprOperator : org/partiql/ast/expr/Expr { public final field lhs Lorg/partiql/ast/expr/Expr; public final field rhs Lorg/partiql/ast/expr/Expr; @@ -2771,6 +2838,25 @@ public class org/partiql/ast/expr/TrimSpec : org/partiql/ast/AstEnum { public static fun parse (Ljava/lang/String;)Lorg/partiql/ast/expr/TrimSpec; } +public class org/partiql/ast/expr/TruthValue : org/partiql/ast/AstEnum { + public static final field FALSE I + public static final field TRUE I + public static final field UNK I + public static final field UNKNOWN I + public fun (I)V + public static fun FALSE ()Lorg/partiql/ast/expr/TruthValue; + public static fun TRUE ()Lorg/partiql/ast/expr/TruthValue; + public static fun UNK ()Lorg/partiql/ast/expr/TruthValue; + public static fun UNKNOWN ()Lorg/partiql/ast/expr/TruthValue; + public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + protected fun canEqual (Ljava/lang/Object;)Z + public fun code ()I + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun name ()Ljava/lang/String; +} + public class org/partiql/ast/expr/WindowFunction : org/partiql/ast/AstEnum { public static final field LAG I public static final field LEAD I @@ -3224,6 +3310,8 @@ public abstract class org/partiql/ast/sql/SqlDialect : org/partiql/ast/AstVisito public fun visitExprBag (Lorg/partiql/ast/expr/ExprBag;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprBetween (Lorg/partiql/ast/expr/ExprBetween;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprBetween (Lorg/partiql/ast/expr/ExprBetween;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprBoolTest (Lorg/partiql/ast/expr/ExprBoolTest;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprBoolTest (Lorg/partiql/ast/expr/ExprBoolTest;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprCall (Lorg/partiql/ast/expr/ExprCall;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprCall (Lorg/partiql/ast/expr/ExprCall;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprCase (Lorg/partiql/ast/expr/ExprCase;Ljava/lang/Object;)Ljava/lang/Object; @@ -3244,10 +3332,14 @@ public abstract class org/partiql/ast/sql/SqlDialect : org/partiql/ast/AstVisito public fun visitExprLike (Lorg/partiql/ast/expr/ExprLike;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprLit (Lorg/partiql/ast/expr/ExprLit;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprLit (Lorg/partiql/ast/expr/ExprLit;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprMissingPredicate (Lorg/partiql/ast/expr/ExprMissingPredicate;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprMissingPredicate (Lorg/partiql/ast/expr/ExprMissingPredicate;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprNot (Lorg/partiql/ast/expr/ExprNot;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprNot (Lorg/partiql/ast/expr/ExprNot;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprNullIf (Lorg/partiql/ast/expr/ExprNullIf;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprNullIf (Lorg/partiql/ast/expr/ExprNullIf;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; + public synthetic fun visitExprNullPredicate (Lorg/partiql/ast/expr/ExprNullPredicate;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitExprNullPredicate (Lorg/partiql/ast/expr/ExprNullPredicate;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprOperator (Lorg/partiql/ast/expr/ExprOperator;Ljava/lang/Object;)Ljava/lang/Object; public fun visitExprOperator (Lorg/partiql/ast/expr/ExprOperator;Lorg/partiql/ast/sql/SqlBlock;)Lorg/partiql/ast/sql/SqlBlock; public synthetic fun visitExprOr (Lorg/partiql/ast/expr/ExprOr;Ljava/lang/Object;)Ljava/lang/Object; diff --git a/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java b/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java index 7fabefe19..14d8c62a0 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java +++ b/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java @@ -32,6 +32,9 @@ import org.partiql.ast.expr.ExprCoalesce; import org.partiql.ast.expr.ExprExtract; import org.partiql.ast.expr.ExprInCollection; +import org.partiql.ast.expr.ExprMissingPredicate; +import org.partiql.ast.expr.ExprNullPredicate; +import org.partiql.ast.expr.ExprBoolTest; import org.partiql.ast.expr.ExprIsType; import org.partiql.ast.expr.ExprLike; import org.partiql.ast.expr.ExprLit; @@ -346,6 +349,18 @@ public R visitExprInCollection(ExprInCollection node, C ctx) { return defaultVisit(node, ctx); } + public R visitExprNullPredicate(ExprNullPredicate node, C ctx) { + return defaultVisit(node, ctx); + } + + public R visitExprMissingPredicate(ExprMissingPredicate node, C ctx) { + return defaultVisit(node, ctx); + } + + public R visitExprBoolTest(ExprBoolTest node, C ctx) { + return defaultVisit(node, ctx); + } + public R visitExprIsType(ExprIsType node, C ctx) { return defaultVisit(node, ctx); } diff --git a/partiql-ast/src/main/java/org/partiql/ast/DataType.java b/partiql-ast/src/main/java/org/partiql/ast/DataType.java index 90e6e439d..e6d9eda8d 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/DataType.java +++ b/partiql-ast/src/main/java/org/partiql/ast/DataType.java @@ -10,7 +10,6 @@ @EqualsAndHashCode(callSuper = false) public class DataType extends AstEnum { - /** * A field definition with in a Struct Type Definition */ @@ -70,80 +69,68 @@ public R accept(@NotNull AstVisitor visitor, C ctx) { } public static final int UNKNOWN = 0; - // TODO remove `NULL` and `MISSING` variants from DataType - // - public static final int NULL = 1; - public static final int MISSING = 2; // - public static final int CHARACTER = 3; - public static final int CHAR = 4; - public static final int CHARACTER_VARYING = 5; - public static final int CHAR_VARYING = 6; // TODO not defined in parser yet - public static final int VARCHAR = 7; - public static final int CHARACTER_LARGE_OBJECT = 8; // TODO not defined in parser yet - public static final int CHAR_LARGE_OBJECT = 9; // TODO not defined in parser yet - public static final int CLOB = 10; - public static final int STRING = 11; - public static final int SYMBOL = 12; + public static final int CHARACTER = 1; + public static final int CHAR = 2; + public static final int CHARACTER_VARYING = 3; + public static final int CHAR_VARYING = 4; // TODO not defined in parser yet + public static final int VARCHAR = 5; + public static final int CHARACTER_LARGE_OBJECT = 6; // TODO not defined in parser yet + public static final int CHAR_LARGE_OBJECT = 7; // TODO not defined in parser yet + public static final int CLOB = 8; + public static final int STRING = 9; + public static final int SYMBOL = 10; // - public static final int BLOB = 13; - public static final int BINARY_LARGE_OBJECT = 14; // TODO not defined in parser yet + public static final int BLOB = 11; + public static final int BINARY_LARGE_OBJECT = 12; // TODO not defined in parser yet // - public static final int BIT = 15; // TODO not defined in parser yet - public static final int BIT_VARYING = 16; // TODO not defined in parser yet + public static final int BIT = 13; // TODO not defined in parser yet + public static final int BIT_VARYING = 14; // TODO not defined in parser yet // - - public static final int NUMERIC = 17; - public static final int DECIMAL = 18; - public static final int DEC = 19; - public static final int BIGINT = 20; - public static final int INT8 = 21; - public static final int INTEGER8 = 22; - public static final int INT4 = 23; - public static final int INTEGER4 = 24; - public static final int INTEGER = 25; - public static final int INT = 26; - public static final int INT2 = 27; - public static final int INTEGER2 = 28; - public static final int SMALLINT = 29; - public static final int TINYINT = 30; // TODO not defined in parser yet + public static final int NUMERIC = 15; + public static final int DECIMAL = 16; + public static final int DEC = 17; + public static final int BIGINT = 18; + public static final int INT8 = 19; + public static final int INTEGER8 = 20; + public static final int INT4 = 21; + public static final int INTEGER4 = 22; + public static final int INTEGER = 23; + public static final int INT = 24; + public static final int INT2 = 25; + public static final int INTEGER2 = 26; + public static final int SMALLINT = 27; + public static final int TINYINT = 28; // TODO not defined in parser yet // - - public static final int FLOAT = 31; - public static final int REAL = 32; - public static final int DOUBLE_PRECISION = 33; + public static final int FLOAT = 29; + public static final int REAL = 30; + public static final int DOUBLE_PRECISION = 31; // - public static final int BOOLEAN = 34; - public static final int BOOL = 35; + public static final int BOOLEAN = 32; + public static final int BOOL = 33; // - public static final int DATE = 36; - public static final int TIME = 37; - public static final int TIME_WITH_TIME_ZONE = 38; - public static final int TIMESTAMP = 39; - public static final int TIMESTAMP_WITH_TIME_ZONE = 40; + public static final int DATE = 34; + public static final int TIME = 35; + public static final int TIME_WITH_TIME_ZONE = 36; + public static final int TIMESTAMP = 37; + public static final int TIMESTAMP_WITH_TIME_ZONE = 38; // - public static final int INTERVAL = 41; // TODO not defined in parser yet + public static final int INTERVAL = 39; // TODO not defined in parser yet // - public static final int STRUCT = 42; - public static final int TUPLE = 43; + public static final int STRUCT = 40; + public static final int TUPLE = 41; // - public static final int LIST = 44; - public static final int ARRAY = 48; // TODO: Fix the numbering - public static final int BAG = 45; - public static final int SEXP = 46; + public static final int LIST = 42; + public static final int ARRAY = 43; + public static final int BAG = 44; + public static final int SEXP = 45; // - public static final int USER_DEFINED = 47; + public static final int USER_DEFINED = 46; public static DataType UNKNOWN() { return new DataType(UNKNOWN); } - public static DataType NULL() { - return new DataType(NULL); - } - - public static DataType MISSING() { - return new DataType(MISSING); - } - public static DataType BOOL() { return new DataType(BOOL); } @@ -508,8 +495,6 @@ public int code() { @Override public String name() { switch (code) { - case NULL: return "NULL"; - case MISSING: return "MISSING"; case CHARACTER: return "CHARACTER"; case CHAR: return "CHAR"; case CHARACTER_VARYING: return "CHARACTER_VARYING"; @@ -562,8 +547,6 @@ public String name() { @NotNull private static final int[] codes = { - NULL, - MISSING, CHARACTER, CHAR, CHARACTER_VARYING, @@ -615,8 +598,6 @@ public String name() { @NotNull public static DataType parse(@NotNull String value) { switch (value) { - case "NULL": return NULL(); - case "MISSING": return MISSING(); case "BOOL": return BOOL(); case "BOOLEAN": return BOOLEAN(); case "TINYINT": return TINYINT(); diff --git a/partiql-ast/src/main/java/org/partiql/ast/expr/ExprBoolTest.java b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprBoolTest.java new file mode 100644 index 000000000..846053015 --- /dev/null +++ b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprBoolTest.java @@ -0,0 +1,45 @@ +package org.partiql.ast.expr; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; +import org.partiql.ast.AstNode; +import org.partiql.ast.AstVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * TODO docs + * Corresponds to SQL99's boolean test (6.30). + */ +@Builder(builderClassName = "Builder") +@EqualsAndHashCode(callSuper = false) +public class ExprBoolTest extends Expr { + @NotNull + public final Expr value; + + public final boolean not; + + @NotNull + public final TruthValue truthValue; + + public ExprBoolTest(@NotNull Expr value, boolean not, @NotNull TruthValue truthValue) { + this.value = value; + this.not = not; + this.truthValue = truthValue; + } + + @Override + @NotNull + public List getChildren() { + List kids = new ArrayList<>(); + kids.add(value); + return kids; + } + + @Override + public R accept(@NotNull AstVisitor visitor, C ctx) { + return visitor.visitExprBoolTest(this, ctx); + } +} diff --git a/partiql-ast/src/main/java/org/partiql/ast/expr/ExprIsType.java b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprIsType.java index 2bb77af8d..bb9e3250e 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/expr/ExprIsType.java +++ b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprIsType.java @@ -11,8 +11,8 @@ import java.util.List; /** - * TODO docs, equals, hashcode - * TODO also support IS NULL, IS MISSING, IS UNKNOWN, IS TRUE, IS FALSE + * TODO docs + further specification on supported types + * Note: this is an experimental API. Class's fields and behavior may change in a subsequent release. */ @Builder(builderClassName = "Builder") @EqualsAndHashCode(callSuper = false) diff --git a/partiql-ast/src/main/java/org/partiql/ast/expr/ExprMissingPredicate.java b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprMissingPredicate.java new file mode 100644 index 000000000..021af7863 --- /dev/null +++ b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprMissingPredicate.java @@ -0,0 +1,41 @@ +package org.partiql.ast.expr; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; +import org.partiql.ast.AstNode; +import org.partiql.ast.AstVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * TODO docs + * Corresponds to PartiQL's `IS [NOT] MISSING`. + */ +@Builder(builderClassName = "Builder") +@EqualsAndHashCode(callSuper = false) +public class ExprMissingPredicate extends Expr { + @NotNull + public final Expr value; + + public final boolean not; + + public ExprMissingPredicate(@NotNull Expr value, boolean not) { + this.value = value; + this.not = not; + } + + @Override + @NotNull + public List getChildren() { + List kids = new ArrayList<>(); + kids.add(value); + return kids; + } + + @Override + public R accept(@NotNull AstVisitor visitor, C ctx) { + return visitor.visitExprMissingPredicate(this, ctx); + } +} diff --git a/partiql-ast/src/main/java/org/partiql/ast/expr/ExprNullPredicate.java b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprNullPredicate.java new file mode 100644 index 000000000..1b469f730 --- /dev/null +++ b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprNullPredicate.java @@ -0,0 +1,41 @@ +package org.partiql.ast.expr; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; +import org.partiql.ast.AstNode; +import org.partiql.ast.AstVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * TODO docs + * Corresponds to SQL99's null predicate (8.7). + */ +@Builder(builderClassName = "Builder") +@EqualsAndHashCode(callSuper = false) +public class ExprNullPredicate extends Expr { + @NotNull + public final Expr value; + + public final boolean not; + + public ExprNullPredicate(@NotNull Expr value, boolean not) { + this.value = value; + this.not = not; + } + + @Override + @NotNull + public List getChildren() { + List kids = new ArrayList<>(); + kids.add(value); + return kids; + } + + @Override + public R accept(@NotNull AstVisitor visitor, C ctx) { + return visitor.visitExprNullPredicate(this, ctx); + } +} diff --git a/partiql-ast/src/main/java/org/partiql/ast/expr/TruthValue.java b/partiql-ast/src/main/java/org/partiql/ast/expr/TruthValue.java new file mode 100644 index 000000000..4a54171d5 --- /dev/null +++ b/partiql-ast/src/main/java/org/partiql/ast/expr/TruthValue.java @@ -0,0 +1,64 @@ +package org.partiql.ast.expr; + +import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; +import org.partiql.ast.AstEnum; +import org.partiql.ast.AstNode; +import org.partiql.ast.AstVisitor; + +import java.util.List; + +/** + * TODO docs + */ +@EqualsAndHashCode(callSuper = false) +public class TruthValue extends AstEnum { + public static final int UNKNOWN = 0; + public static final int TRUE = 1; + public static final int FALSE = 2; + public static final int UNK = 3; + + private final int code; + + public TruthValue(int code) { + this.code = code; + } + + public static TruthValue UNKNOWN() { + return new TruthValue(UNKNOWN); + } + + public static TruthValue TRUE() { + return new TruthValue(TRUE); + } + + public static TruthValue FALSE() { + return new TruthValue(FALSE); + } + + public static TruthValue UNK() { + return new TruthValue(UNK); + } + + @Override + public int code() { + return code; + } + + @NotNull + @Override + public String name() { + return ""; + } + + @NotNull + @Override + public List getChildren() { + return List.of(); + } + + @Override + public R accept(@NotNull AstVisitor visitor, C ctx) { + return null; + } +} diff --git a/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt b/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt index 2d4db952c..e916bb953 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt +++ b/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt @@ -59,6 +59,7 @@ import org.partiql.ast.expr.ExprAnd import org.partiql.ast.expr.ExprArray import org.partiql.ast.expr.ExprBag import org.partiql.ast.expr.ExprBetween +import org.partiql.ast.expr.ExprBoolTest import org.partiql.ast.expr.ExprCall import org.partiql.ast.expr.ExprCase import org.partiql.ast.expr.ExprCast @@ -68,8 +69,10 @@ import org.partiql.ast.expr.ExprInCollection import org.partiql.ast.expr.ExprIsType import org.partiql.ast.expr.ExprLike import org.partiql.ast.expr.ExprLit +import org.partiql.ast.expr.ExprMissingPredicate import org.partiql.ast.expr.ExprNot import org.partiql.ast.expr.ExprNullIf +import org.partiql.ast.expr.ExprNullPredicate import org.partiql.ast.expr.ExprOperator import org.partiql.ast.expr.ExprOr import org.partiql.ast.expr.ExprOverlay @@ -87,6 +90,7 @@ import org.partiql.ast.expr.ExprVarRef import org.partiql.ast.expr.ExprVariant import org.partiql.ast.expr.PathStep import org.partiql.ast.expr.Scope +import org.partiql.ast.expr.TruthValue /** * SqlDialect represents the base behavior for transforming an [AstNode] tree into a [SqlBlock] tree. @@ -180,8 +184,6 @@ public abstract class SqlDialect : AstVisitor() { // TYPES override fun visitDataType(node: DataType, tail: SqlBlock): SqlBlock { return when (node.code()) { - // - DataType.NULL, DataType.MISSING -> tail concat node.name() // // no params DataType.STRING, DataType.SYMBOL -> tail concat node.name() @@ -419,6 +421,33 @@ public abstract class SqlDialect : AstVisitor() { return t } + override fun visitExprNullPredicate(node: ExprNullPredicate, tail: SqlBlock): SqlBlock { + var t = tail + t = visitExprWrapped(node.value, t) + t = t concat if (node.not) " IS NOT NULL" else " IS NULL" + return t + } + + override fun visitExprMissingPredicate(node: ExprMissingPredicate, tail: SqlBlock): SqlBlock { + var t = tail + t = visitExprWrapped(node.value, t) + t = t concat if (node.not) " IS NOT MISSING" else " IS MISSING" + return t + } + + override fun visitExprBoolTest(node: ExprBoolTest, tail: SqlBlock): SqlBlock { + var t = tail + t = visitExprWrapped(node.value, t) + t = t concat if (node.not) " IS NOT " else " IS " + t = t concat when (node.truthValue.code()) { + TruthValue.TRUE -> "TRUE" + TruthValue.FALSE -> "FALSE" + TruthValue.UNK -> "UNKNOWN" + else -> throw UnsupportedOperationException("Cannot print $node") + } + return t + } + override fun visitExprIsType(node: ExprIsType, tail: SqlBlock): SqlBlock { var t = tail t = visitExprWrapped(node.value, t) diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt index 1ca4241f1..28e457d40 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt @@ -25,6 +25,7 @@ import org.partiql.ast.expr.ExprAnd import org.partiql.ast.expr.ExprArray import org.partiql.ast.expr.ExprBag import org.partiql.ast.expr.ExprBetween +import org.partiql.ast.expr.ExprBoolTest import org.partiql.ast.expr.ExprCall import org.partiql.ast.expr.ExprCase import org.partiql.ast.expr.ExprCast @@ -35,8 +36,10 @@ import org.partiql.ast.expr.ExprIsType import org.partiql.ast.expr.ExprLike import org.partiql.ast.expr.ExprLit import org.partiql.ast.expr.ExprMatch +import org.partiql.ast.expr.ExprMissingPredicate import org.partiql.ast.expr.ExprNot import org.partiql.ast.expr.ExprNullIf +import org.partiql.ast.expr.ExprNullPredicate import org.partiql.ast.expr.ExprOperator import org.partiql.ast.expr.ExprOr import org.partiql.ast.expr.ExprOverlay @@ -58,6 +61,7 @@ import org.partiql.ast.expr.PathStep.AllFields import org.partiql.ast.expr.Scope import org.partiql.ast.expr.SessionAttribute import org.partiql.ast.expr.TrimSpec +import org.partiql.ast.expr.TruthValue import org.partiql.ast.expr.WindowFunction import org.partiql.ast.graph.GraphDirection import org.partiql.ast.graph.GraphLabel @@ -130,6 +134,21 @@ public object Ast { return ExprInCollection(lhs, rhs, not) } + @JvmStatic + public fun exprNullPredicate(value: Expr, not: Boolean): ExprNullPredicate { + return ExprNullPredicate(value, not) + } + + @JvmStatic + public fun exprMissingPredicate(value: Expr, not: Boolean): ExprMissingPredicate { + return ExprMissingPredicate(value, not) + } + + @JvmStatic + public fun exprBoolTest(value: Expr, not: Boolean, truthValue: TruthValue): ExprBoolTest { + return ExprBoolTest(value, not, truthValue) + } + @JvmStatic public fun exprIsType(value: Expr, type: DataType, not: Boolean): ExprIsType { return ExprIsType(value, type, not) diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt index 06e594297..58a5eaafe 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt @@ -24,6 +24,7 @@ import org.partiql.ast.expr.ExprAnd import org.partiql.ast.expr.ExprArray import org.partiql.ast.expr.ExprBag import org.partiql.ast.expr.ExprBetween +import org.partiql.ast.expr.ExprBoolTest import org.partiql.ast.expr.ExprCall import org.partiql.ast.expr.ExprCase import org.partiql.ast.expr.ExprCast @@ -34,8 +35,10 @@ import org.partiql.ast.expr.ExprIsType import org.partiql.ast.expr.ExprLike import org.partiql.ast.expr.ExprLit import org.partiql.ast.expr.ExprMatch +import org.partiql.ast.expr.ExprMissingPredicate import org.partiql.ast.expr.ExprNot import org.partiql.ast.expr.ExprNullIf +import org.partiql.ast.expr.ExprNullPredicate import org.partiql.ast.expr.ExprOperator import org.partiql.ast.expr.ExprOr import org.partiql.ast.expr.ExprOverlay @@ -191,6 +194,37 @@ public abstract class AstRewriter : AstVisitor() { } } + override fun visitExprMissingPredicate(node: ExprMissingPredicate, ctx: C): AstNode { + val value = visitExpr(node.value, ctx) as Expr + val not = node.not + return if (value !== node.value || not != node.not) { + ExprMissingPredicate(value, not) + } else { + node + } + } + + override fun visitExprNullPredicate(node: ExprNullPredicate, ctx: C): AstNode { + val value = visitExpr(node.value, ctx) as Expr + val not = node.not + return if (value !== node.value || not != node.not) { + ExprNullPredicate(value, not) + } else { + node + } + } + + override fun visitExprBoolTest(node: ExprBoolTest, ctx: C): AstNode { + val value = visitExpr(node.value, ctx) as Expr + val not = node.not + val truthValue = node.truthValue + return if (value !== node.value || not != node.not || truthValue != node.value) { + ExprBoolTest(value, not, truthValue) + } else { + node + } + } + override fun visitExprIsType(node: ExprIsType, ctx: C): AstNode { val value = visitExpr(node.value, ctx) as Expr val type = node.type diff --git a/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt b/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt index 62718ea35..df7270383 100644 --- a/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt +++ b/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt @@ -24,6 +24,7 @@ import org.partiql.ast.Ast.excludeStepStructWildcard import org.partiql.ast.Ast.exprArray import org.partiql.ast.Ast.exprBag import org.partiql.ast.Ast.exprBetween +import org.partiql.ast.Ast.exprBoolTest import org.partiql.ast.Ast.exprCall import org.partiql.ast.Ast.exprCase import org.partiql.ast.Ast.exprCaseBranch @@ -34,8 +35,10 @@ import org.partiql.ast.Ast.exprInCollection import org.partiql.ast.Ast.exprIsType import org.partiql.ast.Ast.exprLike import org.partiql.ast.Ast.exprLit +import org.partiql.ast.Ast.exprMissingPredicate import org.partiql.ast.Ast.exprNot import org.partiql.ast.Ast.exprNullIf +import org.partiql.ast.Ast.exprNullPredicate import org.partiql.ast.Ast.exprOperator import org.partiql.ast.Ast.exprOverlay import org.partiql.ast.Ast.exprPath @@ -102,6 +105,7 @@ import org.partiql.ast.SetQuantifier import org.partiql.ast.expr.Expr import org.partiql.ast.expr.Scope import org.partiql.ast.expr.TrimSpec +import org.partiql.ast.expr.TruthValue import java.math.BigDecimal import kotlin.test.assertFails @@ -237,7 +241,6 @@ class SqlDialectTest { @JvmStatic fun types() = listOf( // SQL - expect("NULL", DataType.NULL()), expect("BOOL", DataType.BOOL()), expect("SMALLINT", DataType.SMALLINT()), expect("INT", DataType.INT()), @@ -266,7 +269,6 @@ class SqlDialectTest { // TODO INTERVAL // TODO other types in `DataType` // PartiQL - expect("MISSING", DataType.MISSING()), expect("STRING", DataType.STRING()), expect("SYMBOL", DataType.SYMBOL()), expect("STRUCT", DataType.STRUCT()), @@ -1001,6 +1003,87 @@ class SqlDialectTest { not = true ) ), + // IS [NOT] TRUE + expect( + "x IS TRUE", + exprBoolTest( + value = v("x"), + not = false, + truthValue = TruthValue.TRUE() + ) + ), + expect( + "x IS NOT TRUE", + exprBoolTest( + value = v("x"), + not = true, + truthValue = TruthValue.TRUE() + ) + ), + // IS [NOT] FALSE + expect( + "x IS FALSE", + exprBoolTest( + value = v("x"), + not = false, + truthValue = TruthValue.FALSE() + ) + ), + expect( + "x IS NOT FALSE", + exprBoolTest( + value = v("x"), + not = true, + truthValue = TruthValue.FALSE() + ) + ), + // IS [NOT] UNKNOWN + expect( + "x IS UNKNOWN", + exprBoolTest( + value = v("x"), + not = false, + truthValue = TruthValue.UNK() + ) + ), + expect( + "x IS NOT UNKNOWN", + exprBoolTest( + value = v("x"), + not = true, + truthValue = TruthValue.UNK() + ) + ), + // IS [NOT] NULL + expect( + "x IS NULL", + exprNullPredicate( + value = v("x"), + not = false, + ) + ), + expect( + "x IS NOT NULL", + exprNullPredicate( + value = v("x"), + not = true, + ) + ), + // IS [NOT] MISSING + expect( + "x IS MISSING", + exprMissingPredicate( + value = v("x"), + not = false, + ) + ), + expect( + "x IS NOT MISSING", + exprMissingPredicate( + value = v("x"), + not = true, + ) + ), expect( "x IS BOOL", exprIsType( diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt index c2535b420..2b8c9637f 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt @@ -8,6 +8,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.partiql.eval.Mode import org.partiql.eval.compiler.PartiQLCompiler +import org.partiql.spi.value.Datum import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.bagValue @@ -1024,13 +1025,146 @@ class PartiQLEvaluatorTest { int32Value(3), ) ), + // TODO port `IS ` tests to conformance tests + // IS TRUE + SuccessTestCase( + input = "TRUE IS TRUE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "FALSE IS TRUE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "NULL IS TRUE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "MISSING IS TRUE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "'foo' IS TRUE", + expected = Datum.missing(), + mode = Mode.PERMISSIVE() + ), + // IS NOT TRUE + SuccessTestCase( + input = "TRUE IS NOT TRUE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "FALSE IS NOT TRUE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "NULL IS NOT TRUE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "MISSING IS NOT TRUE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "'foo' IS NOT TRUE", + expected = Datum.nullValue(), + mode = Mode.PERMISSIVE() + ), + // IS FALSE + SuccessTestCase( + input = "TRUE IS FALSE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "FALSE IS FALSE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "NULL IS FALSE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "MISSING IS FALSE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "'foo' IS FALSE", + expected = Datum.nullValue(), + mode = Mode.PERMISSIVE() + ), + // IS NOT FALSE + SuccessTestCase( + input = "TRUE IS NOT FALSE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "FALSE IS NOT FALSE;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "NULL IS NOT FALSE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "MISSING IS NOT FALSE;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "'foo' IS NOT FALSE", + expected = Datum.nullValue(), + mode = Mode.PERMISSIVE() + ), + // IS UNKNOWN + SuccessTestCase( + input = "TRUE IS UNKNOWN;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "FALSE IS UNKNOWN;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "NULL IS UNKNOWN;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "MISSING IS UNKNOWN;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "'foo' IS UNKNOWN", + expected = Datum.nullValue(), + mode = Mode.PERMISSIVE() + ), + // IS NOT UNKNOWN + SuccessTestCase( + input = "TRUE IS NOT UNKNOWN;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "FALSE IS NOT UNKNOWN;", + expected = boolValue(true) + ), + SuccessTestCase( + input = "NULL IS NOT UNKNOWN;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "MISSING IS NOT UNKNOWN;", + expected = boolValue(false) + ), + SuccessTestCase( + input = "'foo' IS NOT UNKNOWN", + expected = Datum.missing(), + mode = Mode.PERMISSIVE() + ), SuccessTestCase( input = "MISSING IS MISSING;", expected = boolValue(true) ), SuccessTestCase( input = "MISSING IS MISSING;", - expected = boolValue(true), // TODO: Is this right? + expected = boolValue(true), mode = Mode.STRICT(), ), SuccessTestCase( diff --git a/partiql-parser/src/main/antlr/PartiQLParser.g4 b/partiql-parser/src/main/antlr/PartiQLParser.g4 index 3f050e3d8..a94349b44 100644 --- a/partiql-parser/src/main/antlr/PartiQLParser.g4 +++ b/partiql-parser/src/main/antlr/PartiQLParser.g4 @@ -575,8 +575,8 @@ joinSpec * 3. Multiplication, Division, Modulo (ex: a * b) * 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) + * 6. Predicates (ex: a LIKE b, a < b, a IN b, a = b, IS [NOT] NULL|MISSING) + * 7. IS [NOT] TRUE|FALSE|UNKNOWN * 8. NOT (ex: NOT a) * 8. AND (ex: a AND b) * 9. OR (ex: a OR b) @@ -621,17 +621,24 @@ exprAnd exprNot : op=NOT rhs=exprNot # Not - | parent=exprPredicate # ExprNotBase + | parent=exprBoolTest # ExprNotBase + ; + +exprBoolTest + : exprBoolTest IS NOT? truthValue=(TRUE|FALSE|UNKNOWN) # BoolTest + | parent=exprPredicate # ExprBoolTestBase ; exprPredicate - : lhs=exprPredicate op=comparisonOp rhs=mathOp00 # PredicateComparison - | lhs=exprPredicate IS NOT? type # PredicateIs - | 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 - | lhs=exprPredicate NOT? BETWEEN lower=mathOp00 AND upper=mathOp00 # PredicateBetween - | parent=mathOp00 # PredicateBase + : lhs=exprPredicate op=comparisonOp rhs=mathOp00 # PredicateComparison + | lhs=exprPredicate IS NOT? NULL # PredicateNull + | lhs=exprPredicate IS NOT? MISSING # PredicateMissing + | lhs=exprPredicate IS NOT? type # PredicateIs + | 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 + | lhs=exprPredicate NOT? BETWEEN lower=mathOp00 AND upper=mathOp00 # PredicateBetween + | parent=mathOp00 # PredicateBase ; comparisonOp @@ -934,8 +941,8 @@ literal type : datatype=( - NULL | BOOL | BOOLEAN | SMALLINT | INTEGER2 | INT2 | INTEGER | INT | INTEGER4 | INT4 - | INTEGER8 | INT8 | BIGINT | REAL | CHAR | CHARACTER | MISSING + BOOL | BOOLEAN | SMALLINT | INTEGER2 | INT2 | INTEGER | INT | INTEGER4 | INT4 + | INTEGER8 | INT8 | BIGINT | REAL | CHAR | CHARACTER | STRING | SYMBOL | BLOB | CLOB | DATE | ANY ) # TypeAtomic | datatype=( STRUCT | TUPLE | LIST | ARRAY | SEXP | BAG ) # TypeComplexAtomic 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 77142e8a4..90100e8f1 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 @@ -55,6 +55,7 @@ import org.partiql.ast.Ast.exprAnd import org.partiql.ast.Ast.exprArray import org.partiql.ast.Ast.exprBag import org.partiql.ast.Ast.exprBetween +import org.partiql.ast.Ast.exprBoolTest import org.partiql.ast.Ast.exprCall import org.partiql.ast.Ast.exprCase import org.partiql.ast.Ast.exprCaseBranch @@ -66,8 +67,10 @@ import org.partiql.ast.Ast.exprIsType import org.partiql.ast.Ast.exprLike import org.partiql.ast.Ast.exprLit import org.partiql.ast.Ast.exprMatch +import org.partiql.ast.Ast.exprMissingPredicate import org.partiql.ast.Ast.exprNot import org.partiql.ast.Ast.exprNullIf +import org.partiql.ast.Ast.exprNullPredicate import org.partiql.ast.Ast.exprOperator import org.partiql.ast.Ast.exprOr import org.partiql.ast.Ast.exprOverlay @@ -190,6 +193,7 @@ import org.partiql.ast.expr.PathStep import org.partiql.ast.expr.Scope import org.partiql.ast.expr.SessionAttribute import org.partiql.ast.expr.TrimSpec +import org.partiql.ast.expr.TruthValue import org.partiql.ast.expr.WindowFunction import org.partiql.ast.graph.GraphDirection import org.partiql.ast.graph.GraphLabel @@ -619,7 +623,7 @@ internal class PartiQLParserDefault : PartiQLParser { // The following types are not supported in DDL yet, // either as a top level type or as a element/field type in complex type declaration private fun isValidTypeDeclarationOrThrow(type: DataType, ctx: GeneratedParser.TypeContext) = when (type.code()) { - DataType.UNKNOWN, DataType.NULL, DataType.MISSING, + DataType.UNKNOWN, DataType.BAG, DataType.USER_DEFINED -> throw error(ctx, "declaration attribute with $type is not supported") else -> Unit @@ -1399,6 +1403,17 @@ internal class PartiQLParserDefault : PartiQLParser { exprNot(expr) } + override fun visitBoolTest(ctx: GeneratedParser.BoolTestContext) = translate(ctx) { + val expr = visitAs(ctx.exprBoolTest()) + val not = ctx.NOT() != null + when (ctx.truthValue.type) { + GeneratedParser.TRUE -> exprBoolTest(expr, not, TruthValue.TRUE()) + GeneratedParser.FALSE -> exprBoolTest(expr, not, TruthValue.FALSE()) + GeneratedParser.UNKNOWN -> exprBoolTest(expr, not, TruthValue.UNK()) + else -> throw error(ctx, "Unexpected value for boolean test IS [NOT] TRUE|FALSE|UNKNOWN") + } + } + private fun checkForInvalidTokens(op: ParserRuleContext) { val start = op.start.tokenIndex val stop = op.stop.tokenIndex @@ -1484,6 +1499,18 @@ internal class PartiQLParserDefault : PartiQLParser { exprInCollection(lhs, rhs, not) } + override fun visitPredicateNull(ctx: GeneratedParser.PredicateNullContext) = translate(ctx) { + val value = visitAs(ctx.lhs) + val not = ctx.NOT() != null + exprNullPredicate(value, not) + } + + override fun visitPredicateMissing(ctx: GeneratedParser.PredicateMissingContext) = translate(ctx) { + val value = visitAs(ctx.lhs) + val not = ctx.NOT() != null + exprMissingPredicate(value, not) + } + override fun visitPredicateIs(ctx: GeneratedParser.PredicateIsContext) = translate(ctx) { val value = visitAs(ctx.lhs) val type = visitAs(ctx.type()) @@ -1993,7 +2020,6 @@ internal class PartiQLParserDefault : PartiQLParser { override fun visitTypeAtomic(ctx: GeneratedParser.TypeAtomicContext) = translate(ctx) { when (ctx.datatype.type) { - GeneratedParser.NULL -> DataType.NULL() GeneratedParser.BOOL -> DataType.BOOLEAN() GeneratedParser.BOOLEAN -> DataType.BOOL() GeneratedParser.SMALLINT -> DataType.SMALLINT() @@ -2013,7 +2039,6 @@ internal class PartiQLParserDefault : PartiQLParser { GeneratedParser.TIMESTAMP -> DataType.TIMESTAMP() GeneratedParser.CHAR -> DataType.CHAR() GeneratedParser.CHARACTER -> DataType.CHARACTER() - GeneratedParser.MISSING -> DataType.MISSING() GeneratedParser.STRING -> DataType.STRING() GeneratedParser.SYMBOL -> DataType.SYMBOL() // TODO https://github.com/partiql/partiql-lang-kotlin/issues/1125 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 0f8fab3ae..7f90b039a 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 @@ -29,6 +29,7 @@ import org.partiql.ast.expr.ExprAnd import org.partiql.ast.expr.ExprArray import org.partiql.ast.expr.ExprBag import org.partiql.ast.expr.ExprBetween +import org.partiql.ast.expr.ExprBoolTest import org.partiql.ast.expr.ExprCall import org.partiql.ast.expr.ExprCase import org.partiql.ast.expr.ExprCast @@ -38,8 +39,10 @@ import org.partiql.ast.expr.ExprInCollection import org.partiql.ast.expr.ExprIsType import org.partiql.ast.expr.ExprLike import org.partiql.ast.expr.ExprLit +import org.partiql.ast.expr.ExprMissingPredicate import org.partiql.ast.expr.ExprNot import org.partiql.ast.expr.ExprNullIf +import org.partiql.ast.expr.ExprNullPredicate import org.partiql.ast.expr.ExprOperator import org.partiql.ast.expr.ExprOr import org.partiql.ast.expr.ExprOverlay @@ -57,6 +60,7 @@ import org.partiql.ast.expr.ExprVariant import org.partiql.ast.expr.PathStep import org.partiql.ast.expr.Scope import org.partiql.ast.expr.TrimSpec +import org.partiql.ast.expr.TruthValue import org.partiql.errors.TypeCheckException import org.partiql.planner.internal.Env import org.partiql.planner.internal.ir.Rel @@ -393,6 +397,24 @@ internal object RexConverter { } } + override fun visitExprBoolTest(node: ExprBoolTest, ctx: Env): Rex { + val value = visitExprCoerce(node.value, ctx) + var call = when (node.truthValue.code()) { + TruthValue.TRUE -> call("is_true", value) + TruthValue.FALSE -> call("is_false", value) + TruthValue.UNK -> call("is_unknown", value) + else -> error("Unexpected TruthValue: ${node.truthValue}") + } + // See SQL99 6.30 pg 216 Rule 2 for equivalence + // > IF NOT is specified in a , then let BP be the contained and let + // > TV be the contained . The is equivalent to: + // > ( NOT ( BP IS TV ) ) + if (node.not) { + call = negate(call) + } + return rex(BOOL, call) + } + override fun visitExprNot(node: ExprNot, ctx: Env): Rex { val type = (ANY) // Args @@ -784,7 +806,7 @@ internal object RexConverter { else -> call("like_escape", arg0, arg1, arg2) } // NOT? - if (node.not == true) { + if (node.not) { call = negate(call) } return rex(type, call) @@ -802,7 +824,7 @@ internal object RexConverter { // Call var call = call("between", arg0, arg1, arg2) // NOT? - if (node.not == true) { + if (node.not) { call = negate(call) } rex(type, call) @@ -829,12 +851,36 @@ internal object RexConverter { // Call var call = call("in_collection", arg0, arg1) // NOT? - if (node.not == true) { + if (node.not) { call = negate(call) } return rex(type, call) } + /** + * IS NOT? NULL + */ + override fun visitExprNullPredicate(node: ExprNullPredicate, ctx: Env): Rex { + val value = visitExprCoerce(node.value, ctx) + var call = call("is_null", value) + if (node.not) { + call = negate(call) + } + return rex(BOOL, call) + } + + /** + * IS NOT? MISSING + */ + override fun visitExprMissingPredicate(node: ExprMissingPredicate, ctx: Env): Rex { + val value = visitExprCoerce(node.value, ctx) + var call = call("is_missing", value) + if (node.not) { + call = negate(call) + } + return rex(BOOL, call) + } + /** * IS ? */ @@ -844,9 +890,6 @@ internal object RexConverter { val arg0 = visitExprCoerce(node.value, ctx) val targetType = node.type var call = when (targetType.code()) { - // - DataType.NULL -> call("is_null", arg0) - DataType.MISSING -> call("is_missing", arg0) // // TODO CHAR_VARYING, CHARACTER_LARGE_OBJECT, CHAR_LARGE_OBJECT DataType.CHARACTER, DataType.CHAR -> call("is_char", targetType.length.toRex(), arg0) @@ -895,7 +938,7 @@ internal object RexConverter { else -> error("Unexpected DataType type: $targetType") } - if (node.not == true) { + if (node.not) { call = negate(call) } @@ -1032,9 +1075,6 @@ internal object RexConverter { private fun visitType(type: DataType): CompilerType { return when (type.code()) { - // - DataType.NULL -> error("Casting to NULL is not supported.") - DataType.MISSING -> error("Casting to MISSING is not supported.") // // TODO CHAR_VARYING, CHARACTER_LARGE_OBJECT, CHAR_LARGE_OBJECT DataType.CHARACTER, DataType.CHAR -> { diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/function/Builtins.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/function/Builtins.kt index cdd7f2f47..706c61c57 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/function/Builtins.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/function/Builtins.kt @@ -144,6 +144,11 @@ internal object Builtins { FnGt, FnGte, FnInCollection, + + Fn_IS_TRUE__ANY__BOOL, + Fn_IS_FALSE__ANY__BOOL, + Fn_IS_UNKNOWN__ANY__BOOL, + Fn_IS_ANY__ANY__BOOL, Fn_IS_BAG__ANY__BOOL, Fn_IS_BINARY__ANY__BOOL, diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsFalse.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsFalse.kt new file mode 100644 index 000000000..b0277c5c8 --- /dev/null +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsFalse.kt @@ -0,0 +1,28 @@ +// ktlint-disable filename +@file:Suppress("ClassName") + +package org.partiql.spi.function.builtins + +import org.partiql.spi.function.Function +import org.partiql.spi.function.Parameter +import org.partiql.spi.function.builtins.internal.booleanValue +import org.partiql.spi.value.Datum +import org.partiql.types.PType + +/** + * Function (operator) for the `IS FALSE` special form. + */ +internal val Fn_IS_FALSE__ANY__BOOL = Function.static( + name = "is_false", + returns = PType.bool(), + parameters = arrayOf(Parameter("value", PType.bool())), + isNullCall = false, + isMissingCall = false +) { args -> + val arg = args[0] + if (arg.isNull || arg.isMissing) { + Datum.bool(false) + } else { + Datum.bool(!args[0].booleanValue()) + } +} diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsTrue.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsTrue.kt new file mode 100644 index 000000000..8959a777b --- /dev/null +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsTrue.kt @@ -0,0 +1,28 @@ +// ktlint-disable filename +@file:Suppress("ClassName") + +package org.partiql.spi.function.builtins + +import org.partiql.spi.function.Function +import org.partiql.spi.function.Parameter +import org.partiql.spi.function.builtins.internal.booleanValue +import org.partiql.spi.value.Datum +import org.partiql.types.PType + +/** + * Function (operator) for the `IS TRUE` special form. + */ +internal val Fn_IS_TRUE__ANY__BOOL = Function.static( + name = "is_true", + returns = PType.bool(), + parameters = arrayOf(Parameter("value", PType.bool())), + isNullCall = false, + isMissingCall = false +) { args -> + val arg = args[0] + if (arg.isNull || arg.isMissing) { + Datum.bool(false) + } else { + Datum.bool(args[0].booleanValue()) + } +} diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsUnknown.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsUnknown.kt new file mode 100644 index 000000000..b8536daf8 --- /dev/null +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/function/builtins/FnIsUnknown.kt @@ -0,0 +1,27 @@ +// ktlint-disable filename +@file:Suppress("ClassName") + +package org.partiql.spi.function.builtins + +import org.partiql.spi.function.Function +import org.partiql.spi.function.Parameter +import org.partiql.spi.value.Datum +import org.partiql.types.PType + +/** + * Function (operator) for the `IS UNKNOWN` special form. + */ +internal val Fn_IS_UNKNOWN__ANY__BOOL = Function.static( + name = "is_unknown", + returns = PType.bool(), + parameters = arrayOf(Parameter("value", PType.bool())), + isNullCall = false, + isMissingCall = false +) { args -> + val arg = args[0] + if (arg.isNull || arg.isMissing) { + Datum.bool(true) + } else { + Datum.bool(false) + } +}