diff --git a/be/src/exprs/binary_predicate.cpp b/be/src/exprs/binary_predicate.cpp index 1b08dd665c..c5ef475ffa 100644 --- a/be/src/exprs/binary_predicate.cpp +++ b/be/src/exprs/binary_predicate.cpp @@ -234,6 +234,38 @@ Expr* BinaryPredicate::from_thrift(const TExprNode& node) { return NULL; } } + case TExprOpcode::EQ_FOR_NULL: { + switch (node.child_type) { + case TPrimitiveType::BOOLEAN: + return new EqForNullBooleanValPred(node); + case TPrimitiveType::TINYINT: + return new EqForNullTinyIntValPred(node); + case TPrimitiveType::SMALLINT: + return new EqForNullSmallIntValPred(node); + case TPrimitiveType::INT: + return new EqForNullIntValPred(node); + case TPrimitiveType::BIGINT: + return new EqForNullBigIntValPred(node); + case TPrimitiveType::LARGEINT: + return new EqForNullLargeIntValPred(node); + case TPrimitiveType::FLOAT: + return new EqForNullFloatValPred(node); + case TPrimitiveType::DOUBLE: + return new EqForNullDoubleValPred(node); + case TPrimitiveType::CHAR: + case TPrimitiveType::VARCHAR: + return new EqForNullStringValPred(node); + case TPrimitiveType::DATE: + case TPrimitiveType::DATETIME: + return new EqForNullDateTimeValPred(node); + case TPrimitiveType::DECIMAL: + return new EqForNullDecimalValPred(node); + case TPrimitiveType::DECIMALV2: + return new EqForNullDecimalV2ValPred(node); + default: + return NULL; + } + } default: return NULL; } @@ -374,7 +406,7 @@ Status BinaryPredicate::codegen_compare_fn( RETURN_IF_ERROR(codegen_compare_fn(state, fn, LLVM_PRED)); \ _ir_compute_fn = *fn; \ return Status::OK(); \ - } \ + } // add '/**/' to pass codestyle check of cooder #define BINARY_PRED_INT_FNS(TYPE, FN) \ @@ -419,7 +451,7 @@ BINARY_PRED_FLOAT_FNS(DoubleVal, get_double_val); } \ Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ return get_codegend_compute_fn_wrapper(state, fn); \ - } \ + } #define COMPLICATE_BINARY_PRED_FNS(TYPE, FN, DORIS_TYPE, FROM_FUNC) \ @@ -453,7 +485,7 @@ COMPLICATE_BINARY_PRED_FNS(DecimalV2Val, get_decimalv2_val, DecimalV2Value, from RETURN_IF_ERROR(codegen_compare_fn(state, fn , LLVM_PRED)); \ _ir_compute_fn = *fn; \ return Status::OK(); \ - } \ + } #define DATETIME_BINARY_PRED_FNS() \ DATETIME_BINARY_PRED_FN(Eq##DateTimeVal##Pred, ==, CmpInst::ICMP_EQ) \ @@ -511,6 +543,114 @@ Status EqStringValPred::get_codegend_compute_fn(RuntimeState* state, llvm::Funct return get_codegend_compute_fn_wrapper(state, fn); } +#define BINARY_PRED_FOR_NULL_FN(CLASS, TYPE, FN, OP, LLVM_PRED) \ + BooleanVal CLASS::get_boolean_val(ExprContext* ctx, TupleRow* row) { \ + TYPE v1 = _children[0]->FN(ctx, row); \ + TYPE v2 = _children[1]->FN(ctx, row); \ + if (v1.is_null && v2.is_null) { \ + return BooleanVal(true); \ + } else if (v1.is_null || v2.is_null) { \ + return BooleanVal(false); \ + } \ + return BooleanVal(v1.val OP v2.val); \ + } \ + Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ + if (_ir_compute_fn != NULL) { \ + *fn = _ir_compute_fn; \ + return Status::OK(); \ + } \ + RETURN_IF_ERROR(codegen_compare_fn(state, fn, LLVM_PRED)); \ + _ir_compute_fn = *fn; \ + return Status::OK(); \ + } + +// add '/**/' to pass codestyle check of cooder +#define BINARY_PRED_FOR_NULL_INT_FNS(TYPE, FN) \ + BINARY_PRED_FOR_NULL_FN(EqForNull##TYPE##Pred, TYPE, FN, /**/ == /**/, CmpInst::ICMP_EQ) + +BINARY_PRED_FOR_NULL_INT_FNS(BooleanVal, get_boolean_val); +BINARY_PRED_FOR_NULL_INT_FNS(TinyIntVal, get_tiny_int_val); +BINARY_PRED_FOR_NULL_INT_FNS(SmallIntVal, get_small_int_val); +BINARY_PRED_FOR_NULL_INT_FNS(IntVal, get_int_val); +BINARY_PRED_FOR_NULL_INT_FNS(BigIntVal, get_big_int_val); +BINARY_PRED_FOR_NULL_INT_FNS(LargeIntVal, get_large_int_val); + +#define BINARY_PRED_FOR_NULL_FLOAT_FNS(TYPE, FN) \ + BINARY_PRED_FOR_NULL_FN(EqForNull##TYPE##Pred, TYPE, FN, ==, CmpInst::FCMP_OEQ) + +BINARY_PRED_FOR_NULL_FLOAT_FNS(FloatVal, get_float_val); +BINARY_PRED_FOR_NULL_FLOAT_FNS(DoubleVal, get_double_val); + + +#define COMPLICATE_BINARY_FOR_NULL_PRED_FN(CLASS, TYPE, FN, DORIS_TYPE, FROM_FUNC, OP) \ + BooleanVal CLASS::get_boolean_val(ExprContext* ctx, TupleRow* row) { \ + TYPE v1 = _children[0]->FN(ctx, row); \ + TYPE v2 = _children[1]->FN(ctx, row); \ + if (v1.is_null && v2.is_null) { \ + return BooleanVal(true); \ + } else if (v1.is_null || v2.is_null) { \ + return BooleanVal(false); \ + } \ + DORIS_TYPE pv1 = DORIS_TYPE::FROM_FUNC(v1); \ + DORIS_TYPE pv2 = DORIS_TYPE::FROM_FUNC(v2); \ + return BooleanVal(pv1 OP pv2); \ + } \ + Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ + return get_codegend_compute_fn_wrapper(state, fn); \ + } + + +#define COMPLICATE_BINARY_FOR_NULL_PRED_FNS(TYPE, FN, DORIS_TYPE, FROM_FUNC) \ + COMPLICATE_BINARY_FOR_NULL_PRED_FN(EqForNull##TYPE##Pred, TYPE, FN, DORIS_TYPE, FROM_FUNC, ==) + +COMPLICATE_BINARY_FOR_NULL_PRED_FNS(DecimalVal, get_decimal_val, DecimalValue, from_decimal_val) +COMPLICATE_BINARY_FOR_NULL_PRED_FNS(DecimalV2Val, get_decimalv2_val, DecimalV2Value, from_decimal_val) + +#define DATETIME_BINARY_FOR_NULL_PRED_FN(CLASS, OP, LLVM_PRED) \ + BooleanVal CLASS::get_boolean_val(ExprContext* ctx, TupleRow* row) { \ + DateTimeVal v1 = _children[0]->get_datetime_val(ctx, row); \ + DateTimeVal v2 = _children[1]->get_datetime_val(ctx, row); \ + if (v1.is_null && v2.is_null) { \ + return BooleanVal(true); \ + } else if (v1.is_null || v2.is_null) { \ + return BooleanVal(false); \ + } \ + return BooleanVal(v1.packed_time OP v2.packed_time); \ + } \ + Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ + if (_ir_compute_fn != NULL) { \ + *fn = _ir_compute_fn; \ + return Status::OK(); \ + } \ + RETURN_IF_ERROR(codegen_compare_fn(state, fn , LLVM_PRED)); \ + _ir_compute_fn = *fn; \ + return Status::OK(); \ + } + +#define DATETIME_BINARY_FOR_NULL_PRED_FNS() \ + DATETIME_BINARY_FOR_NULL_PRED_FN(EqForNull##DateTimeVal##Pred, ==, CmpInst::ICMP_EQ) + +DATETIME_BINARY_FOR_NULL_PRED_FNS() + +BooleanVal EqForNullStringValPred::get_boolean_val(ExprContext* ctx, TupleRow* row) { + StringVal v1 = _children[0]->get_string_val(ctx, row); + StringVal v2 = _children[1]->get_string_val(ctx, row); + if (v1.is_null && v2.is_null) { + return BooleanVal(true); + } else if (v1.is_null || v2.is_null) { + return BooleanVal(false); + } + + if (v1.len != v2.len) { + return BooleanVal(false); + } + return BooleanVal(string_compare((char*)v1.ptr, v1.len, (char*)v2.ptr, v2.len, v1.len) == 0); +} + +Status EqForNullStringValPred::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { + return get_codegend_compute_fn_wrapper(state, fn); +} + #if 0 Status EqStringValPred::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { LlvmCodeGen* codegen = NULL; diff --git a/be/src/exprs/binary_predicate.h b/be/src/exprs/binary_predicate.h index 6e18b5f2bb..236832660c 100644 --- a/be/src/exprs/binary_predicate.h +++ b/be/src/exprs/binary_predicate.h @@ -32,7 +32,8 @@ namespace doris { class BinaryPredicate : public Predicate { public: static Expr* from_thrift(const TExprNode& node); - BinaryPredicate(const TExprNode& node) : Predicate(node) { } + BinaryPredicate(const TExprNode& node) : Predicate(node) { + } virtual ~BinaryPredicate() { } protected: @@ -41,6 +42,7 @@ protected: // virtual Status prepare(RuntimeState* state, const RowDescriptor& desc); virtual std::string debug_string() const; + Status codegen_compare_fn( RuntimeState* state, llvm::Function** fn, llvm::CmpInst::Predicate pred); }; @@ -54,7 +56,7 @@ protected: return pool->add(new CLASS(*this)); } \ \ virtual Status get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn); \ - virtual BooleanVal get_boolean_val(ExprContext* context, TupleRow*); \ + virtual BooleanVal get_boolean_val(ExprContext* context, TupleRow* row); \ }; #define BIN_PRED_CLASSES_DEFINE(TYPE) \ @@ -63,7 +65,7 @@ protected: BIN_PRED_CLASS_DEFINE(Lt##TYPE##Pred) \ BIN_PRED_CLASS_DEFINE(Le##TYPE##Pred) \ BIN_PRED_CLASS_DEFINE(Gt##TYPE##Pred) \ - BIN_PRED_CLASS_DEFINE(Ge##TYPE##Pred) + BIN_PRED_CLASS_DEFINE(Ge##TYPE##Pred) BIN_PRED_CLASSES_DEFINE(BooleanVal) BIN_PRED_CLASSES_DEFINE(TinyIntVal) @@ -77,5 +79,35 @@ BIN_PRED_CLASSES_DEFINE(StringVal) BIN_PRED_CLASSES_DEFINE(DateTimeVal) BIN_PRED_CLASSES_DEFINE(DecimalVal) BIN_PRED_CLASSES_DEFINE(DecimalV2Val) + + +#define BIN_PRED_FOR_NULL_CLASS_DEFINE(CLASS) \ + class CLASS : public BinaryPredicate { \ + public: \ + CLASS(const TExprNode& node) : BinaryPredicate(node) { } \ + virtual ~CLASS() { } \ + virtual Expr* clone(ObjectPool* pool) const override { \ + return pool->add(new CLASS(*this)); } \ + \ + virtual Status get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn); \ + virtual BooleanVal get_boolean_val(ExprContext* context, TupleRow* row); \ + }; + +#define BIN_PRED_FOR_NULL_CLASSES_DEFINE(TYPE) \ + BIN_PRED_FOR_NULL_CLASS_DEFINE(EqForNull##TYPE##Pred) + + +BIN_PRED_FOR_NULL_CLASSES_DEFINE(BooleanVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(TinyIntVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(SmallIntVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(IntVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(BigIntVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(LargeIntVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(FloatVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(DoubleVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(StringVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(DateTimeVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(DecimalVal) +BIN_PRED_FOR_NULL_CLASSES_DEFINE(DecimalV2Val) } #endif diff --git a/be/src/exprs/in_predicate.cpp b/be/src/exprs/in_predicate.cpp index dc3b821b3b..f9ac0223bf 100644 --- a/be/src/exprs/in_predicate.cpp +++ b/be/src/exprs/in_predicate.cpp @@ -129,13 +129,13 @@ BooleanVal InPredicate::get_boolean_val(ExprContext* ctx, TupleRow* row) { if (lhs_slot == NULL) { return BooleanVal::null(); } - if (_null_in_set) { - return BooleanVal::null(); - } // if find in const set, return true if (_hybird_set->find(lhs_slot)) { return BooleanVal(!_is_not_in); } + if (_null_in_set) { + return BooleanVal::null(); + } return BooleanVal(_is_not_in); } diff --git a/fe/src/main/cup/sql_parser.cup b/fe/src/main/cup/sql_parser.cup index 7720bc8645..5dbcfe99d6 100644 --- a/fe/src/main/cup/sql_parser.cup +++ b/fe/src/main/cup/sql_parser.cup @@ -3640,7 +3640,7 @@ comparison_predicate ::= | expr:e1 GREATERTHAN:op expr:e2 {: RESULT = new BinaryPredicate(BinaryPredicate.Operator.GT, e1, e2); :} | expr:e1 LESSTHAN EQUAL GREATERTHAN:op expr:e2 - {: RESULT = new BinaryPredicate(BinaryPredicate.Operator.EQ, e1, e2); :} + {: RESULT = new BinaryPredicate(BinaryPredicate.Operator.EQ_FOR_NULL, e1, e2); :} ; like_predicate ::= diff --git a/fe/src/main/java/org/apache/doris/analysis/BinaryPredicate.java b/fe/src/main/java/org/apache/doris/analysis/BinaryPredicate.java index a089b0c1e3..79ea427a22 100644 --- a/fe/src/main/java/org/apache/doris/analysis/BinaryPredicate.java +++ b/fe/src/main/java/org/apache/doris/analysis/BinaryPredicate.java @@ -56,7 +56,8 @@ public class BinaryPredicate extends Predicate implements Writable { LE("<=", "le", TExprOpcode.LE), GE(">=", "ge", TExprOpcode.GE), LT("<", "lt", TExprOpcode.LT), - GT(">", "gt", TExprOpcode.GT); + GT(">", "gt", TExprOpcode.GT), + EQ_FOR_NULL("<=>", "eq_for_null", TExprOpcode.EQ_FOR_NULL); private final String description; private final String name; @@ -97,6 +98,8 @@ public class BinaryPredicate extends Predicate implements Writable { return GT; case GT: return LE; + case EQ_FOR_NULL: + return this; } return null; } @@ -115,6 +118,8 @@ public class BinaryPredicate extends Predicate implements Writable { return GT; case GT: return LT; + case EQ_FOR_NULL: + return EQ_FOR_NULL; // case DISTINCT_FROM: return DISTINCT_FROM; // case NOT_DISTINCT: return NOT_DISTINCT; // case NULL_MATCHING_EQ: @@ -124,7 +129,7 @@ public class BinaryPredicate extends Predicate implements Writable { } } - public boolean isEquivalence() { return this == EQ; }; + public boolean isEquivalence() { return this == EQ || this == EQ_FOR_NULL; }; public boolean isUnequivalence() { return this == NE; } } @@ -132,7 +137,7 @@ public class BinaryPredicate extends Predicate implements Writable { private Operator op; // check if left is slot and right isnot slot. private Boolean slotIsleft = null; - + // for restoring public BinaryPredicate() { super(); @@ -172,6 +177,8 @@ public class BinaryPredicate extends Predicate implements Writable { Operator.LT.getName(), Lists.newArrayList(t, t), Type.BOOLEAN)); functionSet.addBuiltin(ScalarFunction.createBuiltinOperator( Operator.GT.getName(), Lists.newArrayList(t, t), Type.BOOLEAN)); + functionSet.addBuiltin(ScalarFunction.createBuiltinOperator( + Operator.EQ_FOR_NULL.getName(), Lists.newArrayList(t, t), Type.BOOLEAN)); } } @@ -368,6 +375,7 @@ public class BinaryPredicate extends Predicate implements Writable { slotRef = (SlotRef) getChild(1).getChild(0); } } + if (slotRef != null && slotRef.getSlotId() == id) { slotIsleft = false; return getChild(0); @@ -507,13 +515,24 @@ public class BinaryPredicate extends Predicate implements Writable { } private Expr compareLiteral(LiteralExpr first, LiteralExpr second) throws AnalysisException { - if (first instanceof NullLiteral || second instanceof NullLiteral) { - return new NullLiteral(); + final boolean isFirstNull = (first instanceof NullLiteral); + final boolean isSecondNull = (second instanceof NullLiteral); + if (op == Operator.EQ_FOR_NULL) { + if (isFirstNull && isSecondNull) { + return new BoolLiteral(true); + } else if (isFirstNull || isSecondNull) { + return new BoolLiteral(false); + } + } else { + if (isFirstNull || isSecondNull){ + return new NullLiteral(); + } } final int compareResult = first.compareLiteral(second); switch(op) { case EQ: + case EQ_FOR_NULL: return new BoolLiteral(compareResult == 0); case GE: return new BoolLiteral(compareResult == 1 || compareResult == 0); diff --git a/fe/src/main/java/org/apache/doris/analysis/InPredicate.java b/fe/src/main/java/org/apache/doris/analysis/InPredicate.java index deef5ca1ed..6765ff8e9e 100644 --- a/fe/src/main/java/org/apache/doris/analysis/InPredicate.java +++ b/fe/src/main/java/org/apache/doris/analysis/InPredicate.java @@ -248,10 +248,18 @@ public class InPredicate extends Predicate { return this; } + if (leftChildValue instanceof NullLiteral) { + return leftChildValue; + } + List inListChildren = children.subList(1, children.size()); if (inListChildren.contains(leftChildValue)) { return new BoolLiteral(true); } else { + final NullLiteral nullLiteral = new NullLiteral(); + if (inListChildren.contains(nullLiteral)) { + return nullLiteral; + } return new BoolLiteral(false); } } diff --git a/gensrc/thrift/Opcodes.thrift b/gensrc/thrift/Opcodes.thrift index 842f5d6930..09db1372d4 100644 --- a/gensrc/thrift/Opcodes.thrift +++ b/gensrc/thrift/Opcodes.thrift @@ -82,4 +82,5 @@ enum TExprOpcode { BITNOT, FACTORIAL, LAST_OPCODE, + EQ_FOR_NULL, }