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

            Line data    Source code
       1              : //! The state shared between the analysis passes.
       2              : 
       3              : use super::types::*;
       4              : use super::AnalyzedScript;
       5              : use crate::catalog::*;
       6              : use dashql_buffers::analyzer::{AnalyzerErrorSeverity, AnalyzerErrorType, NameTag, SemanticNodeMarkerType};
       7              : use dashql_buffers::parser::{Node, NodeType, SymbolSpan};
       8              : use dashql_parser::names::NameId;
       9              : use dashql_parser::parser::context::resolve_text_span;
      10              : use dashql_parser::parser::ParsedScript;
      11              : use dashql_parser::scanner::ScannedScript;
      12              : use dashql_parser::span::TextSpan;
      13              : use crate::hash::HashMap;
      14              : use std::sync::Arc;
      15              : 
      16              : /// The state shared between the analysis passes of a script
      17              : pub(super) struct AnalysisState<'a> {
      18              :     /// The parsed script
      19              :     pub parsed: &'a ParsedScript,
      20              :     /// The AST
      21              :     pub ast: &'a [Node],
      22              :     /// The catalog
      23              :     pub catalog: &'a Catalog,
      24              :     /// The catalog entry id of the script
      25              :     pub entry_id: CatalogEntryId,
      26              :     /// The texts of the names
      27              :     pub name_texts: Vec<Arc<str>>,
      28              :     /// The output
      29              :     pub out: AnalyzedScript,
      30              :     /// The expression of every AST node, if it was translated
      31              :     pub expression_index: Vec<Option<ExprId>>,
      32              :     /// How often each qualified name was declared by a table so far
      33              :     table_name_occurrences: HashMap<QualifiedKey, u32>,
      34              :     /// How often each qualified name was declared by a function so far
      35              :     function_name_occurrences: HashMap<QualifiedKey, u32>,
      36              : }
      37              : 
      38              : impl<'a> AnalysisState<'a> {
      39         4003 :     pub fn new(parsed: &'a Arc<ParsedScript>, catalog: &'a Catalog) -> Self {
      40         4003 :         let scanned = parsed.scanned.clone();
      41         4003 :         let entry_id = scanned.external_id;
      42         4003 :         let mut names = scanned.names.clone();
      43         4003 :         let empty_name = names.register("", TextSpan::default());
      44         4003 :         let mut name_tags = vec![NameTag::NONE; names.len()];
      45         4003 :         name_tags[empty_name as usize] = NameTag(NameTag::DATABASE_NAME.0 | NameTag::SCHEMA_NAME.0);
      46        17810 :         let name_texts = names.iter().map(|n| Arc::<str>::from(n.text.as_str())).collect();
      47         4003 :         let node_count = parsed.nodes.len();
      48         4003 :         let out = AnalyzedScript {
      49         4003 :             parsed: parsed.clone(),
      50         4003 :             snapshot: catalog.snapshot().clone(),
      51         4003 :             entry: Arc::new(CatalogEntryData::new(entry_id, catalog.version())),
      52         4003 :             names,
      53         4003 :             name_tags,
      54         4003 :             name_objects: Vec::new(),
      55         4003 :             empty_name,
      56         4003 :             errors: Vec::new(),
      57         4003 :             node_markers: vec![SemanticNodeMarkerType::NONE; node_count],
      58         4003 :             table_references: Vec::new(),
      59         4003 :             insert_statements: Vec::new(),
      60         4003 :             expressions: Vec::new(),
      61         4003 :             function_arguments: Vec::new(),
      62         4003 :             name_scopes: Vec::new(),
      63         4003 :             name_scopes_by_root_node: Default::default(),
      64         4003 :             constant_expressions: Vec::new(),
      65         4003 :             parameters: Vec::new(),
      66         4003 :             client_function_calls: Vec::new(),
      67         4003 :             visualization_specs: Vec::new(),
      68         4003 :             board: None,
      69         4003 :             flow: None,
      70         4003 :             param_values: Vec::new(),
      71         4003 :             manual: false,
      72         4003 :             inference_constraints: Vec::new(),
      73         4003 :             inferred_table_schemas: Vec::new(),
      74         4003 :             inferred_table_schemas_by_name: Default::default(),
      75         4003 :             name_search_index: Default::default(),
      76         4003 :             table_parameters: None,
      77         4003 :         };
      78         4003 :         Self {
      79         4003 :             parsed,
      80         4003 :             ast: &parsed.nodes,
      81         4003 :             catalog,
      82         4003 :             entry_id,
      83         4003 :             name_texts,
      84         4003 :             out,
      85         4003 :             expression_index: vec![None; node_count],
      86         4003 :             table_name_occurrences: HashMap::default(),
      87         4003 :             function_name_occurrences: HashMap::default(),
      88         4003 :         }
      89         4003 :     }
      90              : 
      91              :     /// Reserve the id of the next table that the script declares, keyed by its name and the
      92              :     /// occurrence of the name among the tables of the script
      93         1943 :     pub fn reserve_table_id(&mut self, name: &QualifiedTableName) -> CatalogTableId {
      94         1943 :         let key = name.key();
      95         1943 :         let occurrence = self.table_name_occurrences.entry(key.clone()).or_insert(0);
      96         1943 :         let id = self.catalog.reserve_table_id(self.entry_id, &key, *occurrence);
      97         1943 :         *occurrence += 1;
      98         1943 :         id
      99         1943 :     }
     100              : 
     101              :     /// Reserve the id of the next function that the script declares, like `reserve_table_id`
     102           36 :     pub fn reserve_function_id(&mut self, name: &QualifiedFunctionName) -> CatalogFunctionId {
     103           36 :         let key = name.key();
     104           36 :         let occurrence = self.function_name_occurrences.entry(key.clone()).or_insert(0);
     105           36 :         let id = self.catalog.reserve_function_id(self.entry_id, &key, *occurrence);
     106           36 :         *occurrence += 1;
     107           36 :         id
     108           36 :     }
     109              : 
     110              :     /// Reserve the ids of the columns of a table, keyed by their names and their occurrences
     111         1957 :     pub fn reserve_column_ids<'n>(&self, table: CatalogTableId, names: impl Iterator<Item = &'n Arc<str>>) -> Vec<u32> {
     112         1957 :         let mut occurrences: HashMap<&Arc<str>, u32> = HashMap::default();
     113         1957 :         names
     114         4507 :             .map(|name| {
     115         4507 :                 let occurrence = occurrences.entry(name).or_insert(0);
     116         4507 :                 let id = self.catalog.reserve_column_id(table, name, *occurrence);
     117         4507 :                 *occurrence += 1;
     118         4507 :                 id
     119         4507 :             })
     120         1957 :             .collect()
     121         1957 :     }
     122              : 
     123              :     /// Finish the analysis: collect the names of the script for completion
     124         4003 :     pub fn into_script(mut self) -> AnalyzedScript {
     125         4003 :         let out = &mut self.out;
     126         4003 :         let entry_id = out.entry.catalog_entry_id;
     127         4003 :         let mut names: Vec<IndexedName> = out
     128         4003 :             .names
     129         4003 :             .iter()
     130         4003 :             .map(|n| IndexedName {
     131        17810 :                 text: self.name_texts[n.name_id as usize].clone(),
     132        17810 :                 occurrences: n.occurrences,
     133        17810 :                 location: n.location,
     134        17810 :                 tags: out.name_tags[n.name_id as usize],
     135        17810 :                 objects: Vec::new(),
     136        17810 :             })
     137         4003 :             .collect();
     138         9320 :         for &(entry, name, object) in &out.name_objects {
     139         9091 :             if entry == entry_id {
     140         9013 :                 names[name as usize].objects.push(object);
     141         9013 :             }
     142              :         }
     143         4003 :         out.name_search_index = Arc::new(NameSearchIndex::new(names));
     144         4003 :         self.out
     145         4003 :     }
     146              : 
     147              :     /// The declarations of the script, unshared while the analysis runs
     148        15371 :     pub fn entry_mut(&mut self) -> &mut CatalogEntryData {
     149        15371 :         Arc::make_mut(&mut self.out.entry)
     150        15371 :     }
     151              : 
     152         9659 :     pub fn scanned(&self) -> &ScannedScript {
     153         9659 :         &self.parsed.scanned
     154         9659 :     }
     155              : 
     156              :     /// The children of a node
     157        45737 :     pub fn children(&self, node: &Node) -> &'a [Node] {
     158        45737 :         let count = node.children_count() as usize;
     159        45737 :         if count == 0 {
     160            0 :             return &[];
     161        45737 :         }
     162        45737 :         let begin = node.children_begin_or_value() as usize;
     163        45737 :         &self.ast[begin..begin + count]
     164        45737 :     }
     165              : 
     166              :     /// The id of a node of the AST
     167        16256 :     pub fn node_id(&self, node: &Node) -> u32 {
     168        16256 :         ((node as *const Node as usize - self.ast.as_ptr() as usize) / std::mem::size_of::<Node>()) as u32
     169        16256 :     }
     170              : 
     171              :     /// A name of the script
     172        25141 :     pub fn name(&self, id: NameId) -> Name {
     173        25141 :         Name {
     174        25141 :             entry: self.entry_id,
     175        25141 :             id,
     176        25141 :             text: self.name_texts[id as usize].clone(),
     177        25141 :         }
     178        25141 :     }
     179              : 
     180              :     /// The location of an object of this script
     181         2651 :     pub fn loc(&self, index: usize) -> ObjectLoc {
     182         2651 :         ObjectLoc {
     183         2651 :             entry: self.entry_id,
     184         2651 :             index: index as u32,
     185         2651 :         }
     186         2651 :     }
     187              : 
     188              :     /// The empty name
     189        10310 :     pub fn empty_name(&self) -> Name {
     190        10310 :         self.name(self.out.empty_name)
     191        10310 :     }
     192              : 
     193              :     /// The name of a NAME node
     194         5368 :     pub fn node_name(&self, node: &Node) -> Name {
     195         5368 :         self.name(node.children_begin_or_value())
     196         5368 :     }
     197              : 
     198              :     /// Add analyzer tags to a name of the script
     199        13285 :     pub fn tag(&mut self, name: &Name, tag: NameTag) {
     200        13285 :         if name.entry == self.entry_id {
     201        13285 :             let tags = &mut self.out.name_tags[name.id as usize];
     202        13285 :             *tags = NameTag(tags.0 | tag.0);
     203        13285 :         }
     204        13285 :     }
     205              : 
     206              :     /// Remember an object a name resolves to
     207         9101 :     pub fn resolve_name(&mut self, name: &Name, object: CatalogObjectRef) {
     208         9101 :         self.out.name_objects.push((name.entry, name.id, object));
     209         9101 :     }
     210              : 
     211              :     /// Resolve a symbol span to a text span
     212         9659 :     pub fn text_span(&self, span: SymbolSpan) -> TextSpan {
     213         9659 :         let scanned = self.scanned();
     214         9659 :         resolve_text_span(&scanned.text, &scanned.symbols, span)
     215         9659 :     }
     216              : 
     217              :     /// The text at a symbol span
     218           70 :     pub fn text_at(&self, span: SymbolSpan) -> &str {
     219           70 :         let text = &self.parsed.scanned.text;
     220           70 :         let ts = self.text_span(span);
     221           70 :         let begin = (ts.offset as usize).min(text.len());
     222           70 :         let end = (begin + ts.length as usize).min(text.len());
     223           70 :         &text[begin..end]
     224           70 :     }
     225              : 
     226              :     /// Mark a node
     227         8619 :     pub fn mark(&mut self, node_id: u32, marker: SemanticNodeMarkerType) {
     228         8619 :         self.out.node_markers[node_id as usize] = marker;
     229         8619 :     }
     230              : 
     231              :     /// Add an expression
     232         6332 :     pub fn add_expression(&mut self, node_id: u32, location: SymbolSpan, inner: ExpressionInner) -> ExprId {
     233         6332 :         let id = self.out.expressions.len() as ExprId;
     234         6332 :         self.out.expressions.push(Expression {
     235         6332 :             expression_id: id,
     236         6332 :             ast_node_id: node_id,
     237         6332 :             location: Some(location),
     238         6332 :             ast_statement_id: None,
     239         6332 :             inner,
     240         6332 :             is_constant_expression: false,
     241         6332 :         });
     242         6332 :         id
     243         6332 :     }
     244              : 
     245              :     /// The expression of a node
     246         8876 :     pub fn expression_of(&self, node_id: u32) -> Option<ExprId> {
     247         8876 :         self.expression_index.get(node_id as usize).copied().flatten()
     248         8876 :     }
     249              : 
     250              :     /// Add an error at an AST node
     251           89 :     pub fn add_error(&mut self, error_type: AnalyzerErrorType, severity: AnalyzerErrorSeverity, node_id: u32, message: String) {
     252           89 :         let span = *self.ast[node_id as usize].symbol_span();
     253           89 :         let text_span = self.text_span(span);
     254           89 :         self.out.errors.push(AnalyzerError {
     255           89 :             error_type,
     256           89 :             severity,
     257           89 :             ast_node_id: Some(node_id),
     258           89 :             symbol_span: Some(span),
     259           89 :             text_span: Some(text_span),
     260           89 :             message,
     261           89 :         });
     262           89 :     }
     263              : 
     264              :     /// Read a name path
     265         8585 :     pub fn read_name_path(&self, node: &Node) -> Vec<NameId> {
     266         8585 :         let mut path = Vec::new();
     267         8585 :         if node.node_type() != NodeType::ARRAY {
     268            0 :             return path;
     269         8585 :         }
     270         9500 :         for child in self.children(node) {
     271         9500 :             match child.node_type() {
     272              :                 // Skip over trailing dots
     273           35 :                 NodeType::OBJECT_EXT_TRAILING_DOT => continue,
     274         9465 :                 NodeType::NAME => path.push(child.children_begin_or_value()),
     275              :                 // Not a plain name path
     276              :                 _ => {
     277            0 :                     path.clear();
     278            0 :                     break;
     279              :                 }
     280              :             }
     281              :         }
     282         8585 :         path
     283         8585 :     }
     284              : 
     285              :     /// Read a qualified table name
     286         5410 :     pub fn read_qualified_table_name(&mut self, node: Option<&Node>) -> Option<QualifiedTableName> {
     287         5410 :         let node = node?;
     288         4540 :         let path = self.read_name_path(node);
     289         4540 :         let ast_node_id = Some(self.node_id(node));
     290         4540 :         let (db, schema, table) = match path.len() {
     291          213 :             3 => (self.name(path[0]), self.name(path[1]), self.name(path[2])),
     292          104 :             2 => (self.empty_name(), self.name(path[0]), self.name(path[1])),
     293         4223 :             1 => (self.empty_name(), self.empty_name(), self.name(path[0])),
     294            0 :             _ => return None,
     295              :         };
     296         4540 :         if path.len() == 3 {
     297          213 :             self.tag(&db, NameTag::DATABASE_NAME);
     298         4328 :         }
     299         4540 :         if path.len() >= 2 {
     300          317 :             self.tag(&schema, NameTag::SCHEMA_NAME);
     301         4228 :         }
     302         4540 :         self.tag(&table, NameTag::TABLE_NAME);
     303         4540 :         Some(QualifiedTableName {
     304         4540 :             ast_node_id,
     305         4540 :             database_name: db,
     306         4540 :             schema_name: schema,
     307         4540 :             table_name: table,
     308         4540 :         })
     309         5410 :     }
     310              : 
     311              :     /// Read a qualified column name.
     312              :     /// Paths with more than two names access fields of a row-typed column.
     313         2715 :     pub fn read_qualified_column_name(&mut self, node: Option<&Node>) -> Option<QualifiedColumnName> {
     314         2715 :         let node = node?;
     315         2715 :         let path = self.read_name_path(node);
     316         2715 :         let ast_node_id = Some(self.node_id(node));
     317         2715 :         match path.len() {
     318            0 :             0 => None,
     319              :             1 => {
     320         2381 :                 let column = self.name(path[0]);
     321         2381 :                 self.tag(&column, NameTag::COLUMN_NAME);
     322         2381 :                 Some(QualifiedColumnName {
     323         2381 :                     ast_node_id,
     324         2381 :                     table_alias: None,
     325         2381 :                     column_name: column,
     326         2381 :                 })
     327              :             }
     328              :             _ => {
     329          334 :                 let alias = self.name(path[0]);
     330          334 :                 let column = self.name(path[1]);
     331          334 :                 self.tag(&alias, NameTag::TABLE_ALIAS);
     332          334 :                 self.tag(&column, NameTag::COLUMN_NAME);
     333          334 :                 Some(QualifiedColumnName {
     334          334 :                     ast_node_id,
     335          334 :                     table_alias: Some(alias),
     336          334 :                     column_name: column,
     337          334 :                 })
     338              :             }
     339              :         }
     340         2715 :     }
     341              : 
     342              :     /// Read a qualified function name
     343          381 :     pub fn read_qualified_function_name(&mut self, node: Option<&Node>) -> Option<QualifiedFunctionName> {
     344          381 :         let node = node?;
     345          381 :         let path = self.read_name_path(node);
     346          381 :         let ast_node_id = Some(self.node_id(node));
     347          381 :         let (db, schema, function) = match path.len() {
     348            5 :             3 => (self.name(path[0]), self.name(path[1]), self.name(path[2])),
     349            4 :             2 => (self.empty_name(), self.name(path[0]), self.name(path[1])),
     350          372 :             1 => (self.empty_name(), self.empty_name(), self.name(path[0])),
     351            0 :             _ => return None,
     352              :         };
     353          381 :         if path.len() == 3 {
     354            5 :             self.tag(&db, NameTag::DATABASE_NAME);
     355          376 :         }
     356          381 :         if path.len() >= 2 {
     357            9 :             self.tag(&schema, NameTag::SCHEMA_NAME);
     358          372 :         }
     359          381 :         self.tag(&function, NameTag::FUNCTION_NAME);
     360          381 :         Some(QualifiedFunctionName {
     361          381 :             ast_node_id,
     362          381 :             database_name: db,
     363          381 :             schema_name: schema,
     364          381 :             function_name: function,
     365          381 :         })
     366          381 :     }
     367              : 
     368              :     /// A table of this script or of the catalog
     369         8122 :     pub fn table(&self, loc: TableLoc) -> Option<&TableDeclaration> {
     370         8122 :         if loc.entry == self.entry_id {
     371          512 :             self.out.entry.table_declarations.get(loc.index as usize)
     372         7610 :         } else if loc.entry == crate::table_parameters::TABLE_PARAMETERS_ENTRY_ID {
     373           34 :             self.out.table_parameters.as_ref()?.table(loc)
     374              :         } else {
     375         7576 :             self.catalog.table(loc)
     376              :         }
     377         8122 :     }
     378              : 
     379              :     /// The statement that contains an AST node, None outside of statements
     380        10902 :     pub fn find_statement_id(&self, ast_node_id: u32) -> Option<u32> {
     381        10902 :         let mut root = ast_node_id;
     382              :         // Statement roots are their own parents
     383        10902 :         for _ in 0..self.ast.len() {
     384        16592 :             let parent = self.ast[root as usize].parent();
     385        16592 :             if parent as usize >= self.ast.len() || parent == root {
     386        10902 :                 break;
     387         5690 :             }
     388         5690 :             root = parent;
     389              :         }
     390        10902 :         let statements = &self.parsed.statements;
     391        18036 :         if let Some(i) = statements.iter().position(|s| s.root == root) {
     392        10893 :             return Some(i as u32);
     393            9 :         }
     394            9 :         statements
     395            9 :             .iter()
     396            9 :             .position(|s| ast_node_id >= s.nodes_begin && ast_node_id < s.nodes_begin + s.node_count)
     397            9 :             .map(|i| i as u32)
     398        10902 :     }
     399              : }
     400              : 
     401              : /// The literal type of a literal node type
     402         3178 : pub(super) fn literal_type(node_type: NodeType) -> dashql_buffers::algebra::LiteralType {
     403         3178 :     dashql_buffers::algebra::LiteralType((node_type.0 - 5) as u8)
     404         3178 : }
     405              : 
     406              : pub(super) use crate::utils::quote_identifier;
        

Generated by: LCOV version 2.0-1