Skip to main content

risingwave_expr/expr/wrapper/
non_strict.rs

1// Copyright 2023 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 std::sync::LazyLock;
16
17use auto_impl::auto_impl;
18use risingwave_common::array::{ArrayRef, DataChunk};
19use risingwave_common::log::LogSuppressor;
20use risingwave_common::row::OwnedRow;
21use risingwave_common::types::{DataType, Datum};
22use thiserror_ext::AsReport;
23
24use crate::ExprError;
25use crate::error::Result;
26use crate::expr::{AsyncExpression, ExpressionInfo, SyncExpression, ValueImpl};
27
28/// Report an error during evaluation.
29#[auto_impl(&, Arc)]
30pub trait EvalErrorReport: Clone + Send + Sync {
31    /// Perform the error reporting.
32    ///
33    /// Called when an error occurs during row-level evaluation of a non-strict expression,
34    /// that is, wrapped by [`NonStrict`].
35    fn report(&self, error: ExprError);
36}
37
38/// A dummy implementation that panics when called.
39///
40/// Used as the type parameter for the expression builder when non-strict evaluation is not
41/// required.
42impl EvalErrorReport for ! {
43    fn report(&self, _error: ExprError) {
44        unreachable!()
45    }
46}
47
48/// Log the error to report an error during evaluation.
49#[derive(Clone)]
50pub struct LogReport;
51
52impl EvalErrorReport for LogReport {
53    fn report(&self, error: ExprError) {
54        static LOG_SUPPRESSOR: LazyLock<LogSuppressor> = LazyLock::new(LogSuppressor::default);
55        if let Ok(suppressed_count) = LOG_SUPPRESSOR.check() {
56            tracing::error!(error=%error.as_report(), suppressed_count, "failed to evaluate expression");
57        }
58    }
59}
60
61/// A wrapper of an expression that evaluates in a non-strict way. Basically...
62/// - When an error occurs during chunk-level evaluation, pad with NULL for each failed row.
63/// - Report all error occurred during row-level evaluation to the [`EvalErrorReport`].
64pub(crate) struct NonStrict<E, R> {
65    inner: E,
66    report: R,
67}
68
69impl<E, R> std::fmt::Debug for NonStrict<E, R>
70where
71    E: std::fmt::Debug,
72{
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        f.debug_struct("NonStrict")
75            .field("inner", &self.inner)
76            .field("report", &std::any::type_name::<R>())
77            .finish()
78    }
79}
80
81impl<E, R> NonStrict<E, R>
82where
83    E: ExpressionInfo,
84    R: EvalErrorReport,
85{
86    pub fn new(inner: E, report: R) -> Self {
87        Self { inner, report }
88    }
89}
90
91impl<E, R> ExpressionInfo for NonStrict<E, R>
92where
93    E: ExpressionInfo,
94    R: EvalErrorReport,
95{
96    fn return_type(&self) -> DataType {
97        self.inner.return_type()
98    }
99
100    fn input_ref_index(&self) -> Option<usize> {
101        self.inner.input_ref_index()
102    }
103}
104
105macro_rules! non_strict_eval_array {
106    ($mode:ident, $this:expr, $input:expr) => {{
107        Ok(match forward!($mode, $this.inner, eval($input)) {
108            Ok(array) => array,
109            Err(ExprError::Multiple(array, errors)) => {
110                for error in errors {
111                    $this.report.report(error);
112                }
113                array
114            }
115            Err(e) => {
116                $this.report.report(e);
117                let mut builder = $this.return_type().create_array_builder($input.capacity());
118                builder.append_n_null($input.capacity());
119                builder.finish().into()
120            }
121        })
122    }};
123}
124
125macro_rules! non_strict_eval_value {
126    ($mode:ident, $this:expr, $input:expr) => {{
127        Ok(match forward!($mode, $this.inner, eval_v2($input)) {
128            Ok(array) => array,
129            Err(ExprError::Multiple(array, errors)) => {
130                for error in errors {
131                    $this.report.report(error);
132                }
133                array.into()
134            }
135            Err(e) => {
136                $this.report.report(e);
137                ValueImpl::Scalar {
138                    value: None,
139                    capacity: $input.capacity(),
140                }
141            }
142        })
143    }};
144}
145
146macro_rules! non_strict_eval_row {
147    ($mode:ident, $this:expr, $input:expr) => {{
148        Ok(match forward!($mode, $this.inner, eval_row($input)) {
149            Ok(datum) => datum,
150            Err(error) => {
151                $this.report.report(error);
152                None // NULL
153            }
154        })
155    }};
156}
157
158impl<E, R> SyncExpression for NonStrict<E, R>
159where
160    E: SyncExpression,
161    R: EvalErrorReport,
162{
163    fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
164        non_strict_eval_array!(sync, self, input)
165    }
166
167    fn eval_v2(&self, input: &DataChunk) -> Result<ValueImpl> {
168        non_strict_eval_value!(sync, self, input)
169    }
170
171    /// Evaluate expression on a single row, report error and return NULL if failed.
172    fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
173        non_strict_eval_row!(sync, self, input)
174    }
175
176    fn eval_const(&self) -> Result<Datum> {
177        self.inner.eval_const() // do not handle error
178    }
179}
180
181impl<E, R> AsyncExpression for NonStrict<E, R>
182where
183    E: AsyncExpression,
184    R: EvalErrorReport,
185{
186    async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
187        non_strict_eval_array!(async, self, input)
188    }
189
190    async fn eval_v2(&self, input: &DataChunk) -> Result<ValueImpl> {
191        non_strict_eval_value!(async, self, input)
192    }
193
194    /// Evaluate expression on a single row, report error and return NULL if failed.
195    async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
196        non_strict_eval_row!(async, self, input)
197    }
198}