Function mz_sql::query_model::attribute::rejected_nulls::rejected_nulls
source · pub(crate) fn rejected_nulls(
expr: &BoxScalarExpr,
set: &mut HashSet<ColumnReference>
)Expand description
Returns all columns that must be non-NULL for the boolean expr
to be NULL or FALSE.
An expression expr rejects nulls in a set of column references
C if it evaluates to either FALSE or NULL whenever some
c in C is null.
An expression expr propagates nulls in a set of column references
C if it evaluates to NULL whenever some c in C is null.
Consequently, results returned by propagated_nulls must be
included in rejected_nulls.
Unfortunately, boolean functions such as “and” and “or” are not propagating nulls in their inputs, but we still need to handle them here, as they are used quite frequently in predicates. The procedure for doing this is derived below.
Observe the truth values for the following terms:
For AND(A, B):
| F | N | T | |
|---|---|---|---|
| F | F | F | F |
| N | F | N | N |
| T | F | N | T |
For OR(A, B):
| F | N | T | |
|---|---|---|---|
| F | F | N | T |
| N | N | N | T |
| T | T | T | T |
For NOT(AND(A, B)):
| F | N | T | |
|---|---|---|---|
| F | T | T | T |
| N | T | N | N |
| T | T | N | F |
For NOT(OR(A, B)):
| F | N | T | |
|---|---|---|---|
| F | T | N | F |
| N | N | N | F |
| T | F | F | F |
Based on the above truth tables, we can establish the following statements are always true:
- If either
AorBrejects nulls inC, thenAND(A, B)rejects nulls inC. - If both
AandBreject nulls inC, thenOR(A, B)rejects nulls inC. - If both
AandBpropagate nulls inC, thenNOT(AND(A, B))rejects nulls inC. - If either
AorBpropagates nulls inC, thenNOT(OR(A, B))rejects nulls inC.
Based on the above statements, the algorithm implemented by this function can be described by the following pseudo-code:
def rejected_nulls(expr: Expr, sign: bool = true) -> Set[Expr]:
match expr:
case NOT(ISNULL(c)):
{ c }
case NOT(expr):
rejected_nulls(expr, !sign)
case AND(lhs, rhs):
if sign > 0:
rejected_nulls(lhs, sign) ∪ rejected_nulls(rhs, sign)
else:
propagated_nulls(lhs) ∩ propagated_nulls(rhs)
case OR(lhs, rhs):
if sign > 0:
rejected_nulls(lhs, sign) ∩ rejected_nulls(rhs, sign)
else:
propagated_nulls(lhs) ∪ propagated_nulls(rhs)
case expr:
propagated_nulls(expr)