LCOV - code coverage report
Current view: top level - dashql-core/src/analyzer - name_resolution.rs Coverage Total Hit
Test: DashQL d805a4b Lines: 97.8 % 1482 1450
Test Date: 2026-11-09 20:22:13

            Line data    Source code
       1              : //! Name resolution.
       2              : //!
       3              : //! The visit collects table references, derived tables, column references, result targets, CTEs and
       4              : //! declarations bottom-up and groups them into name scopes. Finish resolves the names of all scopes,
       5              : //! populates derived tables, assigns statement ids and runs the schema inference.
       6              : //!
       7              : //! Column refs resolve against the FROM items of a scope (tables, CTEs, derived tables), each with
       8              : //! its ordered columns. Expression subqueries see the FROM items of their parents, derived tables and
       9              : //! CTEs only those above their parent, lateral derived tables also the FROM items before them.
      10              : //!
      11              : //! The node states hold vectors of ids, and merging a child moves its entries to the end.
      12              : 
      13              : use super::sql_types::read_sql_type;
      14              : use super::state::{quote_identifier, AnalysisState};
      15              : use super::types::*;
      16              : use crate::catalog::*;
      17              : use crate::table_parameters::{TableParameterLookup, TABLE_PARAMETERS_ENTRY_ID};
      18              : use dashql_buffers::analyzer::{AnalyzerErrorSeverity, AnalyzerErrorType, NameTag, SemanticNodeMarkerType};
      19              : use dashql_buffers::parser::{AttributeKey as K, JoinType, Node, NodeType, StatementType, SymbolSpan};
      20              : use dashql_parser::attrs;
      21              : use crate::hash::{HashMap, HashSet};
      22              : use std::ops::Range;
      23              : use std::sync::Arc;
      24              : 
      25              : /// The maximum number of tables a reference may resolve to
      26              : const MAX_TABLE_REF_AMBIGUITY: usize = 100;
      27              : 
      28              : /// A table column collected from a column definition
      29              : #[derive(Clone)]
      30              : struct PendingColumn {
      31              :     /// The AST node id of the column definition
      32              :     ast_node_id: u32,
      33              :     /// The column name
      34              :     column_name: Name,
      35              :     /// The text of the declared type
      36              :     column_type: Option<Arc<str>>,
      37              : }
      38              : 
      39              : /// The state of a node during name resolution
      40              : #[derive(Default)]
      41              : struct NodeState {
      42              :     /// The scopes created in the subtree that have no parent scope yet
      43              :     child_scopes: Vec<ScopeId>,
      44              :     /// The column definitions of a CREATE TABLE
      45              :     table_columns: Vec<PendingColumn>,
      46              :     /// The table references not yet attached to a scope
      47              :     table_references: Vec<TableRefId>,
      48              :     /// The column ref expressions not yet attached to a scope
      49              :     column_references: Vec<ExprId>,
      50              :     /// The result targets of the enclosing SELECT
      51              :     result_targets: Vec<ResultTarget>,
      52              :     /// The CTE definitions of the enclosing WITH clause
      53              :     ctes: Vec<CteDefinition>,
      54              :     /// The derived tables, the FROM items of the next scope
      55              :     derived_tables: Vec<ScopeSource>,
      56              :     /// The joins with USING or NATURAL of the next scope
      57              :     using_joins: Vec<UsingJoin>,
      58              : }
      59              : 
      60              : /// Move the entries of a list to the end of another one. Takes over the buffer if the destination is
      61              : /// empty, so states move up the tree without copying.
      62              : #[inline]
      63       527376 : fn append<T>(dst: &mut Vec<T>, src: &mut Vec<T>) {
      64       527376 :     if dst.is_empty() {
      65       510232 :         std::mem::swap(dst, src);
      66       510232 :     } else {
      67        17144 :         dst.append(src);
      68        17144 :     }
      69       527376 : }
      70              : 
      71              : impl NodeState {
      72        65922 :     fn merge(&mut self, other: &mut NodeState) {
      73        65922 :         append(&mut self.child_scopes, &mut other.child_scopes);
      74        65922 :         append(&mut self.table_columns, &mut other.table_columns);
      75        65922 :         append(&mut self.table_references, &mut other.table_references);
      76        65922 :         append(&mut self.column_references, &mut other.column_references);
      77        65922 :         append(&mut self.result_targets, &mut other.result_targets);
      78        65922 :         append(&mut self.ctes, &mut other.ctes);
      79        65922 :         append(&mut self.derived_tables, &mut other.derived_tables);
      80        65922 :         append(&mut self.using_joins, &mut other.using_joins);
      81        65922 :     }
      82              : 
      83         5830 :     fn clear(&mut self) {
      84         5830 :         *self = NodeState::default();
      85         5830 :     }
      86              : }
      87              : 
      88              : /// A join with USING, or NATURAL that uses all common columns. The USING columns of both inputs
      89              : /// are one column of the join.
      90              : struct UsingJoin {
      91              :     /// The AST node id of the joined table
      92              :     ast_node_id: u32,
      93              :     /// The USING columns, all common columns of a NATURAL join if None
      94              :     using: Option<Vec<Arc<str>>>,
      95              : }
      96              : 
      97              : impl UsingJoin {
      98            9 :     fn uses(&self, column_name: &str) -> bool {
      99            9 :         self.using.as_ref().is_none_or(|using| using.iter().any(|c| &**c == column_name))
     100            9 :     }
     101              : }
     102              : 
     103              : /// The result of looking up a column ref in the FROM items of a scope
     104              : #[derive(Clone, Copy, PartialEq, Eq)]
     105              : enum Lookup {
     106              :     Resolved,
     107              :     Ambiguous,
     108              :     Missing,
     109              : }
     110              : 
     111              : /// How a column ref sees the output columns of its own scope
     112              : #[derive(Clone, Copy, PartialEq, Eq)]
     113              : enum OutputNames {
     114              :     Invisible,
     115              :     /// A bare name in ORDER BY refers to an output column before a FROM item column
     116              :     Preferred,
     117              :     /// A bare name in GROUP BY refers to an output column if no FROM item has the column
     118              :     Fallback,
     119              : }
     120              : 
     121              : /// A declaration whose columns are derived after its SELECT scope is resolved
     122              : struct PendingDerivedTable {
     123              :     /// The table declaration (index into the table declarations of the script)
     124              :     table: u32,
     125              :     /// The AST node id of the SELECT that defines the columns
     126              :     select_node_id: u32,
     127              :     /// The declared column names, they rename the leading output columns
     128              :     column_aliases: Vec<Name>,
     129              : }
     130              : 
     131              : /// The root scopes.
     132              : ///
     133              : /// The set iterates in insertion order, and erasing moves the last element into the hole (see
     134              : /// docs/design/core_quirks.md).
     135              : #[derive(Default)]
     136              : struct RootScopes {
     137              :     /// The root scopes in insertion order
     138              :     values: Vec<ScopeId>,
     139              :     /// The position of every root scope in the values
     140              :     positions: HashMap<ScopeId, usize>,
     141              : }
     142              : 
     143              : impl RootScopes {
     144         5774 :     fn insert(&mut self, scope: ScopeId) {
     145         5774 :         if self.positions.contains_key(&scope) {
     146            0 :             return;
     147         5774 :         }
     148         5774 :         self.positions.insert(scope, self.values.len());
     149         5774 :         self.values.push(scope);
     150         5774 :     }
     151              : 
     152          610 :     fn erase(&mut self, scope: ScopeId) {
     153          610 :         let Some(pos) = self.positions.remove(&scope) else { return };
     154          610 :         let last = self.values.pop().unwrap();
     155          610 :         if pos < self.values.len() {
     156           82 :             self.values[pos] = last;
     157           82 :             self.positions.insert(last, pos);
     158          528 :         }
     159          610 :     }
     160              : }
     161              : 
     162              : /// The pass that groups names into scopes and resolves tables and columns
     163              : pub(super) struct NameResolutionPass {
     164              :     /// The state of every AST node
     165              :     node_states: Vec<NodeState>,
     166              :     /// Does the script contain property graph syntax?
     167              :     has_graph_syntax: bool,
     168              :     /// The scopes without a parent scope
     169              :     root_scopes: RootScopes,
     170              :     /// The table declarations whose columns are derived after resolution
     171              :     pending_derived_tables: Vec<PendingDerivedTable>,
     172              :     /// The column actions of `ALTER` statements on tables of `CREATE TABLE AS`: the table and the action
     173              :     pending_alters: Vec<(u32, u32)>,
     174              :     /// The joins with USING or NATURAL of the scopes
     175              :     using_joins: HashMap<ScopeId, Vec<UsingJoin>>,
     176              :     /// The column refs that were reported as ambiguous
     177              :     ambiguous_column_refs: HashSet<ExprId>,
     178              : }
     179              : 
     180              : impl NameResolutionPass {
     181         4003 :     pub fn new(state: &AnalysisState) -> Self {
     182         4003 :         let mut node_states = Vec::with_capacity(state.ast.len());
     183         4003 :         node_states.resize_with(state.ast.len(), NodeState::default);
     184         4003 :         let has_graph_syntax = state
     185         4003 :             .ast
     186         4003 :             .iter()
     187        83811 :             .any(|n| matches!(n.node_type(), NodeType::OBJECT_SQL_PROPERTY_GRAPH | NodeType::OBJECT_SQL_GRAPH_TABLE));
     188         4003 :         Self {
     189         4003 :             node_states,
     190         4003 :             has_graph_syntax,
     191         4003 :             root_scopes: RootScopes::default(),
     192         4003 :             pending_derived_tables: Vec::new(),
     193         4003 :             pending_alters: Vec::new(),
     194         4003 :             using_joins: HashMap::default(),
     195         4003 :             ambiguous_column_refs: HashSet::default(),
     196         4003 :         }
     197         4003 :     }
     198              : 
     199              :     /// Merge the states of all children of a node
     200        77839 :     fn merge_child_states(&mut self, dst: &mut NodeState, node: &Node) {
     201        77839 :         let begin = node.children_begin_or_value() as usize;
     202        77839 :         for child in begin..begin + node.children_count() as usize {
     203        64145 :             dst.merge(&mut self.node_states[child]);
     204        64145 :         }
     205        77839 :     }
     206              : 
     207              :     /// Create a naming scope
     208         5774 :     fn create_scope(&mut self, state: &mut AnalysisState, target: &mut NodeState, scope_root: u32) -> ScopeId {
     209         5774 :         let scope_id = state.out.name_scopes.len() as ScopeId;
     210         5774 :         state.out.name_scopes_by_root_node.entry(scope_root).or_insert(scope_id);
     211         5774 :         for &child in &target.child_scopes {
     212          610 :             state.out.name_scopes[child as usize].parent_scope = Some(scope_id);
     213          610 :             self.root_scopes.erase(child);
     214          610 :         }
     215         5790 :         for &expr in &target.column_references {
     216         2704 :             if let Some(column_ref) = state.out.expressions[expr as usize].column_ref_mut() {
     217         2704 :                 column_ref.ast_scope_root = Some(scope_root);
     218         2704 :             }
     219              :         }
     220         5776 :         for &table_ref in &target.table_references {
     221         2568 :             state.out.table_references[table_ref as usize].ast_scope_root = Some(scope_root);
     222         2568 :         }
     223         5774 :         let using_joins = std::mem::take(&mut target.using_joins);
     224         5774 :         if !using_joins.is_empty() {
     225           46 :             self.using_joins.insert(scope_id, using_joins);
     226         5728 :         }
     227         5774 :         state.out.name_scopes.push(NameScope {
     228         5774 :             name_scope_id: scope_id,
     229         5774 :             ast_node_id: scope_root,
     230         5774 :             child_scopes: std::mem::take(&mut target.child_scopes),
     231         5774 :             expressions: std::mem::take(&mut target.column_references),
     232         5774 :             table_references: std::mem::take(&mut target.table_references),
     233         5774 :             // The table refs are added when they are resolved
     234         5774 :             sources: std::mem::take(&mut target.derived_tables),
     235         5774 :             ..Default::default()
     236         5774 :         });
     237              :         // Start a new scope
     238         5774 :         target.clear();
     239         5774 :         target.child_scopes.push(scope_id);
     240         5774 :         self.root_scopes.insert(scope_id);
     241         5774 :         scope_id
     242         5774 :     }
     243              : 
     244              :     /// Register a schema and its database
     245         1979 :     fn register_schema(state: &mut AnalysisState, database_name: &Name, schema_name: &Name) -> QualifiedCatalogObjectId {
     246         1979 :         let db_id = match state.out.entry.databases_by_name.get(&database_name.text) {
     247          680 :             Some(&i) => state.out.entry.database_references[i].object_id,
     248              :             None => {
     249         1299 :                 let db_id = state.catalog.reserve_database_id(&database_name.text);
     250         1299 :                 let index = state.out.entry.database_references.len();
     251         1299 :                 state.entry_mut().database_references.push(DatabaseReference {
     252         1299 :                     object_id: db_id,
     253         1299 :                     database_name: database_name.text.clone(),
     254         1299 :                     database_alias: Arc::from(""),
     255         1299 :                 });
     256         1299 :                 state.entry_mut().databases_by_name.insert(database_name.text.clone(), index);
     257         1299 :                 state.resolve_name(database_name, CatalogObjectRef::Database(state.loc(index)));
     258         1299 :                 db_id
     259              :             }
     260              :         };
     261         1979 :         let key = (database_name.text.clone(), schema_name.text.clone());
     262         1979 :         match state.out.entry.schemas_by_qualified_name.get(&key) {
     263          663 :             Some(&i) => state.out.entry.schema_references[i].object_id,
     264              :             None => {
     265         1316 :                 let schema_id = state.catalog.reserve_schema_id(&database_name.text, &schema_name.text, db_id);
     266         1316 :                 let index = state.out.entry.schema_references.len();
     267         1316 :                 state.entry_mut().schema_references.push(SchemaReference {
     268         1316 :                     object_id: schema_id,
     269         1316 :                     database_name: database_name.text.clone(),
     270         1316 :                     schema_name: schema_name.text.clone(),
     271         1316 :                 });
     272         1316 :                 state.entry_mut().schemas_by_qualified_name.insert(key, index);
     273         1316 :                 state.resolve_name(schema_name, CatalogObjectRef::Schema(state.loc(index)));
     274         1316 :                 schema_id
     275              :             }
     276              :         }
     277         1979 :     }
     278              : 
     279         1996 :     fn is_explain(state: &AnalysisState, statement_id: Option<u32>) -> bool {
     280         1996 :         statement_id
     281         1996 :             .and_then(|id| state.parsed.statements.get(id as usize))
     282         1996 :             .is_some_and(|s| s.statement_type == StatementType::EXPLAIN)
     283         1996 :     }
     284              : 
     285              :     /// Apply an `ALTER` to the declaration of the script, later statements read the table as the statement leaves it. A
     286              :     /// table that the script doesn't declare stays what the catalog says, the connection answers once the statement ran. A
     287              :     /// table of `CREATE TABLE AS` gets its columns later, its actions wait for them.
     288           41 :     fn visit_alter(&mut self, state: &mut AnalysisState, node: &Node, node_id: u32) {
     289           41 :         let [name_node, actions] = attrs!(state.children(node), [K::SQL_ALTER_TABLE_NAME, K::SQL_ALTER_TABLE_ACTIONS]);
     290           41 :         let Some(name) = state.read_qualified_table_name(name_node) else { return };
     291           41 :         let Some(actions) = actions.filter(|n| n.node_type() == NodeType::ARRAY) else { return };
     292              :         // The parts that a name leaves out match any, as in a reference to the table
     293           64 :         let part = |named: &Name, declared: &Name| named.text.is_empty() || named.text == declared.text;
     294           41 :         let declared = state.out.entry.table_declarations.iter().rposition(|t| {
     295           34 :             t.table_name.table_name.text == name.table_name.text
     296           32 :                 && part(&name.schema_name, &t.table_name.schema_name)
     297           30 :                 && part(&name.database_name, &t.table_name.database_name)
     298           34 :         });
     299           41 :         let Some(mut table) = declared.map(|index| index as u32) else { return };
     300           33 :         for action in state.children(actions) {
     301           33 :             match action.node_type() {
     302              :                 NodeType::OBJECT_SQL_ALTER_RENAME_TABLE | NodeType::OBJECT_SQL_ALTER_SET_SCHEMA => {
     303           13 :                     let [new_table, new_schema] = attrs!(state.children(action), [K::SQL_ALTER_ACTION_NEW_TABLE, K::SQL_ALTER_ACTION_NEW_NAME]);
     304           13 :                     let source = state.out.entry.table_declarations[table as usize].table_name.clone();
     305           13 :                     let moved = match new_table {
     306           11 :                         Some(new_table) => state.read_qualified_table_name(Some(new_table)),
     307            2 :                         None => Some(source.clone()),
     308              :                     };
     309           13 :                     let Some(mut moved) = moved else { continue };
     310              :                     // What a new name leaves out stays as it is
     311           13 :                     if let Some(new_schema) = new_schema.filter(|n| n.node_type() == NodeType::NAME) {
     312            2 :                         moved.schema_name = state.node_name(new_schema);
     313           11 :                     } else if moved.schema_name.text.is_empty() {
     314            9 :                         moved.schema_name = source.schema_name.clone();
     315            9 :                     }
     316           13 :                     if moved.database_name.text.is_empty() {
     317           12 :                         moved.database_name = source.database_name.clone();
     318           12 :                     }
     319           13 :                     table = self.move_declaration(state, table, &moved, node_id);
     320              :                 }
     321           20 :                 _ => match self.pending_derived_tables.iter().find(|p| p.table == table) {
     322            4 :                     Some(_) => self.pending_alters.push((table, state.node_id(action))),
     323           16 :                     None => Self::apply_column_action(state, table, action),
     324              :                 },
     325              :             }
     326              :         }
     327           41 :     }
     328              : 
     329              :     /// The declaration of a relation under its new name, the old one stays for the statements before the `ALTER`
     330           13 :     fn move_declaration(&mut self, state: &mut AnalysisState, table: u32, name: &QualifiedTableName, node_id: u32) -> u32 {
     331           13 :         let schema_id = Self::register_schema(state, &name.database_name, &name.schema_name);
     332           13 :         let table_id = state.reserve_table_id(name);
     333           13 :         let source = &state.out.entry.table_declarations[table as usize];
     334           13 :         let (is_view, source_columns) = (source.is_view, source.table_columns.clone());
     335           13 :         let index = state.out.entry.table_declarations.len() as u32;
     336           13 :         let mut moved = TableDeclaration::new(schema_id, table_id, name.clone());
     337           13 :         moved.ast_node_id = Some(node_id);
     338           13 :         moved.ast_statement_id = state.find_statement_id(node_id);
     339           13 :         moved.is_view = is_view;
     340           13 :         let column_ids = state.reserve_column_ids(table_id, source_columns.iter().map(|c| &c.column_name.text));
     341           14 :         for (column_index, source) in source_columns.iter().enumerate() {
     342           12 :             moved.table_columns_by_name.entry(source.column_name.text.clone()).or_insert(column_index);
     343           12 :             moved.table_columns.push(TableColumn {
     344           12 :                 object_id: QualifiedCatalogObjectId::table_column(table_id, column_ids[column_index]),
     345           12 :                 table: Some(table_id),
     346           12 :                 ..source.clone()
     347           12 :             });
     348           12 :             state.resolve_name(&source.column_name, CatalogObjectRef::Column(ColumnLoc { table: TableLoc { entry: state.entry_id, index }, column: column_index as u32 }));
     349           12 :         }
     350           13 :         state.entry_mut().table_declarations.push(moved);
     351           13 :         state.resolve_name(&name.table_name, CatalogObjectRef::Table(TableLoc { entry: state.entry_id, index }));
     352              :         // A table of `CREATE TABLE AS` gets its columns from the same query
     353           13 :         if let Some(pending) = self.pending_derived_tables.iter().find(|p| p.table == table) {
     354            2 :             let pending = PendingDerivedTable { table: index, select_node_id: pending.select_node_id, column_aliases: pending.column_aliases.clone() };
     355            2 :             self.pending_derived_tables.push(pending);
     356            2 :             let queued: Vec<(u32, u32)> = self.pending_alters.iter().filter(|(t, _)| *t == table).map(|(_, action)| (index, *action)).collect();
     357            2 :             self.pending_alters.extend(queued);
     358           11 :         }
     359           13 :         index
     360           13 :     }
     361              : 
     362              :     /// Apply an action that changes the columns of a declared table
     363           21 :     fn apply_column_action(state: &mut AnalysisState, table: u32, action: &Node) {
     364           21 :         let table_id = state.out.entry.table_declarations[table as usize].table_id();
     365           21 :         let entry_id = state.entry_id;
     366           21 :         let loc = move |column: u32| ColumnLoc { table: TableLoc { entry: entry_id, index: table }, column };
     367           21 :         let [column, new_name, column_type] =
     368           21 :             attrs!(state.children(action), [K::SQL_ALTER_ACTION_COLUMN, K::SQL_ALTER_ACTION_NEW_NAME, K::SQL_ALTER_ACTION_TYPE]);
     369           21 :         let Some(column) = column else { return };
     370           21 :         match action.node_type() {
     371              :             NodeType::OBJECT_SQL_ALTER_ADD_COLUMN => {
     372           10 :                 let [name, declared_type] = attrs!(state.children(column), [K::SQL_COLUMN_DEF_NAME, K::SQL_COLUMN_DEF_TYPE]);
     373           10 :                 let Some(name) = name.filter(|n| n.node_type() == NodeType::NAME) else { return };
     374           10 :                 let name = state.node_name(name);
     375           10 :                 state.tag(&name, NameTag::COLUMN_NAME);
     376           10 :                 if state.out.entry.table_declarations[table as usize].table_columns_by_name.contains_key(&name.text) {
     377            1 :                     return;
     378            9 :                 }
     379            9 :                 let column_id = state.reserve_column_ids(table_id, std::iter::once(&name.text))[0];
     380            9 :                 let column_type = declared_type.and_then(|t| read_sql_type(state.parsed, t)).map(|t| Arc::from(t.to_string()));
     381            9 :                 let ast_node_id = state.node_id(column);
     382            9 :                 let location = state.text_span(*column.symbol_span());
     383            9 :                 let table_declaration = &mut state.entry_mut().table_declarations[table as usize];
     384            9 :                 let column_index = table_declaration.table_columns.len();
     385            9 :                 table_declaration.table_columns_by_name.insert(name.text.clone(), column_index);
     386            9 :                 table_declaration.table_columns.push(TableColumn {
     387            9 :                     object_id: QualifiedCatalogObjectId::table_column(table_id, column_id),
     388            9 :                     table: Some(table_id),
     389            9 :                     ast_node_id: Some(ast_node_id),
     390            9 :                     column_name: name.clone(),
     391            9 :                     column_type,
     392            9 :                     location: Some(location),
     393            9 :                 });
     394            9 :                 let object = CatalogObjectRef::Column(loc(column_index as u32));
     395            9 :                 state.resolve_name(&name, object);
     396              :             }
     397              :             NodeType::OBJECT_SQL_ALTER_DROP_COLUMN | NodeType::OBJECT_SQL_ALTER_RENAME_COLUMN | NodeType::OBJECT_SQL_ALTER_COLUMN_TYPE => {
     398           11 :                 if column.node_type() != NodeType::NAME {
     399            0 :                     return;
     400           11 :                 }
     401           11 :                 let name = state.node_name(column);
     402           11 :                 let Some(&column_index) = state.out.entry.table_declarations[table as usize].table_columns_by_name.get(&name.text) else { return };
     403           11 :                 match action.node_type() {
     404              :                     NodeType::OBJECT_SQL_ALTER_DROP_COLUMN => {
     405            7 :                         let table_declaration = &mut state.entry_mut().table_declarations[table as usize];
     406            7 :                         table_declaration.table_columns.remove(column_index);
     407            7 :                         table_declaration.table_columns_by_name.clear();
     408            8 :                         for (i, column) in table_declaration.table_columns.iter().enumerate() {
     409            7 :                             let text = column.column_name.text.clone();
     410            7 :                             table_declaration.table_columns_by_name.entry(text).or_insert(i);
     411            7 :                         }
     412              :                         // The name index holds positions of columns
     413            7 :                         let entry = state.entry_id;
     414           19 :                         state.out.name_objects.retain_mut(|(_, _, object)| match object {
     415           19 :                             CatalogObjectRef::Column(c) if c.table.entry == entry && c.table.index == table => {
     416           14 :                                 if c.column as usize == column_index {
     417            7 :                                     return false;
     418            7 :                                 }
     419            7 :                                 if c.column as usize > column_index {
     420            7 :                                     c.column -= 1;
     421            7 :                                 }
     422            7 :                                 true
     423              :                             }
     424           29 :                             _ => true,
     425           43 :                         });
     426              :                     }
     427              :                     NodeType::OBJECT_SQL_ALTER_RENAME_COLUMN => {
     428            3 :                         let Some(new_name) = new_name.filter(|n| n.node_type() == NodeType::NAME) else { return };
     429            3 :                         let new_name = state.node_name(new_name);
     430            3 :                         state.tag(&new_name, NameTag::COLUMN_NAME);
     431            3 :                         let column_id = state.reserve_column_ids(table_id, std::iter::once(&new_name.text))[0];
     432            3 :                         let table_declaration = &mut state.entry_mut().table_declarations[table as usize];
     433            3 :                         table_declaration.table_columns_by_name.remove(&name.text);
     434            3 :                         table_declaration.table_columns_by_name.insert(new_name.text.clone(), column_index);
     435            3 :                         let declared = &mut table_declaration.table_columns[column_index];
     436            3 :                         declared.object_id = QualifiedCatalogObjectId::table_column(table_id, column_id);
     437            3 :                         declared.column_name = new_name.clone();
     438            3 :                         let (entry, object) = (state.entry_id, CatalogObjectRef::Column(loc(column_index as u32)));
     439            4 :                         state.out.name_objects.retain(|(_, _, o)| !matches!(o, CatalogObjectRef::Column(c) if c.table.entry == entry && c.table.index == table && c.column as usize == column_index));
     440            3 :                         state.resolve_name(&new_name, object);
     441              :                     }
     442              :                     _ => {
     443            1 :                         let declared_type = column_type.and_then(|t| read_sql_type(state.parsed, t)).map(|t| Arc::from(t.to_string()));
     444            1 :                         state.entry_mut().table_declarations[table as usize].table_columns[column_index].column_type = declared_type;
     445              :                     }
     446              :                 }
     447              :             }
     448            0 :             _ => {}
     449              :         }
     450           21 :     }
     451              : 
     452              :     /// Register a table-like declaration backed by a SELECT scope
     453          177 :     fn register_derived_table(
     454          177 :         &mut self,
     455          177 :         state: &mut AnalysisState,
     456          177 :         declaration_node: &Node,
     457          177 :         name_node: Option<&Node>,
     458          177 :         columns_node: Option<&Node>,
     459          177 :         select_node: &Node,
     460          177 :     ) {
     461          177 :         let declaration_id = state.node_id(declaration_node);
     462              :         // EXPLAIN describes a declaration, publishing it would create a phantom catalog producer
     463          177 :         if Self::is_explain(state, state.find_statement_id(declaration_id)) {
     464           24 :             return;
     465          153 :         }
     466          153 :         let Some(table_name) = state.read_qualified_table_name(name_node) else { return };
     467          153 :         let schema_id = Self::register_schema(state, &table_name.database_name, &table_name.schema_name);
     468          153 :         let index = state.out.entry.table_declarations.len() as u32;
     469          153 :         let table_id = state.reserve_table_id(&table_name);
     470          153 :         let name = table_name.table_name.clone();
     471          153 :         let mut table = TableDeclaration::new(schema_id, table_id, table_name);
     472          153 :         table.ast_node_id = Some(declaration_id);
     473          153 :         table.ast_statement_id = state.find_statement_id(declaration_id);
     474          153 :         table.ast_scope_root = Some(state.node_id(select_node));
     475          153 :         table.is_view = declaration_node.node_type() == NodeType::OBJECT_SQL_VIEW;
     476          153 :         table.location = name_node.map(|n| state.text_span(*n.symbol_span()));
     477          153 :         state.entry_mut().table_declarations.push(table);
     478          153 :         state.resolve_name(&name, CatalogObjectRef::Table(TableLoc { entry: state.entry_id, index }));
     479              : 
     480          153 :         let mut column_aliases = Vec::new();
     481          153 :         if let Some(columns) = columns_node.filter(|n| n.node_type() == NodeType::ARRAY) {
     482            7 :             for column in state.children(columns) {
     483            7 :                 if column.node_type() != NodeType::NAME {
     484            0 :                     continue;
     485            7 :                 }
     486            7 :                 let name = state.node_name(column);
     487            7 :                 state.tag(&name, NameTag::COLUMN_NAME);
     488            7 :                 column_aliases.push(name);
     489              :             }
     490          149 :         }
     491          153 :         self.pending_derived_tables.push(PendingDerivedTable {
     492          153 :             table: index,
     493          153 :             select_node_id: state.node_id(select_node),
     494          153 :             column_aliases,
     495          153 :         });
     496          177 :     }
     497              : 
     498              :     /// Materialize a pending declaration after its SELECT scope has output columns
     499          155 :     fn populate_derived_table(state: &mut AnalysisState, pending: &PendingDerivedTable, scope: ScopeId) {
     500          155 :         let mut output_scope = scope;
     501              :         {
     502          155 :             let s = &state.out.name_scopes[scope as usize];
     503          155 :             if s.output_columns.is_empty() && !s.child_scopes.is_empty() {
     504            0 :                 output_scope = s.child_scopes[0];
     505          155 :             }
     506              :         }
     507          155 :         let outputs = &state.out.name_scopes[output_scope as usize].output_columns;
     508              :         // The names of the columns with their output columns
     509          155 :         let columns: Vec<(Name, u32)> = if !pending.column_aliases.is_empty() {
     510            4 :             let count = pending.column_aliases.len().min(outputs.len());
     511            4 :             pending.column_aliases[..count].iter().cloned().zip(0..).collect()
     512              :         } else {
     513              :             // A declared table has unique column names
     514          151 :             let mut seen = HashSet::default();
     515          151 :             outputs
     516          151 :                 .iter()
     517          151 :                 .zip(0..)
     518          175 :                 .filter(|(o, _)| !o.column_name.text.is_empty() && seen.insert(o.column_name.text.clone()))
     519          173 :                 .map(|(o, output)| (o.column_name.clone(), output))
     520          151 :                 .collect()
     521              :         };
     522          155 :         let table_index = pending.table;
     523          155 :         let table_id = state.out.entry.table_declarations[table_index as usize].table_id();
     524          155 :         let column_ids = state.reserve_column_ids(table_id, columns.iter().map(|(n, _)| &n.text));
     525          180 :         for (column_index, (column_name, output)) in columns.into_iter().enumerate() {
     526              :             // Only a column that is passed through keeps a known type, expressions have no type in the analyzer
     527          136 :             let source = Self::resolved_table_column(state, ColumnResolution::ScopeColumn { scope: output_scope, column: output });
     528          136 :             let column_type = source.and_then(|loc| state.table(loc.table)?.table_columns.get(loc.column as usize)?.column_type.clone());
     529          136 :             let table = &mut state.entry_mut().table_declarations[table_index as usize];
     530          136 :             table.table_columns_by_name.entry(column_name.text.clone()).or_insert(column_index);
     531          136 :             table.table_columns.push(TableColumn {
     532          136 :                 object_id: QualifiedCatalogObjectId::table_column(table_id, column_ids[column_index]),
     533          136 :                 table: Some(table_id),
     534          136 :                 ast_node_id: None,
     535          136 :                 column_name: column_name.clone(),
     536          136 :                 column_type,
     537          136 :                 location: None,
     538          136 :             });
     539          136 :             let loc = ColumnLoc {
     540          136 :                 table: TableLoc { entry: state.entry_id, index: table_index },
     541          136 :                 column: column_index as u32,
     542          136 :             };
     543          136 :             state.resolve_name(&column_name, CatalogObjectRef::Column(loc));
     544              :         }
     545          155 :     }
     546              : 
     547              :     /// The alias under which a table reference is registered in its scope
     548         2556 :     fn table_ref_alias(table_ref: &TableReference, fallback: &Arc<str>) -> Arc<str> {
     549         2556 :         match &table_ref.alias {
     550          193 :             Some((alias, _)) => alias.text.clone(),
     551         2363 :             None => fallback.clone(),
     552              :         }
     553         2556 :     }
     554              : 
     555              :     /// Read an alias clause, the alias name with its location and the column names
     556         2640 :     fn read_alias(state: &mut AnalysisState, alias: Option<&Node>) -> (Option<(Name, SymbolSpan)>, Vec<Name>) {
     557          266 :         let (name, columns) = match alias {
     558          266 :             Some(alias) if alias.node_type() == NodeType::NAME => (Some(alias), None),
     559            9 :             Some(alias) if alias.node_type() == NodeType::OBJECT_SQL_ALIAS => {
     560            9 :                 let [name, columns] = attrs!(state.children(alias), [K::SQL_ALIAS_NAME, K::SQL_ALIAS_COLUMN_NAMES]);
     561            9 :                 (name.filter(|n| n.node_type() == NodeType::NAME), columns.filter(|n| n.node_type() == NodeType::ARRAY))
     562              :             }
     563         2374 :             _ => return (None, Vec::new()),
     564              :         };
     565          266 :         let name = name.map(|node| {
     566          266 :             let name = state.node_name(node);
     567          266 :             state.tag(&name, NameTag::TABLE_ALIAS);
     568          266 :             (name, *node.symbol_span())
     569          266 :         });
     570          266 :         let mut column_aliases = Vec::new();
     571          266 :         for column in columns.map(|c| state.children(c)).unwrap_or_default() {
     572           13 :             if column.node_type() == NodeType::NAME {
     573           13 :                 let column_name = state.node_name(column);
     574           13 :                 state.tag(&column_name, NameTag::COLUMN_NAME);
     575           13 :                 column_aliases.push(column_name);
     576           13 :             }
     577              :         }
     578          266 :         (name, column_aliases)
     579         2640 :     }
     580              : 
     581              :     /// The FROM item of a table parameter `:foo` that reads the result of the script `foo.sql` or the attachment
     582              :     /// `foo.parquet`, None if the parameter is positional, the target of a write, ambiguous, or names no table parameter
     583              :     /// with columns
     584          870 :     fn table_parameter_source(state: &mut AnalysisState, node_id: u32, node: &Node, table_node: Option<&Node>, alias_node: Option<&Node>) -> Option<(ScopeSource, TableRefId)> {
     585          870 :         let parameter = table_node.filter(|n| n.node_type() == NodeType::OBJECT_SQL_PARAMETER_REF)?;
     586          717 :         if matches!(node.attribute_key(), K::SQL_INSERT_TARGET | K::SQL_DELETE_TARGET) {
     587           21 :             return None;
     588          696 :         }
     589          696 :         let results = state.out.table_parameters.clone()?;
     590           17 :         let [name] = attrs!(state.children(parameter), [K::SQL_PARAMETER_NAME]);
     591           17 :         let name = name.and_then(|n| state.read_name_path(n).last().map(|&id| state.name(id).text))?;
     592           17 :         let name: Arc<str> = match name.strip_prefix('\'').and_then(|n| n.strip_suffix('\'')) {
     593            0 :             Some(inner) => inner.into(),
     594           17 :             None => name,
     595              :         };
     596           17 :         let TableParameterLookup::Found(index) = results.find(&name) else { return None };
     597           13 :         let table = results.entries[index].table.as_ref()?;
     598           12 :         let (alias, column_aliases) = Self::read_alias(state, alias_node);
     599              :         // A table reference that is resolved already, the cursor, the focus and the semantic spans see the parameter
     600              :         // like a table. Scope resolution, the planner and the script references skip it.
     601           12 :         let id = state.out.table_references.len() as TableRefId;
     602           12 :         state.out.table_references.push(TableReference {
     603           12 :             table_reference_id: ExternalObjectId::new(state.entry_id, id),
     604           12 :             ast_node_id: node_id,
     605           12 :             location: Some(*node.symbol_span()),
     606           12 :             ast_statement_id: None,
     607           12 :             ast_scope_root: None,
     608           12 :             alias: alias.clone(),
     609           12 :             column_aliases: column_aliases.clone(),
     610           12 :             inner: Some(RelationExpression {
     611           12 :                 table_name: table.table_name.clone(),
     612           12 :                 resolved_table: Some(ResolvedTableEntry {
     613           12 :                     table_name: table.table_name.clone(),
     614           12 :                     catalog_schema_id: table.catalog_schema_id,
     615           12 :                     catalog_table_id: table.object_id,
     616           12 :                     referenced_catalog_version: table.catalog_version,
     617           12 :                 }),
     618           12 :                 resolved_alternatives: Vec::new(),
     619           12 :             }),
     620           12 :             role: TableRole::Read,
     621           12 :         });
     622           12 :         state.mark(node_id, SemanticNodeMarkerType::TABLE_REFERENCE);
     623           12 :         let source = ScopeSource {
     624           12 :             ast_node_id: node_id,
     625           12 :             name: Some(alias.map(|(alias, _)| alias.text).unwrap_or(name)),
     626           12 :             column_aliases,
     627           12 :             table: ReferencedTable::Table(TableLoc { entry: TABLE_PARAMETERS_ENTRY_ID, index: index as u32 }),
     628              :         };
     629           12 :         Some((source, id))
     630          870 :     }
     631              : 
     632              :     /// Is a FROM item a table parameter?
     633         5276 :     fn is_table_parameter(source: &ScopeSource) -> bool {
     634         4040 :         matches!(source.table, ReferencedTable::Table(loc) if loc.entry == TABLE_PARAMETERS_ENTRY_ID)
     635         5276 :     }
     636              : 
     637              :     /// Add a FROM item to a scope, unless its name is taken
     638         2638 :     fn add_source(state: &mut AnalysisState, scope: ScopeId, source: ScopeSource) {
     639         2638 :         if let Some(name) = &source.name {
     640         2633 :             if state.out.name_scopes[scope as usize].source(name).is_some() {
     641            8 :                 let message = format!("duplicate table alias {}", quote_identifier(name));
     642            8 :                 state.add_error(AnalyzerErrorType::DUPLICATE_TABLE_ALIAS, AnalyzerErrorSeverity::ERROR, source.ast_node_id, message);
     643            8 :                 return;
     644         2625 :             }
     645            5 :         }
     646         2630 :         state.out.name_scopes[scope as usize].sources.push(source);
     647         2638 :     }
     648              : 
     649              :     /// Resolve all table refs in a scope and register the FROM items
     650         5774 :     fn resolve_table_refs_in_scope(state: &mut AnalysisState, scope: ScopeId) {
     651              :         // The derived tables were added when the scope was created
     652         5774 :         let mut sources = std::mem::take(&mut state.out.name_scopes[scope as usize].sources);
     653         5774 :         let table_refs = state.out.name_scopes[scope as usize].table_references.clone();
     654         5776 :         for table_ref_id in table_refs {
     655         2568 :             let table_ref = &state.out.table_references[table_ref_id as usize];
     656         2568 :             let Some(relation) = &table_ref.inner else { continue };
     657              :             // The result of a script is a FROM item already
     658         2568 :             if table_ref.reads_table_parameter() {
     659           12 :                 continue;
     660         2556 :             }
     661         2556 :             if relation.resolved_table.is_some() {
     662            0 :                 continue;
     663         2556 :             }
     664         2556 :             let table_name = relation.table_name.clone();
     665         2556 :             let ref_name = table_name.table_name.text.clone();
     666         2556 :             let role = table_ref.role;
     667         2556 :             let reference_offset = state.ast[table_ref.ast_node_id as usize].symbol_span().offset();
     668         2556 :             let source = |table_ref: &TableReference, name: Arc<str>, table: ReferencedTable| ScopeSource {
     669         2556 :                 ast_node_id: table_ref.ast_node_id,
     670         2556 :                 name: Some(name),
     671         2556 :                 column_aliases: table_ref.column_aliases.clone(),
     672         2556 :                 table,
     673         2556 :             };
     674              : 
     675              :             // Does the table ref match a CTE definition in this or any parent scope?
     676         2556 :             let mut matched_cte = None;
     677         2556 :             let mut s = Some(scope);
     678         5250 :             while let Some(sid) = s {
     679         2842 :                 let candidate = &state.out.name_scopes[sid as usize];
     680         2842 :                 if let Some(index) = candidate.cte_definitions.get_index(&ref_name) {
     681          152 :                     let mut visible = true;
     682          152 :                     if !candidate.ctes_recursive {
     683          123 :                         let definition = &state.ast[candidate.cte_definitions.at(index).definition_node_id as usize];
     684          123 :                         let span = definition.symbol_span();
     685          123 :                         let definition_end = span.offset() + span.length();
     686          123 :                         visible = reference_offset > definition_end;
     687          123 :                     }
     688          152 :                     if visible {
     689          148 :                         matched_cte = Some(CteLoc {
     690          148 :                             scope: sid,
     691          148 :                             index: index as u32,
     692          148 :                         });
     693          148 :                         break;
     694            4 :                     }
     695         2690 :                 }
     696         2694 :                 s = candidate.parent_scope;
     697              :             }
     698         2556 :             if let (Some(cte), TableRole::Read) = (matched_cte, role) {
     699          147 :                 let table_ref = &state.out.table_references[table_ref_id as usize];
     700          147 :                 sources.push(source(table_ref, Self::table_ref_alias(table_ref, &ref_name), ReferencedTable::Cte(cte)));
     701          147 :                 continue;
     702         2409 :             }
     703              : 
     704              :             // Resolve as a table of the own script, then through the catalog
     705         2409 :             let mut resolved = Vec::new();
     706         2409 :             state.out.entry.resolve_table(&table_name, &mut resolved, MAX_TABLE_REF_AMBIGUITY);
     707         2409 :             if resolved.is_empty() {
     708         2224 :                 state.catalog.resolve_table(&table_name, state.entry_id, &mut resolved, MAX_TABLE_REF_AMBIGUITY);
     709         2225 :             }
     710         2409 :             if let Some(&best_loc) = resolved.first() {
     711         2008 :                 let entries: Vec<ResolvedTableEntry> = resolved
     712         2008 :                     .iter()
     713         2021 :                     .filter_map(|&loc| state.table(loc))
     714         2008 :                     .map(|t| ResolvedTableEntry {
     715         2021 :                         table_name: t.table_name.clone(),
     716         2021 :                         catalog_schema_id: t.catalog_schema_id,
     717         2021 :                         catalog_table_id: t.object_id,
     718         2021 :                         referenced_catalog_version: t.catalog_version,
     719         2021 :                     })
     720         2008 :                     .collect();
     721         2008 :                 let best_name = entries[0].table_name.table_name.text.clone();
     722         2008 :                 let relation = state.out.table_references[table_ref_id as usize].inner.as_mut().unwrap();
     723         2008 :                 let mut entries = entries.into_iter();
     724         2008 :                 relation.resolved_table = entries.next();
     725         2008 :                 relation.resolved_alternatives.extend(entries);
     726         2008 :                 let table_ref = &state.out.table_references[table_ref_id as usize];
     727         2008 :                 sources.push(source(table_ref, Self::table_ref_alias(table_ref, &best_name), ReferencedTable::Table(best_loc)));
     728         2008 :                 continue;
     729          401 :             }
     730              : 
     731              :             // Neither a CTE nor in the catalog. Register an unresolved relation so that columns used
     732              :             // against it can be associated for schema inference.
     733          401 :             let table_ref = &state.out.table_references[table_ref_id as usize];
     734          401 :             let unresolved = ReferencedTable::Unresolved(UnresolvedRelation {
     735          401 :                 table_ref: table_ref_id,
     736          401 :                 table_name,
     737          401 :             });
     738          401 :             sources.push(source(table_ref, Self::table_ref_alias(table_ref, &ref_name), unresolved));
     739              :         }
     740              :         // Register the FROM items in FROM order, table refs follow the nodes of earlier FROM items in
     741              :         // the post-order AST
     742         5774 :         sources.sort_by_key(|s| s.ast_node_id);
     743              :         // The result of a script yields its name to the other FROM items, the planner renames its common table
     744              :         // expression as well
     745         5776 :         let taken: HashSet<Arc<str>> = sources.iter().filter(|s| !Self::is_table_parameter(s)).filter_map(|s| s.name.clone()).collect();
     746         5776 :         for source in sources.iter_mut().filter(|s| Self::is_table_parameter(s)) {
     747           12 :             if source.name.as_ref().is_some_and(|n| taken.contains(n)) {
     748            1 :                 source.name = None;
     749           11 :             }
     750              :         }
     751         5776 :         for source in sources {
     752         2638 :             Self::add_source(state, scope, source);
     753         2638 :         }
     754         5774 :     }
     755              : 
     756              :     /// Find a column among the columns of a FROM item, the column aliases rename the leading columns
     757         3346 :     fn find_column(aliases: &[Name], column_count: usize, column_name: &str, by_name: Option<usize>) -> Option<usize> {
     758         3346 :         match aliases.iter().position(|a| &*a.text == column_name) {
     759           71 :             Some(column) => (column < column_count).then_some(column),
     760         3275 :             None => by_name.filter(|&column| column >= aliases.len()),
     761              :         }
     762         3346 :     }
     763              : 
     764              :     /// Resolve a column against the output columns of a scope
     765          233 :     fn resolve_scope_column(state: &AnalysisState, scope: ScopeId, aliases: &[Name], column_name: &str) -> ColumnResolution {
     766          233 :         let s = &state.out.name_scopes[scope as usize];
     767          233 :         let by_name = s.output_columns_by_name.get(column_name).copied();
     768          233 :         match Self::find_column(aliases, s.output_columns.len(), column_name, by_name) {
     769          149 :             Some(column) => ColumnResolution::ScopeColumn {
     770          149 :                 scope,
     771          149 :                 column: column as u32,
     772          149 :             },
     773           84 :             None => ColumnResolution::Unresolved,
     774              :         }
     775          233 :     }
     776              : 
     777              :     /// Resolve a column against a FROM item
     778         3752 :     fn resolve_column(state: &AnalysisState, source: &ScopeSource, column_name: &str) -> ColumnResolution {
     779         3752 :         match &source.table {
     780         3113 :             ReferencedTable::Table(loc) => {
     781         3113 :                 let Some(table) = state.table(*loc) else { return ColumnResolution::Unresolved };
     782         3113 :                 let by_name = table.table_columns_by_name.get(column_name).copied();
     783         3113 :                 match Self::find_column(&source.column_aliases, table.table_columns.len(), column_name, by_name) {
     784         1940 :                     Some(column) => ColumnResolution::TableColumn(ColumnLoc {
     785         1940 :                         table: *loc,
     786         1940 :                         column: column as u32,
     787         1940 :                     }),
     788         1173 :                     None => ColumnResolution::Unresolved,
     789              :                 }
     790              :             }
     791              :             // An unresolved relation has an unknown schema
     792          406 :             ReferencedTable::Unresolved(_) => ColumnResolution::Unresolved,
     793          135 :             ReferencedTable::Cte(loc) => {
     794          135 :                 let cte = state.out.name_scopes[loc.scope as usize].cte_definitions.at(loc.index as usize);
     795          135 :                 let Some(child) = cte.child_scope else {
     796            0 :                     return ColumnResolution::Unresolved;
     797              :                 };
     798          135 :                 let aliases = if source.column_aliases.is_empty() { &cte.column_aliases } else { &source.column_aliases };
     799          135 :                 Self::resolve_scope_column(state, child, aliases, column_name)
     800              :             }
     801           98 :             ReferencedTable::Subquery(child) => Self::resolve_scope_column(state, *child, &source.column_aliases, column_name),
     802              :         }
     803         3752 :     }
     804              : 
     805              :     /// Is a node within the subtree of another one?
     806           15 :     fn is_within(state: &AnalysisState, mut node_id: u32, ancestor: u32) -> bool {
     807              :         // Parents follow their children in the post-order AST
     808           43 :         while node_id < ancestor {
     809           28 :             let parent = state.ast[node_id as usize].parent();
     810              :             // Statement roots are their own parents
     811           28 :             if parent == node_id || parent as usize >= state.ast.len() {
     812            0 :                 return false;
     813           28 :             }
     814           28 :             node_id = parent;
     815              :         }
     816           15 :         node_id == ancestor
     817           15 :     }
     818              : 
     819              :     /// Do the FROM items that have a column all lie within a join that uses it?
     820           40 :     fn is_using_column(&self, state: &AnalysisState, scope: ScopeId, source_nodes: &[u32], column_name: &str) -> bool {
     821           40 :         let Some(joins) = self.using_joins.get(&scope) else { return false };
     822            9 :         joins.iter().any(|join| {
     823           15 :             join.uses(column_name) && source_nodes.iter().all(|&node_id| Self::is_within(state, node_id, join.ast_node_id))
     824            9 :         })
     825           40 :     }
     826              : 
     827              :     /// Look up a column ref in the visible FROM items of a scope
     828         3527 :     fn try_resolve_column_in_scope(&mut self, state: &mut AnalysisState, expr: ExprId, target: ScopeId, visibility: SourceVisibility) -> Lookup {
     829         3527 :         let Some(column_ref) = state.out.expressions[expr as usize].column_ref() else { return Lookup::Missing };
     830         3527 :         let column_name = column_ref.column_name.column_name.text.clone();
     831         3527 :         let alias = column_ref.column_name.table_alias.as_ref().map(|a| a.text.clone());
     832         3527 :         let scope = &state.out.name_scopes[target as usize];
     833         4583 :         let mut sources = scope.sources.iter().filter(|s| visibility.sees(s));
     834              : 
     835         3527 :         if let Some(alias) = alias {
     836          614 :             let Some(source) = sources.find(|s| s.name.as_deref() == Some(&*alias)) else { return Lookup::Missing };
     837          309 :             let resolution = Self::resolve_column(state, source, &column_name);
     838          309 :             if !resolution.is_resolved() {
     839          108 :                 return Lookup::Missing;
     840          201 :             }
     841          201 :             state.out.expressions[expr as usize].column_ref_mut().unwrap().resolved = resolution;
     842          201 :             return Lookup::Resolved;
     843         2997 :         }
     844              : 
     845              :         // No table alias, scan all FROM items for the column and check for ambiguity
     846         2997 :         let mut candidates = Vec::new();
     847         3953 :         for source in sources {
     848         3443 :             let resolution = Self::resolve_column(state, source, &column_name);
     849         3443 :             if resolution.is_resolved() {
     850         1888 :                 candidates.push((source.ast_node_id, source.name.clone(), resolution));
     851         2567 :             }
     852              :         }
     853         2997 :         let resolution = match candidates.as_slice() {
     854         2997 :             [] => return Lookup::Missing,
     855         1807 :             [(_, _, resolution)] => *resolution,
     856              :             // A USING column resolves to the column of the first input
     857           40 :             [(_, _, resolution), ..]
     858           40 :                 if self.is_using_column(state, target, &candidates.iter().map(|c| c.0).collect::<Vec<_>>(), &column_name) =>
     859              :             {
     860            6 :                 *resolution
     861              :             }
     862              :             _ => {
     863           34 :                 let mut message = String::from("column reference is ambiguous, candidates: ");
     864           69 :                 for (i, (_, table_name, _)) in candidates.iter().enumerate() {
     865           69 :                     if i > 0 {
     866           35 :                         message.push_str(", ");
     867           35 :                     }
     868           69 :                     match table_name {
     869           68 :                         Some(table_name) => message.push_str(&quote_identifier(table_name)),
     870            1 :                         None => message.push_str("(subquery)"),
     871              :                     }
     872           69 :                     message.push('.');
     873           69 :                     message.push_str(&quote_identifier(&column_name));
     874              :                 }
     875           34 :                 let node_id = state.out.expressions[expr as usize].ast_node_id;
     876           34 :                 state.add_error(AnalyzerErrorType::COLUMN_REF_AMBIGUOUS, AnalyzerErrorSeverity::ERROR, node_id, message);
     877           34 :                 self.ambiguous_column_refs.insert(expr);
     878           34 :                 return Lookup::Ambiguous;
     879              :             }
     880              :         };
     881         1813 :         state.out.expressions[expr as usize].column_ref_mut().unwrap().resolved = resolution;
     882         1813 :         Lookup::Resolved
     883         3527 :     }
     884              : 
     885              :     /// Try to resolve a column ref against the output columns of a scope
     886           60 :     fn try_resolve_column_from_outputs(state: &mut AnalysisState, expr: ExprId, target: ScopeId) -> bool {
     887           60 :         let Some(column_ref) = state.out.expressions[expr as usize].column_ref() else { return false };
     888           60 :         if column_ref.column_name.table_alias.is_some() {
     889            0 :             return false;
     890           60 :         }
     891           60 :         let scope = &state.out.name_scopes[target as usize];
     892           60 :         match scope.output_columns_by_name.get(&column_ref.column_name.column_name.text) {
     893           57 :             Some(&column) => {
     894           57 :                 state.out.expressions[expr as usize].column_ref_mut().unwrap().resolved = ColumnResolution::ScopeColumn {
     895           57 :                     scope: target,
     896           57 :                     column: column as u32,
     897           57 :                 };
     898           57 :                 true
     899              :             }
     900            3 :             None => false,
     901              :         }
     902           60 :     }
     903              : 
     904         8112 :     fn is_unresolved_column_ref(state: &AnalysisState, expr: ExprId) -> bool {
     905         8112 :         state.out.expressions[expr as usize].column_ref().is_some_and(|c| !c.resolved.is_resolved())
     906         8112 :     }
     907              : 
     908              :     /// How a column ref sees the output columns of its scope
     909         2704 :     fn output_names(state: &AnalysisState, expr: ExprId) -> OutputNames {
     910         2704 :         let node = &state.ast[state.out.expressions[expr as usize].ast_node_id as usize];
     911         2704 :         let (output_names, list_key) = match node.attribute_key() {
     912           49 :             K::SQL_ORDER_VALUE => (OutputNames::Preferred, K::SQL_SELECT_ORDER),
     913           59 :             K::SQL_GROUP_BY_ITEM_ARG => (OutputNames::Fallback, K::SQL_SELECT_GROUPS),
     914         2596 :             _ => return OutputNames::Invisible,
     915              :         };
     916              :         // The item must be in the clause of the SELECT, not in a window or an aggregate
     917          108 :         let list = state.ast.get(node.parent() as usize).and_then(|item| state.ast.get(item.parent() as usize));
     918          108 :         if list.is_some_and(|list| list.attribute_key() == list_key) {
     919          104 :             output_names
     920              :         } else {
     921            4 :             OutputNames::Invisible
     922              :         }
     923         2704 :     }
     924              : 
     925              :     /// Resolve column refs against the FROM items and the output columns of the own scope
     926         5774 :     fn resolve_column_refs_locally(&mut self, state: &mut AnalysisState, scope: ScopeId) {
     927         5774 :         let exprs = state.out.name_scopes[scope as usize].expressions.clone();
     928         5790 :         for expr in exprs {
     929         2704 :             if !Self::is_unresolved_column_ref(state, expr) {
     930            0 :                 continue;
     931         2704 :             }
     932         2704 :             match Self::output_names(state, expr) {
     933         2600 :                 OutputNames::Invisible => {
     934         2600 :                     self.try_resolve_column_in_scope(state, expr, scope, SourceVisibility::All);
     935         2600 :                 }
     936              :                 OutputNames::Preferred => {
     937           45 :                     if !Self::try_resolve_column_from_outputs(state, expr, scope) {
     938            1 :                         self.try_resolve_column_in_scope(state, expr, scope, SourceVisibility::All);
     939           44 :                     }
     940              :                 }
     941              :                 OutputNames::Fallback => {
     942           59 :                     if self.try_resolve_column_in_scope(state, expr, scope, SourceVisibility::All) == Lookup::Missing {
     943           15 :                         Self::try_resolve_column_from_outputs(state, expr, scope);
     944           54 :                     }
     945              :                 }
     946              :             }
     947              :         }
     948         5774 :     }
     949              : 
     950              :     /// Resolve column refs by walking up the parent scopes
     951         5774 :     fn resolve_column_refs_from_parents(&mut self, state: &mut AnalysisState, scope: ScopeId) {
     952         5774 :         let exprs = state.out.name_scopes[scope as usize].expressions.clone();
     953         5790 :         for expr in exprs {
     954         2704 :             if Self::is_unresolved_column_ref(state, expr) && !self.ambiguous_column_refs.contains(&expr) {
     955          646 :                 self.resolve_from_parent_chain(state, expr, scope);
     956         2109 :             }
     957              :         }
     958         5774 :     }
     959              : 
     960              :     /// Try the FROM items that a scope sees in its parent scopes, stopping at correlation barriers
     961          722 :     fn resolve_from_parent_chain(&mut self, state: &mut AnalysisState, expr: ExprId, scope: ScopeId) -> bool {
     962          722 :         let mut current = scope;
     963              :         loop {
     964          792 :             let s = &state.out.name_scopes[current as usize];
     965          792 :             let (Some(parent), false) = (s.parent_scope, s.column_correlation_barrier) else { return false };
     966          115 :             match self.try_resolve_column_in_scope(state, expr, parent, s.parent_visibility) {
     967           45 :                 Lookup::Resolved => return true,
     968            0 :                 Lookup::Ambiguous => return false,
     969           70 :                 Lookup::Missing => current = parent,
     970              :             }
     971              :         }
     972          722 :     }
     973              : 
     974              :     /// Attach parsed CTE definitions to a scope
     975         3991 :     fn attach_ctes(state: &mut AnalysisState, scope: ScopeId, ctes: Vec<CteDefinition>) {
     976         3991 :         for cte in ctes {
     977          141 :             let Some(&child_scope) = state.out.name_scopes_by_root_node.get(&cte.select_node_id) else { continue };
     978              :             // A CTE cannot correlate with the FROM items of the statement it is attached to
     979          141 :             state.out.name_scopes[child_scope as usize].parent_visibility = SourceVisibility::None;
     980          141 :             let mut column_aliases = Vec::new();
     981          141 :             if let Some(columns_node_id) = cte.columns_node_id.filter(|_| cte.columns_count > 0) {
     982           19 :                 let columns = &state.ast[columns_node_id as usize];
     983           24 :                 for ci in 0..cte.columns_count as u32 {
     984           24 :                     let column = &state.ast[(columns.children_begin_or_value() + ci) as usize];
     985           24 :                     if column.node_type() == NodeType::NAME {
     986           24 :                         column_aliases.push(state.node_name(column));
     987           24 :                     }
     988              :                 }
     989          122 :             }
     990          141 :             let definition = ResolvedCte {
     991          141 :                 cte_name: cte.cte_name.clone(),
     992          141 :                 child_scope: Some(child_scope),
     993          141 :                 definition_node_id: cte.ast_node_id,
     994          141 :                 column_aliases,
     995          141 :             };
     996          141 :             let s = &mut state.out.name_scopes[scope as usize];
     997          141 :             s.cte_definitions.insert(cte.cte_name.text.clone(), definition);
     998          141 :             s.cte_definition_nodes.entry(cte.cte_name.text.clone()).or_insert(cte.ast_node_id);
     999              :         }
    1000         3991 :     }
    1001              : 
    1002              :     /// Look up a FROM item by name in a scope and in the parent scopes it sees
    1003          263 :     fn lookup_source_in_scope_chain<'s>(state: &'s AnalysisState, scope: ScopeId, name: &str) -> Option<&'s ScopeSource> {
    1004          263 :         let mut current = scope;
    1005          263 :         let mut visibility = SourceVisibility::All;
    1006              :         loop {
    1007          296 :             let s = &state.out.name_scopes[current as usize];
    1008          308 :             if let Some(source) = s.sources.iter().find(|x| visibility.sees(x) && x.name.as_deref() == Some(name)) {
    1009          110 :                 return Some(source);
    1010          186 :             }
    1011          186 :             let (Some(parent), false) = (s.parent_scope, s.column_correlation_barrier) else { return None };
    1012           33 :             visibility = s.parent_visibility;
    1013           33 :             current = parent;
    1014              :         }
    1015          263 :     }
    1016              : 
    1017              :     /// Resolve qualified column refs whose qualifier is not a table in scope as field accesses of
    1018              :     /// row-typed columns
    1019         5774 :     fn resolve_row_field_accesses(&mut self, state: &mut AnalysisState, scope: ScopeId) {
    1020         5774 :         let exprs = state.out.name_scopes[scope as usize].expressions.clone();
    1021         5790 :         for expr in exprs {
    1022         2704 :             let Some(column_ref) = state.out.expressions[expr as usize].column_ref() else { continue };
    1023         2704 :             if column_ref.resolved.is_resolved() {
    1024         2068 :                 continue;
    1025          636 :             }
    1026          636 :             let Some(qualifier) = column_ref.column_name.table_alias.clone() else { continue };
    1027          133 :             if Self::lookup_source_in_scope_chain(state, scope, &qualifier.text).is_some() {
    1028           55 :                 continue;
    1029           78 :             }
    1030              :             // In p.x, p is not a table in scope but may be a row-typed column with a field x
    1031           78 :             let qualified = column_ref.column_name.clone();
    1032           78 :             {
    1033           78 :                 let column_ref = state.out.expressions[expr as usize].column_ref_mut().unwrap();
    1034           78 :                 column_ref.column_name.table_alias = None;
    1035           78 :                 column_ref.column_name.column_name = qualifier.clone();
    1036           78 :             }
    1037           78 :             let resolved = self.try_resolve_column_in_scope(state, expr, scope, SourceVisibility::All) == Lookup::Resolved
    1038           76 :                 || self.resolve_from_parent_chain(state, expr, scope);
    1039           78 :             if resolved {
    1040            3 :                 state.tag(&qualifier, NameTag::COLUMN_NAME);
    1041           75 :             } else {
    1042           75 :                 state.out.expressions[expr as usize].column_ref_mut().unwrap().column_name = qualified;
    1043           75 :             }
    1044              :         }
    1045         5774 :     }
    1046              : 
    1047              :     /// Associate still-unresolved column refs with unresolved tables in scope
    1048         5774 :     fn associate_unresolved_columns(state: &mut AnalysisState, scope: ScopeId) {
    1049         5774 :         let s = &state.out.name_scopes[scope as usize];
    1050              :         // The unresolved relations of this scope in FROM order
    1051         5774 :         let local_unresolved: Vec<InferenceCandidate> = s
    1052         5774 :             .sources
    1053         5774 :             .iter()
    1054         5776 :             .filter_map(|source| match &source.table {
    1055          400 :                 ReferencedTable::Unresolved(rel) => Some(InferenceCandidate {
    1056          400 :                     table_name: rel.table_name.clone(),
    1057          400 :                     table_ref: rel.table_ref,
    1058          400 :                 }),
    1059         2230 :                 _ => None,
    1060         2630 :             })
    1061         5774 :             .collect();
    1062              : 
    1063         5790 :         for expr in s.expressions.clone() {
    1064         2704 :             let expression = &state.out.expressions[expr as usize];
    1065         2704 :             let Some(column_ref) = expression.column_ref() else { continue };
    1066         2704 :             if column_ref.resolved.is_resolved() || column_ref.inferred.is_some() {
    1067         2071 :                 continue;
    1068          633 :             }
    1069          633 :             let column_name = column_ref.column_name.column_name.clone();
    1070          633 :             let constraint = match &column_ref.column_name.table_alias {
    1071              :                 // Alias-qualified, the owner is pinned to whatever the alias refers to
    1072          130 :                 Some(alias) => {
    1073           34 :                     let Some(ReferencedTable::Unresolved(rel)) =
    1074          130 :                         Self::lookup_source_in_scope_chain(state, scope, &alias.text).map(|s| &s.table)
    1075              :                     else {
    1076           96 :                         continue;
    1077              :                     };
    1078           34 :                     InferenceConstraint {
    1079           34 :                         kind: InferenceConstraintKind::Forced,
    1080           34 :                         expression_id: expression.expression_id,
    1081           34 :                         column_name,
    1082           34 :                         candidates: vec![InferenceCandidate {
    1083           34 :                             table_name: rel.table_name.clone(),
    1084           34 :                             table_ref: rel.table_ref,
    1085           34 :                         }],
    1086           34 :                     }
    1087              :                 }
    1088              :                 // Unqualified, the candidates are the unresolved tables in scope
    1089              :                 None => {
    1090          503 :                     if local_unresolved.is_empty() {
    1091          352 :                         continue;
    1092          151 :                     }
    1093              :                     InferenceConstraint {
    1094          151 :                         kind: if local_unresolved.len() == 1 {
    1095          140 :                             InferenceConstraintKind::Forced
    1096              :                         } else {
    1097           11 :                             InferenceConstraintKind::Disjunctive
    1098              :                         },
    1099          151 :                         expression_id: expression.expression_id,
    1100          151 :                         column_name,
    1101          151 :                         candidates: local_unresolved.clone(),
    1102              :                     }
    1103              :                 }
    1104              :             };
    1105          185 :             state.out.inference_constraints.push(constraint);
    1106              :         }
    1107         5774 :     }
    1108              : 
    1109              :     /// The columns of a FROM item, as a star forwards them
    1110         1019 :     fn source_columns(state: &AnalysisState, source: &ScopeSource) -> Vec<ScopeColumn> {
    1111         1019 :         let scope_columns = |scope: ScopeId| state.out.name_scopes[scope as usize].output_columns.clone();
    1112         1019 :         let mut columns = match &source.table {
    1113          646 :             ReferencedTable::Table(loc) => match state.table(*loc) {
    1114          646 :                 Some(table) => table
    1115          646 :                     .table_columns
    1116          646 :                     .iter()
    1117          646 :                     .enumerate()
    1118          646 :                     .map(|(ci, column)| ScopeColumn {
    1119          848 :                         column_name: column.column_name.clone(),
    1120          848 :                         source: ScopeColumnSource::TableColumn(ColumnLoc {
    1121          848 :                             table: *loc,
    1122          848 :                             column: ci as u32,
    1123          848 :                         }),
    1124          848 :                     })
    1125          646 :                     .collect(),
    1126            0 :                 None => Vec::new(),
    1127              :             },
    1128              :             // An unresolved relation has an unknown schema
    1129          267 :             ReferencedTable::Unresolved(_) => Vec::new(),
    1130           86 :             ReferencedTable::Cte(loc) => {
    1131           86 :                 let cte = state.out.name_scopes[loc.scope as usize].cte_definitions.at(loc.index as usize);
    1132           86 :                 let mut columns = cte.child_scope.map(scope_columns).unwrap_or_default();
    1133           86 :                 for (column, alias) in columns.iter_mut().zip(&cte.column_aliases) {
    1134           14 :                     column.column_name = alias.clone();
    1135           14 :                 }
    1136           86 :                 columns
    1137              :             }
    1138           20 :             ReferencedTable::Subquery(child) => scope_columns(*child),
    1139              :         };
    1140         1019 :         for (column, alias) in columns.iter_mut().zip(&source.column_aliases) {
    1141            0 :             column.column_name = alias.clone();
    1142            0 :         }
    1143         1019 :         columns
    1144         1019 :     }
    1145              : 
    1146              :     /// Populate the output columns of a scope from its result targets
    1147         5774 :     fn populate_output_columns(state: &mut AnalysisState, scope: ScopeId) {
    1148         5774 :         let mut columns = Vec::new();
    1149         5774 :         let scope_node = &state.ast[state.out.name_scopes[scope as usize].ast_node_id as usize];
    1150         5774 :         let [combine_inputs, values] = attrs!(state.children(scope_node), [K::SQL_COMBINE_INPUT, K::SQL_SELECT_VALUES]);
    1151         5774 :         if let Some(combine_inputs) = combine_inputs.filter(|n| n.children_count() > 0) {
    1152              :             // Set operations expose the column names of the left input
    1153           32 :             if let Some(&left) = state.out.name_scopes_by_root_node.get(&combine_inputs.children_begin_or_value()) {
    1154           32 :                 columns = state.out.name_scopes[left as usize].output_columns.clone();
    1155           32 :             }
    1156         5742 :         }
    1157              :         // The columns of VALUES are unnamed, aliases of a derived table can name them
    1158         5774 :         if let Some(row) = values.and_then(|v| state.children(v).first()).filter(|r| r.node_type() == NodeType::ARRAY) {
    1159          251 :             for value in state.children(row) {
    1160          251 :                 columns.push(ScopeColumn {
    1161          251 :                     column_name: state.empty_name(),
    1162          251 :                     source: match state.expression_of(state.node_id(value)) {
    1163          251 :                         Some(expr) => ScopeColumnSource::Expression(expr),
    1164            0 :                         None => ScopeColumnSource::Computed,
    1165              :                     },
    1166              :                 });
    1167              :             }
    1168         5558 :         }
    1169         5774 :         let mut result_targets = std::mem::take(&mut state.out.name_scopes[scope as usize].result_targets);
    1170         5796 :         for rt in result_targets.iter_mut() {
    1171         4020 :             if rt.is_star {
    1172              :                 // Forward the columns of all FROM items, or of the qualified one
    1173         1324 :                 let s = &state.out.name_scopes[scope as usize];
    1174         1368 :                 for source in &s.sources {
    1175         1022 :                     if rt.star_qualifier.as_ref().is_none_or(|q| source.name.as_deref() == Some(q.text())) {
    1176         1019 :                         columns.extend(Self::source_columns(state, source));
    1177         1019 :                     }
    1178              :                 }
    1179         1324 :                 continue;
    1180         2696 :             }
    1181         2696 :             let mut expr = rt.expression_id;
    1182         2696 :             if expr.is_none() {
    1183         1134 :                 let rt_node = &state.ast[rt.ast_node_id as usize];
    1184         1134 :                 let [value] = attrs!(state.children(rt_node), [K::SQL_RESULT_TARGET_VALUE]);
    1185         1134 :                 if let Some(value) = value {
    1186         1134 :                     expr = state.expression_of(state.node_id(value));
    1187         1134 :                     rt.expression_id = expr;
    1188         1134 :                 }
    1189         1562 :             }
    1190         2696 :             columns.push(ScopeColumn {
    1191         2696 :                 column_name: rt.column_name.clone().unwrap_or_else(|| state.empty_name()),
    1192              :                 // Computed columns keep their position for column aliases
    1193         2696 :                 source: match expr {
    1194         2593 :                     Some(expr) => ScopeColumnSource::Expression(expr),
    1195          103 :                     None => ScopeColumnSource::Computed,
    1196              :                 },
    1197              :             });
    1198              :         }
    1199         5774 :         let s = &mut state.out.name_scopes[scope as usize];
    1200         5774 :         s.result_targets = result_targets;
    1201              :         // A name refers to its first column, references to later ones would be ambiguous
    1202         5797 :         for column in columns {
    1203         3942 :             if !column.column_name.text.is_empty() {
    1204         2906 :                 s.output_columns_by_name.entry(column.column_name.text.clone()).or_insert(s.output_columns.len());
    1205         2917 :             }
    1206         3942 :             s.output_columns.push(column);
    1207              :         }
    1208         5774 :     }
    1209              : 
    1210              :     /// Resolve all names
    1211         4003 :     fn resolve_names(&mut self, state: &mut AnalysisState) {
    1212         4003 :         let roots = self.root_scopes.values.clone();
    1213              : 
    1214              :         // Pass 1, bottom-up: resolve the table refs, populate the output columns for the parent, then
    1215              :         // resolve the column refs against the own scope
    1216         5253 :         let mut dfs: Vec<(ScopeId, bool)> = roots.iter().map(|&r| (r, false)).collect();
    1217        15551 :         while let Some(top) = dfs.last_mut() {
    1218        11548 :             let scope = top.0;
    1219        11548 :             if !top.1 {
    1220         5774 :                 top.1 = true;
    1221              :                 // Push children in reverse order so that earlier siblings are processed first
    1222         5774 :                 let children = state.out.name_scopes[scope as usize].child_scopes.clone();
    1223         5774 :                 dfs.extend(children.iter().rev().map(|&c| (c, false)));
    1224         5774 :             } else {
    1225         5774 :                 dfs.pop();
    1226         5774 :                 Self::resolve_table_refs_in_scope(state, scope);
    1227         5774 :                 Self::populate_output_columns(state, scope);
    1228         5774 :                 self.resolve_column_refs_locally(state, scope);
    1229         5774 :             }
    1230              :         }
    1231              : 
    1232              :         // Pass 2, top-down: resolve the remaining column refs through the parent scopes
    1233         4003 :         let mut dfs: Vec<ScopeId> = roots.clone();
    1234         9777 :         while let Some(scope) = dfs.pop() {
    1235              :             // Recursive CTE outputs are available only after the bottom-up pass
    1236         5790 :             for expr in state.out.name_scopes[scope as usize].expressions.clone() {
    1237         2704 :                 if Self::is_unresolved_column_ref(state, expr) && !self.ambiguous_column_refs.contains(&expr) {
    1238          674 :                     self.try_resolve_column_in_scope(state, expr, scope, SourceVisibility::All);
    1239         2081 :                 }
    1240              :             }
    1241         5774 :             self.resolve_column_refs_from_parents(state, scope);
    1242         5774 :             self.resolve_row_field_accesses(state, scope);
    1243         5774 :             dfs.extend(state.out.name_scopes[scope as usize].child_scopes.iter().copied());
    1244              :         }
    1245              : 
    1246              :         // Pass 3: associate the remaining unresolved columns with unresolved tables, in scope order
    1247         5857 :         for scope in 0..state.out.name_scopes.len() {
    1248         5774 :             Self::associate_unresolved_columns(state, scope as ScopeId);
    1249         5774 :         }
    1250         4003 :     }
    1251              : 
    1252              :     /// Is the node nested inside syntax-only property graph syntax?
    1253        13889 :     fn is_inside_graph_syntax(&self, state: &AnalysisState, ast_node_id: u32) -> bool {
    1254        13889 :         if !self.has_graph_syntax {
    1255        13816 :             return false;
    1256           73 :         }
    1257           73 :         let mut node_id = ast_node_id;
    1258           73 :         for _ in 0..state.ast.len() {
    1259          212 :             let parent = state.ast[node_id as usize].parent();
    1260          212 :             if parent as usize >= state.ast.len() {
    1261            0 :                 return false;
    1262          212 :             }
    1263          212 :             match state.ast[parent as usize].node_type() {
    1264           46 :                 NodeType::OBJECT_SQL_PROPERTY_GRAPH | NodeType::OBJECT_SQL_GRAPH_TABLE => return true,
    1265              :                 // Statement roots are their own parents
    1266          166 :                 _ if parent == node_id => return false,
    1267          139 :                 _ => node_id = parent,
    1268              :             }
    1269              :         }
    1270            0 :         false
    1271        13889 :     }
    1272              : 
    1273         3855 :     pub fn visit(&mut self, state: &mut AnalysisState, morsel: Range<usize>) {
    1274        84087 :         for node_id in morsel {
    1275        84087 :             let node = &state.ast[node_id];
    1276        84087 :             let mut node_state = std::mem::take(&mut self.node_states[node_id]);
    1277        84087 :             self.visit_node(state, node, node_id as u32, &mut node_state);
    1278        84087 :             self.node_states[node_id] = node_state;
    1279        84087 :         }
    1280         3855 :     }
    1281              : 
    1282        84087 :     fn visit_node(&mut self, state: &mut AnalysisState, node: &Node, node_id: u32, node_state: &mut NodeState) {
    1283        84087 :         match node.node_type() {
    1284              :             NodeType::OBJECT_SQL_COLUMN_DEF => {
    1285         4383 :                 let parent = node.parent() as usize;
    1286         4383 :                 if parent < state.ast.len() {
    1287              :                     // Fields of descriptors and row types are not table columns
    1288         4383 :                     let key = state.ast[parent].attribute_key();
    1289         4383 :                     if key == K::SQL_DESCRIPTOR_COLUMNS || key == K::SQL_ROW_TYPE_FIELDS {
    1290           13 :                         return;
    1291         4370 :                     }
    1292            0 :                 }
    1293         4370 :                 let [name, column_type] = attrs!(state.children(node), [K::SQL_COLUMN_DEF_NAME, K::SQL_COLUMN_DEF_TYPE]);
    1294         4370 :                 if let Some(name) = name.filter(|n| n.node_type() == NodeType::NAME) {
    1295         4370 :                     let name = state.node_name(name);
    1296         4370 :                     state.tag(&name, NameTag::COLUMN_NAME);
    1297         4370 :                     node_state.table_columns.push(PendingColumn {
    1298         4370 :                         ast_node_id: node_id,
    1299         4370 :                         column_name: name,
    1300         4370 :                         column_type: column_type.and_then(|t| read_sql_type(state.parsed, t)).map(|t| Arc::from(t.to_string())),
    1301              :                     });
    1302            0 :                 }
    1303              :             }
    1304              : 
    1305              :             NodeType::OBJECT_SQL_COLUMN_REF => {
    1306         2737 :                 if self.is_inside_graph_syntax(state, node_id) {
    1307           16 :                     return;
    1308         2721 :                 }
    1309              :                 // The qualifier of t.* names a FROM item, the result target reads it
    1310         2721 :                 if Self::is_star_qualifier(state, node) {
    1311            6 :                     return;
    1312         2715 :                 }
    1313         2715 :                 let [path] = attrs!(state.children(node), [K::SQL_COLUMN_REF_PATH]);
    1314         2715 :                 if let Some(column_name) = state.read_qualified_column_name(path) {
    1315         2715 :                     let column_ref = ColumnRef {
    1316         2715 :                         column_name,
    1317         2715 :                         ast_scope_root: None,
    1318         2715 :                         resolved: ColumnResolution::Unresolved,
    1319         2715 :                         resolved_ids: None,
    1320         2715 :                         inferred: None,
    1321         2715 :                     };
    1322         2715 :                     let expr = state.add_expression(node_id, *node.symbol_span(), ExpressionInner::ColumnRef(column_ref));
    1323         2715 :                     node_state.column_references.push(expr);
    1324         2715 :                     state.expression_index[node_id as usize] = Some(expr);
    1325         2715 :                     state.mark(node_id, SemanticNodeMarkerType::COLUMN_REFERENCE);
    1326         2715 :                 }
    1327              :                 // Column refs may be recursive
    1328         2715 :                 self.merge_child_states(node_state, node);
    1329              :             }
    1330              : 
    1331              :             NodeType::OBJECT_SQL_TABLEREF => {
    1332         3443 :                 if self.is_inside_graph_syntax(state, node_id) {
    1333           15 :                     return;
    1334         3428 :                 }
    1335         3428 :                 let [name_node, alias_node, table_node, lateral] = attrs!(state.children(node), [
    1336              :                     K::SQL_TABLEREF_NAME,
    1337              :                     K::SQL_TABLEREF_ALIAS,
    1338              :                     K::SQL_TABLEREF_TABLE,
    1339              :                     K::SQL_TABLEREF_LATERAL,
    1340              :                 ]);
    1341         3428 :                 if let Some(name) = state.read_qualified_table_name(name_node) {
    1342         2558 :                     let (alias, column_aliases) = Self::read_alias(state, alias_node);
    1343         2558 :                     let id = state.out.table_references.len() as TableRefId;
    1344         2558 :                     state.out.table_references.push(TableReference {
    1345         2558 :                         table_reference_id: ExternalObjectId::new(state.entry_id, id),
    1346         2558 :                         ast_node_id: node_id,
    1347         2558 :                         location: Some(*node.symbol_span()),
    1348         2558 :                         ast_statement_id: None,
    1349         2558 :                         ast_scope_root: None,
    1350         2558 :                         alias,
    1351         2558 :                         column_aliases,
    1352         2558 :                         inner: Some(RelationExpression {
    1353         2558 :                             table_name: name,
    1354         2558 :                             resolved_table: None,
    1355         2558 :                             resolved_alternatives: Vec::new(),
    1356         2558 :                         }),
    1357         2558 :                         role: TableRole::Read,
    1358         2558 :                     });
    1359         2558 :                     node_state.table_references.push(id);
    1360         2558 :                     state.mark(node_id, SemanticNodeMarkerType::TABLE_REFERENCE);
    1361         2576 :                 } else if let Some((source, table_ref)) = Self::table_parameter_source(state, node_id, node, table_node, alias_node) {
    1362           12 :                     // A table parameter that reads the result of a script of the session
    1363           12 :                     node_state.derived_tables.push(source);
    1364           12 :                     node_state.table_references.push(table_ref);
    1365          860 :                 } else if let Some(select) = table_node.filter(|n| n.node_type() == NodeType::OBJECT_SQL_SELECT) {
    1366              :                     // A derived table, the scope of its SELECT is a FROM item
    1367           70 :                     if let Some(&child) = state.out.name_scopes_by_root_node.get(&state.node_id(select)) {
    1368           70 :                         let (alias, column_aliases) = Self::read_alias(state, alias_node);
    1369           70 :                         state.out.name_scopes[child as usize].parent_visibility = match lateral {
    1370            4 :                             Some(_) => SourceVisibility::Before(node_id),
    1371           66 :                             None => SourceVisibility::None,
    1372              :                         };
    1373           70 :                         node_state.derived_tables.push(ScopeSource {
    1374           70 :                             ast_node_id: node_id,
    1375           70 :                             name: alias.map(|(name, _)| name.text),
    1376           70 :                             column_aliases,
    1377           70 :                             table: ReferencedTable::Subquery(child),
    1378              :                         });
    1379            0 :                     }
    1380          788 :                 }
    1381              :                 // Table refs may be recursive
    1382         3428 :                 self.merge_child_states(node_state, node);
    1383              :             }
    1384              : 
    1385              :             NodeType::OBJECT_SQL_RESULT_TARGET => {
    1386         4036 :                 if self.is_inside_graph_syntax(state, node_id) {
    1387           14 :                     return;
    1388         4022 :                 }
    1389         4022 :                 self.merge_child_states(node_state, node);
    1390         4022 :                 let [value, name, star] = attrs!(state.children(node), [
    1391              :                     K::SQL_RESULT_TARGET_VALUE,
    1392              :                     K::SQL_RESULT_TARGET_NAME,
    1393              :                     K::SQL_RESULT_TARGET_STAR,
    1394              :                 ]);
    1395         4022 :                 let mut rt = ResultTarget {
    1396         4022 :                     ast_node_id: node_id,
    1397         4022 :                     ..Default::default()
    1398         4022 :                 };
    1399         4022 :                 if star.is_some() {
    1400         1326 :                     rt.is_star = true;
    1401         1326 :                     let [path] = value.map(|v| attrs!(state.children(v), [K::SQL_COLUMN_REF_PATH])).unwrap_or([None]);
    1402         1326 :                     if let Some(&qualifier) = path.map(|p| state.read_name_path(p)).unwrap_or_default().last() {
    1403            6 :                         let qualifier = state.name(qualifier);
    1404            6 :                         state.tag(&qualifier, NameTag::TABLE_ALIAS);
    1405            6 :                         rt.star_qualifier = Some(qualifier);
    1406         1320 :                     }
    1407              :                 } else {
    1408         2696 :                     if let Some(name) = name.filter(|n| n.node_type() == NodeType::NAME) {
    1409          427 :                         rt.column_name = Some(state.node_name(name));
    1410         2279 :                     }
    1411         2696 :                     if let Some(expr) = value.and_then(|v| state.expression_of(state.node_id(v))) {
    1412         1562 :                         rt.expression_id = Some(expr);
    1413         1562 :                         if rt.column_name.is_none() {
    1414         1508 :                             if let Some(column_ref) = state.out.expressions[expr as usize].column_ref() {
    1415         1508 :                                 rt.column_name = Some(column_ref.column_name.column_name.clone());
    1416         1508 :                             }
    1417           54 :                         }
    1418         1134 :                     }
    1419              :                 }
    1420         4022 :                 node_state.result_targets.push(rt);
    1421              :             }
    1422              : 
    1423              :             NodeType::OBJECT_SQL_CTE => {
    1424          149 :                 let [name, columns, statement] =
    1425          149 :                     attrs!(state.children(node), [K::SQL_CTE_NAME, K::SQL_CTE_COLUMNS, K::SQL_CTE_STATEMENT]);
    1426          149 :                 self.merge_child_states(node_state, node);
    1427          149 :                 if let (Some(name), Some(statement)) = (name.filter(|n| n.node_type() == NodeType::NAME), statement) {
    1428          141 :                     node_state.ctes.push(CteDefinition {
    1429          141 :                         cte_name: state.node_name(name),
    1430          141 :                         select_node_id: state.node_id(statement),
    1431          141 :                         columns_node_id: columns.map(|c| state.node_id(c)),
    1432          141 :                         columns_count: columns.map(|c| c.children_count() as u16).unwrap_or(0),
    1433          141 :                         ast_node_id: node_id,
    1434              :                     });
    1435            8 :                 }
    1436              :             }
    1437              : 
    1438              :             NodeType::OBJECT_SQL_SELECT => {
    1439         3673 :                 if self.is_inside_graph_syntax(state, node_id) {
    1440            1 :                     return;
    1441         3672 :                 }
    1442         3672 :                 self.merge_child_states(node_state, node);
    1443         3672 :                 let result_targets = std::mem::take(&mut node_state.result_targets);
    1444         3672 :                 let ctes = std::mem::take(&mut node_state.ctes);
    1445         3672 :                 let scope = self.create_scope(state, node_state, node_id);
    1446         3672 :                 let [recursive] = attrs!(state.children(node), [K::SQL_SELECT_WITH_RECURSIVE]);
    1447         3672 :                 {
    1448         3672 :                     let s = &mut state.out.name_scopes[scope as usize];
    1449         3672 :                     s.result_targets.extend(result_targets);
    1450         3672 :                     // Completion needs the declaration order and the recursive visibility of CTEs
    1451         3672 :                     s.ctes_recursive = recursive.is_some();
    1452         3672 :                 }
    1453         3672 :                 Self::attach_ctes(state, scope, ctes);
    1454         3672 :                 let [into] = attrs!(state.children(node), [K::SQL_SELECT_INTO]);
    1455         3672 :                 if let Some(into) = into.filter(|n| n.node_type() == NodeType::OBJECT_SQL_INTO) {
    1456           19 :                     let [into_name] = attrs!(state.children(into), [K::SQL_TEMP_NAME]);
    1457           19 :                     self.register_derived_table(state, into, into_name, None, node);
    1458         3653 :                 }
    1459              :             }
    1460              : 
    1461              :             NodeType::OBJECT_SQL_INSERT => {
    1462          267 :                 let [target, columns, source, default_values, returning, recursive] = attrs!(state.children(node), [
    1463              :                     K::SQL_INSERT_TARGET,
    1464              :                     K::SQL_INSERT_COLUMNS,
    1465              :                     K::SQL_INSERT_SOURCE,
    1466              :                     K::SQL_INSERT_DEFAULT_VALUES,
    1467              :                     K::SQL_INSERT_RETURNING,
    1468              :                     K::SQL_SELECT_WITH_RECURSIVE,
    1469              :                 ]);
    1470          267 :                 let target_ref = target.and_then(|t| self.node_states[state.node_id(t) as usize].table_references.first().copied());
    1471          267 :                 let Some(target_ref) = target_ref else {
    1472           14 :                     self.merge_child_states(node_state, node);
    1473           14 :                     return;
    1474              :                 };
    1475          253 :                 state.out.table_references[target_ref as usize].role = TableRole::Write;
    1476              : 
    1477          253 :                 self.merge_child_states(node_state, node);
    1478          253 :                 let result_targets = std::mem::take(&mut node_state.result_targets);
    1479          253 :                 let ctes = std::mem::take(&mut node_state.ctes);
    1480          253 :                 let scope = self.create_scope(state, node_state, node_id);
    1481          253 :                 {
    1482          253 :                     let s = &mut state.out.name_scopes[scope as usize];
    1483          253 :                     s.result_targets.extend(result_targets);
    1484          253 :                     s.ctes_recursive = recursive.is_some();
    1485          253 :                 }
    1486          253 :                 Self::attach_ctes(state, scope, ctes);
    1487          253 :                 if let Some(source) = source {
    1488          252 :                     if let Some(&source_scope) = state.out.name_scopes_by_root_node.get(&state.node_id(source)) {
    1489          252 :                         state.out.name_scopes[source_scope as usize].column_correlation_barrier = true;
    1490          252 :                     }
    1491            1 :                 }
    1492          253 :                 self.set_cte_correlation_barriers(state, scope);
    1493              : 
    1494          253 :                 let mut insert = InsertStatement {
    1495          253 :                     ast_node_id: node_id,
    1496          253 :                     ast_statement_id: state.find_statement_id(node_id),
    1497          253 :                     target: target_ref,
    1498          253 :                     source_ast_node_id: source.map(|s| state.node_id(s)),
    1499          253 :                     default_values: default_values.is_some(),
    1500          253 :                     returning: returning.is_some(),
    1501          253 :                     target_columns: Vec::new(),
    1502              :                 };
    1503          253 :                 if let Some(columns) = columns.filter(|n| n.node_type() == NodeType::ARRAY) {
    1504           13 :                     for column in state.children(columns) {
    1505           13 :                         if column.node_type() != NodeType::NAME {
    1506            0 :                             continue;
    1507           13 :                         }
    1508           13 :                         let name = state.node_name(column);
    1509           13 :                         state.tag(&name, NameTag::COLUMN_NAME);
    1510           13 :                         insert.target_columns.push(InsertTargetColumn {
    1511           13 :                             ast_node_id: state.node_id(column),
    1512           13 :                             column_name: name,
    1513           13 :                             resolved: None,
    1514           13 :                         });
    1515              :                     }
    1516          244 :                 }
    1517          253 :                 state.out.insert_statements.push(insert);
    1518              :             }
    1519              : 
    1520              :             NodeType::OBJECT_SQL_DELETE => {
    1521           66 :                 let [target, recursive] = attrs!(state.children(node), [K::SQL_DELETE_TARGET, K::SQL_SELECT_WITH_RECURSIVE]);
    1522           66 :                 if let Some(target) = target {
    1523           66 :                     for &table_ref in &self.node_states[state.node_id(target) as usize].table_references {
    1524           59 :                         state.out.table_references[table_ref as usize].role = TableRole::Write;
    1525           59 :                     }
    1526            0 :                 }
    1527           66 :                 self.merge_child_states(node_state, node);
    1528           66 :                 let result_targets = std::mem::take(&mut node_state.result_targets);
    1529           66 :                 let ctes = std::mem::take(&mut node_state.ctes);
    1530           66 :                 let scope = self.create_scope(state, node_state, node_id);
    1531           66 :                 {
    1532           66 :                     let s = &mut state.out.name_scopes[scope as usize];
    1533           66 :                     s.result_targets.extend(result_targets);
    1534           66 :                     s.ctes_recursive = recursive.is_some();
    1535           66 :                 }
    1536           66 :                 Self::attach_ctes(state, scope, ctes);
    1537              :                 // CTE definitions cannot correlate to the DELETE target or USING relations
    1538           66 :                 self.set_cte_correlation_barriers(state, scope);
    1539              :             }
    1540              : 
    1541              :             NodeType::OBJECT_SQL_JOINED_TABLE => {
    1542           70 :                 self.merge_child_states(node_state, node);
    1543           70 :                 let [join_type, using] = attrs!(state.children(node), [K::SQL_JOIN_TYPE, K::SQL_JOIN_USING]);
    1544           70 :                 if let Some(using) = using.filter(|n| n.node_type() == NodeType::ARRAY) {
    1545           44 :                     let mut columns = Vec::new();
    1546           44 :                     for column in state.children(using) {
    1547           44 :                         if column.node_type() == NodeType::NAME {
    1548           44 :                             let name = state.node_name(column);
    1549           44 :                             state.tag(&name, NameTag::COLUMN_NAME);
    1550           44 :                             columns.push(name.text);
    1551           44 :                         }
    1552              :                     }
    1553           44 :                     node_state.using_joins.push(UsingJoin {
    1554           44 :                         ast_node_id: node_id,
    1555           44 :                         using: Some(columns),
    1556           44 :                     });
    1557           26 :                 } else if join_type.is_some_and(|t| t.children_begin_or_value() as u8 & JoinType::NATURAL_.0 != 0) {
    1558            2 :                     node_state.using_joins.push(UsingJoin {
    1559            2 :                         ast_node_id: node_id,
    1560            2 :                         using: None,
    1561            2 :                     });
    1562           25 :                 }
    1563              :             }
    1564              : 
    1565              :             NodeType::OBJECT_SQL_CREATE => {
    1566         1783 :                 if Self::is_explain(state, state.find_statement_id(node_id)) {
    1567            6 :                     self.merge_child_states(node_state, node);
    1568            6 :                     self.create_scope(state, node_state, node_id);
    1569            6 :                     return;
    1570         1777 :                 }
    1571         1777 :                 let [name_node, elements] =
    1572         1777 :                     attrs!(state.children(node), [K::SQL_CREATE_TABLE_NAME, K::SQL_CREATE_TABLE_ELEMENTS]);
    1573         1777 :                 let Some(table_name) = state.read_qualified_table_name(name_node) else { return };
    1574         1777 :                 let schema_id = Self::register_schema(state, &table_name.database_name, &table_name.schema_name);
    1575         1777 :                 let index = state.out.entry.table_declarations.len() as u32;
    1576         1777 :                 let table_id = state.reserve_table_id(&table_name);
    1577              :                 // Only the elements are merged
    1578         1777 :                 if let Some(elements) = elements {
    1579         1777 :                     let elements_id = state.node_id(elements) as usize;
    1580         1777 :                     node_state.merge(&mut self.node_states[elements_id]);
    1581         1777 :                 }
    1582         1777 :                 let mut columns = std::mem::take(&mut node_state.table_columns);
    1583              :                 // Sort the columns by name.
    1584              :                 // The sort is stable, duplicate names keep their declaration order.
    1585         7338 :                 columns.sort_by(|l, r| l.column_name.text.as_bytes().cmp(r.column_name.text.as_bytes()));
    1586         1777 :                 self.create_scope(state, node_state, node_id);
    1587              : 
    1588         1777 :                 let name = table_name.table_name.clone();
    1589         1777 :                 let mut table = TableDeclaration::new(schema_id, table_id, table_name);
    1590         1777 :                 table.ast_node_id = Some(node_id);
    1591         1777 :                 table.ast_statement_id = state.find_statement_id(node_id);
    1592         1777 :                 table.location = name_node.map(|n| state.text_span(*n.symbol_span()));
    1593         1777 :                 let column_ids = state.reserve_column_ids(table_id, columns.iter().map(|c| &c.column_name.text));
    1594         4347 :                 for (column_index, column) in columns.iter().enumerate() {
    1595         4347 :                     table.table_columns_by_name.entry(column.column_name.text.clone()).or_insert(column_index);
    1596         4347 :                     table.table_columns.push(TableColumn {
    1597         4347 :                         object_id: QualifiedCatalogObjectId::table_column(table_id, column_ids[column_index]),
    1598         4347 :                         table: Some(table_id),
    1599         4347 :                         ast_node_id: Some(column.ast_node_id),
    1600         4347 :                         column_name: column.column_name.clone(),
    1601         4347 :                         column_type: column.column_type.clone(),
    1602         4347 :                         location: Some(state.text_span(*state.ast[column.ast_node_id as usize].symbol_span())),
    1603         4347 :                     });
    1604         4347 :                 }
    1605         1777 :                 state.entry_mut().table_declarations.push(table);
    1606         1777 :                 state.resolve_name(&name, CatalogObjectRef::Table(TableLoc { entry: state.entry_id, index }));
    1607         4347 :                 for (column_index, column) in columns.iter().enumerate() {
    1608         4347 :                     let loc = ColumnLoc {
    1609         4347 :                         table: TableLoc { entry: state.entry_id, index },
    1610         4347 :                         column: column_index as u32,
    1611         4347 :                     };
    1612         4347 :                     state.resolve_name(&column.column_name, CatalogObjectRef::Column(loc));
    1613         4347 :                 }
    1614              :             }
    1615              : 
    1616              :             NodeType::OBJECT_SQL_CREATE_FUNCTION => {
    1617           36 :                 if Self::is_explain(state, state.find_statement_id(node_id)) {
    1618            0 :                     self.merge_child_states(node_state, node);
    1619            0 :                     return;
    1620           36 :                 }
    1621           36 :                 let [name_node, params, returns, is_aggregate] = attrs!(state.children(node), [
    1622              :                     K::SQL_CREATE_FUNCTION_NAME,
    1623              :                     K::SQL_CREATE_FUNCTION_PARAMS,
    1624              :                     K::SQL_CREATE_FUNCTION_RETURNS,
    1625              :                     K::SQL_CREATE_FUNCTION_IS_AGGREGATE,
    1626              :                 ]);
    1627           36 :                 let Some(function_name) = state.read_qualified_function_name(name_node) else { return };
    1628           36 :                 let schema_id = Self::register_schema(state, &function_name.database_name, &function_name.schema_name);
    1629           36 :                 let index = state.out.entry.function_declarations.len() as u32;
    1630           36 :                 let function_id = state.reserve_function_id(&function_name);
    1631           36 :                 let return_type: Arc<str> = returns.map(|r| Arc::from(state.text_at(*r.symbol_span()))).unwrap_or_else(|| Arc::from(""));
    1632           36 :                 let mut declaration = FunctionDeclaration {
    1633           36 :                     object_id: QualifiedCatalogObjectId::function(function_id),
    1634           36 :                     catalog_schema_id: schema_id,
    1635           36 :                     is_aggregate: is_aggregate.is_some(),
    1636           36 :                     ast_node_id: Some(node_id),
    1637           36 :                     ast_statement_id: None,
    1638           36 :                     function_name: function_name.clone(),
    1639           36 :                     params: Vec::new(),
    1640           36 :                     return_type,
    1641           36 :                     location: name_node.map(|n| state.text_span(*n.symbol_span())),
    1642              :                 };
    1643           36 :                 if let Some(params) = params.filter(|n| n.node_type() == NodeType::ARRAY) {
    1644           34 :                     for param in state.children(params) {
    1645           34 :                         if param.node_type() != NodeType::OBJECT_SQL_FUNCTION_PARAM {
    1646            0 :                             continue;
    1647           34 :                         }
    1648           34 :                         let [param_name, param_type] =
    1649           34 :                             attrs!(state.children(param), [K::SQL_FUNCTION_PARAM_NAME, K::SQL_FUNCTION_PARAM_TYPE]);
    1650           34 :                         let Some(param_name) = param_name.filter(|n| n.node_type() == NodeType::NAME) else { continue };
    1651           34 :                         let param_type = param_type.map(|t| state.text_at(*t.symbol_span())).unwrap_or("");
    1652           34 :                         declaration.params.push(FunctionParam {
    1653           34 :                             ast_node_id: Some(state.node_id(param)),
    1654           34 :                             param_name: state.node_name(param_name),
    1655           34 :                             param_type: Arc::from(param_type),
    1656           34 :                         });
    1657              :                     }
    1658            5 :                 }
    1659           36 :                 state.entry_mut().function_declarations.push(declaration);
    1660           36 :                 state.tag(&function_name.function_name, NameTag::FUNCTION_NAME);
    1661           36 :                 state.resolve_name(&function_name.function_name, CatalogObjectRef::Function(state.loc(index as usize)));
    1662              :             }
    1663              : 
    1664              :             NodeType::OBJECT_SQL_CREATE_AS | NodeType::OBJECT_SQL_VIEW => {
    1665          158 :                 let [name, columns, statement] = if node.node_type() == NodeType::OBJECT_SQL_CREATE_AS {
    1666           82 :                     attrs!(state.children(node), [K::SQL_CREATE_AS_NAME, K::SQL_CREATE_AS_COLUMNS, K::SQL_CREATE_AS_STATEMENT])
    1667              :                 } else {
    1668           76 :                     attrs!(state.children(node), [K::SQL_VIEW_NAME, K::SQL_VIEW_COLUMNS, K::SQL_VIEW_STATEMENT])
    1669              :                 };
    1670          158 :                 if let Some(statement) = statement.filter(|n| n.node_type() == NodeType::OBJECT_SQL_SELECT) {
    1671          158 :                     self.register_derived_table(state, node, name, columns, statement);
    1672          158 :                 }
    1673          158 :                 self.merge_child_states(node_state, node);
    1674              :             }
    1675              : 
    1676           41 :             NodeType::OBJECT_SQL_ALTER_TABLE | NodeType::OBJECT_SQL_ALTER_VIEW => {
    1677           41 :                 // The columns of the actions belong to the table, not to a scope
    1678           41 :                 self.merge_child_states(node_state, node);
    1679           41 :                 node_state.clear();
    1680           41 :                 self.visit_alter(state, node, node_id);
    1681           41 :             }
    1682              : 
    1683           15 :             NodeType::OBJECT_SQL_PROPERTY_GRAPH | NodeType::OBJECT_SQL_GRAPH_TABLE => {
    1684           15 :                 // Property graph analysis is syntax-only. Keep graph-local references in the
    1685           15 :                 // analyzed buffers, but prevent them from entering or affecting an SQL scope.
    1686           15 :                 self.merge_child_states(node_state, node);
    1687           15 :                 node_state.clear();
    1688           15 :             }
    1689              : 
    1690              :             // By default, merge child states into the node state
    1691        63230 :             _ => self.merge_child_states(node_state, node),
    1692              :         }
    1693        84087 :     }
    1694              : 
    1695              :     /// Is a column ref the qualifier of a star result target (`t.*`)?
    1696         2721 :     fn is_star_qualifier(state: &AnalysisState, node: &Node) -> bool {
    1697         2721 :         if node.attribute_key() != K::SQL_RESULT_TARGET_VALUE {
    1698         1152 :             return false;
    1699         1569 :         }
    1700         1569 :         let Some(target) = state.ast.get(node.parent() as usize) else { return false };
    1701         1569 :         let [star] = attrs!(state.children(target), [K::SQL_RESULT_TARGET_STAR]);
    1702         1569 :         target.node_type() == NodeType::OBJECT_SQL_RESULT_TARGET && star.is_some()
    1703         2721 :     }
    1704              : 
    1705              :     /// Mark the CTEs of a scope as correlation barriers
    1706          319 :     fn set_cte_correlation_barriers(&self, state: &mut AnalysisState, scope: ScopeId) {
    1707          319 :         let children: Vec<ScopeId> = state.out.name_scopes[scope as usize]
    1708          319 :             .cte_definitions
    1709          319 :             .iter()
    1710          319 :             .filter_map(|(_, cte)| cte.child_scope)
    1711          319 :             .collect();
    1712          319 :         for child in children {
    1713           18 :             state.out.name_scopes[child as usize].column_correlation_barrier = true;
    1714           18 :         }
    1715          319 :     }
    1716              : 
    1717         4003 :     pub fn finish(&mut self, state: &mut AnalysisState) {
    1718              :         // Index the tables and functions
    1719         4003 :         state.entry_mut().index_declarations();
    1720              : 
    1721              :         // Assign statement ids to scopes before resolving ordered script-local names
    1722         5857 :         for i in 0..state.out.name_scopes.len() {
    1723         5774 :             let node_id = state.out.name_scopes[i].ast_node_id;
    1724         5774 :             state.out.name_scopes[i].ast_statement_id = state.find_statement_id(node_id);
    1725         5774 :         }
    1726              : 
    1727              :         // Resolve all names
    1728         4003 :         self.resolve_names(state);
    1729              : 
    1730         4003 :         for pending in std::mem::take(&mut self.pending_derived_tables) {
    1731          155 :             if let Some(&scope) = state.out.name_scopes_by_root_node.get(&pending.select_node_id) {
    1732          155 :                 Self::populate_derived_table(state, &pending, scope);
    1733          155 :             }
    1734              :         }
    1735         4003 :         for (table, action) in std::mem::take(&mut self.pending_alters) {
    1736            5 :             let action = state.ast[action as usize];
    1737            5 :             Self::apply_column_action(state, table, &action);
    1738            5 :         }
    1739              : 
    1740              :         // Assign statement ids, the objects are ordered by their AST nodes
    1741         4003 :         if !state.parsed.statements.is_empty() {
    1742         3767 :             let statements = &state.parsed.statements;
    1743        15068 :             let assign = |nodes: &mut dyn Iterator<Item = (u32, &mut Option<u32>)>| {
    1744        15068 :                 let mut statement_id = 0usize;
    1745        15068 :                 let mut statement_begin = statements[0].nodes_begin;
    1746        15068 :                 let mut statement_end = statement_begin + statements[0].node_count;
    1747        17283 :                 for (ast_node_id, statement) in nodes {
    1748        18191 :                     while statement_end <= ast_node_id && statement_id < statements.len() {
    1749         3514 :                         statement_id += 1;
    1750         3514 :                         if statement_id == statements.len() {
    1751            0 :                             break;
    1752         3514 :                         }
    1753         3514 :                         statement_begin = statements[statement_id].nodes_begin;
    1754         3514 :                         statement_end = statement_begin + statements[statement_id].node_count;
    1755              :                     }
    1756        14677 :                     if statement_id == statements.len() {
    1757            0 :                         break;
    1758        14677 :                     }
    1759        14677 :                     if statement_begin <= ast_node_id {
    1760        14535 :                         *statement = Some(statement_id as u32);
    1761        14535 :                     }
    1762              :                 }
    1763        15068 :             };
    1764         3776 :             assign(&mut state.out.table_references.iter_mut().map(|r| (r.ast_node_id, &mut r.ast_statement_id)));
    1765         6468 :             assign(&mut state.out.expressions.iter_mut().map(|e| (e.ast_node_id, &mut e.ast_statement_id)));
    1766         3767 :             let mut scope_statements: Vec<Option<u32>> = vec![None; state.out.name_scopes.len()];
    1767         3767 :             assign(
    1768         3767 :                 &mut state
    1769         3767 :                     .out
    1770         3767 :                     .name_scopes
    1771         3767 :                     .iter()
    1772         3767 :                     .zip(scope_statements.iter_mut())
    1773         5768 :                     .map(|(s, statement)| (s.ast_node_id, statement)),
    1774              :             );
    1775         5768 :             for (scope, statement) in state.out.name_scopes.iter_mut().zip(scope_statements) {
    1776         5765 :                 if statement.is_some() {
    1777         5765 :                     scope.ast_statement_id = statement;
    1778         5765 :                 }
    1779              :             }
    1780         3767 :             assign(
    1781         3767 :                 &mut Arc::make_mut(&mut state.out.entry)
    1782         3767 :                     .function_declarations
    1783         3767 :                     .iter_mut()
    1784         3767 :                     .filter_map(|f| Some((f.ast_node_id?, &mut f.ast_statement_id))),
    1785              :             );
    1786          236 :         }
    1787              : 
    1788              :         // Index the columns of the table declarations
    1789         4003 :         state.entry_mut().index_table_columns();
    1790              : 
    1791              :         // Solve the schema-inference constraints
    1792         4003 :         Self::run_schema_inference(state);
    1793         4003 :         Self::resolve_insert_target_columns(state);
    1794         4003 :         Self::compute_resolved_column_ids(state);
    1795         4003 :     }
    1796              : 
    1797              :     /// Bind explicit INSERT target columns after target-table resolution
    1798         4003 :     fn resolve_insert_target_columns(state: &mut AnalysisState) {
    1799         4003 :         for i in 0..state.out.insert_statements.len() {
    1800          253 :             let target = state.out.insert_statements[i].target;
    1801          253 :             let Some(resolved) = state.out.table_references[target as usize].inner.as_ref().and_then(|r| r.resolved_table.as_ref()) else {
    1802            3 :                 continue;
    1803              :             };
    1804          250 :             let table_id = resolved.catalog_table_id.unpack_table_id();
    1805          250 :             let loc = if table_id.origin == state.entry_id {
    1806           29 :                 state.out.entry.table_loc_by_id(table_id)
    1807              :             } else {
    1808          221 :                 state.catalog.entry(table_id.origin).and_then(|e| e.data().table_loc_by_id(table_id))
    1809              :             };
    1810          250 :             let Some(loc) = loc else { continue };
    1811          250 :             let Some(table) = state.table(loc) else { continue };
    1812          250 :             let resolved: Vec<Option<ColumnLoc>> = state.out.insert_statements[i]
    1813          250 :                 .target_columns
    1814          250 :                 .iter()
    1815          251 :                 .map(|c| {
    1816           12 :                     table.table_columns_by_name.get(&c.column_name.text).map(|&column| ColumnLoc {
    1817           10 :                         table: loc,
    1818           10 :                         column: column as u32,
    1819           10 :                     })
    1820           12 :                 })
    1821          250 :                 .collect();
    1822          251 :             for (column, resolved) in state.out.insert_statements[i].target_columns.iter_mut().zip(resolved) {
    1823           12 :                 if resolved.is_some() {
    1824           10 :                     column.resolved = resolved;
    1825           11 :                 }
    1826              :             }
    1827              :         }
    1828         4003 :     }
    1829              : 
    1830              :     /// The table column behind a resolved column, scope columns are followed through their source expressions
    1831         2851 :     fn resolved_table_column(state: &AnalysisState, resolution: ColumnResolution) -> Option<ColumnLoc> {
    1832         2851 :         let mut current = resolution;
    1833         2851 :         for _ in 0..256 {
    1834         2989 :             match current {
    1835          671 :                 ColumnResolution::Unresolved => return None,
    1836         1966 :                 ColumnResolution::TableColumn(loc) => return Some(loc),
    1837          352 :                 ColumnResolution::ScopeColumn { scope, column } => {
    1838          352 :                     let column = state.out.name_scopes[scope as usize].output_columns.get(column as usize)?;
    1839          352 :                     match column.source {
    1840          117 :                         ScopeColumnSource::TableColumn(loc) => return Some(loc),
    1841           11 :                         ScopeColumnSource::Computed => return None,
    1842          224 :                         ScopeColumnSource::Expression(expr) => {
    1843          224 :                             current = state.out.expressions[expr as usize].column_ref()?.resolved;
    1844              :                         }
    1845              :                     }
    1846              :                 }
    1847              :             }
    1848              :         }
    1849            0 :         None
    1850         2851 :     }
    1851              : 
    1852              :     /// The catalog ids of a resolved column
    1853         2715 :     fn resolved_column_ids(state: &AnalysisState, resolution: ColumnResolution) -> Option<ResolvedColumnIds> {
    1854         2715 :         let loc = Self::resolved_table_column(state, resolution)?;
    1855         1972 :         let table = state.table(loc.table)?;
    1856         1972 :         let column = table.table_columns.get(loc.column as usize)?;
    1857         1972 :         Some(ResolvedColumnIds {
    1858         1972 :             catalog_schema_id: table.catalog_schema_id,
    1859         1972 :             catalog_table_column_id: column.object_id,
    1860         1972 :             referenced_catalog_version: table.catalog_version,
    1861         1972 :         })
    1862         2715 :     }
    1863              : 
    1864         4003 :     fn compute_resolved_column_ids(state: &mut AnalysisState) {
    1865         6599 :         for i in 0..state.out.expressions.len() {
    1866         6332 :             let Some(column_ref) = state.out.expressions[i].column_ref() else { continue };
    1867         2715 :             let ids = Self::resolved_column_ids(state, column_ref.resolved);
    1868         2715 :             state.out.expressions[i].column_ref_mut().unwrap().resolved_ids = ids;
    1869              :         }
    1870         4003 :     }
    1871              : 
    1872              :     /// Solve the schema-inference constraints into inferred table schemas
    1873         4003 :     fn run_schema_inference(state: &mut AnalysisState) {
    1874         4003 :         let constraints = std::mem::take(&mut state.out.inference_constraints);
    1875         4003 :         if constraints.is_empty() {
    1876         3887 :             return;
    1877          116 :         }
    1878          116 :         let out = &mut state.out;
    1879              : 
    1880          184 :         fn get_or_create_schema(out: &mut super::AnalyzedScript, table_name: &QualifiedTableName) -> usize {
    1881          184 :             let key = table_name.key();
    1882          184 :             if let Some(&i) = out.inferred_table_schemas_by_name.get(&key) {
    1883           60 :                 return i;
    1884          124 :             }
    1885          124 :             out.inferred_table_schemas.push(InferredTableSchema {
    1886          124 :                 table_name: table_name.clone(),
    1887          124 :                 confidence: InferenceConfidence::None,
    1888          124 :                 columns: Vec::new(),
    1889          124 :                 columns_by_name: HashMap::default(),
    1890          124 :             });
    1891          124 :             let i = out.inferred_table_schemas.len() - 1;
    1892          124 :             out.inferred_table_schemas_by_name.insert(key, i);
    1893          124 :             i
    1894          184 :         }
    1895          184 :         fn add_column(schema: &mut InferredTableSchema, column: &Name, confidence: InferenceConfidence) -> usize {
    1896          184 :             if let Some(&i) = schema.columns_by_name.get(&column.text) {
    1897           16 :                 let c = &mut schema.columns[i];
    1898           16 :                 c.confidence = c.confidence.max(confidence);
    1899           16 :                 schema.confidence = schema.confidence.max(confidence);
    1900           16 :                 return i;
    1901          168 :             }
    1902          168 :             let i = schema.columns.len();
    1903          168 :             schema.columns_by_name.insert(column.text.clone(), i);
    1904          168 :             schema.columns.push(InferredColumn {
    1905          168 :                 column_name: column.clone(),
    1906          168 :                 confidence,
    1907          168 :                 candidate_table_names: Vec::new(),
    1908          168 :             });
    1909          168 :             schema.confidence = schema.confidence.max(confidence);
    1910          168 :             i
    1911          184 :         }
    1912          117 :         let has_confident_column = |out: &super::AnalyzedScript, table_name: &QualifiedTableName, column: &str| {
    1913           22 :             let Some(&i) = out.inferred_table_schemas_by_name.get(&table_name.key()) else { return false };
    1914            2 :             let schema = &out.inferred_table_schemas[i];
    1915            2 :             schema
    1916            2 :                 .columns_by_name
    1917            2 :                 .get(column)
    1918            2 :                 .is_some_and(|&c| schema.columns[c].confidence == InferenceConfidence::Confident)
    1919           22 :         };
    1920          185 :         let write_owner = |out: &mut super::AnalyzedScript, expr: u32, owner: &InferenceCandidate, confidence| {
    1921          185 :             if let Some(column_ref) = out.expressions.get_mut(expr as usize).and_then(|e| e.column_ref_mut()) {
    1922          185 :                 column_ref.inferred = Some(InferredOwner {
    1923          185 :                     table_ref: owner.table_ref,
    1924          185 :                     table_name: owner.table_name.clone(),
    1925          185 :                     confidence,
    1926          185 :                 });
    1927          185 :             }
    1928          185 :         };
    1929              : 
    1930          116 :         let mut resolved = vec![false; constraints.len()];
    1931              : 
    1932              :         // Step 1: seed the forced constraints
    1933          185 :         for (i, c) in constraints.iter().enumerate() {
    1934          185 :             if c.kind == InferenceConstraintKind::Forced && !c.candidates.is_empty() {
    1935          174 :                 let candidate = &c.candidates[0];
    1936          174 :                 let schema = get_or_create_schema(out, &candidate.table_name);
    1937          174 :                 add_column(&mut out.inferred_table_schemas[schema], &c.column_name, InferenceConfidence::Confident);
    1938          174 :                 write_owner(out, c.expression_id, candidate, InferenceConfidence::Confident);
    1939          174 :                 resolved[i] = true;
    1940          174 :             }
    1941              :         }
    1942              : 
    1943              :         // Step 2: propagate over disjunctive constraints to a fixpoint. A disjunction is explained
    1944              :         // when exactly one candidate confidently owns its column.
    1945          116 :         let mut changed = true;
    1946          233 :         while changed {
    1947          117 :             changed = false;
    1948          187 :             for (i, c) in constraints.iter().enumerate() {
    1949          187 :                 if resolved[i] || c.kind != InferenceConstraintKind::Disjunctive {
    1950          176 :                     continue;
    1951           11 :                 }
    1952           11 :                 let owners: Vec<&InferenceCandidate> = c
    1953           11 :                     .candidates
    1954           11 :                     .iter()
    1955           22 :                     .filter(|candidate| has_confident_column(out, &candidate.table_name, &c.column_name.text))
    1956           11 :                     .collect();
    1957           11 :                 if owners.is_empty() {
    1958           10 :                     continue;
    1959            1 :                 }
    1960              :                 // Several owners make the use ambiguous, record a best guess
    1961            1 :                 let confidence = if owners.len() == 1 {
    1962            1 :                     InferenceConfidence::Confident
    1963              :                 } else {
    1964            0 :                     InferenceConfidence::Candidate
    1965              :                 };
    1966            1 :                 write_owner(out, c.expression_id, owners[0], confidence);
    1967            1 :                 resolved[i] = true;
    1968            1 :                 changed = true;
    1969              :             }
    1970              :         }
    1971              : 
    1972              :         // Step 3: assign the residual disjunctions to the candidate with the most inferred columns
    1973          185 :         for (i, c) in constraints.iter().enumerate() {
    1974          185 :             if resolved[i] || c.kind != InferenceConstraintKind::Disjunctive {
    1975          175 :                 continue;
    1976           10 :             }
    1977           10 :             if c.candidates.is_empty() || c.candidates.len() > MAX_TABLE_REF_AMBIGUITY {
    1978            0 :                 resolved[i] = true;
    1979            0 :                 continue;
    1980           10 :             }
    1981           10 :             let mut best = 0;
    1982           10 :             let mut best_columns = 0;
    1983           20 :             for (ci, candidate) in c.candidates.iter().enumerate() {
    1984           20 :                 let n = out
    1985           20 :                     .inferred_table_schemas_by_name
    1986           20 :                     .get(&candidate.table_name.key())
    1987           20 :                     .map(|&s| out.inferred_table_schemas[s].columns.len())
    1988           20 :                     .unwrap_or(0);
    1989           20 :                 if n > best_columns {
    1990            3 :                     best_columns = n;
    1991            3 :                     best = ci;
    1992           17 :                 }
    1993              :             }
    1994           10 :             let schema = get_or_create_schema(out, &c.candidates[best].table_name);
    1995           10 :             let schema = &mut out.inferred_table_schemas[schema];
    1996           10 :             let column = add_column(schema, &c.column_name, InferenceConfidence::Candidate);
    1997           20 :             for (ci, candidate) in c.candidates.iter().enumerate() {
    1998           20 :                 if ci != best {
    1999           10 :                     schema.columns[column].candidate_table_names.push(candidate.table_name.clone());
    2000           10 :                 }
    2001              :             }
    2002           10 :             write_owner(out, c.expression_id, &c.candidates[best], InferenceConfidence::Candidate);
    2003           10 :             resolved[i] = true;
    2004              :         }
    2005          116 :         state.out.inference_constraints = constraints;
    2006         4003 :     }
    2007              : }
        

Generated by: LCOV version 2.0-1