From 7c0c0b636b776f279eb67a1f81ed409ea2268db1 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 17 Feb 2024 23:51:49 +0800 Subject: [PATCH] fix: cast numeric to boolean and `and/or` only has Null on one side --- src/expression/value_compute.rs | 16 ++++----- src/types/value.rs | 18 ++++++++++ tests/slt/crdb/and_or.slt | 63 +++++++++++++++++++++++++++++++++ tests/slt/dummy.slt | 46 ++++++++++++++++++++++++ tests/slt/filter.slt | 2 ++ 5 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 tests/slt/crdb/and_or.slt create mode 100644 tests/slt/dummy.slt diff --git a/src/expression/value_compute.rs b/src/expression/value_compute.rs index ad8a67e6..13e1b898 100644 --- a/src/expression/value_compute.rs +++ b/src/expression/value_compute.rs @@ -469,19 +469,19 @@ impl DataValue { match op { BinaryOperator::And => { - let value = if let (Some(v1), Some(v2)) = (left_value, right_value) { - Some(v1 && v2) - } else { - None + let value = match (left_value, right_value) { + (Some(v1), Some(v2)) => Some(v1 && v2), + (Some(false), _) | (_, Some(false)) => Some(false), + _ => None }; DataValue::Boolean(value) } BinaryOperator::Or => { - let value = if let (Some(v1), Some(v2)) = (left_value, right_value) { - Some(v1 || v2) - } else { - None + let value = match (left_value, right_value) { + (Some(v1), Some(v2)) => Some(v1 || v2), + (Some(true), _) | (_, Some(true)) => Some(true), + _ => None }; DataValue::Boolean(value) diff --git a/src/types/value.rs b/src/types/value.rs index 49729093..f05397a3 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -247,6 +247,16 @@ macro_rules! varchar_cast { }; } +macro_rules! numeric_to_boolean { + ($value:expr) => { + match $value { + Some(0) => Ok(DataValue::Boolean(Some(false))), + Some(1) => Ok(DataValue::Boolean(Some(true))), + _ => Err(DatabaseError::CastFail), + } + }; +} + impl DataValue { pub fn date(&self) -> Option { if let DataValue::Date32(Some(val)) = self { @@ -670,6 +680,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::Int16(value) => match to { @@ -696,6 +707,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::Int32(value) => match to { @@ -721,6 +733,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::Int64(value) => match to { @@ -745,6 +758,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::UInt8(value) => match to { @@ -765,6 +779,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::UInt16(value) => match to { @@ -783,6 +798,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::UInt32(value) => match to { @@ -799,6 +815,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::UInt64(value) => match to { @@ -813,6 +830,7 @@ impl DataValue { decimal }))), + LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, DataValue::Utf8(value) => match to { diff --git a/tests/slt/crdb/and_or.slt b/tests/slt/crdb/and_or.slt new file mode 100644 index 00000000..88d33984 --- /dev/null +++ b/tests/slt/crdb/and_or.slt @@ -0,0 +1,63 @@ +statement ok +DROP TABLE IF EXISTS t + +statement ok +CREATE TABLE t (k INT PRIMARY KEY null, a INT null, b INT null) + +statement ok +INSERT INTO t VALUES (1, NULL, NULL), (2, NULL, 1), (3, 1, NULL), (4, 2, 0), (5, 3, 3) + +statement error (?s)1006.*divided by zero while evaluating function `divide\(3, 0\)` +SELECT a <> 2 AND 3 / b = 1 FROM t ORDER BY k + +query I +SELECT a FROM t WHERE a <> 2 AND 3 / b = 1 ORDER BY k +---- +3 + +statement error (?s)1006.*divided by zero while evaluating function `divide\(3, 0\)` +SELECT a = 2 OR 3 / b = 1 FROM t ORDER BY k + +query I +SELECT a FROM t WHERE a = 2 OR 3 / b = 1 ORDER BY k +---- +2 +3 + +statement ok +truncate table t + +statement ok +INSERT INTO t VALUES (1, NULL, NULL), (2, NULL, 1), (3, 1, NULL), (4, 2, 1), (5, 3, 3) + +query T +SELECT a <> 2 AND 3 / b = 1 FROM t ORDER BY k +---- +null +false +null +false +true + +query I +SELECT a FROM t WHERE a <> 2 AND 3 / b = 1 ORDER BY k +---- +3 + +query T +SELECT a = 2 OR 3 / b = 1 FROM t ORDER BY k +---- +null +null +null +true +true + +query I +SELECT a FROM t WHERE a = 2 OR 3 / b = 1 ORDER BY k +---- +2 +3 + +statement ok +DROP TABLE IF EXISTS t \ No newline at end of file diff --git a/tests/slt/dummy.slt b/tests/slt/dummy.slt new file mode 100644 index 00000000..7b0f189d --- /dev/null +++ b/tests/slt/dummy.slt @@ -0,0 +1,46 @@ +query I +SELECT 1 +---- +1 + +statement error +SELECT x + +query T +SELECT 'a' +---- +a + +query B +SELECT NOT(1=1) +---- +false + +query B +SELECT NOT(1::boolean) +---- +false + +query B +SELECT TRUE +---- +true + +query B +SELECT FALSE +---- +false + +query B +SELECT NOT(TRUE) +---- +false + +# issue: https://github.com/sqlparser-rs/sqlparser-rs/issues/362 +# query T +# SELECT 'That\'s good.' +# ---- +# That's good. + +statement error +SELECT * \ No newline at end of file diff --git a/tests/slt/filter.slt b/tests/slt/filter.slt index 6786d2df..9e41adc3 100644 --- a/tests/slt/filter.slt +++ b/tests/slt/filter.slt @@ -146,6 +146,7 @@ select * from t1 where id not in (1, 2) query IT select * from t1 where id in (1, null) ---- +1 KipDB query IT select * from t1 where null in (1, 2) @@ -195,6 +196,7 @@ select * from t1 where null between 1 and null query IT select * from t1 where id not between 1 and null ---- +0 KipSQL query IT select * from t1 where null not between 1 and 2