From 6841be011747ebc28dc0d8c3a914453dc7109043 Mon Sep 17 00:00:00 2001 From: Elliot Chance Date: Wed, 18 Oct 2023 23:32:47 -0400 Subject: [PATCH] Remove .is_unknown for BOOLEAN (#164) This is only needed for interval storage, it's not distinct from the NULL property of BOOLEAN itself. There are no changes needed to the database version. This also found (and fixes) two cases with incorrect BOOLEAN logic. --- docs/v-client-library-docs.rst | 7 ++-- tests/boolean.sql | 4 +-- vsql/eval.v | 30 ++++++---------- vsql/operators.v | 63 ++++++++++++++++------------------ vsql/row.v | 6 ++-- vsql/value.v | 16 ++++----- 6 files changed, 58 insertions(+), 68 deletions(-) diff --git a/docs/v-client-library-docs.rst b/docs/v-client-library-docs.rst index b35f15d..2e4187c 100644 --- a/docs/v-client-library-docs.rst +++ b/docs/v-client-library-docs.rst @@ -334,9 +334,10 @@ enum Boolean pub enum Boolean { // These must not be negative values because they are encoded as u8 on disk. - is_unknown = 0 // same as NULL - is_false = 1 - is_true = 2 + // 0 is resevered for encoding NULL on disk, but is not a valid value in + // memory. + is_false = 1 + is_true = 2 } diff --git a/tests/boolean.sql b/tests/boolean.sql index 01958ea..10d847f 100644 --- a/tests/boolean.sql +++ b/tests/boolean.sql @@ -111,7 +111,7 @@ VALUES FALSE IS NULL; -- COL1: FALSE VALUES UNKNOWN IS NULL; --- COL1: FALSE +-- COL1: TRUE VALUES TRUE IS NOT TRUE; -- COL1: FALSE @@ -147,7 +147,7 @@ VALUES FALSE IS NOT NULL; -- COL1: TRUE VALUES UNKNOWN IS NOT NULL; --- COL1: TRUE +-- COL1: FALSE VALUES NOT TRUE; -- COL1: FALSE diff --git a/vsql/eval.v b/vsql/eval.v index ad15387..430c425 100644 --- a/vsql/eval.v +++ b/vsql/eval.v @@ -437,26 +437,18 @@ fn eval_cast(mut conn Connection, data Row, e CastExpr, params map[string]Value) } fn eval_truth(mut conn Connection, data Row, e TruthExpr, params map[string]Value) !Value { + // See ISO/IEC 9075-2:2016(E), 6.39, , + // "Table 15 — Truth table for the IS boolean operator" + value := eval_as_value(mut conn, data, e.expr, params)! - result := match value.bool_value() { - .is_true { - match e.value.bool_value() { - .is_true { new_boolean_value(true) } - .is_false, .is_unknown { new_boolean_value(false) } - } - } - .is_false { - match e.value.bool_value() { - .is_true, .is_unknown { new_boolean_value(false) } - .is_false { new_boolean_value(true) } - } - } - .is_unknown { - match e.value.bool_value() { - .is_true, .is_false { new_boolean_value(false) } - .is_unknown { new_boolean_value(true) } - } - } + mut result := new_boolean_value(false) + + if value.is_null { + result = new_boolean_value(e.value.is_null) + } else if value.bool_value() == .is_true { + result = new_boolean_value(e.value.bool_value() == .is_true) + } else { + result = new_boolean_value(e.value.bool_value() == .is_false) } if e.not { diff --git a/vsql/operators.v b/vsql/operators.v index b404981..caba49f 100644 --- a/vsql/operators.v +++ b/vsql/operators.v @@ -157,10 +157,13 @@ fn unary_negate_double_precision(conn &Connection, v Value) !Value { } fn unary_not_boolean(conn &Connection, v Value) !Value { + if v.is_null { + return new_unknown_value() + } + return match v.bool_value() { .is_true { new_boolean_value(false) } .is_false { new_boolean_value(true) } - .is_unknown { new_unknown_value() } } } @@ -229,47 +232,41 @@ fn binary_varchar_concat_varchar(conn &Connection, a Value, b Value) !Value { } fn binary_boolean_and_boolean(conn &Connection, a Value, b Value) !Value { - match a.bool_value() { - .is_true { - return match b.bool_value() { - .is_true { new_boolean_value(true) } - .is_false { new_boolean_value(false) } - .is_unknown { new_unknown_value() } - } - } - .is_false { + // See ISO/IEC 9075-2:2016(E), 6.39, , + // "Table 13 — Truth table for the AND boolean operator" + + if a.is_null { + if b.bool_value() == .is_false { return new_boolean_value(false) } - .is_unknown { - return match b.bool_value() { - .is_true { new_unknown_value() } - .is_false { new_boolean_value(false) } - .is_unknown { new_unknown_value() } - } - } + + return new_unknown_value() + } + + if a.bool_value() == .is_true { + return b } + + return new_boolean_value(false) } fn binary_boolean_or_boolean(conn &Connection, a Value, b Value) !Value { - match a.bool_value() { - .is_true { + // See ISO/IEC 9075-2:2016(E), 6.39, , + // "Table 13 — Truth table for the AND boolean operator" + + if a.is_null { + if b.bool_value() == .is_true { return new_boolean_value(true) } - .is_false { - return match b.bool_value() { - .is_true { new_boolean_value(true) } - .is_false { new_boolean_value(false) } - .is_unknown { new_unknown_value() } - } - } - .is_unknown { - return match b.bool_value() { - .is_true { new_boolean_value(true) } - .is_false { new_unknown_value() } - .is_unknown { new_unknown_value() } - } - } + + return new_unknown_value() + } + + if a.bool_value() == .is_true { + return new_boolean_value(true) } + + return b } fn binary_float_equal_float(conn &Connection, a Value, b Value) !Value { diff --git a/vsql/row.v b/vsql/row.v index 4ef03fc..3779fa4 100644 --- a/vsql/row.v +++ b/vsql/row.v @@ -280,9 +280,11 @@ fn new_row_from_bytes(t Table, data []u8, tid int) Row { match col.typ.typ { .is_boolean { unsafe { - v.v.bool_value = Boolean(buf.read_u8()) - if v.bool_value() == .is_unknown { + b := buf.read_u8() + if b == 0 { v.is_null = true + } else { + v.v.bool_value = Boolean(b) } } } diff --git a/vsql/value.v b/vsql/value.v index 4c48122..e107be7 100644 --- a/vsql/value.v +++ b/vsql/value.v @@ -11,9 +11,10 @@ import regex // Possible values for a BOOLEAN. pub enum Boolean { // These must not be negative values because they are encoded as u8 on disk. - is_unknown = 0 // same as NULL - is_false = 1 - is_true = 2 + // 0 is resevered for encoding NULL on disk, but is not a valid value in + // memory. + is_false = 1 + is_true = 2 } // Returns ``TRUE``, ``FALSE`` or ``UNKNOWN``. @@ -21,7 +22,6 @@ pub fn (b Boolean) str() string { return match b { .is_false { 'FALSE' } .is_true { 'TRUE' } - .is_unknown { 'UNKNOWN' } } } @@ -80,9 +80,7 @@ pub fn new_boolean_value(b bool) Value { pub fn new_unknown_value() Value { return Value{ typ: Type{.is_boolean, 0, 0, false} - v: InternalValue{ - bool_value: .is_unknown - } + is_null: true } } @@ -286,8 +284,8 @@ fn (v Value) as_numeric() !big.Integer { // The string representation of this value. Different types will have different // formatting. pub fn (v Value) str() string { - if v.is_null && v.typ.typ != .is_boolean { - return 'NULL' + if v.is_null { + return if v.typ.typ == .is_boolean { 'UNKNOWN' } else { 'NULL' } } return match v.typ.typ {