SELECT * FROM features

Everything it does.

Six chapters: write a statement, read what comes back, change data, understand a schema, run the server, and make the app yours.

SELECT chapter 1 · write

An editor that knows the database.

Tabs that never ask to be saved, completion from the catalog, and a run that takes the selection, the statement under the caret or the whole script.

Run

The selection, the statement, or the script.

A script is split by the rules of its database: dollar quoting in PostgreSQL, DELIMITER in MySQL, GO in SQL Server, PL/SQL blocks in Oracle. Each statement gets a result tab with its title and row count, messages come with timings.

Cancel ends the statement on the server and keeps the session. A time limit is optional.

Cmd/Ctrl+Enter statement · Cmd/Ctrl+Shift+Enter script · Cmd/Ctrl+. cancel
report.sql
CREATE TEMP TABLE late AS  SELECT * FROM orders WHERE shipped_at IS NULL;SELECT status, count(*) FROM late GROUP BY status;DROP TABLE late;
1 · CREATE2 · status · 3 rows3 · running …

Completion

Tables, the columns of an alias, the join.

Behind FROM come tables and views, behind o. the columns of what o stands for, behind JOIN the condition from the foreign key. Keywords and types fit the database of the tab.

“Go to anything” finds a table, view, routine, column, tab or connection across all schemas from a few letters.

Ctrl+Space complete · Cmd/Ctrl+P go to anything
query.sql
SELECT o.id, o.FROM   orders o
customer_idint4 · → customers
placed_atdate
shipped_atdate · null
statustext
totalnumeric

Errors where they happen

Underlined in the editor, explained beside it.

The place the database names is marked and the caret jumps there. Code, detail and hint of the server are shown apart, not as one long line.

query.sql
SELECT name, contryFROM   customers;
42703column "contry" does not exist · Hint: perhaps you meant “customers.country”. · line 1, column 14

Format SQL

Only white space and keyword case change. Checked.

The selection, the statement or the script, as one undo step. Keyword case, indentation, line width, commas and AND/OR are settings with a live preview.

Strings, comments and quoted names are never touched: the formatter compares the tokens of its result with those of your text and leaves the text alone if they differ.

Cmd/Ctrl+Shift+F format · Cmd/Ctrl+/ comment
before · after
select c.name,sum(o.total) from customers c join orders o on …SELECT c.name,       sum(o.total)FROM   customers cJOIN   orders o ON o.customer_id = c.idGROUP  BY c.name;
:name · ${name} · ? · $1

Parameters

Asked when the statement runs, remembered per tab, bound by the driver. Run again with the same values with Cmd/Ctrl+Alt+Enter.

tabs

Tabs that keep their text

Each tab has its own connection and session. The text is backed up as you type and is back after a restart or a crash.

.sql

Saved queries

Plain files in a folder of your choice, with name, description and tags in a comment header. A sidebar view with search and preview.

sel⇥

Snippets

A short word and Tab become a statement with placeholders to tab through. A set per database, and your own.

history

History

Every statement with connection, time and outcome, searchable. It can be switched off and deleted for good.

split

Two tab groups

Side by side or stacked, the result below or beside the editor, hidden or maximised, and a zen mode.

FETCH chapter 2 · read

A result is more than a grid.

It stays in the backend. The window asks for the slice it shows, and for answers: sorted, filtered, found, added up.

Cells as they are

NULL is not an empty string, and it looks different.

NULL, empty text, numbers, booleans and binary data each have their look; the type stands in the column head. Control characters are made visible, long values are cut on screen only.

Keep columns at the left, hide them, move them, fit their widths. The column list finds one of 500 columns by name. The layout is remembered per table.

Cmd/Ctrl+K Cmd/Ctrl+C column list
result 1 · 5 rows
#idint4notetextpaidboolreceiptbyteatotalnumeric
14001gift wraptrueNULL129.00
24002NULLtrue89 50 4E 47 … 14 kB18.40
34003(empty)falseNULL1,204.75
44004line 1⏎line 2trueNULL39.99
54005call backNULL25 50 44 46 … 2 MB0.00

Sort, filter, search

Without running the statement again.

A table opens as a table view: conditions on columns and ORDER BY run on the server, with bound values. Any other result is filtered and sorted where it lies, in the backend’s store.

Search the result for text, a whole word or a regular expression, in all columns or one. The first hits come at once, F3 walks through them.

