qub v0.44.9

There are great open-source SQL editors out there, but none of them ever really clicked for me. DataGrip was the one that gave me the feeling of productivity and control over my database workspace. But once my enterprise license got canceled, instead of picking a new one I decided to build myself an editor the way I always wanted it to be. So qub was built with an emphasis on SQL centricity, the basics done well, and a clean interface I enjoy looking at all day.

During development, to keep the interface consistent and easier to build, I created a Qt/QML component library (including fonts, icons, and theming) called Mahina, which I also open-sourced. If you're building desktop apps with Qt, it might save you from reimplementing the same primitives from scratch.

🤖

AI assistance was used to build this application, but all the code was human-reviewed.

Multi-connection PostgreSQL, MySQL, MariaDB, SQLite, Oracle, Firebird, and ODBC — all open in parallel tabs.
AI assistant Generate SQL from natural language via command palette or inline /* @ai */ blocks. Supports Anthropic, OpenAI, and Ollama.
Analyze without leaving Every result set also opens as a chart, a per-column profile, a pivot table, a query plan, a set of data-quality checks, or a diff against an earlier run.
Schema browser Explore tables, columns, FK relationships, table statistics, DDL, and a visual schema graph.
Snippets Save SQL you reuse, organize it in folders, insert it at the cursor, and share it as JSON.
Workspaces Named work environments, each with restored tabs and its own allowed connections.
Live share Stream query results to any browser on your network via a one-click shareable link.
Profiles Named safety rule sets — read-only mode, destructive query guards — assignable per connection.
Themes Full palette customization. Ships with 4 built-in themes and supports custom JSON themes.

Connections

qub supports multiple simultaneous connections. Each connection lives in its own tab in the editor. You can switch freely between them while queries run in the background.

Creating a connection

  1. From the welcome screen, click the + icon in the Data Sources list.

  2. Choose a driver from the dropdown (PostgreSQL, MySQL, MariaDB, SQLite, ODBC, Oracle, Firebird).

  3. Fill in host, port, database name, username, and password. For SQLite, pick a Source instead — open an existing file, create a new database, or Import CSV to build one from a delimited-text file.

  4. Running the database in Docker? Click Discover Docker container and pick one from the list instead of typing any of that — see below.

  5. Optionally assign a profile to enforce safety rules on this connection.

  6. Click Test to verify the credentials, then Save to return to the list (its status dot shows whether the connection came up), or Save & Connect to jump into the workspace once the connection actually opens — if it fails, you stay on the form with the error and can fix and retry.

Discover a Docker container

When the docker CLI is on your PATH, a Discover Docker container button appears on the form for a new server connection. It lists the running containers that look like a database and have a published port, showing each one's name, image, and port; picking one fills in the driver, host, port, database name, username, and password from the container's own environment, and names the connection after it if you left the name blank.

The port it fills in is the one mapped on the host, not the one inside the container, so the connection works from outside Docker. A container whose image sets no password in its environment prefills without one and says so — type it in yourself. The button is hidden for SQLite and when editing an existing connection, and qub only ever reads containers: it never starts, stops, or creates one.

Connecting and disconnecting

Each row in the Data Sources list has a plug button that toggles the connection: it connects when the connection is down and disconnects when it is up, without leaving the welcome screen. Clicking the row itself also connects, but additionally takes you into the workspace. Inside a workspace, a Disconnect button appears in the top bar while the active connection is up, and a Reconnect button while it is down.

Disconnecting closes the database session and tears down its SSH tunnel, if any. Reconnecting reads the password from the system keychain and re-establishes the tunnel automatically. With Auto-reconnect enabled in Settings, qub also reconnects on its own when a connection drops mid-query.

Passwords and the system keychain

qub never writes a password to its own files. Connection passwords, SSH passwords and AI provider keys go to the credential store your operating system already runs, under the service name qub: Keychain on macOS, the Secret Service (GNOME Keyring or KWallet) on Linux, Credential Manager on Windows. That store is the one asking when you see a prompt — qub has no password of its own to ask for.

Expect a prompt the first time a saved password is read, which is usually the first connection after installing, and again after an update:

SystemWhat you see
macOS A dialog: "qub wants to use your confidential information stored in 'qub' in your keychain." Enter your login password and choose Always Allow so it stops asking. The permission is tied to that exact build of the app, so a new version of qub asks once more.
Linux Usually nothing: on most desktops the login keyring is unlocked when you log in. If your keyring has its own password, or you use automatic login, a dialog asks to unlock it the first time qub stores or reads a password.
Windows Nothing. Credential Manager stores the entry for your Windows account without prompting.

