risingwave_frontend/optimizer/plan_expr_visitor/
strong.rs1use fixedbitset::FixedBitSet;
16
17use crate::expr::{ExprImpl, ExprType, FunctionCall, InputRef};
18
19#[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 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 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 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 ExprType::And | ExprType::Or | ExprType::Coalesce => self.all_null(func_call),
123 #[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 #[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 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}