Cmd/Ctrl+F in the grid · Cmd/Ctrl+G go to row · Alt+↓ next other value
orders
status = 'open'placed_at ≥ 2026-05-01ORDER BY total DESC
Find.* 4[0-9]{3}3 of 12 hits · column id
#idint4statustextplaced_atdatetotalnumeric
14044open2026-06-03318.40
24045open2026-06-11201.75
34047open2026-06-19189.50

Figures of the selection

Count, sum, mean, smallest, largest. Exact.

Mark cells and the status line shows the figures, added up in the backend as decimals: a NUMERIC(38,10) sum is right to the last digit. For dates it shows the span, for text the lengths. A click copies a figure.

“Summarize the column” adds the ten most frequent values.

Cmd/Ctrl+K Cmd/Ctrl+A summarize the column
revenue.sql
#monthdateordersint8revenuenumeric
12026-01-0181,804.10
22026-02-0182,117.45
32026-03-0181,650.00
42026-04-0182,402.90
4 rowsCount 3 · Sum 5,571.55 · Avg 1,857.18 · Min 1,650.00 · Max 2,117.45
{ } </> 0x

Value panel

The whole value of the cell under the cursor: long text, JSON and XML laid out without changing a digit, binary as hex or as a picture, instants in UTC.

row

A row as a form

Every column a line beside the grid, filtered by column name. Marked rows stand side by side with what differs.

pin

Result tabs, pinned

Pin a result so the next run keeps it, move it into a tab of its own, and compare two of them by key or by position.

▁▃▆

Chart

Lines over time, bars, scatter plots, suggested from the column types. Zoom, tooltip, legend, export as SVG.

⧉

Copy as

TSV, CSV, JSON, Markdown, HTML, INSERT, IN (…), WHERE. Written by the backend, with literals of the database.

⤓

Export

CSV, TSV, JSON, NDJSON, INSERT, Markdown, HTML, Excel, optionally gzip. A job with progress that you can stop.

UPDATE chapter 3 · change

See the statement before it runs.

Whatever writes is collected first, shown as SQL, and written in one transaction. The window never sends SQL of its own: the backend builds it, with every value bound.

Edit data

Cells, rows, blocks from a spreadsheet.

Change cells with F2 or a double click, with NULL and the default at hand. Add a row, duplicate rows, mark rows to delete, paste a block of cells, fill down.

Everything is written together: deletes, then changes, then inserts. Each statement must touch exactly one row, or all of it is rolled back. A row somebody else changed in the meantime is reported.

Cmd/Ctrl+S write · Cmd/Ctrl+Shift+I new row · Cmd/Ctrl+D fill down
products · 3 changes
3 changes1 changed · 1 new · 1 to deletePreview SQLWrite
#idint4nametextpricenumericstockint4
1101Trail shoe129.0042
2102Rain jacket179.5017
3104Head torch39.990
+defaultGaiters34.0060

Edit the structure

Columns, indexes, keys and checks as a form.

The statements are shown before they run, with the steps that may lose data marked. Where the database allows it, they run in one transaction.

New table, duplicate, empty, drop. The definition of a view or a routine opens as a statement that replaces it.

products · structure
ALTER TABLE products ADD COLUMN weight_g int4;ALTER TABLE products ALTER COLUMN name TYPE varchar(40);ALTER TABLE products DROP COLUMN legacy_code;CREATE INDEX products_name_idx ON products (name);
2may cut values longer than 40 characters3drops a column and its data
↧ csv · json · ndjson · gz

Import a file

Read as a stream in any common encoding. Preview, mapping by name, a proposed CREATE TABLE; all or nothing, or block by block with an error file.

⟿

Copy tables

From one connection to another, also across databases, as a stream. Types are mapped, losses named, tables go in the order of their foreign keys.

seed = 42

Test data

A generator per column, proposed from type, key and name. The same seed gives the same rows; unique columns and foreign keys are respected.

BEGIN … COMMIT

Transactions

A session per tab, auto-commit or manual. The state comes from the server, never from a guess, and closing a tab with an open transaction asks.

EXPLAIN chapter 4 · understand

What is in there, and how it hangs together.

A schema tree, details for every object, relations to walk along, a diagram, a comparison, and the plan of a statement.

Follow foreign keys

From an order to its customer, and back to all their orders.

