Skip to main content

nodedb_sql/resolver/
scope.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! The column namespace an expression converts against.
4
5use crate::error::Result;
6use crate::resolver::columns::TableScope;
7
8/// The relations an identifier in this expression can name.
9#[derive(Debug, Clone, Copy)]
10pub enum ColumnScope<'a> {
11    /// No relation is in scope, so no identifier is checkable here.
12    ///
13    /// Used where an expression has no FROM clause behind it: stored DEFAULT
14    /// expressions, partial-index predicates, and the constant folders. A
15    /// column reference reaching one of those fails in constant folding as
16    /// `SqlError::Unsupported`, which is the correct answer there — the value
17    /// is not row-dependent.
18    Unchecked,
19    /// Identifiers resolve against these relations. One that resolves against
20    /// no relation, output alias, or synthetic column raises
21    /// `SqlError::UnknownColumn`.
22    Relations(&'a TableScope),
23}
24
25impl ColumnScope<'_> {
26    /// Reject `column`, optionally qualified by `table_ref`, when it names
27    /// nothing in this scope.
28    pub fn check_column(&self, table_ref: Option<&str>, column: &str) -> Result<()> {
29        match self {
30            Self::Unchecked => Ok(()),
31            Self::Relations(scope) => scope.check_name(table_ref, column),
32        }
33    }
34
35    /// Check `column`, optionally qualified by `table_ref`, and return the
36    /// qualifier its converted reference carries.
37    ///
38    /// Outside a join's input clauses the qualifier is kept as written. In
39    /// them, an unqualified column gets its owning relation's qualifier, so
40    /// it reads the `qualifier.column` key of the merged row.
41    pub fn column_qualifier(
42        &self,
43        table_ref: Option<&str>,
44        column: &str,
45    ) -> Result<Option<String>> {
46        match self {
47            Self::Unchecked => Ok(table_ref.map(str::to_owned)),
48            Self::Relations(scope) => match table_ref {
49                None => scope.bare_column_qualifier(column),
50                Some(table) => {
51                    scope.check_name(Some(table), column)?;
52                    Ok(scope.qualified_column_qualifier(table))
53                }
54            },
55        }
56    }
57
58    /// Whether an expression here is evaluated once per row of some relation.
59    ///
60    /// `Unchecked` stands behind stored DEFAULTs, index predicates, and the
61    /// constant folders, none of which iterate rows.
62    pub fn is_row_scope(&self) -> bool {
63        match self {
64            Self::Unchecked => false,
65            Self::Relations(scope) => scope.is_row_scope(),
66        }
67    }
68
69    /// Whether a sequence accessor resolves here although rows are in scope.
70    ///
71    /// Only a SELECT-list scope over a relation says yes; the Control Plane
72    /// evaluates that item per output row. `Unchecked` never iterates rows,
73    /// so the question does not arise there.
74    pub fn allows_cp_functions(&self) -> bool {
75        match self {
76            Self::Unchecked => false,
77            Self::Relations(scope) => scope.allows_cp_functions(),
78        }
79    }
80}