Getting started
Gelite compiles a Gel-like schema and query language to SQLite. The current CLI can plan or apply an initial schema and can compile or execute queries through the REPL.
Apply the example schema
From the repository root:
cargo run -p gelite-cli -- schema apply examples/organization.geli --database organization.db
This creates a SQLite database for the organization example. Schema application is for a new database; migration diffing and migration history are not implemented.
Open the REPL
cargo run -p gelite-cli -- repl --database organization.db
Enter each query without a trailing semicolon. Regular Enter continues while
braces are unbalanced. Alt+Enter inserts a newline without submitting input.
Use --schema instead when only compilation and rendered SQL are needed:
cargo run -p gelite-cli -- repl --schema examples/organization.geli --debug \
'select Employee { name } filter .active = true'
The --schema mode does not open a database or execute the query. The
--database mode loads the catalog stored in the database and executes the
current select, insert, update, and delete subsets.
Continue with the Examples overview and choose one of the three runnable domains.
Build this documentation
Install the same mdBook version used by CI, then run:
cargo install mdbook --version 0.5.4 --locked
mdbook serve docs
The Markdown under docs/src is also readable directly on GitHub.
Examples
The examples are small adaptations of familiar SQL teaching domains. Together they cover the current Gelite schema, query, and execution surface without requiring one large application model.
| Example | Model | Main query cases |
|---|---|---|
| Organization | departments and employees | optional self-links, null, nested shapes, boolean filters |
| Store | customers, products, orders, and line items | association objects, arithmetic, transactions, membership filters |
| Music catalog | artists, albums, tracks, and playlists | deep path traversal, string functions, ordering, pagination |
Character names are drawn from Magical Girl Witch Trials. Roles, departments, products, albums, and other values are fictional, spoiler-free sample data made for these examples.
Organization
This EMP/DEPT-style example demonstrates a required department link and an
optional self-referencing manager link.
type Department {
required unique code: str
required name: str
}
type Employee {
required unique employee_no: str
required name: str
required title: str
required salary: int64
required active: bool
required hired_at: datetime
required link department: Department
link manager: Employee
}
Create data
Apply the schema to a new database:
cargo run -p gelite-cli -- schema apply examples/organization.geli --database organization.db
Open gelite repl --database organization.db, then insert two departments and
keep their generated IDs:
insert Department { code := "INVESTIGATION", name := "Investigation" }
insert Department { code := "ARCHIVE", name := "Records Archive" }
Insert the manager first and keep her generated ID:
insert Employee {
employee_no := "MG-667",
name := "Sheri Tachibana",
title := "Chief Investigator",
salary := 92000,
active := true,
hired_at := "2026-04-01T09:00:00Z",
department := "<investigation-department-id>"
}
insert Employee {
employee_no := "MG-001",
name := "Emma Sakuraba",
title := "Investigator",
salary := 68000,
active := true,
hired_at := "2026-04-15T09:00:00Z",
department := "<investigation-department-id>",
manager := "<sheri-id>"
}
insert Employee {
employee_no := "MG-002",
name := "Hiro Nikaido",
title := "Archivist",
salary := 64000,
active := true,
hired_at := "2026-05-01T09:00:00Z",
department := "<archive-department-id>",
manager := null
}
Query nested links
select Employee {
employee_no,
name,
title,
department: {
code,
name
},
manager: {
name,
title
}
}
filter .active = true
and .department.code in ["INVESTIGATION", "ARCHIVE"]
and .employee_no not in ["MG-999"]
order by .salary desc, .name asc
limit 10
offset 0
Find top-level employees whose optional manager link is absent:
select Employee { employee_no, name, title }
filter .manager.id = null
order by .name asc
The REPL executes both queries but currently prints nested selections as flat tab-separated columns.
Store
This Northwind-style example models a purchase order as a header plus explicit
line items. OrderItem represents the many-to-many relationship between
purchase orders and products while keeping mutations executable with the
current single-link syntax.
type Customer {
required unique email: str
required name: str
required tier: str
}
type Product {
required unique sku: str
required name: str
required price: float64
required active: bool
}
type PurchaseOrder {
required unique order_no: str
required status: str
required ordered_at: datetime
required link customer: Customer
}
type OrderItem {
required quantity: int64
required unit_price: float64
required link purchase: PurchaseOrder
required link product: Product
}
Create data
cargo run -p gelite-cli -- schema apply examples/store.geli --database store.db
cargo run -p gelite-cli -- repl --database store.db
Insert a customer and two products, keeping their generated IDs:
insert Customer {
email := "[email protected]",
name := "Margo Hosho",
tier := "gold"
}
insert Product {
sku := "CASE-NOTEBOOK",
name := "Detective Notebook",
price := 12.5,
active := true
}
insert Product {
sku := "OWL-PIN",
name := "Owl Enamel Pin",
price := 8.0,
active := true
}
Create the purchase order and its items in one interactive transaction. Replace the placeholders with generated IDs and the ID printed by the purchase order insert:
start transaction
insert PurchaseOrder {
order_no := "TRIAL-0001",
status := "paid",
ordered_at := "2026-08-03T10:00:00Z",
customer := "<margo-id>"
}
insert OrderItem {
quantity := 2,
unit_price := 12.5,
purchase := "<purchase-order-id>",
product := "<notebook-id>"
}
insert OrderItem {
quantity := 1,
unit_price := 8.0,
purchase := "<purchase-order-id>",
product := "<pin-id>"
}
commit
Query line totals
select OrderItem {
line_total := f64(.quantity) * .unit_price,
product: {
sku,
name
},
purchase: {
order_no,
status,
customer: {
name,
tier
}
}
}
filter .purchase.status in ["paid", "shipped"]
and .product.active = true
order by f64(.quantity) * .unit_price desc
Computed projections are executed by SQLite. Their current REPL column labels are generated implementation aliases rather than the logical output names.
Music catalog
This Chinook-style example demonstrates several consecutive single links and an explicit playlist association object.
type Artist {
required unique name: str
country: str
}
type Album {
required title: str
required release_year: int64
required link artist: Artist
}
type Track {
required title: str
required track_no: int64
required duration_seconds: int64
required genre: str
required link album: Album
}
type Playlist {
required unique name: str
}
type PlaylistTrack {
required position: int64
required link playlist: Playlist
required link track: Track
}
Create data
cargo run -p gelite-cli -- schema apply examples/music.geli --database music.db
cargo run -p gelite-cli -- repl --database music.db
Insert an artist, album, two tracks, and a playlist in that order. Keep every generated ID used by the next insert:
insert Artist { name := "Coco Sawatari", country := "Japan" }
insert Album {
title := "Midnight Testimony",
release_year := 2026,
artist := "<coco-id>"
}
insert Track {
title := "First Deduction",
track_no := 1,
duration_seconds := 214,
genre := "mystery pop",
album := "<album-id>"
}
insert Track {
title := "After the Bell",
track_no := 2,
duration_seconds := 188,
genre := "mystery pop",
album := "<album-id>"
}
insert Playlist { name := "Sheri's Case Notes" }
insert PlaylistTrack {
position := 1,
playlist := "<playlist-id>",
track := "<first-track-id>"
}
Query through the catalog
select PlaylistTrack {
position,
track: {
label := concat(.title, " / ", .album.title),
duration_seconds,
genre,
album: {
title,
artist: {
name,
country
}
}
},
playlist: {
name
}
}
filter .playlist.name = "Sheri's Case Notes"
and .track.duration_seconds >= 180
order by .position asc
limit 20
offset 0
The filter and ordering traverse stored single links. Runtime nested result reconstruction remains deferred.
CLI reference
The current executable command paths are:
gelite schema plan <schema.geli>
gelite schema apply <schema.geli> --database <app.db>
gelite repl --schema <schema.geli> [--debug] [QUERY]...
gelite repl --database <app.db> [--debug] [QUERY]...
When running from this repository, prefix each command with
cargo run -p gelite-cli --.
Schema modes
schema plan prints initial SQLite DDL and metadata bind values without opening
a database. schema apply creates the initial schema in a new database.
REPL modes
repl --schema compiles queries without executing them. repl --database
loads the schema catalog from the database and executes supported data queries.
With no query argument, either mode starts an interactive REPL. Enter each
statement without a semicolon. Regular Enter continues while braces are
unbalanced; Alt+Enter inserts a newline without submitting.
Database-backed interactive sessions also accept these inputs:
start transaction
commit
rollback
Transaction commands must be entered separately and are not accepted in compile-only or one-shot sessions.
Current limitations
The examples document behavior that exists in the current pipeline. The main limitations are:
- The REPL executes the supported queries but prints flat tab-separated rows. Nested select shapes are present in the resolved IR and SQLite plan, but runtime nested result reconstruction is not implemented.
gelite repl --schemacompiles and renders queries but cannot execute them. Use--debugto inspect SQL and bind values.gelite repl --databaseexecutesselect,insert,update, anddelete. It does not provide a JSON result format.- Initial schema application expects a new database. Migration diffing and migration history are not implemented.
- Inserts and updates accept scalar literals and single-link IDs. Nested inserts, subqueries, and multi-link mutations are not implemented.
- Composite unique constraints are not available in the current schema syntax.
Association objects such as
OrderItemandPlaylistTrackrely on the application to reject duplicate link pairs when needed. - Update and delete filters are optional. The CLI does not ask for confirmation before an unfiltered mutation.
- Transaction commands work only in an interactive database-backed REPL. Enter
start transaction,commit, orrollbackas separate inputs.
The authoritative syntax and semantic contracts remain in spec/schema.md and
spec/query.md.