Sequences and lookups
Two query patterns recur often enough that Liberty ships a wizard for each:
| Pattern | Purpose | Typical SQL |
|---|---|---|
| Sequence | Compute the next value for an id column. | SELECT COALESCE(MAX(<col>), 0) + 1 AS NEXT_ID FROM <table> |
| Lookup | Supply value + label pairs for a dropdown bound to a screen column. | SELECT <value>, <label> FROM <table> ORDER BY <label> |
Both go through the Scaffold modal — tick a table, tick column(s), get a live SQL preview, save. The save writes two files: the query into the connector and the matching entry into the dictionary, atomically.
Where they live
Liberty Next → Build → Connectors → pick a connector → mode bar: Sequences (or Lookups) → + Add sequence (or + Add lookup).
The modal opens, introspects the pool, lists the schemas / tables.
The scaffold starts from a table. For a lookup or a sequence built on a join or any hand-written SELECT, create the record in Liberty Next → Build → Dictionary (Lookups or Sequences tab) and create its query from there — a blank query, or Generate from a table with the columns mapped to the record's fields.
Sequence — generate the next id
A sequence in Liberty is a query that returns one number — the next id to assign to a new row. It backs the dictionary's SEQUENCE rule: when a row is inserted, the server runs the sequence query inside the insert's transaction and writes the result into the column.
Field by field
| Field | Notes |
|---|---|
| Table | The source. Schema picker shows up on multi-schema pools. |
| Key column | The column whose MAX(...) + 1 gives the next value. The pool introspection lists every column with its SQL type. |
| Optional WHERE | The body of a WHERE clause (without the keyword). Use :placeholder params if the filter needs to vary per call — e.g. region = :region to scope the sequence per region. |
| Query name | Auto-suggested as get_<keyCol>_from_<table>_get (kept short via slugify). Override freely. The _get suffix is convention — sequences are read queries. |
| Sequence id | The key under [sequences.*] in the dictionary. Auto-suggested as get_<keyCol>_from_<table>. |
| Description | Shown in listings and tooltips. Auto-filled as "Get <col> from <table>". |
| Generated SQL | Live preview. Editable — your edits stick until you change the column / table / WHERE inputs, which restores the generated form. |
What gets saved
| File | Entry |
|---|---|
| Connector file | The query, in the connector's Sequences section. |
| Dictionary file | [connectors.<conn>.sequences.<seqId>] query = "<queryName>", dd_id = "<keyCol>", optional description |
A dictionary entry (or a screen column) with the rule SEQUENCE and Rules values = <seqId> gets its number from this query on each insert. With dd_id set, every column whose entry has that key is numbered too.
Concurrency note
SELECT MAX(...) + 1 is not atomic — two simultaneous INSERTs can both read the same MAX and produce a collision. Adequate when:
- Your INSERT happens behind a transactional UI (the user sees one dialog at a time).
- The id column has a database-level uniqueness constraint that will reject the duplicate (the screen reports the error and the operator retries).
For high-concurrency workloads, define the column as a database SEQUENCE (Postgres SEQUENCE, Oracle SEQUENCE, Identity column) and have the sequence query return nextval(...) instead — the database guarantees uniqueness.
Lookup — supply a dropdown's options
A lookup in Liberty is a query that returns rows of (value, label) pairs — the source for a screen column's dropdown. It backs the dictionary's LOOKUP rule: a column with the rule LOOKUP and Rules values = <lookupId> shows the label instead of the code and offers the lookup's rows in a searchable list.
Field by field
| Field | Notes |
|---|---|
| Table | The source of the dropdown rows. |
| Value column | The code stored on the row when the user picks an option. Type matters — VARCHAR(8) value column expects a VARCHAR target column on the screen. |
| Label column | The text shown to the user. Pick Same as value column when the value is already human-readable (e.g. country code displayed as code). |
| Optional WHERE | Body of a WHERE clause without the keyword. Common patterns: active = 'Y', deleted_at IS NULL. Use :placeholder params for filter-cascaded lookups (e.g. show only regions for the selected country — see Parameter binding). |
| Query name | Auto-suggested as get_<table>_get. |
| Lookup id | The key under [lookups.*] in the dictionary. Auto-suggested as get_<table>. |
| Description | Shown in listings and tooltips. |
| Generated SQL | Live preview, editable. The ORDER BY <label> is added automatically — drop it if the rows have an intrinsic order. |
What gets saved
| File | Entry |
|---|---|
| Connector file | The query, in the connector's Lookups section. |
| Dictionary file | [connectors.<conn>.lookups.<lookupId>] query = "<queryName>", value = "<valueCol>", label = "<labelCol>", optional description |
Cascading lookups
When the list should depend on another field of the same row — Role depends on Application — the lookup query takes a parameter, and the screen column binds it:
- in the dialog and grid editing, the column's lookup_param_binds —
{ param = "APP_ID", source = "APP_ID" }; - in the grid's filter panel, the column's filter_from, which narrows the filter's options on the value of another filter.
See Parameter binding and Columns.
SELECT ROLE_ID, ROLE_NAME
FROM roles
WHERE APP_ID = :APP_ID
ORDER BY ROLE_NAME
What's next
- Parameter binding — for sequences/lookups that take parameters from screen context.
- Per-dialect SQL variants — for a lookup that hits Postgres and Oracle.
- Dictionary — every field of a lookup and a sequence, and the other rules.