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 : }
|