Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

ExampleModelMain query cases
Organizationdepartments and employeesoptional self-links, null, nested shapes, boolean filters
Storecustomers, products, orders, and line itemsassociation objects, arithmetic, transactions, membership filters
Music catalogartists, albums, tracks, and playlistsdeep 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
}
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 --schema compiles and renders queries but cannot execute them. Use --debug to inspect SQL and bind values.
  • gelite repl --database executes select, insert, update, and delete. 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 OrderItem and PlaylistTrack rely 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, or rollback as separate inputs.

The authoritative syntax and semantic contracts remain in spec/schema.md and spec/query.md.