glimr/db/gen/schema_parser
Schema Parser
Schema files are just Gleam code — table("users", [ id(), string("email"), ... ]) — which means we’re parsing Gleam
source text, not a custom DSL. This module extracts table
names, column definitions, modifiers like nullable() and
default(), and index declarations from that source text so
the code generator and migration differ have a structured
representation to work with.
Types
Carries everything needed to generate both the Gleam type field and the SQL column definition. The renamed_from field is transient — it only exists during migration generation so the differ can emit RENAME instead of drop+add.
pub type Column {
Column(
name: String,
column_type: ColumnType,
nullable: Bool,
default: option.Option(DefaultValue),
renamed_from: option.Option(String),
)
}
Constructors
-
Column( name: String, column_type: ColumnType, nullable: Bool, default: option.Option(DefaultValue), renamed_from: option.Option(String), )
The codegen module maps each variant to a Gleam type, a decoder function, and a JSON encoder. The SQL module maps them to driver-specific DDL types. Adding a new variant here means updating both modules — the compiler will tell you everywhere you missed.
pub type ColumnType {
Id
String
Text
Int
SmallInt
BigInt
Float
Boolean
Timestamp
UnixTimestamp
Date
Json
Uuid
Foreign(
table: String,
on_delete: option.Option(ForeignAction),
on_update: option.Option(ForeignAction),
)
Array(ColumnType)
Enum(name: String, variants: List(String))
Decimal(precision: Int, scale: Int)
Blob
Time
}
Constructors
-
Id -
String -
Text -
Int -
SmallInt -
BigInt -
Float -
Boolean -
Timestamp -
UnixTimestamp -
Date -
Json -
Uuid -
Foreign( table: String, on_delete: option.Option(ForeignAction), on_update: option.Option(ForeignAction), ) -
Array(ColumnType) -
Enum(name: String, variants: List(String)) -
Decimal(precision: Int, scale: Int) -
Blob -
Time
Each variant maps to a specific SQL DEFAULT clause that may differ between Postgres and SQLite. DefaultNow becomes CURRENT_TIMESTAMP everywhere, but DefaultAutoUuid needs gen_random_uuid() on Postgres and a randomblob hack on SQLite.
pub type DefaultValue {
DefaultBool(Bool)
DefaultString(String)
DefaultInt(Int)
DefaultFloat(Float)
DefaultNow
DefaultUnixNow
DefaultAutoUuid
DefaultNull
DefaultEmptyArray
}
Constructors
-
DefaultBool(Bool) -
DefaultString(String) -
DefaultInt(Int) -
DefaultFloat(Float) -
DefaultNow -
DefaultUnixNow -
DefaultAutoUuid -
DefaultNull -
DefaultEmptyArray
What should the database do when a referenced row is deleted or updated? Without specifying this, most databases default to RESTRICT (block the operation), which is safe but rigid. Cascade deletes child rows automatically, SetNull clears the FK, and NoAction defers the check to transaction commit time.
pub type ForeignAction {
Cascade
Restrict
SetNull
SetDefault
NoAction
}
Constructors
-
Cascade -
Restrict -
SetNull -
SetDefault -
NoAction
The parsed representation of an index(["col"]) or
unique(["col"]) |> named("custom") call from a schema
file. The migration system compares these against the
previous snapshot to detect added or removed indexes.
pub type Index {
Index(
columns: List(String),
unique: Bool,
name: option.Option(String),
)
}
Constructors
-
Index( columns: List(String), unique: Bool, name: option.Option(String), )
Column definitions in schema files are pipe chains like
string("email") |> nullable() |> array(). Rather than
threading all these flags through individual parse
functions, we extract everything into one record up front.
This keeps parse_column_item clean — it just reads the
modifiers and applies them to the base column.
pub type Modifiers {
Modifiers(
base: String,
nullable: Bool,
default: option.Option(DefaultValue),
renamed_from: option.Option(String),
array_depth: Int,
on_delete: option.Option(ForeignAction),
on_update: option.Option(ForeignAction),
enum_name_override: option.Option(String),
)
}
Constructors
-
Modifiers( base: String, nullable: Bool, default: option.Option(DefaultValue), renamed_from: option.Option(String), array_depth: Int, on_delete: option.Option(ForeignAction), on_update: option.Option(ForeignAction), enum_name_override: option.Option(String), )
Values
pub fn columns(table: Table) -> List(Column)
Accessor for the column list. Table is opaque to downstream modules, so the code generator and migration differ use this instead of reaching into the record directly.