LCOV - code coverage report
Current view: top level - dashql-core/src/catalog - entry.rs Coverage Total Hit
Test: DashQL 6e74948 Lines: 99.1 % 217 215
Test Date: 2026-11-06 17:46:50

            Line data    Source code
       1              : //! The declarations of a catalog entry.
       2              : //!
       3              : //! Analyzed scripts and descriptor pools both expose their declarations through [`CatalogEntryData`].
       4              : //! Names are shared strings, so qualified names stay valid across entries without borrowing the name
       5              : //! registries of other entries.
       6              : 
       7              : use super::ids::*;
       8              : use dashql_parser::span::TextSpan;
       9              : use crate::hash::HashMap;
      10              : use std::collections::BTreeMap;
      11              : use std::sync::Arc;
      12              : 
      13              : /// The id of a name within the names of its catalog entry
      14              : pub type NameId = u32;
      15              : 
      16              : /// A name of a catalog entry
      17              : #[derive(Clone, Debug)]
      18              : pub struct Name {
      19              :     /// The catalog entry that registered the name
      20              :     pub entry: CatalogEntryId,
      21              :     /// The name id within that entry
      22              :     pub id: NameId,
      23              :     /// The text
      24              :     pub text: Arc<str>,
      25              : }
      26              : 
      27              : impl Name {
      28          320 :     pub fn text(&self) -> &str {
      29          320 :         &self.text
      30          320 :     }
      31              : }
      32              : 
      33              : /// A qualified table name
      34              : #[derive(Clone, Debug)]
      35              : pub struct QualifiedTableName {
      36              :     /// The AST node id in the declaring script
      37              :     pub ast_node_id: Option<u32>,
      38              :     /// The database name, empty when not qualified
      39              :     pub database_name: Name,
      40              :     /// The schema name, empty when not qualified
      41              :     pub schema_name: Name,
      42              :     /// The table name
      43              :     pub table_name: Name,
      44              : }
      45              : 
      46              : /// The lookup key of a qualified name: (database, schema, table)
      47              : pub type QualifiedKey = (Arc<str>, Arc<str>, Arc<str>);
      48              : 
      49              : impl QualifiedTableName {
      50         9129 :     pub fn key(&self) -> QualifiedKey {
      51         9129 :         (self.database_name.text.clone(), self.schema_name.text.clone(), self.table_name.text.clone())
      52         9129 :     }
      53              : 
      54              :     /// The qualified name for debug output
      55           84 :     pub fn debug_string(&self) -> String {
      56           84 :         debug_string(&self.database_name.text, &self.schema_name.text, &self.table_name.text)
      57           84 :     }
      58              : }
      59              : 
      60          157 : fn debug_string(database: &str, schema: &str, name: &str) -> String {
      61          157 :     let mut out = String::new();
      62          157 :     if !database.is_empty() {
      63           20 :         out.push_str(database);
      64           20 :         out.push('.');
      65           20 :         out.push_str(schema);
      66           20 :         out.push('.');
      67          137 :     } else if !schema.is_empty() {
      68            7 :         out.push_str(schema);
      69            7 :         out.push('.');
      70          130 :     }
      71          157 :     out.push_str(name);
      72          157 :     out
      73          157 : }
      74              : 
      75              : /// A qualified column name
      76              : #[derive(Clone, Debug)]
      77              : pub struct QualifiedColumnName {
      78              :     /// The AST node id in the declaring script
      79              :     pub ast_node_id: Option<u32>,
      80              :     /// The table alias qualifying the column, if any
      81              :     pub table_alias: Option<Name>,
      82              :     /// The column name
      83              :     pub column_name: Name,
      84              : }
      85              : 
      86              : /// A qualified function name
      87              : #[derive(Clone, Debug)]
      88              : pub struct QualifiedFunctionName {
      89              :     /// The AST node id in the declaring script
      90              :     pub ast_node_id: Option<u32>,
      91              :     /// The database name, empty when not qualified
      92              :     pub database_name: Name,
      93              :     /// The schema name, empty when not qualified
      94              :     pub schema_name: Name,
      95              :     /// The function name
      96              :     pub function_name: Name,
      97              : }
      98              : 
      99              : impl QualifiedFunctionName {
     100           78 :     pub fn key(&self) -> QualifiedKey {
     101           78 :         (self.database_name.text.clone(), self.schema_name.text.clone(), self.function_name.text.clone())
     102           78 :     }
     103              : 
     104              :     /// The qualified name for debug output
     105           73 :     pub fn debug_string(&self) -> String {
     106           73 :         debug_string(&self.database_name.text, &self.schema_name.text, &self.function_name.text)
     107           73 :     }
     108              : }
     109              : 
     110              : /// A table column
     111              : #[derive(Clone, Debug)]
     112              : pub struct TableColumn {
     113              :     /// The qualified object id, packs the table id and the column index
     114              :     pub object_id: QualifiedCatalogObjectId,
     115              :     /// The table of the column
     116              :     pub table: Option<CatalogTableId>,
     117              :     /// The AST node id in the declaring script
     118              :     pub ast_node_id: Option<u32>,
     119              :     /// The column name
     120              :     pub column_name: Name,
     121              :     /// The text of the column definition in the declaring script, None for derived columns
     122              :     pub location: Option<TextSpan>,
     123              : }
     124              : 
     125              : impl TableColumn {
     126              :     /// The id of the column within its table, stable across versions of the declaring entry
     127         2675 :     pub fn column_id(&self) -> u32 {
     128         2675 :         self.object_id.unpack_table_column_id().1
     129         2675 :     }
     130              : }
     131              : 
     132              : /// A table declaration
     133              : #[derive(Clone, Debug)]
     134              : pub struct TableDeclaration {
     135              :     /// The qualified object id of the table
     136              :     pub object_id: QualifiedCatalogObjectId,
     137              :     /// The catalog version
     138              :     pub catalog_version: CatalogVersion,
     139              :     /// The schema of the table
     140              :     pub catalog_schema_id: QualifiedCatalogObjectId,
     141              :     /// The AST node id of the declaration in the declaring script
     142              :     pub ast_node_id: Option<u32>,
     143              :     /// The id of the declaring statement
     144              :     pub ast_statement_id: Option<u32>,
     145              :     /// The root node of the name scope of the defining SELECT, for derived tables
     146              :     pub ast_scope_root: Option<u32>,
     147              :     /// Declared by CREATE VIEW?
     148              :     pub is_view: bool,
     149              :     /// The qualified table name
     150              :     pub table_name: QualifiedTableName,
     151              :     /// The columns, in declaration order
     152              :     pub table_columns: Vec<TableColumn>,
     153              :     /// The column indexes by name
     154              :     pub table_columns_by_name: HashMap<Arc<str>, usize>,
     155              :     /// The text of the table name in the declaring script: the script of a session, or the script
     156              :     /// that a descriptor was built from
     157              :     pub location: Option<TextSpan>,
     158              : }
     159              : 
     160              : impl TableDeclaration {
     161         1992 :     pub fn new(schema: QualifiedCatalogObjectId, table_id: CatalogTableId, table_name: QualifiedTableName) -> Self {
     162         1992 :         Self {
     163         1992 :             object_id: QualifiedCatalogObjectId::table(table_id),
     164         1992 :             catalog_version: 0,
     165         1992 :             catalog_schema_id: schema,
     166         1992 :             ast_node_id: None,
     167         1992 :             ast_statement_id: None,
     168         1992 :             ast_scope_root: None,
     169         1992 :             is_view: false,
     170         1992 :             table_name,
     171         1992 :             table_columns: Vec::new(),
     172         1992 :             table_columns_by_name: HashMap::default(),
     173         1992 :             location: None,
     174         1992 :         }
     175         1992 :     }
     176              : 
     177         3444 :     pub fn table_id(&self) -> CatalogTableId {
     178         3444 :         self.object_id.unpack_table_id()
     179         3444 :     }
     180              : 
     181              :     /// Find a column by name
     182            4 :     pub fn column_by_name(&self, name: &str) -> Option<&TableColumn> {
     183            4 :         self.table_columns_by_name.get(name).map(|&i| &self.table_columns[i])
     184            4 :     }
     185              : 
     186              :     /// Find a column by its id
     187           66 :     pub fn column_by_id(&self, column_id: u32) -> Option<&TableColumn> {
     188           96 :         self.table_columns.iter().find(|c| c.column_id() == column_id)
     189           66 :     }
     190              : }
     191              : 
     192              : /// A function parameter
     193              : #[derive(Clone, Debug)]
     194              : pub struct FunctionParam {
     195              :     /// The AST node id in the declaring script
     196              :     pub ast_node_id: Option<u32>,
     197              :     /// The parameter name
     198              :     pub param_name: Name,
     199              :     /// The parameter type as written, empty when not specified
     200              :     pub param_type: Arc<str>,
     201              : }
     202              : 
     203              : /// A function declaration
     204              : #[derive(Clone, Debug)]
     205              : pub struct FunctionDeclaration {
     206              :     /// The qualified object id of the function
     207              :     pub object_id: QualifiedCatalogObjectId,
     208              :     /// The schema of the function
     209              :     pub catalog_schema_id: QualifiedCatalogObjectId,
     210              :     /// Whether the function is an aggregate function
     211              :     pub is_aggregate: bool,
     212              :     /// The AST node id in the declaring script, None for descriptor functions
     213              :     pub ast_node_id: Option<u32>,
     214              :     /// The id of the declaring statement
     215              :     pub ast_statement_id: Option<u32>,
     216              :     /// The qualified function name
     217              :     pub function_name: QualifiedFunctionName,
     218              :     /// The parameters, in declaration order
     219              :     pub params: Vec<FunctionParam>,
     220              :     /// The return type as written, empty when not specified
     221              :     pub return_type: Arc<str>,
     222              :     /// The text of the function name in the declaring script
     223              :     pub location: Option<TextSpan>,
     224              : }
     225              : 
     226              : impl FunctionDeclaration {
     227            9 :     pub fn function_id(&self) -> CatalogFunctionId {
     228            9 :         self.object_id.unpack_function_id()
     229            9 :     }
     230              : }
     231              : 
     232              : /// A referenced database
     233              : #[derive(Clone, Debug)]
     234              : pub struct DatabaseReference {
     235              :     /// The qualified object id of the database
     236              :     pub object_id: QualifiedCatalogObjectId,
     237              :     /// The database name
     238              :     pub database_name: Arc<str>,
     239              :     /// The database alias, empty when not aliased
     240              :     pub database_alias: Arc<str>,
     241              : }
     242              : 
     243              : impl DatabaseReference {
     244           10 :     pub fn database_id(&self) -> CatalogDatabaseId {
     245           10 :         self.object_id.unpack_database_id()
     246           10 :     }
     247              : }
     248              : 
     249              : /// A referenced schema
     250              : #[derive(Clone, Debug)]
     251              : pub struct SchemaReference {
     252              :     /// The qualified object id, packs the database and schema ids
     253              :     pub object_id: QualifiedCatalogObjectId,
     254              :     /// The database name
     255              :     pub database_name: Arc<str>,
     256              :     /// The schema name
     257              :     pub schema_name: Arc<str>,
     258              : }
     259              : 
     260              : impl SchemaReference {
     261            8 :     pub fn database_id(&self) -> CatalogDatabaseId {
     262            8 :         self.object_id.unpack_schema_id().0
     263            8 :     }
     264            8 :     pub fn schema_id(&self) -> CatalogSchemaId {
     265            8 :         self.object_id.unpack_schema_id().1
     266            8 :     }
     267              : }
     268              : 
     269              : /// The location of a table: catalog entry and index of the declaration
     270              : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
     271              : pub struct TableLoc {
     272              :     /// The catalog entry that declares the table
     273              :     pub entry: CatalogEntryId,
     274              :     /// The index in the table declarations of the entry
     275              :     pub index: u32,
     276              : }
     277              : 
     278              : /// The location of a table column
     279              : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
     280              : pub struct ColumnLoc {
     281              :     /// The table of the column
     282              :     pub table: TableLoc,
     283              :     /// The index in the columns of the table
     284              :     pub column: u32,
     285              : }
     286              : 
     287              : /// The location of an object in the vectors of a catalog entry
     288              : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
     289              : pub struct ObjectLoc {
     290              :     /// The catalog entry that declares the object
     291              :     pub entry: CatalogEntryId,
     292              :     /// The index in the declaration vector of the object kind
     293              :     pub index: u32,
     294              : }
     295              : 
     296              : /// A catalog object: a declaration of a catalog entry
     297              : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
     298              : pub enum CatalogObjectRef {
     299              :     Database(ObjectLoc),
     300              :     Schema(ObjectLoc),
     301              :     Table(TableLoc),
     302              :     Column(ColumnLoc),
     303              :     Function(ObjectLoc),
     304              : }
     305              : 
     306              : impl CatalogObjectRef {
     307              :     /// The entry that declares the object
     308          216 :     pub fn entry(&self) -> CatalogEntryId {
     309          216 :         match self {
     310           27 :             CatalogObjectRef::Database(l) | CatalogObjectRef::Schema(l) | CatalogObjectRef::Function(l) => l.entry,
     311           60 :             CatalogObjectRef::Table(t) => t.entry,
     312          129 :             CatalogObjectRef::Column(c) => c.table.entry,
     313              :         }
     314          216 :     }
     315              : }
     316              : 
     317              : /// The declarations of a catalog entry and their indexes
     318              : #[derive(Clone, Debug, Default)]
     319              : pub struct CatalogEntryData {
     320              :     /// The id of the catalog entry
     321              :     pub catalog_entry_id: CatalogEntryId,
     322              :     /// The catalog version when the entry was created
     323              :     pub catalog_version: CatalogVersion,
     324              :     /// The databases the entry references, in registration order
     325              :     pub database_references: Vec<DatabaseReference>,
     326              :     /// The schemas the entry references, in registration order
     327              :     pub schema_references: Vec<SchemaReference>,
     328              :     /// The tables the entry declares, in declaration order
     329              :     pub table_declarations: Vec<TableDeclaration>,
     330              :     /// The functions the entry declares, in declaration order
     331              :     pub function_declarations: Vec<FunctionDeclaration>,
     332              :     /// Database references by name
     333              :     pub databases_by_name: HashMap<Arc<str>, usize>,
     334              :     /// Schema references by (database, schema)
     335              :     pub schemas_by_qualified_name: BTreeMap<(Arc<str>, Arc<str>), usize>,
     336              :     /// Tables by (database, schema, table)
     337              :     pub tables_by_qualified_name: HashMap<QualifiedKey, usize>,
     338              :     /// Tables by name, in declaration order
     339              :     pub tables_by_unqualified_name: BTreeMap<Arc<str>, Vec<usize>>,
     340              :     /// Tables by (schema, database), in declaration order
     341              :     pub tables_by_unqualified_schema: BTreeMap<(Arc<str>, Arc<str>), Vec<usize>>,
     342              :     /// Table columns by name, in declaration order
     343              :     pub table_columns_by_name: BTreeMap<Arc<str>, Vec<ColumnLoc>>,
     344              :     /// Functions by (database, schema, function)
     345              :     pub functions_by_qualified_name: HashMap<QualifiedKey, usize>,
     346              :     /// Functions by name, in declaration order
     347              :     pub functions_by_unqualified_name: BTreeMap<Arc<str>, Vec<usize>>,
     348              :     /// Table indexes by table id
     349              :     pub tables_by_id: HashMap<u32, usize>,
     350              : }
     351              : 
     352              : impl CatalogEntryData {
     353         3778 :     pub fn new(catalog_entry_id: CatalogEntryId, catalog_version: CatalogVersion) -> Self {
     354         3778 :         Self {
     355         3778 :             catalog_entry_id,
     356         3778 :             catalog_version,
     357         3778 :             ..Default::default()
     358         3778 :         }
     359         3778 :     }
     360              : 
     361              :     /// Does another version of the entry declare the same objects?
     362              :     ///
     363              :     /// Compares what other entries resolve against: the ids and names of the databases, schemas,
     364              :     /// tables, columns and functions. AST positions and name ids are ignored, only the declaring
     365              :     /// script reads them.
     366          214 :     pub fn same_declarations(&self, other: &CatalogEntryData) -> bool {
     367          205 :         fn same_table_name(a: &QualifiedTableName, b: &QualifiedTableName) -> bool {
     368          205 :             a.key() == b.key()
     369          205 :         }
     370          203 :         fn same_column(a: &TableColumn, b: &TableColumn) -> bool {
     371          203 :             a.object_id == b.object_id && a.table == b.table && a.column_name.text == b.column_name.text
     372          203 :         }
     373          207 :         fn same_table(a: &TableDeclaration, b: &TableDeclaration) -> bool {
     374          207 :             a.object_id == b.object_id
     375          205 :                 && a.catalog_version == b.catalog_version
     376          205 :                 && a.catalog_schema_id == b.catalog_schema_id
     377          205 :                 && a.is_view == b.is_view
     378          205 :                 && same_table_name(&a.table_name, &b.table_name)
     379          205 :                 && a.table_columns.len() == b.table_columns.len()
     380          203 :                 && a.table_columns.iter().zip(&b.table_columns).all(|(a, b)| same_column(a, b))
     381          207 :         }
     382            6 :         fn same_param(a: &FunctionParam, b: &FunctionParam) -> bool {
     383            6 :             a.param_name.text == b.param_name.text && a.param_type == b.param_type
     384            6 :         }
     385            8 :         fn same_function(a: &FunctionDeclaration, b: &FunctionDeclaration) -> bool {
     386            8 :             a.object_id == b.object_id
     387            8 :                 && a.catalog_schema_id == b.catalog_schema_id
     388            8 :                 && a.is_aggregate == b.is_aggregate
     389            7 :                 && a.function_name.key() == b.function_name.key()
     390            7 :                 && a.return_type == b.return_type
     391            6 :                 && a.params.len() == b.params.len()
     392            6 :                 && a.params.iter().zip(&b.params).all(|(a, b)| same_param(a, b))
     393            8 :         }
     394          851 :         fn same<T>(a: &[T], b: &[T], eq: impl Fn(&T, &T) -> bool) -> bool {
     395          851 :             a.len() == b.len() && a.iter().zip(b).all(|(a, b)| eq(a, b))
     396          851 :         }
     397          214 :         self.catalog_entry_id == other.catalog_entry_id
     398          214 :             && same(&self.database_references, &other.database_references, |a, b| {
     399          214 :                 a.object_id == b.object_id && a.database_name == b.database_name && a.database_alias == b.database_alias
     400          214 :             })
     401          214 :             && same(&self.schema_references, &other.schema_references, |a, b| {
     402          214 :                 a.object_id == b.object_id && a.database_name == b.database_name && a.schema_name == b.schema_name
     403          214 :             })
     404          214 :             && same(&self.table_declarations, &other.table_declarations, same_table)
     405          209 :             && same(&self.function_declarations, &other.function_declarations, same_function)
     406          214 :     }
     407              : 
     408              :     /// The location of a table of this entry
     409         2450 :     pub fn table_loc(&self, index: usize) -> TableLoc {
     410         2450 :         TableLoc {
     411         2450 :             entry: self.catalog_entry_id,
     412         2450 :             index: index as u32,
     413         2450 :         }
     414         2450 :     }
     415              : 
     416              :     /// The id of an object of this entry
     417          216 :     pub fn object_id(&self, object: CatalogObjectRef) -> QualifiedCatalogObjectId {
     418          216 :         match object {
     419           13 :             CatalogObjectRef::Database(l) => self.database_references[l.index as usize].object_id,
     420            3 :             CatalogObjectRef::Schema(l) => self.schema_references[l.index as usize].object_id,
     421           60 :             CatalogObjectRef::Table(t) => self.table_declarations[t.index as usize].object_id,
     422          129 :             CatalogObjectRef::Column(c) => self.table_declarations[c.table.index as usize].table_columns[c.column as usize].object_id,
     423           11 :             CatalogObjectRef::Function(l) => self.function_declarations[l.index as usize].object_id,
     424              :         }
     425          216 :     }
     426              : 
     427              :     /// A table by id
     428          172 :     pub fn table_by_id(&self, table_id: CatalogTableId) -> Option<&TableDeclaration> {
     429          172 :         self.table_loc_by_id(table_id).map(|loc| &self.table_declarations[loc.index as usize])
     430          172 :     }
     431              : 
     432              :     /// The location of a table by id
     433          423 :     pub fn table_loc_by_id(&self, table_id: CatalogTableId) -> Option<TableLoc> {
     434          423 :         if table_id.origin != self.catalog_entry_id {
     435            0 :             return None;
     436          423 :         }
     437          423 :         self.tables_by_id.get(&table_id.object).map(|&index| self.table_loc(index))
     438          423 :     }
     439              : 
     440              :     /// Resolve a table by name
     441         2383 :     pub fn resolve_table(&self, name: &QualifiedTableName, out: &mut Vec<TableLoc>, limit: usize) {
     442         2383 :         if let Some(&i) = self.tables_by_qualified_name.get(&name.key()) {
     443          153 :             out.push(self.table_loc(i));
     444          153 :             return;
     445         2230 :         }
     446         2230 :         if name.database_name.text.is_empty() {
     447         2130 :             if name.schema_name.text.is_empty() {
     448         2084 :                 self.resolve_table_everywhere(&name.table_name.text, out, limit);
     449         2084 :             } else {
     450           46 :                 self.resolve_table_in_schema(&name.schema_name.text, &name.table_name.text, out, limit);
     451           46 :             }
     452          100 :         }
     453         2383 :     }
     454              : 
     455              :     /// Resolve a table by schema and name across databases
     456           52 :     pub fn resolve_table_in_schema(&self, schema_name: &str, table_name: &str, out: &mut Vec<TableLoc>, limit: usize) {
     457           52 :         let lower = (Arc::<str>::from(schema_name), Arc::<str>::from(""));
     458           52 :         for ((schema, _), tables) in self.tables_by_unqualified_schema.range(lower..) {
     459           14 :             if &**schema != schema_name {
     460            6 :                 break;
     461            8 :             }
     462           11 :             for &i in tables {
     463           11 :                 if &*self.table_declarations[i].table_name.table_name.text != table_name {
     464           10 :                     continue;
     465            1 :                 }
     466            1 :                 out.push(self.table_loc(i));
     467            1 :                 if out.len() >= limit {
     468            0 :                     return;
     469            1 :                 }
     470              :             }
     471              :         }
     472           52 :     }
     473              : 
     474              :     /// Resolve a table by name only
     475         2218 :     pub fn resolve_table_everywhere(&self, table_name: &str, out: &mut Vec<TableLoc>, limit: usize) {
     476         2218 :         if let Some(tables) = self.tables_by_unqualified_name.get(table_name) {
     477           57 :             for &i in tables {
     478           57 :                 out.push(self.table_loc(i));
     479           57 :                 if out.len() >= limit {
     480            1 :                     return;
     481           56 :                 }
     482              :             }
     483         2167 :         }
     484         2218 :     }
     485              : 
     486              :     /// The table columns with a name
     487          115 :     pub fn resolve_table_columns(&self, column_name: &str, out: &mut Vec<ColumnLoc>) {
     488          115 :         if let Some(columns) = self.table_columns_by_name.get(column_name) {
     489           13 :             out.extend_from_slice(columns);
     490          102 :         }
     491          115 :     }
     492              : 
     493              :     /// Index the tables and functions (the first step of `NameResolutionPass::finish`)
     494         3734 :     pub fn index_declarations(&mut self) {
     495         3971 :         for (i, table) in self.table_declarations.iter().enumerate() {
     496         1889 :             self.tables_by_id.insert(table.table_id().object, i);
     497         1889 :             let name = &table.table_name;
     498         1889 :             self.tables_by_qualified_name.entry(name.key()).or_insert(i);
     499         1889 :             self.tables_by_unqualified_name.entry(name.table_name.text.clone()).or_default().push(i);
     500         1889 :             if !name.schema_name.text.is_empty() {
     501          142 :                 self.tables_by_unqualified_schema
     502          142 :                     .entry((name.schema_name.text.clone(), name.database_name.text.clone()))
     503          142 :                     .or_default()
     504          142 :                     .push(i);
     505         1751 :             }
     506              :         }
     507         3734 :         for (i, function) in self.function_declarations.iter().enumerate() {
     508           30 :             let name = &function.function_name;
     509           30 :             self.functions_by_qualified_name.entry(name.key()).or_insert(i);
     510           30 :             self.functions_by_unqualified_name.entry(name.function_name.text.clone()).or_default().push(i);
     511           30 :         }
     512         3734 :     }
     513              : 
     514              :     /// Index the columns of all tables by name
     515         3734 :     pub fn index_table_columns(&mut self) {
     516         3971 :         for (ti, table) in self.table_declarations.iter().enumerate() {
     517         4172 :             for (ci, column) in table.table_columns.iter().enumerate() {
     518         4172 :                 self.table_columns_by_name.entry(column.column_name.text.clone()).or_default().push(ColumnLoc {
     519         4172 :                     table: TableLoc {
     520         4172 :                         entry: self.catalog_entry_id,
     521         4172 :                         index: ti as u32,
     522         4172 :                     },
     523         4172 :                     column: ci as u32,
     524         4172 :                 });
     525         4172 :             }
     526              :         }
     527         3734 :     }
     528              : }
        

Generated by: LCOV version 2.0-1