Skip to main content

Module sql

Module sql 

Source
Expand description

SQL DDL in the format-neutral model: a small CREATE TABLE subset.

parse reads a file of statements into a Schema of tables, one per CREATE TABLE, each column a Field with its type as written (VARCHAR(20), kept as the native type) and the model’s type for it (string). It reads:

  • column names and types, quoted ("x", `x`, [x]) or not, with their parameters (NUMERIC(10, 2)), several words (DOUBLE PRECISION, TIMESTAMP WITH TIME ZONE) and arrays (INT[]);
  • NOT NULL and NULL, DEFAULT (any expression, kept as written), PRIMARY KEY, UNIQUE and REFERENCES table (column), on a column or, for one or several columns, on the table (PRIMARY KEY (a, b), UNIQUE (a, b), FOREIGN KEY (a) REFERENCES t (x)), named with CONSTRAINT name or not, and MySQL’s ENUM('a', 'b') and COMMENT;
  • -- and /* … */ comments, PostgreSQL’s dollar-quoted strings ($$ … $$, a function body’s), and any number of statements, each ended by ; or, in a script that leaves it out, by a GO line or the CREATE that starts the next.

CHECK constraints, indexes, generated columns and other clauses are skipped, as is every statement but CREATE TABLE (CREATE INDEX, ALTER TABLE, INSERT, …); each skip is a Note with its line, so nothing is dropped silently. Text that cannot be read (an unclosed string, comment or parenthesis, a CREATE TABLE without a column list) is a SqlError with its line. Input is bounded by MAX_INPUT, MAX_STATEMENTS, MAX_TOKENS, MAX_COLUMNS and MAX_NESTING, statements are read one at a time, and the reader never panics.

use rich_ext::schema::{sql, DataType};

let ddl = "
    -- The people.
    CREATE TABLE users (
        id    BIGINT PRIMARY KEY,
        email VARCHAR(255) NOT NULL UNIQUE,
        team  INT REFERENCES teams (id),
        CHECK (email <> '')
    );
    CREATE INDEX users_email ON users (email);
";
let parsed = sql::parse(ddl).unwrap();
let users = parsed.schema.table("users").unwrap();
let email = users.field("email").unwrap();
assert_eq!(email.native_type(), Some("VARCHAR(255)"));
assert_eq!(email.data_type(), &DataType::String);
assert!(email.is_required() && email.is_unique());
assert_eq!(users.primary_key(), ["id"]);
assert_eq!(users.foreign_keys()[0].to_string(), "team → teams.id");
let notes: Vec<String> = parsed.notes.iter().map(ToString::to_string).collect();
assert_eq!(
    notes,
    [
        "line 7: users: CHECK constraint skipped",
        "line 9: CREATE INDEX statement skipped",
    ]
);

Structs§

Note
Something skipped, and on which line.
Parsed
DDL read into the model: a schema of tables, and what was skipped.
SqlError
Why DDL could not be read, and on which line.

Constants§

MAX_COLUMNS
The most columns (and table constraints) in one table, and the most columns one key names.
MAX_INPUT
The most bytes of DDL read.
MAX_NESTING
The deepest parentheses nest.
MAX_NOTES
The most notes kept; past this they are counted in a last one.
MAX_STATEMENTS
The most statements read.
MAX_TOKENS
The most tokens (words, names, values, punctuation) in one statement. Only a CREATE TABLE statement’s are kept, one statement at a time.

Functions§

parse
Read DDL. See the module docs.