risingwave_frontend/expr/
pure.rs

1// Copyright 2025 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 expr_node::Type;
16use risingwave_pb::expr::expr_node;
17
18use super::{ExprImpl, ExprVisitor};
19use crate::expr::FunctionCall;
20
21#[derive(Default)]
22pub(crate) struct ImpureAnalyzer {
23    pub(crate) impure: bool,
24}
25
26impl ExprVisitor for ImpureAnalyzer {
27    fn visit_user_defined_function(&mut self, _func_call: &super::UserDefinedFunction) {
28        self.impure = true;
29    }
30
31    fn visit_now(&mut self, _: &super::Now) {
32        self.impure = true;
33    }
34
35    fn visit_function_call(&mut self, func_call: &super::FunctionCall) {
36        match func_call.func_type() {
37            Type::Unspecified => unreachable!(),
38            Type::Add
39            | Type::Subtract
40            | Type::Multiply
41            | Type::Divide
42            | Type::Modulus
43            | Type::Equal
44            | Type::NotEqual
45            | Type::LessThan
46            | Type::LessThanOrEqual
47            | Type::GreaterThan
48            | Type::GreaterThanOrEqual
49            | Type::And
50            | Type::Or
51            | Type::Not
52            | Type::In
53            | Type::Some
54            | Type::All
55            | Type::BitwiseAnd
56            | Type::BitwiseOr
57            | Type::BitwiseXor
58            | Type::BitwiseNot
59            | Type::BitwiseShiftLeft
60            | Type::BitwiseShiftRight
61            | Type::Extract
62            | Type::DatePart
63            | Type::TumbleStart
64            | Type::SecToTimestamptz
65            | Type::AtTimeZone
66            | Type::DateTrunc
67            | Type::DateBin
68            | Type::MakeDate
69            | Type::MakeTime
70            | Type::MakeTimestamp
71            | Type::CharToTimestamptz
72            | Type::CharToDate
73            | Type::CastWithTimeZone
74            | Type::AddWithTimeZone
75            | Type::SubtractWithTimeZone
76            | Type::Cast
77            | Type::Substr
78            | Type::Length
79            | Type::Like
80            | Type::ILike
81            | Type::SimilarToEscape
82            | Type::Upper
83            | Type::Lower
84            | Type::Trim
85            | Type::Replace
86            | Type::Position
87            | Type::Ltrim
88            | Type::Rtrim
89            | Type::Case
90            | Type::ConstantLookup
91            | Type::RoundDigit
92            | Type::Round
93            | Type::Ascii
94            | Type::Translate
95            | Type::Coalesce
96            | Type::ConcatWs
97            | Type::ConcatWsVariadic
98            | Type::Abs
99            | Type::SplitPart
100            | Type::Ceil
101            | Type::Floor
102            | Type::Trunc
103            | Type::ToChar
104            | Type::Md5
105            | Type::CharLength
106            | Type::Repeat
107            | Type::ConcatOp
108            | Type::ByteaConcatOp
109            | Type::Concat
110            | Type::ConcatVariadic
111            | Type::BoolOut
112            | Type::OctetLength
113            | Type::BitLength
114            | Type::Overlay
115            | Type::RegexpMatch
116            | Type::RegexpReplace
117            | Type::RegexpCount
118            | Type::RegexpSplitToArray
119            | Type::RegexpEq
120            | Type::Pow
121            | Type::Exp
122            | Type::Ln
123            | Type::Log10
124            | Type::Chr
125            | Type::StartsWith
126            | Type::Initcap
127            | Type::Lpad
128            | Type::Rpad
129            | Type::Reverse
130            | Type::Strpos
131            | Type::ToAscii
132            | Type::ToHex
133            | Type::QuoteIdent
134            | Type::Sin
135            | Type::Cos
136            | Type::Tan
137            | Type::Cot
138            | Type::Asin
139            | Type::Acos
140            | Type::Acosd
141            | Type::Atan
142            | Type::Atan2
143            | Type::Atand
144            | Type::Atan2d
145            | Type::Sqrt
146            | Type::Cbrt
147            | Type::Sign
148            | Type::Scale
149            | Type::MinScale
150            | Type::TrimScale
151            | Type::Left
152            | Type::Right
153            | Type::Degrees
154            | Type::Radians
155            | Type::IsTrue
156            | Type::IsNotTrue
157            | Type::IsFalse
158            | Type::IsNotFalse
159            | Type::IsNull
160            | Type::IsNotNull
161            | Type::IsDistinctFrom
162            | Type::IsNotDistinctFrom
163            | Type::Neg
164            | Type::Field
165            | Type::Array
166            | Type::ArrayAccess
167            | Type::ArrayRangeAccess
168            | Type::Row
169            | Type::ArrayToString
170            | Type::ArrayCat
171            | Type::ArrayMax
172            | Type::ArraySum
173            | Type::ArraySort
174            | Type::ArrayAppend
175            | Type::ArrayPrepend
176            | Type::FormatType
177            | Type::ArrayDistinct
178            | Type::ArrayMin
179            | Type::ArrayDims
180            | Type::ArrayLength
181            | Type::Cardinality
182            | Type::TrimArray
183            | Type::ArrayRemove
184            | Type::ArrayReplace
185            | Type::ArrayPosition
186            | Type::ArrayContains
187            | Type::ArrayContained
188            | Type::ArrayFlatten
189            | Type::HexToInt256
190            | Type::JsonbConcat
191            | Type::JsonbAccess
192            | Type::JsonbAccessStr
193            | Type::JsonbExtractPath
194            | Type::JsonbExtractPathVariadic
195            | Type::JsonbExtractPathText
196            | Type::JsonbExtractPathTextVariadic
197            | Type::JsonbTypeof
198            | Type::JsonbArrayLength
199            | Type::JsonbObject
200            | Type::JsonbPretty
201            | Type::JsonbDeletePath
202            | Type::JsonbContains
203            | Type::JsonbContained
204            | Type::JsonbExists
205            | Type::JsonbExistsAny
206            | Type::JsonbExistsAll
207            | Type::JsonbStripNulls
208            | Type::JsonbBuildArray
209            | Type::JsonbBuildArrayVariadic
210            | Type::JsonbBuildObject
211            | Type::JsonbPopulateRecord
212            | Type::JsonbToArray
213            | Type::JsonbToRecord
214            | Type::JsonbBuildObjectVariadic
215            | Type::JsonbPathExists
216            | Type::JsonbPathMatch
217            | Type::JsonbPathQueryArray
218            | Type::JsonbPathQueryFirst
219            | Type::JsonbSet
220            | Type::JsonbPopulateMap
221            | Type::IsJson
222            | Type::ToJsonb
223            | Type::Sind
224            | Type::Cosd
225            | Type::Cotd
226            | Type::Asind
227            | Type::Sinh
228            | Type::Cosh
229            | Type::Coth
230            | Type::Tanh
231            | Type::Atanh
232            | Type::Asinh
233            | Type::Acosh
234            | Type::Decode
235            | Type::Encode
236            | Type::GetBit
237            | Type::GetByte
238            | Type::SetBit
239            | Type::SetByte
240            | Type::BitCount
241            | Type::Sha1
242            | Type::Sha224
243            | Type::Sha256
244            | Type::Sha384
245            | Type::Sha512
246            | Type::Hmac
247            | Type::SecureCompare
248            | Type::Decrypt
249            | Type::Encrypt
250            | Type::Tand
251            | Type::ArrayPositions
252            | Type::StringToArray
253            | Type::Format
254            | Type::FormatVariadic
255            | Type::PgwireSend
256            | Type::PgwireRecv
257            | Type::ArrayTransform
258            | Type::Greatest
259            | Type::Least
260            | Type::ConvertFrom
261            | Type::ConvertTo
262            | Type::IcebergTransform
263            | Type::InetNtoa
264            | Type::InetAton
265            | Type::QuoteLiteral
266            | Type::QuoteNullable
267            | Type::MapFromEntries
268            | Type::MapAccess
269            | Type::MapKeys
270            | Type::MapValues
271            | Type::MapEntries
272            | Type::MapFromKeyValues
273            | Type::MapCat
274            | Type::MapContains
275            | Type::MapDelete
276            | Type::MapFilter
277            | Type::MapInsert
278            | Type::MapLength
279            | Type::L2Distance
280            | Type::CosineDistance
281            | Type::L1Distance
282            | Type::InnerProduct
283            | Type::VecConcat
284            | Type::L2Norm
285            | Type::L2Normalize
286            | Type::Subvector
287            | Type::VnodeUser
288            | Type::RwEpochToTs
289            | Type::CheckNotNull
290            | Type::CompositeCast =>
291            // expression output is deterministic(same result for the same input)
292            {
293                func_call
294                    .inputs()
295                    .iter()
296                    .for_each(|expr| self.visit_expr(expr));
297            }
298            // expression output is not deterministic
299            Type::Vnode // obtain vnode count from the context
300            | Type::TestFeature
301            | Type::License
302            | Type::Proctime
303            | Type::PgSleep
304            | Type::PgSleepFor
305            | Type::PgSleepUntil
306            | Type::CastRegclass
307            | Type::PgGetIndexdef
308            | Type::ColDescription
309            | Type::PgGetViewdef
310            | Type::PgGetUserbyid
311            | Type::PgIndexesSize
312            | Type::PgRelationSize
313            | Type::PgGetSerialSequence
314            | Type::PgIndexColumnHasProperty
315            | Type::HasTablePrivilege
316            | Type::HasAnyColumnPrivilege
317            | Type::HasSchemaPrivilege
318            | Type::MakeTimestamptz
319            | Type::PgIsInRecovery
320            | Type::RwRecoveryStatus
321            | Type::RwClusterId
322            | Type::RwFragmentVnodes
323            | Type::RwActorVnodes
324            | Type::PgTableIsVisible
325            | Type::HasFunctionPrivilege
326            | Type::OpenaiEmbedding
327            | Type::HasDatabasePrivilege
328            | Type::Random => self.impure = true,
329        }
330    }
331}
332
333pub fn is_pure(expr: &ExprImpl) -> bool {
334    !is_impure(expr)
335}
336
337pub fn is_impure(expr: &ExprImpl) -> bool {
338    let mut a = ImpureAnalyzer::default();
339    a.visit_expr(expr);
340    a.impure
341}
342
343pub fn is_impure_func_call(func_call: &FunctionCall) -> bool {
344    let mut a = ImpureAnalyzer::default();
345    a.visit_function_call(func_call);
346    a.impure
347}
348
349#[cfg(test)]
350mod tests {
351    use risingwave_common::types::DataType;
352    use risingwave_pb::expr::expr_node::Type;
353
354    use crate::expr::{ExprImpl, FunctionCall, InputRef, is_impure, is_pure};
355
356    fn expect_pure(expr: &ExprImpl) {
357        assert!(is_pure(expr));
358        assert!(!is_impure(expr));
359    }
360
361    fn expect_impure(expr: &ExprImpl) {
362        assert!(!is_pure(expr));
363        assert!(is_impure(expr));
364    }
365
366    #[test]
367    fn test_pure_funcs() {
368        let e: ExprImpl = FunctionCall::new(
369            Type::Add,
370            vec![
371                InputRef::new(0, DataType::Int16).into(),
372                InputRef::new(0, DataType::Int16).into(),
373            ],
374        )
375        .unwrap()
376        .into();
377        expect_pure(&e);
378
379        let e: ExprImpl = FunctionCall::new(
380            Type::GreaterThan,
381            vec![
382                InputRef::new(0, DataType::Timestamptz).into(),
383                FunctionCall::new(Type::Proctime, vec![]).unwrap().into(),
384            ],
385        )
386        .unwrap()
387        .into();
388        expect_impure(&e);
389    }
390}