Last updated: 2026-07-08

Visual Query Editor

Open any SQL statement as a structural diagram and edit it round-trip. The Visual Query Editor renders SELECT, INSERT, UPDATE, DELETE, and MERGE shapes as a stack of clause cards — joins, CTEs (including recursive ones), subqueries, set operators, and window functions all become navigable structure — and the Add buttons, inline pills, and chooser popovers write changes back into the same SQL buffer, byte-precise and undoable. You can run the statement without leaving the tab, too.

Buffer is the source of truth. Every edit you make in the Visual Query Editor is rewritten to the SQL buffer behind it. The compact popover commits each edit as a single Monaco undo stop, so Cmd/Ctrl+Z reverts it exactly like a manual keystroke. The full-tab editor keeps its own bounded undo/redo stack instead — up to 50 steps, per tab — since that tab has no Monaco instance of its own; Cmd/Ctrl+Z and Cmd/Ctrl+Shift+Z step back and forward through it there.

Why a structural view?

Long statements bury their structure. A 60-line SELECT with three CTEs, two outer joins, a window function, and a UNION branch is hard to read line-by-line — but the shape is small once it is laid out as boxes and arrows. The Visual Query Editor flips a buffer of SQL into that shape so you can:

  • See every JOIN partner at a glance, including USING / ON conditions and join kind (INNER, LEFT, RIGHT, FULL, CROSS, CROSS APPLY, OUTER APPLY, LATERAL).
  • Walk a CTE chain top-down: each CTE renders its own card and links into the body that consumes it.
  • Spot subqueries in FROM and WHERE as expandable cards rather than nested parentheses.
  • Read a UNION / INTERSECT / EXCEPT pipeline as labeled branches with the operator between each one.
  • Navigate INSERT / UPDATE / DELETE / MERGE shapes with the same structure — column maps, SET assignments, WHEN-MATCHED branches, and so on.

Edits still happen in your Monaco buffer. VQE is the structural lens; the buffer is the source of truth.

When to use VQE — and when not to

The Visual Query Editor is most useful when you are reading SQL someone else wrote, or coming back to your own large query after a few days. It is less useful when you are mid-typing — Jam SQL Studio is a text-first IDE; VQE is the read lens.

  • Use it for: reviewing a long SELECT, exploring an unfamiliar query in a notebook, understanding a procedure body's main statement, walking through a complex MERGE, picking which CTE to comment out.
  • Skip it for: writing a new query from scratch, quick edits to a one-line statement, anything that does not benefit from a structural map.

Opening the Visual Query Editor

You can reach VQE four ways: the main-toolbar Visual button (opens the full workspace tab), the on-demand edit affordance under your cursor (the gutter pencil or the run-bar Edit button → the compact editor popover, with an Open full VQE hand-off), the Cmd/Ctrl+Shift+V shortcut, or the Command Palette. Each one ends up rendering the same statement; the difference is just the entry surface.

Main toolbar — Visual button

The fastest entry point. Click the Visual button in the main toolbar to open a Visual Query Editor tab seeded with the buffer text of your active Query Editor. The new tab inherits the full buffer text so the structural renderer can pick the statement at the cursor; the source Query Editor stays open in its own tab.

Main toolbar showing the Visual button between the standard query buttons and the More menu.
The Visual button in the main toolbar opens a Visual Query Editor tab seeded with the active query's buffer.

If no Query Editor is active (for example, when you are sitting on the start page), the Visual button still works — it opens a Visual Query Editor tab titled Visual Query with a starter canvas instead of an empty buffer: a searchable Start from a table picker, grouped by schema, and a Start from SQL button as the escape hatch. Pick a table and Jam seeds the buffer with SELECT * FROM <table> — a real, executable query from the moment it lands — and switches straight into the structural renderer; click Start from SQL instead to jump to an ordinary Query Editor tab and type your own statement. The table picker stays disabled, with an explanatory tooltip, until a connection is bound and its schema snapshot has loaded.

Edit right where your cursor sits — the compact editor

Inside any Query Editor, an edit affordance appears for the SELECT / INSERT / UPDATE / DELETE / MERGE statement under your cursor. Its form follows the existing Inline Execute/Peek bar editor setting:

  • Setting off (the default) — a small gutter pencil icon shows up in Monaco's glyph margin, to the left of the line numbers, with the tooltip "Edit this statement visually". It renders for the cursor statement in every buffer, single- or multi-statement. When the Execute / Peek gutter buttons also render (those appear only in buffers with 2+ statements), the pencil stacks below them.
  • Setting on — a small pencil-icon Edit button rides on the floating Execute / Peek run bar.

