risingwave_frontend/expr/
pure.rs1use std::borrow::Cow;
16
17use expr_node::Type;
18use risingwave_pb::expr::expr_node;
19
20use super::{ExprImpl, ExprVisitor};
21use crate::expr::FunctionCall;
22
23#[derive(Default)]
24pub(crate) struct ImpureAnalyzer {
25 impure: Option<Cow<'static, str>>,
26}
27
28impl ImpureAnalyzer {
29 pub fn is_impure(&self) -> bool {
33 self.impure.is_some()
34 }
35
36 pub fn impure_expr_desc(&self) -> Option<&str> {
41 self.impure.as_deref()
42 }
43}
44
45impl ExprVisitor for ImpureAnalyzer {
46 fn visit_user_defined_function(&mut self, func_call: &super::UserDefinedFunction) {
47 let name = &func_call.catalog.name;
48 self.impure = Some(format!("user-defined function `{name}`").into());
49 }
50
51 fn visit_table_function(&mut self, func_call: &super::TableFunction) {
52 use crate::expr::table_function::TableFunctionType as Type;
53 let func_type = func_call.function_type;
54 match func_type {
55 Type::Unspecified => unreachable!(),
56
57 Type::GenerateSeries
59 | Type::Unnest
60 | Type::RegexpMatches
61 | Type::Range
62 | Type::GenerateSubscripts
63 | Type::PgExpandarray
64 | Type::JsonbArrayElements
65 | Type::JsonbArrayElementsText
66 | Type::JsonbEach
67 | Type::JsonbEachText
68 | Type::JsonbObjectKeys
69 | Type::JsonbPathQuery
70 | Type::JsonbPopulateRecordset
71 | Type::JsonbToRecordset => {
72 func_call.args.iter().for_each(|expr| self.visit_expr(expr));
73 }
74
75 Type::FileScan
77 | Type::PostgresQuery
78 | Type::MysqlQuery
79 | Type::MssqlQuery
80 | Type::InternalBackfillProgress
81 | Type::InternalSourceBackfillProgress
82 | Type::InternalGetChannelDeltaStats
83 | Type::PgGetKeywords => {
84 self.impure = Some(func_type.as_str_name().into());
85 }
86 Type::UserDefined => {
87 let name = &func_call.user_defined.as_ref().unwrap().name;
88 self.impure = Some(format!("user-defined table function `{name}`").into());
89 }
90 }
91 }
92
93 fn visit_now(&mut self, _: &super::Now) {
94 self.impure = Some("NOW or PROCTIME".into());
95 }
96
97 fn visit_secret_ref(&mut self, secret_ref: &super::SecretRef) {
98 self.impure = Some(format!("secret reference `{}`", secret_ref.secret_name).into());
99 }
100
101 fn visit_function_call(&mut self, func_call: &super::FunctionCall) {
102 let func_type = func_call.func_type();
103 match func_type {
104 Type::Unspecified => unreachable!(),
105 #[expect(deprecated)]
106 Type::Add
107 | Type::Subtract
108 | Type::Multiply
109 | Type::Divide
110 | Type::Modulus
111 | Type::Equal
112 | Type::NotEqual
113 | Type::LessThan
114 | Type::LessThanOrEqual
115 | Type::GreaterThan
116 | Type::GreaterThanOrEqual
117 | Type::And
118 | Type::Or
119 | Type::Not
120 | Type::In
121 | Type::Some
122 | Type::All
123 | Type::BitwiseAnd
124 | Type::BitwiseOr
125 | Type::BitwiseXor
126 | Type::BitwiseNot
127 | Type::BitwiseShiftLeft
128 | Type::BitwiseShiftRight
129 | Type::Extract
130 | Type::DatePart
131 | Type::TumbleStart
132 | Type::SecToTimestamptz
133 | Type::AtTimeZone
134 | Type::DateTrunc
135 | Type::DateBin
136 | Type::MakeDate
137 | Type::MakeTime
138 | Type::MakeTimestamp
139 | Type::CharToTimestamptz
140 | Type::CharToDate
141 | Type::CastWithTimeZone
142 | Type::AddWithTimeZone
143 | Type::SubtractWithTimeZone
144 | Type::Cast
145 | Type::Substr
146 | Type::Length
147 | Type::Like
148 | Type::ILike
149 | Type::SimilarToEscape
150 | Type::Upper
151 | Type::Lower
152 | Type::Trim
153 | Type::Replace
154 | Type::Position
155 | Type::Ltrim
156 | Type::Rtrim
157 | Type::Case
158 | Type::ConstantLookup
159 | Type::RoundDigit
160 | Type::Round
161 | Type::Ascii
162 | Type::Translate
163 | Type::Coalesce
164 | Type::ConcatWs
165 | Type::ConcatWsVariadic
166 | Type::Abs
167 | Type::SplitPart
168 | Type::Ceil
169 | Type::Floor
170 | Type::Trunc
171 | Type::ToChar
172 | Type::Md5
173 | Type::CharLength
174 | Type::Repeat
175 | Type::ConcatOp
176 | Type::ByteaConcatOp
177 | Type::Concat
178 | Type::ConcatVariadic
179 | Type::BoolOut
180 | Type::OctetLength
181 | Type::BitLength
182 | Type::Overlay
183 | Type::RegexpMatch
184 | Type::RegexpReplace
185 | Type::RegexpCount
186 | Type::RegexpSplitToArray
187 | Type::RegexpEq
188 | Type::Pow
189 | Type::Exp
190 | Type::Ln
191 | Type::Log10
192 | Type::Chr
193 | Type::StartsWith
194 | Type::Initcap
195 | Type::Lpad
196 | Type::Rpad
197 | Type::Reverse
198 | Type::Strpos
199 | Type::ToAscii
200 | Type::ToHex
201 | Type::QuoteIdent
202 | Type::Sin
203 | Type::Cos
204 | Type::Tan
205 | Type::Cot
206 | Type::Asin
207 | Type::Acos
208 | Type::Acosd
209 | Type::Atan
210 | Type::Atan2
211 | Type::Atand
212 | Type::Atan2d
213 | Type::Sqrt
214 | Type::Cbrt
215 | Type::Sign
216 | Type::Scale
217 | Type::MinScale
218 | Type::TrimScale
219 | Type::Gamma
220 | Type::Lgamma
221 | Type::Left
222 | Type::Right
223 | Type::Degrees
224 | Type::Radians
225 | Type::IsTrue
226 | Type::IsNotTrue
227 | Type::IsFalse
228 | Type::IsNotFalse
229 | Type::IsNull
230 | Type::IsNotNull
231 | Type::IsDistinctFrom
232 | Type::IsNotDistinctFrom
233 | Type::Neg
234 | Type::Field
235 | Type::Array
236 | Type::ArrayAccess
237 | Type::ArrayRangeAccess
238 | Type::Row
239 | Type::ArrayToString
240 | Type::ArrayCat
241 | Type::ArrayMax
242 | Type::ArraySum
243 | Type::ArraySort
244 | Type::ArrayAppend
245 | Type::ArrayReverse
246 | Type::ArrayPrepend
247 | Type::FormatType
248 | Type::ArrayDistinct
249 | Type::ArrayMin
250 | Type::ArrayDims
251 | Type::ArrayLength
252 | Type::Cardinality
253 | Type::TrimArray
254 | Type::ArrayRemove
255 | Type::ArrayReplace
256 | Type::ArrayPosition
257 | Type::ArrayContains
258 | Type::ArrayContained
259 | Type::ArrayOverlaps
260 | Type::ArrayFlatten
261 | Type::HexToInt256
262 | Type::JsonbConcat
263 | Type::JsonbAccess
264 | Type::JsonbAccessStr
265 | Type::JsonbExtractPath
266 | Type::JsonbExtractPathVariadic
267 | Type::JsonbExtractPathText
268 | Type::JsonbExtractPathTextVariadic
269 | Type::JsonbTypeof
270 | Type::JsonbArrayLength
271 | Type::JsonbObject
272 | Type::JsonbPretty
273 | Type::JsonbDeletePath
274 | Type::JsonbContains
275 | Type::JsonbContained
276 | Type::JsonbExists
277 | Type::JsonbExistsAny
278 | Type::JsonbExistsAll
279 | Type::JsonbStripNulls
280 | Type::JsonbBuildArray
281 | Type::JsonbBuildArrayVariadic
282 | Type::JsonbBuildObject
283 | Type::JsonbPopulateRecord
284 | Type::JsonbToArray
285 | Type::JsonbToRecord
286 | Type::JsonbBuildObjectVariadic
287 | Type::JsonbPathExists
288 | Type::JsonbPathMatch
289 | Type::JsonbPathQueryArray
290 | Type::JsonbPathQueryFirst
291 | Type::JsonbSet
292 | Type::JsonbPopulateMap
293 | Type::IsJson
294 | Type::ToJsonb
295 | Type::ToVariant
296 | Type::VariantGet
297 | Type::TryVariantGet
298 | Type::VariantTypeof
299 | Type::Sind
300 | Type::Cosd
301 | Type::Cotd
302 | Type::Asind
303 | Type::Sinh
304 | Type::Cosh
305 | Type::Coth
306 | Type::Tanh
307 | Type::Atanh
308 | Type::Asinh
309 | Type::Acosh
310 | Type::Decode
311 | Type::Encode
312 | Type::GetBit
313 | Type::GetByte
314 | Type::SetBit
315 | Type::SetByte
316 | Type::BitCount
317 | Type::Sha1
318 | Type::Sha224
319 | Type::Sha256
320 | Type::Sha384
321 | Type::Sha512
322 | Type::Crc32
323 | Type::Crc32c
324 | Type::Hmac
325 | Type::SecureCompare
326 | Type::Decrypt
327 | Type::Encrypt
328 | Type::Tand
329 | Type::ArrayPositions
330 | Type::StringToArray
331 | Type::Format
332 | Type::FormatVariadic
333 | Type::PgwireSend
334 | Type::PgwireRecv
335 | Type::ArrayTransform
336 | Type::Greatest
337 | Type::Least
338 | Type::ConvertFrom
339 | Type::ConvertTo
340 | Type::IcebergTransform
341 | Type::InetNtoa
342 | Type::InetAton
343 | Type::QuoteLiteral
344 | Type::QuoteNullable
345 | Type::MapFromEntries
346 | Type::MapAccess
347 | Type::MapKeys
348 | Type::MapValues
349 | Type::MapEntries
350 | Type::MapFromKeyValues
351 | Type::MapCat
352 | Type::MapContains
353 | Type::MapDelete
354 | Type::MapFilter
355 | Type::MapInsert
356 | Type::MapLength
357 | Type::L2Distance
358 | Type::CosineDistance
359 | Type::L1Distance
360 | Type::InnerProduct
361 | Type::VecConcat
362 | Type::L2Norm
363 | Type::L2Normalize
364 | Type::Subvector
365 | Type::Vnode
370 | Type::VnodeUser
371 | Type::RwEpochToTs
372 | Type::CheckNotNull
373 | Type::CompositeCast =>
374 {
376 func_call
377 .inputs()
378 .iter()
379 .for_each(|expr| self.visit_expr(expr));
380 }
381 Type::TestFeature
383 | Type::License
384 | Type::Proctime
385 | Type::PgSleep
386 | Type::PgSleepFor
387 | Type::PgSleepUntil
388 | Type::CastRegclass
389 | Type::PgGetIndexdef
390 | Type::ColDescription
391 | Type::PgGetViewdef
392 | Type::PgGetUserbyid
393 | Type::PgIndexesSize
394 | Type::PgRelationSize
395 | Type::PgGetSerialSequence
396 | Type::PgIndexColumnHasProperty
397 | Type::HasTablePrivilege
398 | Type::HasAnyColumnPrivilege
399 | Type::HasSchemaPrivilege
400 | Type::MakeTimestamptz
401 | Type::PgIsInRecovery
402 | Type::RwRecoveryStatus
403 | Type::RwClusterId
404 | Type::RwFragmentVnodes
405 | Type::RwActorVnodes
406 | Type::PgTableIsVisible
407 | Type::HasFunctionPrivilege
408 | Type::OpenaiEmbedding
409 | Type::HasDatabasePrivilege
410 | Type::Random
411 | Type::ClockTimestamp
412 | Type::GenRandomUuid => self.impure = Some(func_type.as_str_name().into()),
413 }
414 }
415}
416
417pub fn is_pure(expr: &ExprImpl) -> bool {
418 !is_impure(expr)
419}
420
421pub fn is_impure(expr: &ExprImpl) -> bool {
422 let mut a = ImpureAnalyzer::default();
423 a.visit_expr(expr);
424 a.is_impure()
425}
426
427pub fn is_impure_func_call(func_call: &FunctionCall) -> bool {
428 let mut a = ImpureAnalyzer::default();
429 a.visit_function_call(func_call);
430 a.is_impure()
431}
432
433pub fn impure_expr_desc(expr: &ExprImpl) -> Option<String> {
436 let mut a = ImpureAnalyzer::default();
437 a.visit_expr(expr);
438 a.impure_expr_desc().map(|s| s.to_owned())
439}
440
441#[cfg(test)]
442mod tests {
443 use risingwave_common::types::DataType;
444 use risingwave_pb::expr::expr_node::Type;
445
446 use crate::expr::{ExprImpl, FunctionCall, InputRef, is_impure, is_pure};
447
448 fn expect_pure(expr: &ExprImpl) {
449 assert!(is_pure(expr));
450 assert!(!is_impure(expr));
451 }
452
453 fn expect_impure(expr: &ExprImpl) {
454 assert!(!is_pure(expr));
455 assert!(is_impure(expr));
456 }
457
458 #[test]
459 fn test_pure_funcs() {
460 let e: ExprImpl = FunctionCall::new(
461 Type::Add,
462 vec![
463 InputRef::new(0, DataType::Int16).into(),
464 InputRef::new(0, DataType::Int16).into(),
465 ],
466 )
467 .unwrap()
468 .into();
469 expect_pure(&e);
470
471 let e: ExprImpl = FunctionCall::new(
472 Type::GreaterThan,
473 vec![
474 InputRef::new(0, DataType::Timestamptz).into(),
475 FunctionCall::new(Type::Proctime, vec![]).unwrap().into(),
476 ],
477 )
478 .unwrap()
479 .into();
480 expect_impure(&e);
481 }
482}