Skip to content

Commit d0d1590

Browse files
Merge pull request #12271 from ClickHouse/fix-logical-functions-review
Fix minor issues in ternary logic
2 parents abf1e01 + 5ef4e90 commit d0d1590

4 files changed

Lines changed: 44 additions & 5 deletions

File tree

src/Functions/FunctionsLogical.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ static void executeForTernaryLogicImpl(ColumnRawPtrs arguments, ColumnWithTypeAn
336336
const bool has_consts = extractConstColumnsAsTernary<Op>(arguments, const_3v_value);
337337

338338
/// If the constant value uniquely determines the result, return it.
339-
if (has_consts && (arguments.empty() || Op::isSaturatedValue(const_3v_value)))
339+
if (has_consts && (arguments.empty() || Op::isSaturatedValueTernary(const_3v_value)))
340340
{
341341
result_info.column = ColumnConst::create(
342342
buildColumnFromTernaryData(UInt8Container({const_3v_value}), result_info.type->isNullable()),
@@ -498,7 +498,8 @@ DataTypePtr FunctionAnyArityLogical<Impl, Name>::getReturnTypeImpl(const DataTyp
498498
}
499499

500500
template <typename Impl, typename Name>
501-
void FunctionAnyArityLogical<Impl, Name>::executeImpl(Block & block, const ColumnNumbers & arguments, size_t result_index, size_t input_rows_count)
501+
void FunctionAnyArityLogical<Impl, Name>::executeImpl(
502+
Block & block, const ColumnNumbers & arguments, size_t result_index, size_t input_rows_count)
502503
{
503504
ColumnRawPtrs args_in;
504505
for (const auto arg_index : arguments)

src/Functions/FunctionsLogical.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,21 @@ namespace Ternary
3636
{
3737
using ResultType = UInt8;
3838

39-
static constexpr UInt8 False = 0;
40-
static constexpr UInt8 True = -1;
41-
static constexpr UInt8 Null = 1;
39+
/** These carefully picked values magically work so bitwise "and", "or" on them
40+
* corresponds to the expected results in three-valued logic.
41+
*
42+
* False and True are represented by all-0 and all-1 bits, so all bitwise operations on them work as expected.
43+
* Null is represented as single 1 bit. So, it is something in between False and True.
44+
* And "or" works like maximum and "and" works like minimum:
45+
* "or" keeps True as is and lifts False with Null to Null.
46+
* "and" keeps False as is and downs True with Null to Null.
47+
*
48+
* This logic does not apply for "not" and "xor" - they work with default implementation for NULLs:
49+
* anything with NULL returns NULL, otherwise use conventional two-valued logic.
50+
*/
51+
static constexpr UInt8 False = 0; /// All zero bits.
52+
static constexpr UInt8 True = -1; /// All one bits.
53+
static constexpr UInt8 Null = 1; /// Single one bit.
4254

4355
template <typename T>
4456
inline ResultType makeValue(T value)
@@ -61,9 +73,16 @@ struct AndImpl
6173
using ResultType = UInt8;
6274

6375
static inline constexpr bool isSaturable() { return true; }
76+
77+
/// Final value in two-valued logic (no further operations with True, False will change this value)
6478
static inline constexpr bool isSaturatedValue(bool a) { return !a; }
79+
80+
/// Final value in three-valued logic (no further operations with True, False, Null will change this value)
6581
static inline constexpr bool isSaturatedValueTernary(UInt8 a) { return a == Ternary::False; }
82+
6683
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a & b; }
84+
85+
/// Will use three-valued logic for NULLs (see above) or default implementation (any operation with NULL returns NULL).
6786
static inline constexpr bool specialImplementationForNulls() { return true; }
6887
};
6988

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
1
2+
1
3+
0
4+
0
5+
\N
6+
\N
7+
\N
8+
\N
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
SELECT NULL OR 1;
2+
SELECT materialize(NULL) OR materialize(1);
3+
4+
SELECT NULL AND 0;
5+
SELECT materialize(NULL) AND materialize(0);
6+
7+
SELECT NULL OR 0;
8+
SELECT materialize(NULL) OR materialize(0);
9+
10+
SELECT NULL AND 1;
11+
SELECT materialize(NULL) AND materialize(1);

0 commit comments

Comments
 (0)