Clicking the pencil (or the Edit button) opens the compact Visual Query Editor popover, anchored right at the cursor statement and running over the same byte-precise edit pipeline as the full VQE. The popover shows a summary line (e.g. "SELECT …") plus the clause cards; it covers the full SELECT shape inline — columns, DISTINCT, WHERE, joins, GROUP BY, HAVING (aggregate templates included), ORDER BY, and LIMIT — and each edit writes back into the SQL buffer. Press Esc to close it.

The popover's Open full VQE button hands off to the full visual-query workspace tab, seeded on the exact statement under your cursor — it threads the statement offset, so in a multi-statement buffer the full editor opens on the right statement, not statement #1.

Gutter pencil edit affordance next to a SELECT statement, with the compact Visual Query Editor popover open.
Click the gutter pencil (or the Edit button on the run bar) to open the compact editor anchored at the cursor statement.

DDL statements get their own compact treatment: instead of a clause-card stack, the popover shows a read-only summary card — a kind chip (CREATE TABLE, ALTER TABLE, DROP, and so on), the schema-qualified object name, and a few parsed facts (a column count for a CREATE TABLE, the columns an ALTER TABLE adds or drops, and similar per-kind detail) — plus a Design table button and an Edit as SQL button. Design table opens the Table Designer in create mode from a CREATE TABLE card or alter mode from an ALTER TABLE card, and is disabled with a tooltip when no connection is bound. The summary card covers CREATE, ALTER, DROP, TRUNCATE, RENAME, GRANT, and REVOKE statements. Shapes VQE cannot diagram at all (procedural bodies, parse errors, and DDL-adjacent statements like EXPLAIN or SET) fall back to a read-only view with an Edit as SQL escape hatch, which is the universal way back to the raw buffer.

Keyboard shortcut — Cmd/Ctrl+Shift+V

Press Cmd+Shift+V on macOS or Ctrl+Shift+V on Windows / Linux. The shortcut behaves the same way as the toolbar button — it inherits the active Query Editor's buffer when there is one, otherwise opens the starter canvas above. The shortcut is also listed on the keyboard shortcuts page.

Command Palette

Open the Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows / Linux) and run Open Visual Query Editor. It behaves exactly like the toolbar button: it seeds a new tab from the active Query Editor's buffer, or opens the starter canvas above when there is nothing to inherit.

Reading a structural diagram

Every render mode lays out the statement as a vertical stack of clause cards. Each card carries a header label, a body of pills / rows, and a footer with an Add button (disabled until editing is available). The cards are scoped to the current statement only — multi-statement buffers show a statement picker at the top.

SELECT — the canonical shape

Jam renders cards source-first — in the order the engine actually evaluates the statement, not the order you type it in. A SELECT can render up to nine cards, most of them only when the buffer actually has that clause:

  1. CTEs — one card per CTE, when the statement has a WITH clause. Rendered above everything else; see below.
  2. FROM — one item per from-source. Tables render as schema-qualified pills; subqueries render as expandable cards; the section is a drop zone — drag a table or view from the Object Explorer directly into FROM to add it.
  3. SELECT list — one pill per projected column. Each pill shows the expression, optional alias (AS bb), inline-rename affordance, and a remove (×) button; the whole pill is the drag handle for reordering. A DISTINCT toggle sits in the card header.
  4. WHERE — top-level boolean fragments split by AND / OR. Each fragment is a pill with click-to-edit text and remove; to negate a whole AND / OR group with NOT, switch the card to the Rail (grouped) view via its Auto / Inline / Rail toggle.
  5. GROUP BY — one chip per grouping column, with aggregate-awareness (see below).
  6. HAVING — the same pill / Rail treatment as WHERE, gated on a GROUP BY already existing (see below).
  7. ORDER BY — one row per sort entry, each with a column picker, ASC / DESC toggle, NULLS-first / -last toggle (where the dialect supports it), and remove.
  8. Named windows — one card per named window definition, when the statement has a top-level WINDOW clause.
  9. LIMIT / TOP / FETCH / OFFSET — a click-to-edit numeric span. The label switches to match the dialect: TOP on SQL Server, LIMIT on PostgreSQL / MySQL / SQLite, FETCH FIRST on Oracle.
Visual Query Editor rendering a SELECT statement as a stack of clause cards.
A SELECT statement rendered source-first: FROM, SELECT list, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT.