Denying the prompt is not fatal, but qub gets no password back and the connection fails with whatever the database says about the credentials. Type the password into the connection form and connect again — you will be asked once more, and can allow it then. The same applies on a machine with no keyring service running at all: qub cannot save the password, so it has to be typed each session.

The entries are visible and revocable in the tools you already have — Keychain Access, Seahorse or KDE Wallet Manager, Credential Manager — under the service qub, keyed by connection name (ssh_… for a tunnel's password, ai_… for a provider key). Deleting a connection in qub deletes its entry with it.

Nothing else in qub is protected by that prompt: it guards the stored passwords, not the application. Anyone at an unlocked machine can open qub and use a connection that is already up.

Supported drivers

DriverDatabaseSystem dependency
QPSQLPostgreSQLlibpq
QMYSQLMySQLlibmysqlclient
QMARIADBMariaDBlibmariadb
QSQLITESQLitenone (built-in)
QODBCAny ODBC sourceunixODBC
QOCIOracleOracle Instant Client
QIBASEFirebirdlibfbclient

Which of those a copy of qub can actually load is decided by Qt, not by qub: the official Qt binaries the macOS and Windows builds are made with carry no MySQL/MariaDB driver at all, and the macOS ones none for Oracle or Firebird. The macOS DMG is the exception — it ships a MySQL/MariaDB plugin qub compiles itself. qub lists only the drivers the copy you are running found, so the connection form never offers one that would fail on you.

DriverLinux · AppImagemacOS · DMGWindows · installer
QSQLITEbundledbundledbundled
QPSQLbundledbundledbundled
QODBCbundledbundledsystem unixODBC
QMYSQL / QMARIADBbundledbundledno plugin
QIBASEbundledno pluginbring fbclient.dll
QOCInot bundledno pluginbring Instant Client

No plugin means nothing you install will help — only a qub built against a Qt compiled with that driver, which is what the macOS DMG does for MySQL: it carries a plugin built during packaging against MariaDB Connector/C, and the client library alongside it. Oracle is left out of the AppImage because its client library is not redistributable. Building qub from source, every client library is your system’s and every driver Qt built is available.

PostgreSQL: requires libpq on your system. Fedora: sudo dnf install libpq. Ubuntu: sudo apt install libpq5.

SSL / TLS

Enable Use SSL in the connection form to encrypt the connection. You can provide a CA certificate, a client certificate, and a client private key for mutual TLS authentication. All certificate paths are stored per connection.

SSH tunnel

qub can route a database connection through an SSH tunnel — useful for databases inside private networks that are not directly reachable. Create the tunnel first under the SSH card on the welcome screen (host, port, username, and a private key file — leave the key empty to use your default keys or a running ssh-agent), then select it in the SSH connection dropdown of the database connection form. Authentication is key-based: the tunnel runs ssh non-interactively, so it never prompts for a password.

Test checks the SSH login on its own, without a database behind it — in the form, before you save, and from the plug icon on each saved entry. It logs in, reports what ssh said, and drops the session again; no port is forwarded. The first connection to a host stops on its fingerprint, which you confirm before qub writes it to ~/.ssh/known_hosts.

With a tunnel selected, the connection’s Host and Port are read from the SSH host’s point of view, not from yours: localhost means the SSH machine itself, and a private name like db.internal is resolved over there. If the database runs on the SSH host, that is 127.0.0.1 and its real port; if it runs elsewhere on the private network, use the address the SSH host would use to reach it.

Import and export

Both connections and SSH configurations can be moved between machines as JSON files. Use the (export) and (import) icons in the Data Sources and SSH Connections headers. Connection exports include every setting — driver, host, SSL certificates, timeout, default schema, and the SSH tunnel link. Passwords live in the system keychain and are never written to the file; re-enter them after importing. Importing skips entries that already exist.

Import CSV as a table

qub can turn a delimited-text file into a queryable table. It sniffs the delimiter (comma, tab, semicolon, or pipe — quoted fields, embedded delimiters, and newlines are handled), infers a type for each column (INTEGER, REAL, or TEXT), and imports the full file. Blank cells import as NULL. Once imported, the data is queryable with plain SQL — joins, aggregates, filters, and the profile and chart views all work on it. There are two ways in:

Database info & health

Click the button next to the + tab to open the Database window for the active connection. Its Overview tab shows the database's identity at a glance — version, size, table and view counts, index count, active connections, encoding, and collation. The Health tab shows live metric cards with sparklines (connections, rows read, commits, cache hit ratio, lock waits, database size — varying by driver), polled every 3 seconds while the window is open. It's a separate window, so you can move it to another monitor and keep an eye on the database while you work. Embedded databases like SQLite have no server counters; only the file size is tracked there.

The Alerts section at the bottom of the Health tab lets you get notified when a metric crosses a threshold. Pick a metric, choose above or below, enter a threshold (for example Active connections above 100 or Cache hit ratio below 90), and click Add. Each rule shows the metric's current value live, tinted red while it is in breach. When a metric first crosses its threshold qub raises a toast and writes a warning to the Activity Log, so the breach is still on record after you close the window, and logs a note when it recovers. Alerts are evaluated from the same 3-second poll as the metric cards, which only runs while the Database window is open — closing it stops the checking, it does not run in the background. Rules are saved per connection and persist across restarts; the toggle disables a rule without deleting it.

Schema graph

Open Schema graph from a schema row's ··· menu in the schema browser. It draws that schema's tables and the foreign keys between them in a separate window, which you can move to a second monitor while you keep writing queries. Tables with foreign keys are highlighted; isolated tables are muted. Click a node to read its name.

Drag the background to pan, wheel to zoom, or use the / + / Fit buttons. Labels appear when the tables have room to carry them, so zoom in on a crowded corner rather than reading through overlapping text.

Above 150 tables the window says so instead of drawing: a graph that size is unreadable, and laying it out costs seconds of a frozen window. Where the tables that actually have foreign keys would fit, it offers to draw just those — usually the part worth looking at. A schema, not the whole connection, is the scope: a database with hundreds of tables has no picture that fits on a screen.

Compare schemas

Click the git-diff button next to the + tab to open the schema comparison window. Pick a Base (A) and a Compare (B) connection from the two dropdowns — both must be connected — and qub diffs their live schemas into an add/remove/change tree:

The summary strip tallies the changes, the button swaps A and B, and Differences only hides everything that matches so you see just the drift. This is the quickest way to check whether staging and production have fallen out of sync.

Schema snapshots

Comparing two live connections is great when you have both, but sometimes you want to track how a single database changes over time. The camera button next to Compare schemas opens the schema snapshots window. Pick a connection, give the snapshot a name, and click Capture to save its schema to disk.

Snapshots are listed on the left with their connection, table count, and age. Select one, choose a Live connection to compare it against, and qub diffs the current schema against the saved snapshot using the same add/remove/change tree as Compare schemas — so you can see exactly what has drifted since the snapshot was taken (a migration you forgot to re-apply, a column someone dropped, and so on). Differences only hides everything that still matches. Snapshots persist across restarts until you delete them with the trash button.


Query editor

The editor occupies the upper portion of the workspace. It has SQL syntax highlighting, line numbers, and autocomplete for table and column names.

Command palette

Press Ctrl+P to open the command palette — a searchable launcher for every workspace action. Start typing to filter by name or group, use / to move, Enter to run, and Esc to dismiss. Each command lists its own keyboard shortcut, so the palette doubles as a way to learn them.

It covers running and explaining queries, formatting SQL, asking the AI, opening/closing tabs, opening and saving files, saving snippets, exporting results, viewing the selected row, toggling the activity log and panels, and opening this shortcuts reference.

Running queries

Click ▶ Run or press Ctrl+Enter. If text is selected, only the selection is executed. Otherwise the full buffer runs. Queries run asynchronously — the UI stays responsive while results load.

EXPLAIN (query plan)

Press Ctrl+E (or the lightning button in the editor toolbar) to explain the current query — the selection if you have one, otherwise the whole editor. The plan opens in the Explain tab beside Results, drawn as an indented tree of operations rather than raw text.

Explaining a query that contains parameters prompts for their values first, then explains the filled-in SQL.

Tabs

Open multiple query tabs per connection with Ctrl+T. Each tab has its own SQL buffer, result set, and execution state. Close a tab with Ctrl+W. Tab state is preserved when you switch connections and restored when you switch back.

SQL formatting

Press Ctrl+Shift+F to auto-format the SQL in the editor.

Find and replace

Ctrl+F opens the find bar. Ctrl+H opens find-and-replace. Press Escape to close.

Autocomplete

After typing 2 or more characters, qub offers schema-aware suggestions drawn from the connected database. Completion understands the statement under the cursor:

Use / to navigate and Enter or Tab to accept. Escape dismisses the popup. It only opens while you type: moving the caret into an existing word — by clicking or with the arrow keys — leaves it closed. Autocomplete can be turned off in Settings → Editor.

Schema insertion

Double-click a table or column name in the schema browser to insert it at the cursor position in the editor (requires Insert on double-click to be enabled in Settings).

Saving and opening files

Ctrl+S saves the current query to a .sql file on disk. Ctrl+Shift+S saves it as a snippet. Ctrl+O opens a file picker to load a SQL file into the editor.

Profile guards

If the active connection has a profile assigned, qub inspects every query before it runs. Depending on the profile rules, it will either block the query outright or ask for confirmation before executing destructive statements such as DELETE, DROP, TRUNCATE, or UPDATE without a WHERE clause.

Markdown blocks (literate SQL)

You can interleave narrative with your SQL by writing Markdown inside special block comments. A block starts with /* @md and ends at the normal */:

/* @md
# Creating the user
The account has to exist before the FK on `orders` can reference it.
*/
CREATE TABLE users (id int PRIMARY KEY);

Because the narrative lives in a comment, the buffer stays a perfectly valid .sql file — the database ignores it, and running, profiles, history, and snippets all work unchanged.

Preview: Ctrl+5 (or Panels → Markdown preview) opens a rendered view beside the editor. Markdown blocks are formatted; the SQL between them appears as code blocks. The preview follows your typing live.

Export: choose Export as Markdown… from the editor's ··· menu to save the whole document as a .md file — narrative becomes prose and each SQL chunk becomes a fenced ```sql code block, ready for a wiki, a README, or a pull request. The suggested filename follows the query tab's name.

Export as Markdown + results… does the same but appends a Latest result section containing the current tab's result set rendered as a Markdown table (capped at 100 rows, with a note when more were returned) — handy for pasting a query and its output together into a report or review.


Query parameters

You can write queries with named or positional placeholders. When you run such a query, qub opens a parameter dialog where you fill in the values before execution. This is especially useful for snippets you reuse with different inputs.

Named parameters

Use :name syntax. Each unique name gets one input field in the dialog.

SELECT * FROM orders
WHERE status = :status
  AND created_at BETWEEN :start_date AND :end_date

Positional parameters

Use $1, $2, … syntax (PostgreSQL style). Parameters are filled in order.

SELECT id, total FROM orders
WHERE customer_id = $1
  AND total > $2
ORDER BY created_at DESC
LIMIT $3

Value typing

Values are inserted as SQL literals with a little type inference so comparisons and LIMIT work as expected:

To force a value that looks like a number or keyword to be treated as text (e.g. a ZIP code 01234, or the literal word true), wrap it in single quotes in the field.

Remembered values

The values you enter are kept per tab, so re-running a parameterised query pre-fills the dialog with what you used last time. Values are also collected before an EXPLAIN of a parameterised query.

Named and positional parameters are mutually exclusive in a single query. If both are present, named parameters take priority.


Results & export

Query results appear in the lower portion of the workspace in a scrollable table. Column headers are fixed while you scroll horizontally. The status bar at the bottom shows the row count and execution time.

Status bar

The left of the status bar says where the next statement will land, read left to right: the kind of database (with a check mark while the connection is up), the database itself, and — on PostgreSQL and Oracle, where a schema is a separate thing from a database — the schema an unqualified table name resolves to, each with its own icon. A SET search_path or USE you run moves it, so the schema shown is the live one rather than the one the connection was created with.

Output console

Next to Results sits an Output tab — a console-style stream of everything the active connection did in this session: each executed statement with its outcome (SELECT · 500 rows · 142ms), connection and SSH tunnel events, and errors inline in red with the failing SQL echoed underneath. A statement takes two stamped lines — the SQL at the moment it was sent, the outcome at the moment it came back — so the gap between them is on screen rather than in a duration you have to read. The console is one selectable text buffer, not a list of rows: drag through it and copy the three lines you want, or right-click for Copy, Select all and Copy console (the whole session as plain text). It follows new entries like a terminal; scroll up to read and it pauses, with a Latest button to jump back to the live tail. The global activity log (Ctrl+L) remains the place to see all connections at once with filtering and export.

Chart

A third Chart tab sits beside Results and Output. Pick a chart type — Bar, Line, Area, or Scatter — and choose an X column (categories, or the x-axis for scatter) and a Y column (the numeric value). The plot is drawn straight from the rows currently in view, so filtering the results narrows the chart too. Non-numeric Y values are treated as zero; scatter plots use only rows where both X and Y are numeric. It's a quick way to eyeball a trend or distribution without exporting to a spreadsheet.

Column stats

Click any cell and the status bar shows live aggregates for that whole column, computed over the rows currently in view (so filtering narrows them too) — much like the selection summary at the bottom of a spreadsheet. Numeric columns report Σ sum · avg · min · max · n; text columns report n · nulls · distinct. It's a fast way to sanity-check a result without writing a second GROUP BY query.

Profile

The Profile tab gives you a df.describe()-style summary of the whole result set — one card per column, computed over the rows currently in view (so filtering narrows it too). Each card shows the column's inferred type (numeric, text, or empty), its count · nulls · distinct counts, and a distribution: numeric columns get min · median · mean · max plus a histogram; text columns get their top values with frequency bars. It's a fast first look at the shape of a dataset before you start slicing it with queries.

Explain

The Explain tab shows the query plan for the current statement as a visual tree — see EXPLAIN in the Query editor section. Press Ctrl+E or the editor's lightning button to fill it.

Pivot

The Pivot tab cross-tabulates the result set, spreadsheet pivot-table style. Choose a Rows field and a Columns field to group by, then an aggregation — Count, Sum, Avg, Min or Max — applied to a chosen value column (Count tallies rows and ignores the value column). Row, column and grand totals are computed automatically.

Like the other analysis tabs it works over the rows currently in view, so filtering the results re-pivots them. Cells with no matching rows are left blank, and numeric aggregates skip non-numeric or NULL values. Very high-cardinality fields are capped with a notice so a pivot can't explode into thousands of columns.

Checks (data quality)

The Checks tab lets you assert data-quality expectations about the result and see them pass or fail live. Click Add check, pick a column, and choose a check:

Each row shows a green pass or red fail with the number of offending rows and an example value. The header tallies passed and failed checks. Checks evaluate over the rows currently in view, so filtering re-runs them, and they are remembered per query tab. This turns a quick SELECT into a lightweight data-quality gate without leaving qub.

Diff (compare result sets)

The Diff tab compares the current result against a saved baseline — handy for checking what a query change did, or how data shifted between two runs. Click Set baseline to capture the rows in view, then re-run the query (or edit it and run again) and the diff updates live:

Use Match by to pick the key column that identifies a row across runs (for example a primary key), or leave it on Whole row to compare rows exactly. Differences only hides unchanged rows. If the baseline and the current result have different columns, the diff says so rather than guessing. The baseline is remembered per query tab until you clear it.

To keep a baseline beyond the current tab, click Save… to store the result under a name (it's kept with the connection and the SQL that produced it). Later — even in a new session — click Load… to pick a saved snapshot for this connection and diff the current result against it. Saved snapshots are per connection, so the Load… menu only lists ones captured on the connection in view.

Inline editing

For single-table SELECT queries, results are editable directly in the table. Click any cell to edit it; qub generates and executes an UPDATE statement automatically on confirmation. Primary key columns are protected and shown as read-only.

Which table you are editing is read back out of the query itself, so it works on a schema-qualified SELECT — including the one the quick-browse button writes — and the generated UPDATE quotes its identifiers in the connection's own dialect, backticks on MySQL and MariaDB.

Column widths

Columns start out sharing the width of the grid evenly, which is rarely what a result set wants: a column of two-letter codes gets the same room as one holding a timestamp. Drag the edge of a header to resize one by hand, double-click that edge to fit the column to its widest value, or right-click the header for Fit “column” to contents, Fit all columns to contents and Reset column widths. A fit measures the first 500 rows and stops at 600 px, so one cell holding a page of JSON cannot push every column after it off the screen.

Value inspector

To read a value that's too wide for the grid, open it in the inspector: double-click a cell (in read-only results) or right-click and choose View value…. The dialog shows the full contents in a scrollable, selectable monospace pane, and JSON objects and arrays are automatically pretty-printed (indented and on multiple lines). Copy copies exactly what's shown, so you can grab the formatted JSON. In editable single-table results, double-click still starts an inline edit — use the right-click menu to inspect instead.

Row viewer

For wide tables that scroll off-screen, right-click a cell and choose View row… — or press Ctrl+Shift+R with a cell selected — to see the whole record as a vertical field list (column → value). Click any value to open it in the value inspector (full text or pretty-printed JSON), and use the ‹ Prev / Next › buttons to step through the result set one record at a time. Fields that are foreign keys show a jump icon that opens the referenced row in a new tab, and Copy row copies the record as tab-separated values.

Foreign-key navigation

When a cell sits in a column that participates in a foreign key, its right-click menu offers one-click navigation that opens the related rows in a new query tab:

Click a cell first (which also selects it), then right-click for its navigation options. The new tab is named after the table it lands in, so a trail of jumps reads as the tables you followed. If a tab already answers to that name, the new one keeps its Query n label rather than repeating it; double-click any tab to rename it. Foreign keys are read from the live schema of PostgreSQL, MySQL/MariaDB, and SQLite connections, and the generated query uses the connection's identifier-quoting dialect. Both ends of a key carry their schema, so on a connection exposing more than one the menu names the destination qualified (Go to analytics.dim_customer row) and the generated query qualifies it too — a bare name would resolve against the search path rather than against the schema the row you clicked came from. Keys that cross schemas are followed like any other.

Exporting results

FormatHow to export
CSVCtrl+Shift+E or toolbar button
TSVToolbar export menu → TSV
JSONToolbar export menu → JSON
Excel (.xlsx)Toolbar export menu → Excel
SQL INSERTsToolbar export menu → SQL INSERTs

SQL INSERTs writes one INSERT INTO … VALUES … statement per row — handy for moving data into another database. You're prompted for the target table name (prefilled from the query's table when known), and identifier quoting and value literals follow the active connection's dialect: MySQL/MariaDB use back-ticks, everyone else double quotes; booleans, byte data, and backslashes are escaped per dialect. The export covers the rows currently in view, so it respects any active filter.

Copying results

Right-click a cell or use the toolbar to copy the result set as CSV, JSON, plain text, or SQL INSERT statements.

Result limit

The Limit picker in the toolbar caps the number of rows returned. Set it to None to retrieve all rows. The default can be changed in Settings.

Exporting is not bound by that display limit: when the visible result is truncated, an export re-runs the same query with no limit and writes the complete result set to the file (up to a 500,000-row safety ceiling). A toast confirms how many rows were written. Non-truncated results export straight from what's on screen.


Schema browser

The schema browser lives in the left panel of the workspace. It shows all schemas, tables, and columns for the active connection, and provides quick actions for inspecting your database structure.

Browsing tables

Click a table name to select it. Click the arrow to expand it and see its columns, along with type, primary key, and nullable indicators. Double-click a table or column to insert its name at the cursor in the editor (requires Insert on double-click in Settings).

Quick browse

Enable the quick-browse button in Settings (Show quick browse button). A ▶ icon appears on hover next to each table and runs SELECT * FROM table instantly, in a tab of its own, named after the table — whatever you were writing in the current tab stays where it was. The row limit applies just as it does to the Run button.

Table actions menu

Hover over any table row to reveal a ··· button on the right. Clicking it opens a menu with three actions:

Schema actions menu

A schema row has a ··· button of its own, with Copy name — the schema name alone, unqualified.



Snippets

Snippets are reusable pieces of SQL — the queries and DDL you keep coming back to. They live in one global library shared by all workspaces.

Saving a snippet

Names must be unique within a folder — you'll get an inline error instead of a silent duplicate.

Folders

Type any folder name when saving — folders are just labels, so they spring into existence with their first snippet and disappear with their last. Existing folder names appear as one-click chips below the folder field, which keeps you from accidentally creating "DDL" and "ddl". Folders group the sidebar list under collapsible headers and the Home list under section headers.

Managing snippets

The Snippets card on the welcome screen lists the whole library. Click a row (or its pencil) to edit the name, folder, or SQL; the trash icon deletes after confirmation.

Import and export

Use the / icons in the Snippets header to save the library to a JSON file or load one from another machine. Importing skips snippets that already exist (same name and folder).


AI assistant

qub has two ways to generate SQL from natural language — an inline comment syntax that stays in your editor, and a floating command palette for quick one-off prompts. Both use the same configured AI provider and are schema-aware. The prompt also tells the model which database engine the active connection uses, so the generated SQL is written in the right dialect (PostgreSQL, MySQL, SQLite, …).

Command palette — Ctrl+K

Press Ctrl+K (or click the ✦ button in the query toolbar) to open the AI command palette. Type your prompt and press Enter. The generated SQL appears below the input with two options:

Press Escape or click outside to close without inserting.

Inline prompts — /* @ai */

Write a prompt directly in the editor inside an @ai comment block — the same block-comment convention as /* @md */ markdown blocks — then press Ctrl+Shift+Enter. The block is replaced in-place with the generated SQL. Prompts can span multiple lines:

/* @ai
list all users who signed up in the last 7 days,
grouped by country, sorted by count descending
*/

The block under the cursor is the one that runs (a lone block runs from anywhere). Inline prompts persist with the file and can be saved alongside your queries — useful for documenting intent or keeping a prompt you run regularly.

Both methods send your database schema (table names and column types) to the AI provider as context, so the generated SQL uses your actual table and column names.

Configuring a provider

Go to Settings → AI and choose a provider:

ProviderWhat you need
AnthropicAn API key from console.anthropic.com
OpenAIAn API key from platform.openai.com
OllamaA running Ollama instance; configure its URL in Settings (default: http://localhost:11434)

API keys are stored in the system keychain, not in plain text. You can override the model name for any provider in Settings.


Workspaces

A workspace is a named work environment. It owns its open query tabs and a chosen subset of your global connections. Keep a Staging workspace and a Production workspace, and neither can accidentally run against the other's databases. qub creates a Default workspace for you, so you can start with one normal editing space and add more only when separate scopes are useful. Everything is saved continuously — close the app mid-task and the workspace reopens exactly as you left it.

Creating and switching

The Workspaces card on the welcome screen lists all workspaces with their tab counts and connections — click one to open it, or create, rename, and delete from there. Inside the editor, the workspace name in the toolbar opens a switcher menu with the same actions. Switching saves the current workspace first; nothing is lost.

Workspace connections

Each workspace contains an explicit subset of your global connections, and only those appear in its connection menus and schema browser. Manage the list via Workspace connections… in the switcher menu, or add one on the fly with Add connection to workspace… at the bottom of any connection dropdown. Opening a connection from the welcome screen adds it to the current workspace automatically.

This is a safety boundary: if a tab's connection is removed from the workspace (or deleted entirely), the tab keeps its SQL and stays editable, but running is blocked and a warning banner appears — qub never silently retargets a query at a different database. The banner's Add to workspace button restores access in one click.


Activity log

The activity log is a separate window recording what qub does behind the scenes: connection opens and failures (CONN), executed queries with timing and row counts (QUERY), SSH tunnel events (SSH), and AI requests (AI). Open it with Ctrl+L in the editor or the list icon in the welcome screen's top bar.

Click an entry to expand its details — a query entry shows the full SQL with an Open in editor button, an AI entry shows the prompt and generated SQL. Entries can be filtered by category and connection, and exported as JSON or CSV.

Slow queries

The Slow Queries tab of the same window ranks your executions by total time spent. Structurally identical statements are grouped together by a fingerprint — literal strings and numbers are collapsed to ?, so WHERE id = 1 and WHERE id = 42 count as one query — and each group shows the number of calls, total, average and maximum duration, how many runs failed, and when it last ran. Click a row to open its SQL in the editor, or use the connection dropdown to focus on a single database.

This turns your ordinary query history into a lightweight profiler: the query at the top of the list is where your time is actually going, whether because it is slow or because you run it constantly.


Live share

Live share lets you stream query results to any browser in real time using a shareable link. No installation is required on the viewer's side.

Starting a share

  1. Click Share in the workspace toolbar.

  2. Choose the network scope: same machine, local network, or public internet.

  3. Copy the generated URL and send it to viewers.

Every time you run a query, the result table updates live in all connected browsers. The viewer count is shown in the toolbar while sharing is active.

TLS / HTTPS

qub can serve the share link over HTTPS. By default it generates a self-signed certificate. You can provide your own certificate and private key in Settings → Sharing.

Ending a share on time

The way live share goes wrong is not starting it by mistake — it is leaving it on. Set Stop sharing after in Settings → Sharing to a number of minutes and the session ends by itself: the toolbar counts down, warns a minute before, and tells you when it has stopped. Zero, the default, leaves a session up until you stop it.

A profile can also forbid sharing outright — see Allow Live Share under the profile rules.

Live share exposes your query results over the network. Use with care on production data. Stop sharing with the Stop button in the toolbar.


Profiles

A profile is a named set of safety rules you can assign to a connection. Profiles help you avoid costly mistakes — especially on production databases — by either blocking certain query types outright or requiring explicit confirmation.

Creating a profile

  1. Go to Settings → Profiles → Add Profile.

  2. Give the profile a name (e.g. Production) and pick a color to identify it visually.

  3. Enable the rules you want to enforce.

  4. Click Save.

Available rules

Visual indicators

When a connection has a profile assigned, a colored stripe appears along the right edge of that connection's tab, and the profile name and color dot are shown in the status bar.

A common setup: a red Production profile with all confirm rules enabled, and a green Dev profile with no rules. The color makes the environment obvious at a glance.


Themes

qub supports full palette customization. Every color in the interface — backgrounds, surfaces, borders, text, and status colors — can be changed through the theme system. Themes are applied live and stored locally.

Built-in themes

Default

The standard Mahina palette — clean blues, neutral greys, and warm surfaces.

Dracula

A homage to one of the most beloved dark themes in the developer community, originally created by Zeno Rocha — someone whose work and commitment to the open-source community I deeply admire. Purple accents on a near-black canvas, exactly as it should be.

Solarized

The classic precision-designed palette by Ethan Schoonover. Works beautifully in both light and dark mode.

WoMakersCode

A tribute to all women in computer science and technology — and in particular to the incredible people running the WoMakersCode program. The colors are taken directly from their iconic pixelated butterfly logo.

🦋

This theme exists because representation matters. If you are a woman in tech, know that you belong here.

Creating a custom theme

  1. Go to Settings → Themes → New Theme.

  2. Give your theme a name.

  3. Use the color pickers to customize each palette token. Changes apply live as you pick.

  4. Click Save. The theme appears in the list.

  5. Click the icon next to it to activate it.

Import and export

Use Export (↑ icon) next to any theme to save it as a .json file. Use Import (↓ icon in the section header) to load a theme someone shared with you; a theme whose name already exists is rejected rather than duplicated. Built-in themes cannot be edited, but you can export one as a starting point, edit the name inside the file, and import your customized copy.


Settings

Access settings from the Settings tab on the welcome screen.

Appearance

SettingDescription
Dark modeSwitch between light and dark mode. The active theme adapts automatically.

Connection

SettingDescription
Always close connection when leaving Skip the "keep connections open?" dialog when leaving the workspace.
Auto-reconnect on disconnect Automatically attempt to reconnect if the database connection drops.

Editor

SettingDescription
Font familyMonospace font used in the query editor.
Font sizeEditor font size in pixels (10–24).
Font weightNormal or medium weight.
Tab sizeNumber of spaces per tab (2, 4, or 8).
Insert spaces for TabInsert spaces instead of a tab character.
Line heightCompact, normal, or comfortable line spacing.
Highlight current lineSubtle highlight on the line the cursor is on.
AutocompleteEnable or disable schema-aware autocomplete.
Keyword case in generated SQLWhether the SQL qub writes for you (a table's browse button, a double-clicked table, a foreign-key jump) uses SELECT * FROM or select * from. It never recases SQL you typed, and never an identifier.
Default result limitDefault row cap applied to every query.

Schema browser

SettingDescription
Insert on double-clickDouble-clicking a table or column inserts it at the cursor.
Show quick browse buttonShow the ▶ play button on hover in the schema tree.

History

SettingDescription
History limitMaximum number of history entries to keep (10–1000).

AI

SettingDescription
ProviderAnthropic, OpenAI, or Ollama.
API keyStored in the system keychain, never in plain text.
ModelOverride the default model for the selected provider.
Ollama URLBase URL of your Ollama instance (default: http://localhost:11434).

Sharing

SettingDescription
Warn on start / stopShow a confirmation before starting or stopping live share.
Pulse the buttonRing the Live Share button while a session runs. The button is red and reads "Stop Live Share" either way.
Stop sharing afterMinutes before a running session ends by itself. 0 leaves it up until you stop it.
TLS certificatePath to a custom certificate file for HTTPS sharing.
TLS private keyPath to the private key matching the certificate.

Profiles & Themes

SettingDescription
ProfilesCreate and manage safety rule sets. See Profiles.
ThemesActivate a built-in theme or create your own. See Themes.

Keyboard shortcuts

Query editor

ActionShortcut
Run query (or selection)Ctrl+Enter
Run /* @ai */ block as AI promptCtrl+Shift+Enter
EXPLAIN queryCtrl+E
Format SQLCtrl+Shift+F
FindCtrl+F
Find and replaceCtrl+H
UndoCtrl+Z
RedoCtrl+Shift+Z
Select allCtrl+A

AI

ActionShortcut
Open AI command paletteCtrl+K
Run inline /* @ai */ promptCtrl+Shift+Enter

Tabs

ActionShortcut
New query tabCtrl+T
Close query tabCtrl+W

Files

ActionShortcut
Save selection as snippetCtrl+S
Save to fileCtrl+Shift+S
Open fileCtrl+O
Export as CSVCtrl+Shift+E

Panels

ActionShortcut
Toggle schema panelCtrl+1
Toggle query panelCtrl+2
Toggle results panelCtrl+3
Toggle sidebarCtrl+4
Toggle markdown previewCtrl+5
Toggle activity logCtrl+L
Command paletteCtrl+P
Show keyboard shortcuts?