Skip to main content

risingwave_frontend/optimizer/plan_expr_visitor/
strong.rs

1// Copyright 2024 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use fixedbitset::FixedBitSet;
16
17use crate::expr::{ExprImpl, ExprType, FunctionCall, InputRef};
18
19/// This utilities are with the same definition in calcite.
20/// Utilities for strong predicates.
21/// A predicate is strong (or null-rejecting) with regards to selected subset of inputs
22/// if it is UNKNOWN if all inputs in selected subset are UNKNOWN.
23/// By the way, UNKNOWN is just the boolean form of NULL.
24///
25/// Examples:
26///
27/// UNKNOWN is strong in `[]` (definitely null)
28///
29/// `c = 1` is strong in `[c]` (definitely null if and only if c is null)
30///
31/// `c IS NULL` is not strong (always returns TRUE or FALSE, nevernull)
32///
33/// `p1 AND p2` is strong in `[p1, p2]` (definitely null if either p1 is null or p2 is null)
34///
35/// `p1 OR p2` is strong if p1 and p2 are strong
36
37#[derive(Default)]
38pub struct Strong {
39    null_columns: FixedBitSet,
40}
41
42impl Strong {
43    fn new(null_columns: FixedBitSet) -> Self {
44        Self { null_columns }
45    }
46
47    /// Returns whether the analyzed expression will *definitely* return null if
48    /// all of a given set of input columns are null.
49    /// Note: we could not assume any null-related property for the input expression if `is_null` returns false
50    pub fn is_null(expr: &ExprImpl, null_columns: FixedBitSet) -> bool {
51        let strong = Strong::new(null_columns);
52        strong.is_null_visit(expr)
53    }
54
55    fn is_input_ref_null(&self, input_ref: &InputRef) -> bool {
56        self.null_columns.contains(input_ref.index())
57    }
58
59    fn is_null_visit(&self, expr: &ExprImpl) -> bool {
60        match expr {
61            ExprImpl::InputRef(input_ref) => self.is_input_ref_null(input_ref),
62            ExprImpl::Literal(literal) => literal.get_data().is_none(),
63            ExprImpl::FunctionCall(func_call) => self.is_null_function_call(func_call),
64            ExprImpl::FunctionCallWithLambda(_) => false,
65            ExprImpl::AggCall(_) => false,
66            ExprImpl::Subquery(_) => false,
67            ExprImpl::CorrelatedInputRef(_) => false,
68            ExprImpl::TableFunction(_) => false,
69            ExprImpl::WindowFunction(_) => false,
70            ExprImpl::UserDefinedFunction(_) => false,
71            ExprImpl::Parameter(_) => false,
72            ExprImpl::Now(_) | ExprImpl::SecretRef(_) => false,
73        }
74    }
75
76    fn is_null_function_call(&self, func_call: &FunctionCall) -> bool {
77        match func_call.func_type() {
78            // NOT NULL: This kind of expression is never null. No need to look at its arguments, if it has any.
79            ExprType::IsNull
80            | ExprType::IsNotNull
81            | ExprType::IsDistinctFrom
82            | ExprType::IsNotDistinctFrom
83            | ExprType::IsTrue
84            | ExprType::QuoteNullable
85            | ExprType::IsNotTrue
86            | ExprType::IsFalse
87            | ExprType::IsNotFalse
88            | ExprType::CheckNotNull => false,
89            // ANY: This kind of expression is null if and only if at least one of its arguments is null.
90            ExprType::Not
91            | ExprType::Equal
92            | ExprType::NotEqual
93            | ExprType::LessThan
94            | ExprType::LessThanOrEqual
95            | ExprType::GreaterThan
96            | ExprType::GreaterThanOrEqual
97            | ExprType::Like
98            | ExprType::Add
99            | ExprType::AddWithTimeZone
100            | ExprType::Subtract
101            | ExprType::Multiply
102            | ExprType::Modulus
103            | ExprType::Divide
104            | ExprType::Cast
105            | ExprType::Trim
106            | ExprType::Ltrim
107            | ExprType::Rtrim
108            | ExprType::Ceil
109            | ExprType::Floor
110            | ExprType::Extract
111            | ExprType::L2Distance
112            | ExprType::CosineDistance
113            | ExprType::L1Distance
114            | ExprType::InnerProduct
115            | ExprType::VecConcat
116            | ExprType::L2Norm
117            | ExprType::L2Normalize
118            | ExprType::Subvector
119            | ExprType::Greatest
120            | ExprType::Least => self.any_null(func_call),
121            // ALL: This kind of expression is null if and only if all of its arguments are null.
122            ExprType::And | ExprType::Or | ExprType::Coalesce => self.all_null(func_call),
123            // TODO: Function like case when is important but current its structure is complicated, so we need to implement it later if necessary.
124            // Assume that any other expressions cannot be simplified.
125            #[expect(deprecated)]
126            ExprType::In
127            | ExprType::Some
128            | ExprType::All
129            | ExprType::BitwiseAnd
130            | ExprType::BitwiseOr
131            | ExprType::BitwiseXor
132            | ExprType::BitwiseNot
133            | ExprType::BitwiseShiftLeft
134            | ExprType::BitwiseShiftRight
135            | ExprType::DatePart
136            | ExprType::TumbleStart
137            | ExprType::MakeDate
138            | ExprType::MakeTime
139            | ExprType::MakeTimestamp
140            | ExprType::SecToTimestamptz
141            | ExprType::AtTimeZone
142            | ExprType::DateTrunc
143            | ExprType::DateBin
144            | ExprType::CharToTimestamptz
145            | ExprType::CharToDate
146            | ExprType::CastWithTimeZone
147            | ExprType::SubtractWithTimeZone
148            | ExprType::MakeTimestamptz
149            | ExprType::Substr
150            | ExprType::Length
151            | ExprType::ILike
152            | ExprType::SimilarToEscape
153            | ExprType::Upper
154            | ExprType::Lower
155            | ExprType::Replace
156            | ExprType::Position
157            | ExprType::Case
158            | ExprType::ConstantLookup
159            | ExprType::RoundDigit
160            | ExprType::Round
161            | ExprType::Ascii
162            | ExprType::Translate
163            | ExprType::Concat
164            | ExprType::ConcatVariadic
165            | ExprType::ConcatWs
166            | ExprType::ConcatWsVariadic
167            | ExprType::Abs
168            | ExprType::SplitPart
169            | ExprType::ToChar
170            | ExprType::Md5
171            | ExprType::CharLength
172            | ExprType::Repeat
173            | ExprType::ConcatOp
174            | ExprType::ByteaConcatOp
175            | ExprType::BoolOut
176            | ExprType::OctetLength
177            | ExprType::BitLength
178            | ExprType::Overlay
179            | ExprType::RegexpMatch
180            | ExprType::RegexpReplace
181            | ExprType::RegexpCount
182            | ExprType::RegexpSplitToArray
183            | ExprType::RegexpEq
184            | ExprType::Pow
185            | ExprType::Exp
186            | ExprType::Chr
187            | ExprType::StartsWith
188            | ExprType::Initcap
189            | ExprType::Lpad
190            | ExprType::Rpad
191            | ExprType::Reverse
192            | ExprType::Strpos
193            | ExprType::ToAscii
194            | ExprType::ToHex
195            | ExprType::QuoteIdent
196            | ExprType::QuoteLiteral
197            | ExprType::Sin
198            | ExprType::Cos
199            | ExprType::Tan
200            | ExprType::Cot
201            | ExprType::Asin
202            | ExprType::Acos
203            | ExprType::Acosd
204            | ExprType::Atan
205            | ExprType::Atan2
206            | ExprType::Atand
207            | ExprType::Atan2d
208            | ExprType::Sind
209            | ExprType::Cosd
210            | ExprType::Cotd
211            | ExprType::Tand
212            | ExprType::Asind
213            | ExprType::Sqrt
214            | ExprType::Degrees
215            | ExprType::Radians
216            | ExprType::Cosh
217            | ExprType::Tanh
218            | ExprType::Coth
219            | ExprType::Asinh
220            | ExprType::Acosh
221            | ExprType::Atanh
222            | ExprType::Sinh
223            | ExprType::Trunc
224            | ExprType::Ln
225            | ExprType::Log10
226            | ExprType::Cbrt
227            | ExprType::Sign
228            | ExprType::Scale
229            | ExprType::MinScale
230            | ExprType::TrimScale
231            | ExprType::Gamma
232            | ExprType::Lgamma
233            | ExprType::Encode
234            | ExprType::Decode
235            | ExprType::Sha1
236            | ExprType::Sha224
237            | ExprType::Sha256
238            | ExprType::Sha384
239            | ExprType::Sha512
240            | ExprType::Crc32
241            | ExprType::Crc32c
242            | ExprType::GetBit
243            | ExprType::GetByte
244            | ExprType::SetBit
245            | ExprType::SetByte
246            | ExprType::BitCount
247            | ExprType::Hmac
248            | ExprType::SecureCompare
249            | ExprType::Left
250            | ExprType::Right
251            | ExprType::Format
252            | ExprType::FormatVariadic
253            | ExprType::PgwireSend
254            | ExprType::PgwireRecv
255            | ExprType::ConvertFrom
256            | ExprType::ConvertTo
257            | ExprType::Decrypt
258            | ExprType::Encrypt
259            | ExprType::Neg
260            | ExprType::Field
261            | ExprType::Array
262            | ExprType::ArrayAccess
263            | ExprType::Row
264            | ExprType::ArrayToString
265            | ExprType::ArrayRangeAccess
266            | ExprType::ArrayCat
267            | ExprType::ArrayAppend
268            | ExprType::ArrayPrepend
269            | ExprType::FormatType
270            | ExprType::ArrayDistinct
271            | ExprType::ArrayLength
272            | ExprType::Cardinality
273            | ExprType::ArrayRemove
274            | ExprType::ArrayPositions
275            | ExprType::TrimArray
276            | ExprType::StringToArray
277            | ExprType::ArrayPosition
278            | ExprType::ArrayReplace
279            | ExprType::ArrayDims
280            | ExprType::ArrayTransform
281            | ExprType::ArrayMin
282            | ExprType::ArrayMax
283            | ExprType::ArraySum
284            | ExprType::ArraySort
285            | ExprType::ArrayReverse
286            | ExprType::ArrayContains
287            | ExprType::ArrayContained
288            | ExprType::ArrayOverlaps
289            | ExprType::ArrayFlatten
290            | ExprType::HexToInt256
291            | ExprType::JsonbAccess
292            | ExprType::JsonbAccessStr
293            | ExprType::JsonbExtractPath
294            | ExprType::JsonbExtractPathVariadic
295            | ExprType::JsonbExtractPathText
296            | ExprType::JsonbExtractPathTextVariadic
297            | ExprType::JsonbTypeof
298            | ExprType::JsonbArrayLength
299            | ExprType::IsJson
300            | ExprType::JsonbConcat
301            | ExprType::JsonbObject
302            | ExprType::JsonbPretty
303            | ExprType::JsonbContains
304            | ExprType::JsonbContained
305            | ExprType::JsonbExists
306            | ExprType::JsonbExistsAny
307            | ExprType::JsonbExistsAll
308            | ExprType::JsonbDeletePath
309            | ExprType::JsonbStripNulls
310            | ExprType::ToJsonb
311            | ExprType::JsonbBuildArray
312            | ExprType::JsonbBuildArrayVariadic
313            | ExprType::JsonbBuildObject
314            | ExprType::JsonbBuildObjectVariadic
315            | ExprType::JsonbPathExists
316            | ExprType::JsonbPathMatch
317            | ExprType::JsonbPathQueryArray
318            | ExprType::JsonbPathQueryFirst
319            | ExprType::JsonbPopulateRecord
320            | ExprType::JsonbToArray
321            | ExprType::JsonbToRecord
322            | ExprType::JsonbSet
323            | ExprType::JsonbPopulateMap
324            | ExprType::MapFromEntries
325            | ExprType::MapAccess
326            | ExprType::MapKeys
327            | ExprType::MapValues
328            | ExprType::MapEntries
329            | ExprType::MapFromKeyValues
330            | ExprType::MapCat
331            | ExprType::MapContains
332            | ExprType::MapDelete
333            | ExprType::MapFilter
334            | ExprType::MapInsert
335            | ExprType::MapLength
336            | ExprType::Vnode
337            | ExprType::VnodeUser
338            | ExprType::TestFeature
339            | ExprType::License
340            | ExprType::Proctime
341            | ExprType::PgSleep
342            | ExprType::PgSleepFor
343            | ExprType::PgSleepUntil
344            | ExprType::CastRegclass
345            | ExprType::PgGetIndexdef
346            | ExprType::ColDescription
347            | ExprType::PgGetViewdef
348            | ExprType::PgGetUserbyid
349            | ExprType::PgIndexesSize
350            | ExprType::PgRelationSize
351            | ExprType::PgGetSerialSequence
352            | ExprType::PgIndexColumnHasProperty
353            | ExprType::PgIsInRecovery
354            | ExprType::PgTableIsVisible
355            | ExprType::RwRecoveryStatus
356            | ExprType::RwClusterId
357            | ExprType::RwFragmentVnodes
358            | ExprType::RwActorVnodes
359            | ExprType::IcebergTransform
360            | ExprType::HasTablePrivilege
361            | ExprType::HasFunctionPrivilege
362            | ExprType::HasAnyColumnPrivilege
363            | ExprType::HasSchemaPrivilege
364            | ExprType::InetAton
365            | ExprType::InetNtoa
366            | ExprType::CompositeCast
367            | ExprType::RwEpochToTs
368            | ExprType::OpenaiEmbedding
369            | ExprType::HasDatabasePrivilege
370            | ExprType::Random
371            | ExprType::ClockTimestamp
372            | ExprType::GenRandomUuid => false,
373            ExprType::Unspecified => unreachable!(),
374        }
375    }
376
377    fn any_null(&self, func_call: &FunctionCall) -> bool {
378        func_call
379            .inputs()
380            .iter()
381            .any(|expr| self.is_null_visit(expr))
382    }
383
384    fn all_null(&self, func_call: &FunctionCall) -> bool {
385        func_call
386            .inputs()
387            .iter()
388            .all(|expr| self.is_null_visit(expr))
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    use risingwave_common::types::DataType;
395
396    use super::*;
397    use crate::expr::ExprImpl::Literal;
398
399    #[test]
400    fn test_literal() {
401        let null_columns = FixedBitSet::with_capacity(1);
402        let expr = Literal(crate::expr::Literal::new(None, DataType::Varchar).into());
403        assert!(Strong::is_null(&expr, null_columns.clone()));
404
405        let expr = Literal(
406            crate::expr::Literal::new(Some("test".to_owned().into()), DataType::Varchar).into(),
407        );
408        assert!(!Strong::is_null(&expr, null_columns));
409    }
410
411    #[test]
412    fn test_input_ref1() {
413        let null_columns = FixedBitSet::with_capacity(2);
414        let expr = InputRef::new(0, DataType::Varchar).into();
415        assert!(!Strong::is_null(&expr, null_columns.clone()));
416
417        let expr = InputRef::new(1, DataType::Varchar).into();
418        assert!(!Strong::is_null(&expr, null_columns));
419    }
420
421    #[test]
422    fn test_input_ref2() {
423        let mut null_columns = FixedBitSet::with_capacity(2);
424        null_columns.insert(0);
425        null_columns.insert(1);
426        let expr = InputRef::new(0, DataType::Varchar).into();
427        assert!(Strong::is_null(&expr, null_columns.clone()));
428
429        let expr = InputRef::new(1, DataType::Varchar).into();
430        assert!(Strong::is_null(&expr, null_columns));
431    }
432
433    #[test]
434    fn test_c1_equal_1_or_c2_is_null() {
435        let mut null_columns = FixedBitSet::with_capacity(2);
436        null_columns.insert(0);
437        let expr = FunctionCall::new_unchecked(
438            ExprType::Or,
439            vec![
440                FunctionCall::new_unchecked(
441                    ExprType::Equal,
442                    vec![
443                        InputRef::new(0, DataType::Int64).into(),
444                        Literal(crate::expr::Literal::new(Some(1.into()), DataType::Int32).into()),
445                    ],
446                    DataType::Boolean,
447                )
448                .into(),
449                FunctionCall::new_unchecked(
450                    ExprType::IsNull,
451                    vec![InputRef::new(1, DataType::Int64).into()],
452                    DataType::Boolean,
453                )
454                .into(),
455            ],
456            DataType::Boolean,
457        )
458        .into();
459        assert!(!Strong::is_null(&expr, null_columns));
460    }
461
462    #[test]
463    fn test_divide() {
464        let mut null_columns = FixedBitSet::with_capacity(2);
465        null_columns.insert(0);
466        null_columns.insert(1);
467        let expr = FunctionCall::new_unchecked(
468            ExprType::Divide,
469            vec![
470                InputRef::new(0, DataType::Decimal).into(),
471                InputRef::new(1, DataType::Decimal).into(),
472            ],
473            DataType::Varchar,
474        )
475        .into();
476        assert!(Strong::is_null(&expr, null_columns));
477    }
478
479    /// generate a test case for (0.8 * sum / count) where sum is null and count is not null
480    #[test]
481    fn test_multiply_divide() {
482        let mut null_columns = FixedBitSet::with_capacity(2);
483        null_columns.insert(0);
484        let expr = FunctionCall::new_unchecked(
485            ExprType::Multiply,
486            vec![
487                Literal(crate::expr::Literal::new(Some(0.8f64.into()), DataType::Float64).into()),
488                FunctionCall::new_unchecked(
489                    ExprType::Divide,
490                    vec![
491                        InputRef::new(0, DataType::Decimal).into(),
492                        InputRef::new(1, DataType::Decimal).into(),
493                    ],
494                    DataType::Decimal,
495                )
496                .into(),
497            ],
498            DataType::Decimal,
499        )
500        .into();
501        assert!(Strong::is_null(&expr, null_columns));
502    }
503
504    /// generate test cases for is not null
505    macro_rules! gen_test {
506        ($func:ident, $expr:expr, $expected:expr) => {
507            #[test]
508            fn $func() {
509                let null_columns = FixedBitSet::with_capacity(2);
510                let expr = $expr;
511                assert_eq!(Strong::is_null(&expr, null_columns), $expected);
512            }
513        };
514    }
515
516    gen_test!(
517        test_is_not_null,
518        FunctionCall::new_unchecked(
519            ExprType::IsNotNull,
520            vec![InputRef::new(0, DataType::Varchar).into()],
521            DataType::Varchar
522        )
523        .into(),
524        false
525    );
526    gen_test!(
527        test_is_null,
528        FunctionCall::new_unchecked(
529            ExprType::IsNull,
530            vec![InputRef::new(0, DataType::Varchar).into()],
531            DataType::Varchar
532        )
533        .into(),
534        false
535    );
536    gen_test!(
537        test_is_distinct_from,
538        FunctionCall::new_unchecked(
539            ExprType::IsDistinctFrom,
540            vec![
541                InputRef::new(0, DataType::Varchar).into(),
542                InputRef::new(1, DataType::Varchar).into()
543            ],
544            DataType::Varchar
545        )
546        .into(),
547        false
548    );
549    gen_test!(
550        test_is_not_distinct_from,
551        FunctionCall::new_unchecked(
552            ExprType::IsNotDistinctFrom,
553            vec![
554                InputRef::new(0, DataType::Varchar).into(),
555                InputRef::new(1, DataType::Varchar).into()
556            ],
557            DataType::Varchar
558        )
559        .into(),
560        false
561    );
562    gen_test!(
563        test_is_true,
564        FunctionCall::new_unchecked(
565            ExprType::IsTrue,
566            vec![InputRef::new(0, DataType::Varchar).into()],
567            DataType::Varchar
568        )
569        .into(),
570        false
571    );
572    gen_test!(
573        test_is_not_true,
574        FunctionCall::new_unchecked(
575            ExprType::IsNotTrue,
576            vec![InputRef::new(0, DataType::Varchar).into()],
577            DataType::Varchar
578        )
579        .into(),
580        false
581    );
582    gen_test!(
583        test_is_false,
584        FunctionCall::new_unchecked(
585            ExprType::IsFalse,
586            vec![InputRef::new(0, DataType::Varchar).into()],
587            DataType::Varchar
588        )
589        .into(),
590        false
591    );
592}