F4 opens the referenced row: in the tab, in a new one, or as a quick look. Alt+F7 lists the tables that point to the row, with counts. Several marked rows at once, composite keys, a path with back and forward.

Where the database defines no relation, you add your own.

F4 follow · Shift+F4 quick look · Alt+←/→ back, forward
customers
orderscustomer_id = 3customers
#idint4nametextcountrybpchar
13Chidi OkaforNG
Referenced byorders.customer_id · 7customer_notes.customer_id · 2

Execution plan

One tree table for every database.

Whatever the database returns (JSON, XML, a text tree) is read into one model: rows estimated and actual, own and total time, cost. Hints name what stands out.

“With execution” really runs the statement: inside a transaction that is rolled back, and after the protection of the connection has agreed.

Cmd/Ctrl+K Cmd/Ctrl+X plan
operationrowsmsown time
Sort80.1
  HashAggregate81.4
    Hash Join48,1129.8
      Seq Scan on orders48,11261.3
      Seq Scan on customers80.0
Estimated 900 rows, got 48,112: the statistics may be out of date.

Compare two schemas

What is only here, only there, or different.

Two schemas, or a schema and a saved snapshot. Both definitions stand side by side, and a migration script opens in a tab as a proposal.

The app never runs that script. Every step that may lose data is commented out and marked.

staging → production
ALTER TABLE orders ADD COLUMN channel text;CREATE INDEX orders_placed_idx ON orders (placed_at);-- DANGER: drops the column and its data-- ALTER TABLE customers DROP COLUMN fax;
+ 2≠ 3− 16 differences in 4 tables
tree

Schema tree

Schemas, tables, views, routines of the active connection, with a filter and actions on every node. Names are quoted the way the database wants them.

DDL

Object details

Columns, indexes, constraints, relations, privileges, statistics, and the DDL to read, copy or open in a tab.

ER

ER diagram

A schema or a table with its neighbours, arranged by itself. Export as SVG, Mermaid or DBML.

grep

Search on the server

Where does a text stand? In the rows of all tables, with limits, and in the definitions of views, routines, triggers, checks and defaults.

GRANT chapter 5 · operate

The server, not only its tables.

Who is connected, who may do what, and a copy before you change anything.

Server monitor

Sessions, running statements, who blocks whom.

Reads again on a beat. Cancel a statement or end a session after a question. Statements of other people are shown and nothing more: they never reach the history or a log.

shop-live · monitor
pidint4usernamestatetextsecondsblocked by
8812reportactive184–
9120appwaiting418812
9133appwaiting129120
9140marcidle0–
4 sessions1 active · 2 waitingCancel statement …

Users and rights

What each may do, and through which role.

Users, roles and memberships, a matrix of tables × rights. Changes are collected and shown as statements, with what is dangerous marked, and run after a question.

A password is never shown: not in the preview, not in the history, not in a log. PostgreSQL gets a SCRAM verifier instead of the password itself.

shop · rights of “report”
tableSELECTINSERTUPDATEDELETE
customers✓ via readers–––
orders✓ via readers–––
report_runs✓✓ new––
GRANT INSERT ON report_runs TO report;
pg_dump · mysqldump

Back up and restore

With the tools of the database, found on the machine and started without a shell; the password is never an argument. A SQLite file is copied; a simple SQL export works without tools.

free · ask · read only

Protection

A level per connection. Statements are classified as reading, writing, DDL or dangerous in the backend, which also puts the question.

MCP

AI control

Off by default. An assistant reads the structure, runs reading queries and proposes the rest. How it works

SET chapter 6 · the app

Yours to arrange.

Shortcuts, colours, language and layout are settings, and they survive a restart.

⇧⌘P

Command palette

Every function is an action with a name. The palette finds it, the menu shows it with its current shortcut.

⌘K ⌘S

Your shortcuts

A keymap per system and an editor for your own keys, with chords. The defaults

17 + yours

Colour schemes

Fixed or following the system. A live editor for every colour with contrast hints, import and export.

EN · DE

Two languages

English and German, switched while the app runs.

⌘,

Settings

One dialog with search, reset per value, export and import.

menu

Menus

A native menu bar on macOS, one drawn by the window elsewhere, and context menus. All made of the same actions.

SELECT * FROM your_database;

Run it.

Free and open source under the MIT license. The packages are not signed yet.