Fix bug that <=> operator and in operator get wrong result (#1516)

* Fix bug that <=> operator and in operator get wrong result

* Add some comment to get_result_for_null

* Add an new Binary Operator to replace is_safe_for_null for handleing '<=>' operator

* Add EQ_FOR_NULL to TExprOpcode

* Remove macro definition last backslash
This commit is contained in:
chenhao
2019-07-30 11:17:53 +08:00
committed by GitHub
parent 97718a35a2
commit 2cb82c57bb
7 changed files with 215 additions and 15 deletions

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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 ::=

View File

@ -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);

View File

@ -248,10 +248,18 @@ public class InPredicate extends Predicate {
return this;
}
if (leftChildValue instanceof NullLiteral) {
return leftChildValue;
}
List<Expr> 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);
}
}

View File

@ -82,4 +82,5 @@ enum TExprOpcode {
BITNOT,
FACTORIAL,
LAST_OPCODE,
EQ_FOR_NULL,
}