Skip to main content

risingwave_frontend/binder/expr/
column.rs

1// Copyright 2022 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 itertools::Itertools;
16use risingwave_common::types::DataType;
17use risingwave_sqlparser::ast::Ident;
18
19use crate::binder::{BindContext, Binder, Clause, LateralBindContext};
20use crate::error::{ErrorCode, Result};
21use crate::expr::{
22    CorrelatedInputRef, ExprImpl, ExprType, FunctionCall, InputRef, Literal, ScopeId,
23};
24
25impl Binder {
26    pub fn bind_column(&mut self, idents: &[Ident]) -> Result<ExprImpl> {
27        // TODO: check quote style of `ident`.
28        let (schema_name, table_name, column_name) = match idents {
29            [column] => (None, None, column.real_value()),
30            [table, column] => (None, Some(table.real_value()), column.real_value()),
31            [schema, table, column] => (
32                Some(schema.real_value()),
33                Some(table.real_value()),
34                column.real_value(),
35            ),
36            _ => {
37                return Err(
38                    ErrorCode::InternalError(format!("Too many idents: {:?}", idents)).into(),
39                );
40            }
41        };
42
43        // If we find `sql_udf_arguments` in the current context, it means we're binding an inline SQL UDF
44        // (without a layer of subquery). This only happens when the function body is a trivial `SELECT`
45        // statement without any `FROM` clause etc. In this case, the column must be a UDF parameter.
46        if self.is_binding_inline_sql_udf() {
47            return self.bind_sql_udf_parameter(&column_name);
48        }
49
50        match self
51            .context
52            .get_column_binding_indices(&schema_name, &table_name, &column_name)
53        {
54            Ok(mut indices) => {
55                match indices.len() {
56                    0 => unreachable!(),
57                    1 => {
58                        let index = indices[0];
59                        let column = &self.context.columns[index];
60                        return Ok(
61                            InputRef::new(column.index, column.field.data_type.clone()).into()
62                        );
63                    }
64                    _ => {
65                        indices.sort(); // make sure we have a consistent result
66                        let inputs = indices
67                            .iter()
68                            .map(|index| {
69                                let column = &self.context.columns[*index];
70                                InputRef::new(column.index, column.field.data_type.clone()).into()
71                            })
72                            .collect::<Vec<_>>();
73                        return Ok(FunctionCall::new(ExprType::Coalesce, inputs)?.into());
74                    }
75                }
76            }
77            Err(e) => {
78                // If a column is referenced using three-level qualification and the table has an alias,
79                // prompt the user to use the table alias instead.
80                if let ErrorCode::ItemNotFound(_) = e {
81                    if let (Some(schema), Some(table)) = (&schema_name, &table_name)
82                        && let Some(index) =
83                            self.context.get_table_alias(schema, table, &column_name)?
84                    {
85                        let column = &self.context.columns[index];
86                        return Err(ErrorCode::InvalidReference(format!(
87                            "missing FROM-clause entry for table \"{}\"\n\
88                            HINT:  Perhaps you meant to reference the table alias \"{}\".",
89                            table, column.table_name
90                        ))
91                        .into());
92                    };
93                } else {
94                    // If the error message is not that the column is not found, throw the error
95                    return Err(e.into());
96                }
97            }
98        }
99
100        // Try to find a correlated column in the enclosing scopes, starting from the innermost one.
101        let mut err = ErrorCode::ItemNotFound(format!("Invalid column: {}", column_name));
102
103        for level in self.outer_context_levels() {
104            let mut found = None;
105            for context in level {
106                if matches!(context.clause, Some(Clause::Insert)) {
107                    continue;
108                }
109                match context.get_column_binding_indices(&schema_name, &table_name, &column_name) {
110                    Ok(indices) => {
111                        // All `FROM` items of a query level share one namespace, even when the
112                        // binder keeps the left inputs of a lateral factor in separate contexts.
113                        if found.is_some() {
114                            return Err(match &table_name {
115                                Some(table_name) => ErrorCode::InvalidReference(format!(
116                                    "table reference \"{}\" is ambiguous",
117                                    table_name
118                                )),
119                                None => ErrorCode::InvalidReference(format!(
120                                    "column reference \"{}\" is ambiguous",
121                                    column_name
122                                )),
123                            }
124                            .into());
125                        }
126                        found = Some((context, indices));
127                    }
128                    Err(e @ ErrorCode::ItemNotFound(_)) => err = e,
129                    // An ambiguous name must not fall through to an outer scope.
130                    Err(e) => return Err(e.into()),
131                }
132            }
133
134            if let Some((context, mut indices)) = found {
135                indices.sort(); // make sure we have a consistent result
136                let mut inputs = indices
137                    .iter()
138                    .map(|index| {
139                        let column = &context.columns[*index];
140                        CorrelatedInputRef::new(
141                            column.index,
142                            column.field.data_type.clone(),
143                            context.scope,
144                        )
145                        .into()
146                    })
147                    .collect::<Vec<ExprImpl>>();
148                return if inputs.len() == 1 {
149                    Ok(inputs.pop().unwrap())
150                } else {
151                    // An unqualified reference to a column merged by `NATURAL`/`USING` join.
152                    Ok(FunctionCall::new(ExprType::Coalesce, inputs)?.into())
153                };
154            }
155        }
156
157        // `CTID` is a system column in postgres.
158        // https://www.postgresql.org/docs/current/ddl-system-columns.html
159        //
160        // We return an empty string here to support some tools such as DataGrip.
161        //
162        // FIXME: The type of `CTID` should be `tid`.
163        // FIXME: The `CTID` column should be unique, so literal may break something.
164        // FIXME: At least we should add a notice here.
165        if let ErrorCode::ItemNotFound(_) = err
166            && column_name == "ctid"
167        {
168            return Ok(Literal::new(Some("".into()), DataType::Varchar).into());
169        }
170
171        // Failed to resolve the column in current context. Now check if it's a sql udf parameter.
172        if let ErrorCode::ItemNotFound(_) = err
173            && self.is_binding_subquery_sql_udf()
174        {
175            return self.bind_sql_udf_parameter(&column_name);
176        }
177
178        Err(err.into())
179    }
180
181    /// Return visible outer column contexts grouped by query level, innermost first. A column found
182    /// in one of them is owned by that context's scope.
183    ///
184    /// An upper query context and its visible lateral contexts are the `FROM` items of one query
185    /// level, so they form one namespace: a name found in more than one of them is ambiguous rather
186    /// than resolved to whichever context comes first.
187    fn outer_context_levels(&self) -> Vec<Vec<&BindContext>> {
188        fn visible(lateral_contexts: &[LateralBindContext]) -> impl Iterator<Item = &BindContext> {
189            lateral_contexts
190                .iter()
191                .rev()
192                .filter(|lateral_context| lateral_context.is_visible)
193                .map(|lateral_context| &lateral_context.context)
194        }
195
196        let mut levels = vec![visible(&self.lateral_contexts).collect_vec()];
197        for (context, lateral_contexts) in self.visible_upper_subquery_contexts_rev() {
198            levels.push(
199                std::iter::once(context)
200                    .chain(visible(lateral_contexts))
201                    .collect(),
202            );
203        }
204        levels
205    }
206
207    /// The scope of the nearest outer `FROM` item: the first non-empty context, walking outwards
208    /// through the visible lateral contexts and the innermost upper query level.
209    pub(in crate::binder) fn nearest_outer_scope(&self) -> Option<ScopeId> {
210        self.outer_context_levels()
211            .into_iter()
212            .take(2)
213            .flatten()
214            .find(|context| !context.columns.is_empty())
215            .map(|context| context.scope)
216    }
217}