SELECT — joins

JOINs render as strips inside the FROM section, one per join. Each strip carries a kind dropdown (INNER, LEFT, RIGHT, FULL, CROSS, CROSS APPLY, OUTER APPLY, LATERAL — the available options change to match the dialect), an inline-edit ON / USING condition, and a remove. Self-joins keep the alias on the right-hand pill so you can tell the partners apart.

SELECT — CTEs

A WITH clause renders each CTE as its own card above the body. Recursive CTEs carry a "recursive" badge in the header and, for the common two-branch shape, label their Anchor and Recursive step parts — the recursion happens inline in the body, and it's fully editable (see below), not just displayed. Each card has a rename and remove affordance and an expand toggle so you can collapse a long CTE you have already understood. Cross-CTE references are resolved by the renderer — clicking a CTE name in the body card highlights the source CTE card.

SELECT — subqueries

Subqueries in FROM and JOIN appear as expandable cards inside the FROM section, headed by the derived table's alias rather than its raw SQL. Expand one and its own mini SELECT/WHERE/GROUP BY editor renders inline — fully editable (add a WHERE, change columns, and so on), not just a read-only preview, including LATERAL derived tables; a focus button still opens it in its own full view when you want more room. A column IN (SELECT …) predicate in WHERE renders as a clean wrapping block with a top-right ✕; click the drill button on the block to open the inner query in a nested view and edit it — this works in both the full Visual Query Editor and the compact editor popover. Other subquery forms (EXISTS, scalar subqueries) render as collapsible inline panels.

SELECT — set operators

UNION / UNION ALL / INTERSECT / EXCEPT compose multiple SELECTs into a pipeline. VQE renders each branch as its own SELECT card with a labeled bar between them showing the operator. Click the operator badge to open a dropdown and switch the pipeline's kind — UNION, UNION ALL, INTERSECT, EXCEPT (Oracle offers MINUS in place of EXCEPT, matching its dialect) — without retyping the branches; the branches themselves can't be reordered. When the buffer puts a set operator inside parentheses (precedence override), VQE flags the branch with an "in parens" badge so the precedence is visible. A set operator inside a CTE's own body — as in an ordinary recursive CTE — renders as a normal pipeline and isn't affected by this precedence check at all.

SELECT — window functions

Window functions render with their OVER (…) body as raw text behind an expand toggle — expand it to read the full OVER expression, and use the Edit OVER affordance to rewrite the whole clause as text. PARTITION BY / ORDER BY / frame sub-clauses are not parsed into structured fields.

INSERT

INSERT statements render as a stack of two to four cards depending on shape:

  • Target — schema-qualified table name with the column list as a row of pills, editable (see below).
  • Source — one of: a VALUES grid (rows × columns, with click-to-edit cells), an inline SELECT card (the full SELECT renderer, for INSERT…SELECT — with a focus button that drills into the nested SELECT so you can edit it in place), or a DEFAULT VALUES marker.
  • ON CONFLICT / ON DUPLICATE KEY — when present, renders as an editable card with the conflict target and the action body (DO NOTHING / DO UPDATE …). PostgreSQL and SQLite use ON CONFLICT; MySQL / MariaDB use ON DUPLICATE KEY UPDATE.
  • RETURNING — when present, renders as an editable column list. Available on PostgreSQL and SQLite 3.35+. Oracle's RETURNING…INTO and SQL Server's OUTPUT aren't modeled as editable tails.

UPDATE

UPDATE statements render as Target → SET assignments → optional FROM / JOIN → WHERE → optional RETURNING. Each SET assignment is a pill with a target column, an arrow, and an inline-edit value expression. UPDATE…FROM (PostgreSQL) and UPDATE…JOIN (MySQL / MariaDB) render their join sources as a sub-section under the SET card.

DELETE

DELETE statements render as Target → optional USING / JOIN → WHERE → optional RETURNING. The USING / JOIN section uses the same JOIN strip layout as SELECT.

MERGE

