risingwave_frontend/binder/expr/
column.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 risingwave_common::types::DataType;
16use risingwave_sqlparser::ast::Ident;
17
18use crate::binder::{Binder, Clause};
19use crate::error::{ErrorCode, Result};
20use crate::expr::{CorrelatedInputRef, ExprImpl, ExprType, FunctionCall, InputRef, Literal};
21use crate::handler::create_sql_function::SQL_UDF_PATTERN;
22
23impl Binder {
24    pub fn bind_column(&mut self, idents: &[Ident]) -> Result<ExprImpl> {
25        // TODO: check quote style of `ident`.
26        let (_schema_name, table_name, column_name) = match idents {
27            [column] => (None, None, column.real_value()),
28            [table, column] => (None, Some(table.real_value()), column.real_value()),
29            [schema, table, column] => (
30                Some(schema.real_value()),
31                Some(table.real_value()),
32                column.real_value(),
33            ),
34            _ => {
35                return Err(
36                    ErrorCode::InternalError(format!("Too many idents: {:?}", idents)).into(),
37                );
38            }
39        };
40
41        // Special check for sql udf
42        // Note: The check in `bind_column` is to inline the identifiers,
43        // which, in the context of sql udf, will NOT be perceived as normal
44        // columns, but the actual named input parameters.
45        // Thus, we need to figure out if the current "column name" corresponds
46        // to the name of the defined sql udf parameters stored in `udf_context`.
47        // If so, we will treat this bind as an special bind, the actual expression
48        // stored in `udf_context` will then be bound instead of binding the non-existing column.
49        if self.udf_context.global_count() != 0 {
50            if let Some(expr) = self.udf_context.get_expr(&column_name) {
51                return Ok(expr.clone());
52            } else {
53                // The reason that we directly return error here,
54                // is because during a valid sql udf binding,
55                // there will not exist any column identifiers
56                // And invalid cases should already be caught
57                // during semantic check phase
58                // Note: the error message here also help with hint display
59                // when invalid definition occurs at sql udf creation time
60                return Err(ErrorCode::BindError(format!(
61                    "{SQL_UDF_PATTERN} failed to find named parameter {column_name}"
62                ))
63                .into());
64            }
65        }
66
67        match self
68            .context
69            .get_column_binding_indices(&table_name, &column_name)
70        {
71            Ok(mut indices) => {
72                match indices.len() {
73                    0 => unreachable!(),
74                    1 => {
75                        let index = indices[0];
76                        let column = &self.context.columns[index];
77                        return Ok(
78                            InputRef::new(column.index, column.field.data_type.clone()).into()
79                        );
80                    }
81                    _ => {
82                        indices.sort(); // make sure we have a consistent result
83                        let inputs = indices
84                            .iter()
85                            .map(|index| {
86                                let column = &self.context.columns[*index];
87                                InputRef::new(column.index, column.field.data_type.clone()).into()
88                            })
89                            .collect::<Vec<_>>();
90                        return Ok(FunctionCall::new(ExprType::Coalesce, inputs)?.into());
91                    }
92                }
93            }
94            Err(e) => {
95                // If the error message is not that the column is not found, throw the error
96                if let ErrorCode::ItemNotFound(_) = e {
97                } else {
98                    return Err(e.into());
99                }
100            }
101        }
102
103        // Try to find a correlated column in `upper_contexts`, starting from the innermost context.
104        let mut err = ErrorCode::ItemNotFound(format!("Invalid column: {}", column_name));
105
106        for (i, lateral_context) in self.lateral_contexts.iter().rev().enumerate() {
107            if lateral_context.is_visible {
108                let context = &lateral_context.context;
109                if matches!(context.clause, Some(Clause::Insert)) {
110                    continue;
111                }
112                // input ref from lateral context `depth` starts from 1.
113                let depth = i + 1;
114                match context.get_column_binding_index(&table_name, &column_name) {
115                    Ok(index) => {
116                        let column = &context.columns[index];
117                        return Ok(CorrelatedInputRef::new(
118                            column.index,
119                            column.field.data_type.clone(),
120                            depth,
121                        )
122                        .into());
123                    }
124                    Err(e) => {
125                        err = e;
126                    }
127                }
128            }
129        }
130
131        for (i, (context, lateral_contexts)) in
132            self.upper_subquery_contexts.iter().rev().enumerate()
133        {
134            if matches!(context.clause, Some(Clause::Insert)) {
135                continue;
136            }
137            // `depth` starts from 1.
138            let depth = i + 1;
139            match context.get_column_binding_index(&table_name, &column_name) {
140                Ok(index) => {
141                    let column = &context.columns[index];
142                    return Ok(CorrelatedInputRef::new(
143                        column.index,
144                        column.field.data_type.clone(),
145                        depth,
146                    )
147                    .into());
148                }
149                Err(e) => {
150                    err = e;
151                }
152            }
153
154            for (j, lateral_context) in lateral_contexts.iter().rev().enumerate() {
155                if lateral_context.is_visible {
156                    let context = &lateral_context.context;
157                    if matches!(context.clause, Some(Clause::Insert)) {
158                        continue;
159                    }
160                    // correlated input ref from lateral context `depth` starts from 1.
161                    let depth = i + j + 1;
162                    match context.get_column_binding_index(&table_name, &column_name) {
163                        Ok(index) => {
164                            let column = &context.columns[index];
165                            return Ok(CorrelatedInputRef::new(
166                                column.index,
167                                column.field.data_type.clone(),
168                                depth,
169                            )
170                            .into());
171                        }
172                        Err(e) => {
173                            err = e;
174                        }
175                    }
176                }
177            }
178        }
179        // `CTID` is a system column in postgres.
180        // https://www.postgresql.org/docs/current/ddl-system-columns.html
181        //
182        // We return an empty string here to support some tools such as DataGrip.
183        //
184        // FIXME: The type of `CTID` should be `tid`.
185        // FIXME: The `CTID` column should be unique, so literal may break something.
186        // FIXME: At least we should add a notice here.
187        if let ErrorCode::ItemNotFound(_) = err
188            && column_name == "ctid"
189        {
190            return Ok(Literal::new(Some("".into()), DataType::Varchar).into());
191        }
192        Err(err.into())
193    }
194}