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