Skip to main content

Create from a database table

When the table already lives in your database, the fastest path is the CRUD Wizard. You point it at a schema and a table, tick the columns you want the screen to see, mark which ones identify a row, and Liberty writes the four queries (_get, _put, _post, _delete) for you — including the :NAME_ORIGINAL rebind on UPDATE so a dialog's Save knows which row to find.

This is the recommended path for any table that already exists. Skip it only when the table doesn't exist yet or when you want hand-written SQL — see Create a custom query.


Where it lives

The wizard is reached from Settings → Connectors:

  1. Pick a connector in the left list.
  2. Switch the right pane's mode bar to Tables.
  3. Click + Add table at the top-right.
  4. A choice modal opens. Pick Generate from DB.

The wizard then walks the connector's pool (GET /api/sql/<connector>/_schemas) and presents the schemas it found.


The wizard layout

Generate table from DB · [connectors.crm]1 · SCHEMApublic ▾2 · NAME FILTERcustomers%3 · TABLEcustomers ▾4 · COLUMNS · 7 / 9☑ customer_idINT4KEY☑ nameVARCHAR(120)☑ emailVARCHAR(255)☑ created_atTIMESTAMP☑ owner_idINT4☑ statusVARCHAR(20)☑ regionVARCHAR(60)☐ notes_blobBLOB☐ search_vectorTSVECTOR5 · QUERIES TO EMIT☑ SELECT☑ UPDATE☑ INSERT☑ DELETE6 · LIVE PREVIEWGETcustomers_getSELECT customer_id, name, email, created_at, owner_id, status, regionFROM public.customersPUTcustomers_putUPDATE public.customersSET name = :name, email = :email, created_at = :created_at, owner_id = :owner_id, status = :status, region = :regionWHERE customer_id = :customer_id_ORIGINALCancelSave

Step 1 — Pick a schema

The schema picker shows up only when the pool has more than one. PostgreSQL with a single public and SQLite skip it; Oracle (many schemas) requires it.

The wizard fetches schemas lazily — the initial open just lists names, the per-schema table walk happens after you pick. On a populous Oracle pool this saves you the 10+ seconds a full multi-schema walk would take.


Step 2 — Filter table names (optional)

A SQL LIKE pattern (customers%, F00%) typed in the Name filter box narrows the table list server-side, before the per-table column introspection runs. For pools with thousands of tables this is the difference between a snappy picker and a sluggish one.

The filter is debounced — type freely; the fetch fires ~350 ms after you stop.


Step 3 — Pick the table

Once you pick a table, the wizard pulls its columns and pre-fills two sets:

SetPre-fill rule
IncludeEvery column is ticked.
KeyEvery column the database marks as NOT NULL is ticked.

These are guesses — uncheck the columns you don't want surfaced (BLOBs, audit columns, computed fields) and confirm the keys.


Step 4 — Tick columns and keys

Each row in the Columns list carries:

ElementWhat it does
Include checkboxWhen unticked, the column is dropped from _get's SELECT and from _post's INSERT. The dialog will not see it.
Column name + typeRead-only — comes from the live database.
KEY togglePinned to the right. When ticked, this column drives the _put's WHERE clause (with :NAME_ORIGINAL) and the _delete's WHERE clause.

A row with at least one key is required for UPDATE / DELETE to emit anything. The wizard refuses to save without one.


Step 5 — Pick which queries to emit

Four checkboxes — SELECT (_get), UPDATE (_put), INSERT (_post), DELETE (_delete). Tick the ones the table needs.

PatternTick
Read-only reference table.SELECT only.
Standard CRUD table.All four.
Append-only (audit log).SELECT + INSERT.
Reference table edited only via INSERT-then-purge.SELECT + INSERT + DELETE.

The SELECT query is always emitted — at the very least the screen needs to read the table.


Step 6 — Read the preview, override if needed

The right column shows the generated SQL for every ticked CRUD slot, live. Edit any preview directly — your edit wins over the auto-gen until you change the column / key selection again, which restores the generated version.

The four shapes

-- _get : straight SELECT over the included columns
SELECT col1, col2,
FROM <schema>.<table>

-- _post : INSERT bound to :col placeholders
INSERT INTO <schema>.<table> (col1, col2,)
VALUES (:col1, :col2,)

-- _put : UPDATE non-key columns, WHERE on key with :NAME_ORIGINAL
UPDATE <schema>.<table>
SET non_key1 = :non_key1, non_key2 = :non_key2,
WHERE key1 = :key1_ORIGINAL
AND key2 = :key2_ORIGINAL

-- _delete : DELETE keyed on :NAME (the dialog binds the row's current values)
DELETE FROM <schema>.<table>
WHERE key1 = :key1
AND key2 = :key2

Why :NAME_ORIGINAL on UPDATE?

A dialog's Save sends two snapshots of every key column: the current value (:key1) and the value the row had when the dialog opened (:key1_ORIGINAL). The UPDATE matches on the original so the row is found even when the operator edited the key itself. The CRUD Wizard wires this convention for you — if you hand-write a _put later, mirror it.


Step 7 — Save

The Save button:

  1. Appends the ticked queries to the connector's queries list.
  2. Saves the connector file.
  3. Triggers a hot reload — the new queries are callable immediately.

The four new queries appear in the Tables tab grouped under their shared base name, with the four CRUD slot badges showing which were emitted.


Validation — what blocks Save

ErrorCauseFix
Pick a table.No table selected.Pick one.
Name the table.The base name field is empty (auto-derived from the table name; clear it to override).Type a base name — letters, digits, underscores.
Include at least one column.Every column was unticked.Tick at least one.
The read (_get) query is required.SELECT was unticked.Tick it back.
Pick at least one key column for UPDATE / DELETE.UPDATE or DELETE is ticked but no column has KEY on.Mark the key column(s).
A query named "X" already exists.The base name produces a <base>_get (or _put / _post / _delete) that collides with an existing query on this connector.Pick a different base, or delete the old one first.

What you've just built

For a customers table with customer_id as the key, the wizard generated four queries:

NameTypeUsed by
customers_gettable (read)The grid + form's read.
customers_puttable (write)The dialog's Save (UPDATE).
customers_posttable (write)The dialog's Save (INSERT).
customers_deletetable (write)The grid's Delete action.

They land in the Tables tab as one row labelled customers with the four GET / PUT / POS / DEL badges all green. The next step is to build a screen on top — covered in the upcoming Screens section.


When to use the wizard vs hand-write

Use the wizardHand-write (Add query on Unclassified)
The table exists in the DB.The query is a custom report, an aggregate, or a stored procedure call.
You want the four standard CRUD shapes.You need a JOIN, a GROUP BY, a RECURSIVE CTE.
You're starting from scratch and the columns are the source of truth.You want a query that doesn't map 1-1 to a table.

The wizard is the right tool for 80% of operational tables. The other 20% — analytics, reports, multi-table joins — go through Create a custom query.


What's next