MERGE statements render as Target → Source (table or subquery) → ON condition → WHEN branches, on SQL Server, PostgreSQL 15+, and Oracle (MySQL and SQLite don't have a MERGE statement). Each WHEN branch is its own card showing the match kind (WHEN MATCHED, WHEN NOT MATCHED, WHEN NOT MATCHED BY SOURCE) and the action body (UPDATE assignments, INSERT column-and-values, or DELETE). MSSQL's BY TARGET / BY SOURCE qualifiers are surfaced; standard / PostgreSQL MERGE branches without a qualifier render as plain WHEN MATCHED / WHEN NOT MATCHED. Branches are editable — you can add a branch, switch a matched branch's action between UPDATE and DELETE, and remove a branch (see below).

Read-only fallback for unsupported shapes

VQE renders a safe read-only fallback view for buffers it cannot diagram structurally:

  • DDL (CREATE, ALTER, DROP, TRUNCATE, …) — renders the statement kind, the schema-qualified target, and a banner explaining that DDL is not structurally diagrammed.
  • Procedural bodies (BEGIN…END, CREATE PROCEDURE, CREATE FUNCTION, anonymous PL/SQL blocks) — renders the routine name and a hint that the body is read-only in VQE.
  • Set-operator precedence buffers — when paren-grouped set operators make a single canonical render impossible, VQE shows the outer pipeline only and falls back for the inner branches.
  • Parse errors — when the buffer cannot be parsed at all, VQE shows the error position (line + column), the unexpected token, and an Edit-as-SQL button that lands the cursor at the error offset.
  • No statement at cursor — when the buffer is comments-only or whitespace, VQE shows a neutral "no statement at cursor" panel.

Every fallback view ends with an Edit as SQL button that returns to the source Query Editor at the right offset. The fallback is not a dead end — it is a controlled landing zone with the escape hatch always one click away. Because the fallback panel already owns that button, the readiness banner above it explains the situation in text instead of piling on a second CTA — one primary action per screen.

Read-only fallback view rendering a CREATE TABLE statement with the kind label and Edit-as-SQL button.
DDL statements render in a read-only fallback view with an Edit-as-SQL escape hatch.

Edit as SQL — the escape hatch

Every structure card has an Edit as SQL action. Clicking it does one of two things, depending on where you came from:

  • From a Visual Query Editor tab — if the source Query Editor's buffer hasn't changed since this VQE tab was forked from it, Jam closes the VQE tab, writes your edited SQL straight back into that source tab, focuses it, and selects the matching statement so any keystroke replaces it. If the source buffer changed in the meantime — you edited it elsewhere, or the tab is gone — Jam opens a new Query-N tab seeded with your edited SQL instead, so it never overwrites text you've since changed.
  • From the compact editor / edit affordance — Jam focuses the source Query Editor (it never closes anything) and selects the matching statement at the cursor.

Parse-error fallbacks land the cursor on the error offset instead of selecting a range, so you can fix the typo where the parser flagged it.

Editing — round-trip from structure to SQL

Every clause card carries the inline pills and Add buttons that drive structural edits back into the buffer. Edits route through the same SQL parser the IntelliSense uses, so the buffer round-trips byte-for-byte: nothing is reformatted, no comments are dropped, identifier quoting stays intact. Every edit is undoable with Cmd/Ctrl+Z — the full tab keeps its own bounded undo/redo stack, and the compact popover commits each edit as a single Monaco undo stop.

An edit affordance is enabled when two things are true:

  • The tab is bound to a database connection (so the renderer knows what tables and columns are reachable). Tabs opened without a connection show the disabled scaffolded state — the structure still renders for reading.
  • The chooser needs schema metadata to populate (column / table / join suggestions). Free-text affordances like add CTE, add VALUES row, and add UPDATE assignment activate as soon as the connection is bound; suggestion-driven affordances like add column and add join wait for the schema snapshot to load.
Two helpers worth knowing. A readiness banner above the clause cards tells you exactly when editing is or isn't available — no connection yet, schema still loading, or a read-only shape — and is dismissible per tab. And pressing ? anywhere in the Visual Query Editor opens the keyboard-shortcut guide.
Visual Query Editor showing an enabled add-column popover over a SELECT statement, with a list of suggested columns from the FROM table.
The Add column chooser populated from the live FROM source — pick a column and the SELECT list rewrites in place.
Compact Visual Query Editor popover with the add-filter form open, choosing a column, operator, and value.
The compact popover's Add filter form writes a new WHERE predicate back into the SQL buffer.

SELECT list — add and remove columns

Click Add column in the SELECT list card to open a searchable chooser. By default it lists only the columns from the tables actually in your query's FROM / JOIN — not every table in the schema — grouped under each source table or alias and annotated with their data type and a PK / FK key hint, so you pick only the columns that make sense. Type to filter across every group. A Show all tables toggle at the bottom widens the list to the whole schema when you need it, and each table group has a + all action that adds every column from that table in one click. Pick a column and a new SELECT item appends with the right alias quoting for your dialect. If a SELECT item references a table that isn't in the FROM/JOIN, it's flagged with a red not in FROM warning so you can add the table or remove the column. The per-row × button removes a column. Both edits preserve the SELECT list's existing order and whitespace; the only thing that changes is the items themselves.

The card header also carries a DISTINCT toggle button — click it to add or remove SELECT DISTINCT from the statement. It's available in both the full editor and the compact popover, editing the same shared buffer either way.

FROM — add joins and tables

The FROM card leads with Add join (join a related table — see below) and offers Add cross-join as the second affordance. Add cross-join opens a table chooser drawn from the connection's schema and appends a comma-separated FROM item with the dialect-correct schema-qualified syntax — that produces a Cartesian product, which is why it is labelled a cross-join rather than a plain table add. You can also drag a table or view directly from the Object Explorer into the FROM card to add it as a FROM source. The per-row × button drops a FROM item, including any payload (CTE refs, subqueries, derived tables); JOINs that depend on the dropped item are not auto-removed — VQE flags them inline if the parser detects an unresolved reference. When a button is disabled, hover it for a one-line reason and the suggested alternative.

WHERE and HAVING — add, modify, remove filters

WHERE and HAVING share the same filter engine — everything below applies to both clauses; the HAVING-specific differences are called out at the end.

The WHERE (or HAVING) card offers a single Add button to append a new condition. It opens the same searchable column picker as SELECT — columns grouped by their source table, defaulting to the tables actually in your query (with a Show all tables toggle) — and, once the clause already holds a predicate, an AND / OR connector toggle at the top of the picker chooses how the new condition joins. When the clause is shown inline, each parenthesised group also carries its own Add button that drops a condition into that group (joined by the group's own connector), and each standalone condition carries a trailing Add that wraps it into a new parenthesised group, with an AND / OR choice for the predicate you add. Once you pick a column, a full filter editor opens with the same capabilities as Table Explorer filters: enum value pickers (for native enum columns, check-constraint enums, and sampled categorical values), JSON-path sub-operators (for JSON/JSONB columns), and loose-FK lookup buttons (for columns that carry a logical foreign-key reference in your database metadata). Clicking an existing filter pill opens the same editor pre-populated with the current value, so you can change the operator or value without retyping the whole predicate. A column IN (SELECT …) predicate renders as a clean wrapping block with a top-right ✕ and a drill button — clicking it opens the subquery as a nested query in its own view, in both the full Visual Query Editor and the compact editor popover. Other predicates that cannot be parsed into the structured editor (raw SQL expressions, functions) fall back to an inline text input. A pill whose column references a table that isn't in the FROM/JOIN is shown in red with a not in FROM warning. The × button on a filter pill removes it; removing the last filter drops the clause from the buffer.

HAVING adds two things on top of that. Its Add picker leads with an Aggregates group — COUNT(*), COUNT, SUM, AVG, MIN, and MAX templates — so you don't have to hand-type an aggregate expression; picking a zero-argument template like COUNT(*) commits immediately, and picking SUM / AVG / MIN / MAX re-opens the column list for the argument. And HAVING's empty Add affordance only appears once the statement already has a GROUP BY — a bare SELECT … FROM t has nowhere to invite a HAVING clause, so the section stays hidden until you add one. (A HAVING clause that's already in the buffer, without a GROUP BY, still renders and edits normally — the gate only blocks the empty-state invite.)

One asymmetry to know: adding a HAVING condition on an aggregate goes through the structured Aggregates picker above, but editing an existing aggregate pill — clicking COUNT(*) >= 2 to change it to >= 3 — falls back to a plain text input instead of the structured chip. The shared round-trip parser behind click-to-edit only recognizes plain column operator value shapes; any field containing a function call (an aggregate, or something like LOWER(name)) can't be represented structurally, so the pill opens for free-text editing instead. Press Enter to commit, Escape to cancel, same as any other filter.

JOINs — add, change kind, edit ON, remove

Inside the FROM card, each JOIN strip exposes:

  • Add join — opens a searchable chooser that lists foreign-key-linked tables under a Linked by foreign key heading (ranked first; FK detection works across all engines, including PostgreSQL and Oracle; pick one and Jam emits a fully-resolved INNER JOIN ... ON ... with the FK columns pre-filled) and every other reachable table under Other tables; type to filter both groups. Picking a table with no foreign key opens a structured ON-column step: you pick the table and column on each side of the join condition — the left side can be any table already in the query, not just the first FROM table — with no same-name column auto-selected. Refine the predicate afterward via the inline ON pill.
  • Kind dropdown — flip INNER ↔ LEFT ↔ RIGHT ↔ FULL ↔ CROSS. Switching to a CROSS-style kind drops the ON predicate; switching back synthesizes a placeholder predicate that you edit inline.
  • ON pill — click to edit the ON expression. A simple a.x = b.y predicate opens a structured two-column editor (a table picker and a column picker on each side of the =), with an Edit as text escape for anything more complex; everything else opens free-text editing. Enter commits, Escape reverts. Multi-column equality predicates (a.x = b.x AND a.y = b.y) are preserved verbatim on commit.
  • × button — removes the join, including its ON predicate.

ORDER BY — add, toggle, remove

Add sort opens the same searchable, table-grouped chooser as Add column — it defaults to the columns from the tables in your query (with a Show all tables toggle and a per-table + all bulk action). Each ORDER BY entry's direction toggle flips ASC ↔ DESC in place; the × button removes a sort entry, and an entry that references a table not in the FROM/JOIN is flagged in red. NULLS FIRST / LAST is rendered as a per-row toggle on dialects that support it (PostgreSQL, Oracle, SQLite 3.30+).

LIMIT / TOP / FETCH / OFFSET — inline numeric edit

Click the row-count span to edit it in place — the input only accepts digits; Enter or clicking away commits the new value, and Escape reverts to what was there before. Dialects with a separate OFFSET (PostgreSQL, MySQL, SQLite) get their own click-to-edit span alongside the row count, so you can change one without disturbing the other. Oracle's FETCH FIRST … ROWS ONLY keeps its full keyword text — only the numeric part is what you edit.

GROUP BY — add, reorder, remove (with aggregate awareness)

If the SELECT list contains aggregate functions (COUNT, SUM, AVG, MIN, MAX, plus dialect-specific extensions) and some of its other columns aren't in GROUP BY yet, the SELECT list card — not the GROUP BY card — surfaces a small warning ("N columns aren't in GROUP BY") with a one-click Fix all button that adds every missing non-aggregate column to GROUP BY at once; each affected SELECT pill also carries its own inline warning icon for a one-column fix. The Add group-by chooser on the GROUP BY card itself behaves like ORDER BY's — searchable, grouped by the tables in your query, with a Show all tables toggle, a per-table + all bulk action, and a red not in FROM flag on out-of-scope entries. Drag a chip's handle to reorder the GROUP BY list — this works in both the full editor and the compact popover. The × button removes an entry; removing the last one drops the GROUP BY clause.

CTEs — add, rename, remove

The CTE section above the body card supports:

  • Add CTE — opens a small two-input form (name + body SQL). Submitting prepends the CTE to the WITH list, or synthesizes a new WITH name AS (body) wrapper if the buffer didn't have a WITH clause.
  • Inline rename — click the CTE name pill, type a new name, press Enter. Jam case-folds and renames every reference to the CTE — including the CTE's own self-reference inside a recursive definition's recursive member — across the rest of the statement (subsequent CTE bodies + main statement body), excluding dotted references like t.cte_name.
  • Remove — drops the CTE definition and its trailing comma, with a confirmation prompt. Recursive CTEs support the same rename, remove, and body editing as non-recursive ones — the "recursive" badge and Anchor / Recursive step labels are informational, not restrictions.

Each CTE card also has an expand toggle, and — for a recursive CTE whose body is a plain anchor-member UNION (or UNION ALL) recursive-member shape — the two branches render as their own labeled Anchor / Recursive step cards, each independently editable. A focus button lets you drill into the CTE body as a nested view, the same way an INSERT…SELECT source does.

INSERT / UPDATE / DELETE / MERGE — DML edits

The DML cards expose the same shape as SELECT:

  • INSERT columnsAdd column opens a schema-aware chooser (disabled once every column is already listed); × removes a column, though the last one can't be removed.
  • INSERT VALUES — each cell is click-to-edit; Add row opens a column-aware row form (one input per known column); the per-row × removes a row.
  • INSERT…SELECT — the source query's focus button drills into the nested SELECT as its own full renderer, with a breadcrumb back to the INSERT card.
  • INSERT tails — ON CONFLICT (PostgreSQL / SQLite) and ON DUPLICATE KEY (MySQL / MariaDB) render as editable cards with a conflict-target / action switch; RETURNING (PostgreSQL / SQLite 3.35+) renders as an editable column list. See the INSERT section above for the full per-engine breakdown, including SQL Server and Oracle's non-goals.
  • UPDATE assignmentsAdd assignment opens a target-column / value form; each value pill is click-to-edit; × removes an assignment.
  • DELETE WHERE — reuses the WHERE filter affordances from SELECT. Identical commit semantics.
  • MERGE branches — an add-branch button at the end of the stack opens a new WHEN MATCHED, WHEN NOT MATCHED, or WHEN NOT MATCHED BY SOURCE branch (matched→UPDATE, matched→DELETE, or not-matched→INSERT); each matched branch also carries an action-kind dropdown to switch UPDATE ⇄ DELETE. Nested SET assignments inside a matched UPDATE branch support the same add / modify / remove operations as a top-level UPDATE. MSSQL and PostgreSQL 15+ allow multiple same-kind branches, disambiguated by an AND condition; Oracle allows at most one matched and one not-matched branch (the add-branch button disables once both exist) and has no standalone DELETE branch, so its action-kind switch stays disabled. × removes a branch.

Edit-as-SQL — still the universal escape hatch

For shapes structural editing does not cover (window function frames, MERGE's target / source / ON predicate, dialect-specific syntax the parser flagged as a recovery point), every card still carries an Edit as SQL button that lands the cursor in the Query Editor at the right offset. Use it when the structural edit you want isn't expressible — anything you can type into Monaco is fair game.

What does not ship

  • Structured window PARTITION BY / frame editing. The OVER body renders as raw text behind an expand toggle; the Edit OVER affordance rewrites the whole OVER (…) clause as text, but there are no structured PARTITION BY / frame fields.
  • Set operator branch reorder. UNION / INTERSECT / EXCEPT branches render as labeled cards and their operator kind is editable, but the branches themselves can't be reordered — remove and re-add via Edit-as-SQL if you need a different order.
  • Per-pill negation toggle. Individual filter pills carry no negate control — apply NOT to a whole AND / OR group from the Rail (grouped) view of the WHERE / HAVING card, or edit the pill text inline.
  • MERGE target, source, and ON predicate. Only branch add/remove and the action-kind switch are structural; the target table, source, and ON condition are read-only here — edit them via Edit-as-SQL.
  • RETURNING editing is INSERT-only. UPDATE and DELETE display an existing RETURNING list but don't offer an editable tail card for it.

For everything else: edit in VQE, watch the SQL buffer rewrite itself, and Cmd/Ctrl+Z when you change your mind.

Run your query

The Visual Query Editor doesn't just edit SQL — you can run it without leaving the tab. An Execute button (or Cmd/Ctrl+Enter) runs the statement currently selected in the statement picker, exactly like the Query Editor's own statement picker; while it's running, Execute swaps for a Stop button. Destructive statements (DELETE, DROP, TRUNCATE, and friends) go through the same confirmation dialog as the Query Editor — nothing runs unconfirmed just because you're in VQE.

Results land in the same results panel the Query Editor uses: the results grid, its copy / export menu, Results and Messages tabs, and the status line. If you edit the statement after running it, a small notice tells you the results are from an earlier version of the query, until you run it again — editing alone never clears the notice, only a fresh run does. Running works from the starter canvas too: pick a table, and the seeded SELECT * FROM <table> is a real, executable query from the moment it lands.

Performance & budget

The Visual Query Editor is designed to feel instant on real-world queries:

  • Render latency — every supported shape renders in under 50ms on representative buffers (10-line SELECT, 40-line JOIN-heavy SELECT, recursive CTE). The render cost is amortized across the parser and the structural extractor; only the statement under the cursor is rendered, not the whole buffer.
  • Bundle size — the VQE bundle is <120 KB gzipped. The renderer is lazy-loaded; opening it does not affect the cold-start cost of the rest of the app.
  • Re-render rate — buffer changes cause exactly one structural re-render. Unrelated UI mutations (focus changes, expansion toggles) do not invalidate the parsed structure cache.

Reporting parser issues

If VQE renders a buffer in an unexpected shape — wrong fallback, missing JOIN strip, garbled set-operator pipeline — some fallback screens surface a Report button: parse-error fallbacks always get one, and so do the two "this shouldn't happen" read-only fallbacks — an unsupported set-operator precedence shape, or a statement VQE simply doesn't recognize. The two expected fallbacks, DDL and procedural bodies, don't get a Report button, since landing there is by design, not a bug. Clicking Report opens a confirmation dialog showing the exact buffer text and a corpus file path; nothing is uploaded automatically. After you confirm, the SQL is written to a local corpus file you can hand to the Jam team. See the AI Workspace knowledge pack and the developer docs for details on the harness.

Keyboard shortcuts

ActionmacOSWindows / Linux
Open Visual Query Editor (full tab)Cmd+Shift+VCtrl+Shift+V
Open keyboard-shortcut help??
Move focus to the next / previous controlTab / Shift+TabTab / Shift+Tab
Activate the focused control, or open a list / statement pickerEnterEnter
Apply the focused filter or value editEnterEnter
Cancel the open editor / close the popover or pickerEscEsc
Pick up or drop a SELECT or GROUP BY chip to reorder itSpace / EnterSpace / Enter
Move the picked-up chip to a new positionArrow keysArrow keys
Cancel the reorder and keep the original orderEscEsc
Undo / redo the last applied edit in the SQL bufferCmd+Z / Cmd+Shift+ZCtrl+Z / Ctrl+Shift+Z
Move between statements in the statement pickerArrow keysArrow keys
Execute the active statementCmd+EnterCtrl+Enter

Press ? inside the Visual Query Editor to open the in-app keyboard-shortcut help dialog, which lists the same shortcuts grouped by section. See the full keyboard shortcuts reference for global navigation, query execution, and results-grid shortcuts.

Engine support matrix

The structural renderer covers every supported engine, and most editing affordances are dialect-aware too — a ✅ below means both rendering and editing work where VQE offers editing for that shape at all; the sections above spell out exactly which parts of each shape are editable. Some shapes are dialect-specific; the renderer adapts the labels and available join kinds per engine.

CapabilitySQL ServerPostgreSQLMySQL / MariaDBOracleSQLite
SELECT — basic / JOIN / subquery
SELECT — CROSS / OUTER APPLY
SELECT — LATERAL
CTE (non-recursive / recursive)✅ (8.0+)
UNION / INTERSECT / EXCEPT
Window functions✅ (8.0+)✅ (3.25+)
TOP / LIMIT / FETCH FIRSTTOPLIMITLIMITFETCHLIMIT
ORDER BY NULLS FIRST / LAST✅ (3.30+)
INSERT — VALUES / SELECT
INSERT — ON CONFLICT / ON DUPLICATE KEY
RETURNING (INSERT / UPDATE / DELETE)✅ (3.35+)
UPDATE — FROM / JOIN
DELETE — USING / JOIN
MERGE✅ (15+)
HAVING — full editing (pills, Rail, aggregate templates)
Set-operator kind switch (UNION / INTERSECT / EXCEPT / MINUS)
MERGE — branch add/remove + action switch✅ (15+)✅ (constrained)
Recursive CTE — rename / remove / body editing

Oracle's MERGE editing is constrained by its own dialect: at most one WHEN MATCHED and one WHEN NOT MATCHED branch (the add-branch button disables once both exist), and no action-kind switch, since Oracle has no standalone DELETE branch. SQL Server and PostgreSQL 15+ allow multiple same-kind branches and the full UPDATE ⇄ DELETE switch.

Limits

VQE has a few deliberate limits. They are recorded here so you can plan around them.

  • One statement at a time. Multi-statement buffers render the statement under the cursor; switch via the statement picker. There's no side-by-side multi-statement view.
  • OVER bodies are opaque. Window function frames render as text behind an expand toggle; the Edit OVER affordance rewrites the whole clause as text, but there's no structured PARTITION BY / frame editing.
  • Large edits preview before commit. A full Visual Query Editor edit opens a preview-before-commit diff dialog once it replaces at least the Preview threshold bytes (Settings → UI → Visual editor, default 50) — set it to 0 to always preview, or to a large number to disable. Smaller full-tab edits commit straight to the buffer, and the compact inline popover always commits straight to the buffer regardless of size — it has no preview dialog. Use Cmd/Ctrl+Z to undo either way. (DDL edits reached through the Table Designer always preview, regardless of this setting.)
  • The results panel doesn't edit data. Running a query from VQE shows the same grid, copy/export menu, and Results/Messages tabs as the Query Editor, but grid cell editing, add-to-WHERE-from-a-cell, and pinned result tabs aren't wired in here — use Table Explorer or the Query Editor's own results grid for those.

Related docs

  • Query Editor — write and execute SQL with IntelliSense, peek results, and column profiling.
  • Keyboard shortcuts — full reference for global, editor, and results shortcuts.
  • Scripting — generate DDL scripts from the Object Explorer.
  • Changelog — every release of Jam SQL Studio.