Skip to main content

weavatrix_parse/sql/
mod.rs

1//! Structural extraction for SQL.
2//!
3//! SQL declares objects rather than functions and depends on other objects by
4//! name rather than by path, so the fact shapes mean something slightly
5//! different here: a `CREATE` is a declaration, and every table a statement
6//! reads or writes is an import whose specifier is the object name. That is
7//! what makes a view resolvable to the file that creates the table it selects
8//! from, which is the edge repository intelligence actually wants.
9//!
10//! Keywords are matched case-insensitively because SQL is written both ways,
11//! often in the same file.
12
13use crate::facts::{Declaration, DeclarationKind, Facts, Import, Reference, ReferenceKind, Span};
14use crate::syntax::Language;
15use crate::token::{Mode, Token, TokenKind, Tokenizer};
16
17/// Extracts structural facts from one SQL source.
18#[must_use]
19pub fn extract(source: &str) -> Facts {
20    let tokens = Tokenizer::new(source, Language::Sql)
21        .mode(Mode::Lite)
22        .collect::<Vec<_>>();
23    let mut state = Extractor {
24        source,
25        tokens: &tokens,
26        facts: Facts::default(),
27        object: None,
28    };
29    state.run();
30    state.facts
31}
32
33/// The object keyword to the kind it declares.
34const OBJECTS: &[(&str, DeclarationKind)] = &[
35    ("table", DeclarationKind::Table),
36    ("view", DeclarationKind::View),
37    ("function", DeclarationKind::Function),
38    ("procedure", DeclarationKind::Procedure),
39    ("trigger", DeclarationKind::Procedure),
40    ("schema", DeclarationKind::Module),
41    ("type", DeclarationKind::TypeAlias),
42];
43
44/// Words that qualify a `CREATE` without naming what it creates.
45const CREATE_MODIFIERS: &[&str] = &[
46    "or",
47    "replace",
48    "temp",
49    "temporary",
50    "unique",
51    "materialized",
52    "global",
53    "local",
54    "if",
55    "not",
56    "exists",
57];
58
59/// Keywords a referenced object name follows.
60const REFERENCES: &[&str] = &["from", "join", "into", "update", "references", "on"];
61
62/// Words that read as an object name but never are one.
63const NOT_A_NAME: &[&str] = &[
64    "select",
65    "lateral",
66    "only",
67    "delete",
68    "conflict",
69    "duplicate",
70    "set",
71    "values",
72    "all",
73    "distinct",
74];
75
76struct Extractor<'source, 'tokens> {
77    source: &'source str,
78    tokens: &'tokens [Token],
79    facts: Facts,
80    /// The object being created, which owns everything until the statement ends.
81    object: Option<String>,
82}
83
84mod extractor;
85
86#[cfg(test)]
87mod tests;