SQLly, the nerdy pig mascot with glasses
// what the pig has been up to

Beavers get all the credit…

Sure, everyone knows beavers are busy. But get a load of what this pig has been up to. Every commit, every fix, every late-night IntelliSense tweak - all here, fresh from the sty.

131
versions shipped
87
days of commits
1194
total commits
1013
sections tracked

The pig's daily grind

Changes per day - because counting commits is so last year.

May Jun Jul Aug Sep
Mon Wed Fri
Less More
v0.98.0
Sep 28, 2026 · 2 sections
Sep 28, 2026
2 sections

Clearer update alerts that match your platform

The app no longer announces a version that isn't available for your operating system and processor. Previously a Mac could be told about 0.97.3 while the newest Mac download was 0.97.1, and the list of changes included releases the download didn't have. The status bar now shows a labeled "Update available" chip with the version number instead of a small dot beside the version. When SQLLY starts and finds an update, it opens the update dialog. A new "Show at startup" checkbox in that dialog turns this off, leaving just the status-bar chip.

Accounts can hold several licenses, and Billing shows which one is in effect

Buying a second plan (say, Lifetime on top of an Annual) used to overwrite the earlier subscription record. That lost track of the first purchase and orphaned its Stripe subscription, so later renewals had nothing to attach to. Each purchase now gets its own subscription. The two exceptions: a redelivered Stripe checkout reuses its row, and a paid plan still replaces a Free one. The Billing page lists every license the account holds, including complimentary ones, with its status and term, and tags the one currently granting installs and features as "in effect". "Cancel renewal" now applies to a specific license. The API gains GET /api/billing/subscriptions (every license, the one in effect first and flagged isCurrent), and POST /api/billing/cancel accepts an optional subscriptionId. GET /api/billing/subscription still returns only the license in effect.

v0.97.3
Sep 27, 2026 · 1 section
Sep 27, 2026
1 section

Relay log tests no longer race other tests

The relay test that checks lifecycle events appear in the default logs could miss events on Windows. The logging library caches per-log-line decisions for the whole process, and a test running in parallel could lock a log line out before this test's capture existed. The two log-capture tests now run alone, one after another, in their own test binary.

v0.97.2
Sep 27, 2026 · 1 section
Sep 27, 2026
1 section

Bridge end-to-end tests wait for a routable bridge

The relay counts a connection as active when it reserves the slot, a moment before the connection can receive a session. The bridge end-to-end tests waited on that count, so under load a handshake could be sent too early, get dropped, and time out, which failed the Linux x64 release build. The relay now reports how many connections a new session could actually reach, and the tests wait on that.

v0.97.1
Sep 27, 2026 · 3 sections
Sep 27, 2026
3 sections

macOS download no longer published as a tiny stub

The release pipeline could publish a 163-byte file instead of the real macOS disk image: packaging on the Mac added hidden metadata entries to the archive, and the publish step picked one of those up as the download. The archive is now built without those entries, the publish step ignores them in older archives, and it refuses to publish any disk image under 1 MB.

Compare hub without the card grid

The /compare hub drops its "Pick a comparison" card grid; the pricing breakdown table and the landing-page comparison table already link to every comparison page, so the grid only repeated them.

Release builds retry after rustc crashes

The Linux release build now also retries when rustc itself panics mid-compile on the build machine, the same way it already retried compiler segfaults, so one flaky crash no longer drops the Linux x64 download from a release.

v0.97.0
Sep 27, 2026 · 11 sections
Sep 27, 2026
11 sections

Nine new comparison pages

Added full comparison pages for DbGate, DbVisualizer, Oracle SQL Developer, Postico, Querious, Redis Insight, Chat2DB, Adminer, and LibreDB Studio - each researched against the vendor's official site, docs, pricing page, and repository, and each ending with an honest "when the other tool wins" section. The /compare hub, its pricing snapshot table, the landing-page comparison table, and the sitemap list now cover all twenty tools.

Compare pages fact-checked, plus pricing everywhere

Every comparison page was re-verified against the vendors' official sites. Corrections: DataGrip has been free for non-commercial use since October 2025 (the page called it subscription-only, and its DuckDB/libSQL claims were too generous), MySQL Workbench does not officially support MariaDB and its visual modeling has not landed in the rebuilt Workbench 26, TablePlus shipped a stable Linux build in August 2026 (the page implied it was not on Linux), Sequel Ace's DBA-tools and connection-import cells understated its real features, and pgAdmin's pgAgent row now notes the June 2026 deprecation. The /compare hub gained a dated pricing breakdown table covering all eleven tools, and the landing page gained a high-level comparison table with price, supported databases, a one-line summary, and a link to each detail page.

Docs: no more cards, search, on-page contents, and a real 404

The documentation section was reworked after a design review. The boxed card grids on section pages are gone: each section now shows one index, built from the same tree as the sidebar, listing every topic with its status and a one-line description, so the page and the sidebar can no longer disagree or leave topics out. Concept boxes inside guides became plain prose, and the crate list became a table. Press / or Cmd/Ctrl+K anywhere in the docs to search every topic, including the section headings inside each guide, and jump straight to the matching section. Longer guides get an "On this page" list with the current section highlighted, and every section heading has a link you can copy. A mistyped or outdated docs link now shows the docs with the closest matching topics and a search shortcut, instead of a blank page, and the rest of the site's missing pages show the proper 404 page again. The sidebar is now a flat, dense tree with a status legend, links in text are underlined, lines are shorter, and the status badges, code comments, and wide code blocks meet accessibility contrast and keyboard requirements.

Home page promise: your feedback shapes SQLly

The home page's "Human designed. AI assisted. Human verified." section is replaced with a personal promise: SQLly is built to be a tool you want to use, it ships set up one way but will make room for how you work, and every piece of feedback is personally read. It links to the two in-app feedback paths, general feedback (screenshot with privacy blackouts and arrows) and IntelliSense F2 reports, and a new Send feedback docs page explains both.

Themes guide on the website

The docs have a new Themes page explaining Light, Dark and System modes, the twelve Light and Dark palettes, the High Contrast shortcut, and where to change them in Settings, with a side-by-side screenshot of the same workbench in all 24 palettes. The IntelliSense page's main screenshot and the F2 feedback dialog screenshot were retaken on the current app.

Lower Lifetime and renewal prices

Lifetime drops from $300 to $200, and Annual renewals drop from $50 to $25 a year; the Annual first year stays $100. The pricing page, home and compare tables, search-engine offer data, and the plan catalog behind checkout and the admin revenue figures all show the new prices.

Tap any screenshot to see it full size

Every app screenshot on the website, on the home page and throughout the docs, now opens full size in an overlay when clicked, or with Enter or Space from the keyboard. Click anywhere or press Escape to close it.

Slimmer home page

The home page's "Everything a database deserves" feature grid and the "Why a pig would choose SQLly" section are gone, and so is the footer's "Why SQLly" link that pointed at it.

Menu items and shortcuts that silently did nothing

Nearly fifty menu items and their keyboard shortcuts did nothing when clicked, including Help ▸ Send Feedback, Tip of the Day, Query ▸ Execute into New Result Tab, Run on Multiple Connections, Go to Object, Zoom In/Out, Format and Minify SQL, the transaction commands, and the plan tools. Every command that is handled at the app level, rather than by the focused view, was being dropped whenever a window was in front. They all work again.

Run on Multiple Connections without connecting first

Query ▸ Run on Multiple Connections… no longer needs an active connection: it opens straight to the connection picker and runs against whatever you tick. When a multi-connection run finishes, the results footer now stops its "Running" timer and the Pivot and Chart views become available; before this fix they kept counting and stayed on "Loading…" indefinitely.

Show literals in IntelliSense feedback

The Improve IntelliSense (F2) dialog still redacts string literals and comments in your query by default, but a new Show literals button reveals them in the preview and sends the query exactly as typed. Use it when the suggestion problem depends on a literal's value; Hide literals redacts them again before you submit.

v0.96.0
Sep 27, 2026 · 7 sections
Sep 27, 2026
7 sections

Fix the server test run exhausting file watchers on the Linux build agent

The Sqlly Store build failed with 60 story tests erroring because each test host watched its settings and content files for changes, and together they used up the Linux agent's limit of 128 file-watcher instances. Test hosts now skip config reloading and use polling file watching, so the suite no longer depends on that system limit.

Collect general app feedback with screenshots and triage it on the store

The store can now receive general feedback from the app's feedback dialog: a message, an annotated screenshot with private areas already blacked out on your machine, and basic app and platform details. Screenshots are kept in private cloud storage that only admins can view, and retried uploads never create duplicates. Admins get a new General feedback page under the Admin menu to filter and search reports, view each screenshot with zoom, see who sent it, move it through submitted, reviewing, planned, rejected and implemented, and write an affirmative test, a negative test, more details and private notes. A matching admin API and a new general-feedback skill let a coding agent pick up open reports, turn the tests into a fix, and update their status.

Plan actions explain when a database cannot produce plans

Every way to capture a query plan — the Explain button and its options menu, the More query tools menu, the in-window menu bar, the command palette, keyboard shortcuts, and the plan pane's Re-capture button and empty state — now shows, by engine name, when the connected database cannot produce that plan, for example "Query plans are disabled for this database type (Redis)", followed by what to use instead. Estimated-only databases (SQLite, libSQL / Turso, ClickHouse) grey out only the actual-plan actions and point to Estimated Plan. Re-capture in the plan pane now repeats the kind of plan on screen, so estimated plans can be refreshed on those engines. Plan shortcuts no longer silently do nothing.

Tip of the Day

SQLLY now shows a short Tip of the Day when it starts, so you can discover features you might otherwise miss — typed query parameters, result diffing, the command palette, production guards, and more. A new install starts with an overview of IntelliSense and the two ways to send feedback: Improve IntelliSense from the completion popup (F2), and Help ▸ Send Feedback… for everything else. Use Back and Next to browse all the tips, and each launch continues from where you left off. Untick "Show tips on startup" in the dialog, or in Settings ▸ Appearance ▸ Display, to turn it off. Help ▸ Tip of the Day… and the command palette open it any time.

Send Feedback with an annotated screenshot

You can now send feedback about anything — a bug, an idea, a confusing screen — from Help ▸ Send Feedback…, the command palette, or Cmd+Option+F (Ctrl+Alt+F on Windows and Linux). SQLLY takes a full-resolution screenshot of the window and opens it in its own window, where you can draw arrows to point at things and cover private data with black boxes you can move and resize, with undo and delete. Blacked-out areas are burned into the image on your computer before anything is sent, so the hidden pixels never leave your machine, and you can untick "Include screenshot" to send just your message. If the upload can't get through, SQLLY keeps retrying in the background for up to five hours, even across restarts, and shows the result in the status bar. Screenshots are captured on macOS for now; on Windows and Linux the message is sent without one.

Completions quote names that need it, in your database's style

Accepting a table, view, column, schema, database, or routine suggestion now inserts the name quoted whenever it has to be: names with spaces, dashes, a leading digit, or other special characters, reserved words like Order or User, and on PostgreSQL and Oracle mixed-case names the database would otherwise fold. SQLLY uses each engine's own style — [brackets] for SQL Server, backticks for MySQL, MariaDB, and ClickHouse, and double quotes for PostgreSQL, Oracle, SQLite, DuckDB, and libSQL — doubles any embedded quote character, and only quotes the parts of a qualified name that need it (dbo.[Order Details]). Alias-qualified columns and suggested joins are quoted the same way, and if you have already typed an opening bracket or quote, the name is completed inside it and closed for you instead of getting a second set.

Choose whether table aliases are added automatically

A new setting under Settings ▸ Editor ▸ Defaults, "When you accept a table or view", lets you pick between having the derived alias added the moment you accept a table after FROM or JOIN (now the default, e.g. Orders AS o) or having it offered as a second, one-tap suggestion that you accept separately. It applies to both the "Capital letters to lower case" and "Auto based on join" alias rules.

v0.95.0
Sep 26, 2026 – Sep 27, 2026 · 21 sections
Sep 27, 2026
6 sections

A landing page for every database

The website now has a Databases section at sqlly.app/databases with its own page for SQL Server and Azure SQL, PostgreSQL, MySQL, MariaDB, SQLite, DuckDB, ClickHouse, Oracle Database, libSQL / Turso and Redis. Each page covers what SQLly does for that database, how to connect step by step, which tools apply (and why when one does not), and common questions, and the hub shows every tool against every engine.

Seven new comparison pages, led by Azure Data Studio

Azure Data Studio was retired on February 28, 2026, so there is now an honest guide for people replacing it: what ADS users relied on, what SQLly covers, what it does not (extensions, SQL projects, Jupyter notebooks), and how to move over. New side-by-side pages also cover Beekeeper Studio, pgAdmin, Navicat, HeidiSQL, MySQL Workbench and Sequel Ace, each saying where the other tool is the better pick.

Free online SQL formatter

sqlly.app/tools/sql-formatter runs SQLly's own formatter in the browser: pick a dialect and style, then format or minify. Your SQL never leaves your device. The release pipeline builds the formatter and the site deploy picks it up automatically.

Five new how-to blog posts

New posts cover moving off Azure Data Studio, querying Parquet and CSV files with DuckDB, reading query plans across PostgreSQL, SQL Server, MySQL and SQLite, using Turso and libSQL from a desktop client, and working with ClickHouse.

Better search and sharing for the website

Every public page now gets its own social-media preview image, and the sitemap tells search engines when each page last changed. The product listing that search engines read now includes the current version, screenshots and a feature list. New pages are announced to Bing and other IndexNow search engines after each deploy. There is an llms.txt summary for AI search tools, and public pages can be cached at the edge for faster loads. The home page's main image also loads faster.

Homebrew and winget packages prepared

Each release now produces a ready-to-submit Homebrew cask and winget manifests with checksums. They are not published yet, and the matching install commands on the download page stay hidden until the channels go live.

Sep 26, 2026
15 sections

Native DuckDB support

SQLLY now connects to DuckDB out of the box, with nothing to install. Choose DuckDB in the connection editor and point it at a .duckdb file or :memory:, or double-click or drop a .duckdb file, and you get the same experience as any other engine: streaming results with DuckDB's types shown faithfully (lists, structs and maps as JSON), per-statement row counts, Cancel, the explorer tree, IntelliSense and hover, the Object Browser, Disk Usage, the Server Dashboard, and estimated and actual query plans. Reading Parquet, CSV and JSON files with read_parquet, read_csv and read_json works offline, an in-memory scratch database is shared across tabs while the app runs, and DuckDB connections work through a bridge just like SQLite. Tools DuckDB has no counterpart for (Activity Monitor, Security, row editing, compare and transfer) are marked unavailable with a reason instead of failing.

Native ClickHouse support

ClickHouse joins SQL Server, PostgreSQL, MySQL/MariaDB, SQLite and DuckDB as a built-in engine: pick ClickHouse in the connection editor (or paste a clickhouse:// URL), connect over its HTTP interface with the same TLS modes, SSH tunnels and proxies as the other network engines, and query it like any other server. Results stream into the grid as ClickHouse produces them, with wide integers and decimals kept exact and arrays, tuples and maps shown as JSON; scripts run statement by statement and report each result or the rows written; Cancel stops the query on the server; and errors show ClickHouse's own message. The explorer, IntelliSense, hover, Object Browser, Disk Usage, Server Dashboard, Activity Monitor (with Kill), Estimated Plan with each index's pruning and the Maintenance menu's OPTIMIZE TABLE … FINAL all read ClickHouse's system tables, while actual plans, row editing, Security, Backup and the compare/transfer tools say why they are unavailable, and the rollback-wrap safety setting is refused because ClickHouse has no transactions.

Native Oracle Database support

SQLLY now connects to Oracle Database with a built-in driver: pick Oracle Database in the connection editor, enter the host, port, service name (or SID: prefixed SID) and credentials, and connect — the free Oracle Instant Client must be installed on your machine, and if it is missing Test Connection tells you exactly what to install and where SQLLY looks for it. Queries and PL/SQL scripts stream into the results grid with Oracle's own types shown faithfully, DBMS_OUTPUT lines land in the Messages pane, Cancel interrupts a running statement, and the explorer, IntelliSense, hover, Object Browser, estimated execution plans, Activity Monitor with session kill, Disk Usage, Server Dashboard and the Maintenance actions (gather statistics, rebuild indexes, validate structure) all understand Oracle's catalog. Row editing, the Security editor, backup, and the compare and transfer tools are marked unavailable on Oracle with a reason.

DuckDB tools: maintenance, row editing, transfer, compare, structure editing, dependencies, and URLs

DuckDB connections now get the full toolset wherever DuckDB has the feature instead of an "unavailable" note. The explorer's Maintenance submenu offers VACUUM, VACUUM ANALYZE, ANALYZE, CHECKPOINT and FORCE CHECKPOINT; result rows can be edited, inserted, duplicated and deleted with the same guarded, transactional saves as the other engines; Data Transfer can copy tables into a DuckDB file; Schema Compare, schema snapshots and Data Compare read DuckDB's catalog (tables, views, indexes, constraints, sequences and macros) and script DuckDB's own DDL; the Table Structure Editor produces DuckDB ALTER statements and calls out the constraint changes DuckDB cannot make in place; View Dependencies and Find Usages work from DuckDB's foreign-key, index and sequence catalog plus view and macro source; the Test Data Generator reads DuckDB columns; DDL documents include indexes and ENUM types; database dumps advance sequences past the rows they load; estimated plans shade their heaviest operators; DuckDB gets its own welcome badge; and duckdb: URLs or bare .duckdb / .ddb paths can be pasted into the connection URL box. Tools DuckDB genuinely lacks (Activity Monitor, Query Insights, Index Analyzer, Security, Backup / Restore) now explain why in DuckDB's own terms.

libSQL and Turso connections

SQLLY now connects natively to Turso Cloud databases and self-hosted libSQL servers. Pick libSQL / Turso in the connection editor, enter the host (or paste a libsql:// URL with its token), paste the database's auth token — stored in your system keychain, or left blank for a local server without authentication — and work with the database exactly like SQLite: queries run with SQLite's dialect and show every value faithfully, scripts run as one unit so transactions and the rollback-wrap safety setting work and the first failing statement stops the script with the server's own message, and the explorer, IntelliSense, Object Browser, Disk Usage, Server Dashboard, Estimated Plan, dependencies, Schema Compare, Schema History, Data Compare, the Test Data Generator, the structure editor and the Maintenance menu all understand it. A Turso host is always reached over HTTPS, and SSH tunnels, proxies, port-forwards and the TLS modes work for a self-hosted server. Row editing, transferring data into libSQL, Activity Monitor, Index Analyzer, Security and Backup are marked unavailable with a reason. While verifying against a live server, two SQLite catalog queries (constraint definitions and per-table index columns) were corrected for SQLite as well.

Every tool with a ClickHouse counterpart now works on ClickHouse

ClickHouse connections now get the full tool set wherever ClickHouse has a real equivalent: Query Insights reads the server's query log (top queries by duration, executions or rows read, with a time window, plus a recent-failures view showing the server's exception text); Schema Compare, Schema History and Data Compare understand ClickHouse tables, views, materialized views, dictionaries, data-skipping indexes and constraints and script ClickHouse's own CREATE OR REPLACE, RENAME and ALTER … UPDATE / DELETE statements; the Test Data Generator reads ClickHouse column types (including Nullable, LowCardinality, Enum, Array, Map and small integers) and produces INSERTs that run; Data Transfer can copy tables from any connection into ClickHouse, creating MergeTree tables ordered by the source key; the Table Structure Editor scripts ADD, DROP, RENAME and MODIFY COLUMN and skipping-index changes and flags key changes for manual review; the Security dialog opens as a read-only view of users, roles and grants and scripts CREATE USER / GRANT statements for editing in a query tab; View Dependencies and Find Usages read ClickHouse's recorded dependencies and CREATE text; and the Backup / Restore dialog scripts BACKUP / RESTORE DATABASE to the server's backups disk. Tools with no ClickHouse counterpart (actual plans, in-grid row editing, Index Analyzer, designers, Extended Events, SQL Agent) now say specifically why.

Redis support: command editor, key browser and monitoring

Redis — and Valkey, KeyDB, DragonflyDB, Amazon ElastiCache and Amazon MemoryDB as Redis-compatible labels — is now a first-party engine. Pick Redis in the connection editor (host, port 6379, optional ACL user, password, logical database number, TLS mode, and a key delimiter for the browser), or paste a redis:// or rediss:// URL, and connect through the same SSH tunnels, proxies and port-forwards as the SQL engines. A Redis query tab runs one command per line in redis-cli syntax (or a JSON list of arguments), shows every reply as a result set — hashes as field/value, sorted sets as member/score, INFO, CLIENT LIST and SLOWLOG as tables — reports the server's own error with the line number, follows SELECT for the rest of the script, refuses commands that would hang forever, and stops when you press Cancel; SQL validation, IntelliSense and formatting stay out of the way. The explorer becomes a key browser: numbered databases with key counts, keys scanned safely and folded into prefix folders that load deeper levels on demand, type icons and TTLs on every key, double-click to open a value with the right read command, and menus to filter by pattern, copy commands, set or remove a TTL and delete a key with confirmation. Server Dashboard, Activity Monitor (with Kill), Query Insights (the slow log) and Disk Usage (memory per database) understand Redis, the SELECT-only lock and the AI's read-only mode allow read-only commands only, destructive commands go through the usual confirmation, and the SQL-only tools explain why they do not apply.

Website catches up with the new engines

The sqlly.app docs and marketing pages now describe the engine roster as it is: thirty engines connect today across the SQL Server, PostgreSQL, MySQL and SQLite families plus DuckDB, ClickHouse, Oracle Database, libSQL/Turso and Redis (with Valkey, KeyDB, DragonflyDB, ElastiCache and MemoryDB), with eight more still modeled. The Databases page gained an engine-by-tool coverage table taken straight from the app's own availability rules — including the reason each tool gives where an engine has no counterpart — and a "Connecting to" guide per new engine covering DuckDB files and :memory:, ClickHouse's HTTP port and TLS, the Oracle Instant Client install steps for macOS, Linux and Windows, Turso auth tokens, and Redis's one-command-per-line editor, key browser and read-only lock. The home page, the four comparison pages (whose FAQs now say Redis yes, MongoDB no), every tool doc, the plan, cancel, TLS, safety and row-editor pages, the engineering deep dive and the blog posts were brought in line, and the read-only docs now explain the database-enforced read-only mode that AI and agent queries run under on each engine.

Every tool with an Oracle counterpart now works on Oracle Database

Oracle connections are no longer limited to browsing and querying: in-grid row editing (keyed by the primary key, or by ROWID for tables without one, with Oracle date and timestamp values saved exactly as shown and DEFAULT resets), Query Insights from the shared pool, the Index Analyzer with Oracle's own usage tracking, Schema Compare and Schema History with storage-free DDL, Data Compare, Data Transfer into Oracle, the Test Data Generator, the Structure Editor and Script as ALTER, View Dependencies and Find Usages, the Security editor for users and roles, actual execution plans with real row counts and timings, and SSH tunnels, proxies and Kubernetes port-forwards all work against Oracle the way they do on the other engines. Only server-side backups and the SQL Server-only SQL Agent, designers and Extended Events remain unavailable, each saying so.

Row editing and Data Transfer on libSQL / Turso

In-grid row editing now works on libSQL and Turso connections: Edit, Insert, Duplicate and Delete Row find the row's real source table through the server's own catalog, offer foreign-key lookups and CHECK-constraint dropdowns, and every save — including a staged multi-row edit set — runs as a single all-or-nothing transaction on the server, so a failed or stale statement leaves nothing half-applied. A table with no declared primary key can still be edited by including rowid in the SELECT. Data Transfer can now write into a libSQL / Turso database too, creating the table with SQLite types and loading rows in batches inside one transaction per table. Query Insights and Security now say precisely why they don't apply to libSQL / Turso instead of a generic message.

Documentation, About panel and CLI catch up with the new engines

The product docs, supported-platforms matrix, feature catalog, architecture notes, welcome screen badges, About credits and the command-line doctor now describe all nine native engines — SQL Server, PostgreSQL, MySQL/MariaDB, SQLite, DuckDB, ClickHouse, Oracle Database, libSQL / Turso and Redis — instead of the original four. The supported-platforms page gains a per-engine section listing the versions each was verified against, how you sign in, which TLS and tunnel options apply, and exactly which tools are available or unavailable and why (the same reasons the app shows in its menus), plus tables of the Redis- and libSQL-compatible services; sqlly-cli doctor prints one row per driver and tells you whether the Oracle Instant Client was found, and its --dialect option accepts engine names such as duckdb, clickhouse or libsql and explains which of the five parser families they use. A new validation report records which live server and which test file proved each engine, and what was not exercised.

Licenses page lists current crate versions

The licenses page on sqlly.app now lists every open-source crate SQLLY currently ships (1,210, up from 974) with its current version and license, regenerated from the lock file after the GPUI Kit migration and the DuckDB engine. The generator, scripts/generate-licenses.js, is now checked in so the page can be refreshed with one command.

Live preview gets its own page

The in-browser demo now lives at sqlly.app/live-preview, where the app fills the whole window between the site header and footer instead of sitting in a box inside the docs. Its docs topic moved to the top of the docs, just above Features, at /docs/live-preview, and now has a link to open the demo in a new window. The homepage's "Try it in your browser" buttons and other links go to the new page, and the old docs address redirects to the new one.

Canonical links no longer depend on server configuration

Website pages and blog posts now always publish full https://sqlly.app/... canonical and social-card links, even when the server's public-origin setting is left empty. Before, an empty setting produced relative canonical links, which broke the server build's SEO test.

Release builds no longer fail when parallel Linux jobs finish

The Linux and browser release jobs share one build machine and used to share one compiler-cache server. When the browser job finished, the build agent shut that server down while the Linux x64 job was still compiling, failing the release. Each job now runs its own cache server. The step that trims old build output also stopped failing on large rebuilds, where its list of new files grew past the system's size limit for a single program argument.

v0.94.0
Sep 26, 2026 · 10 sections
Sep 26, 2026
10 sections

Website search-engine overhaul: per-topic docs pages, blog, and comparison pages

Every docs topic now has its own address (for example /docs/features/results/pivot) instead of living behind a # on one giant page, so search engines can index each guide on its own. Each topic page has its own title, description, breadcrumbs, and previous/next links, and old /docs#... links forward to the new pages. Topics that are still only a one-line placeholder are hidden from search until their guide is written. Two docs links that pointed nowhere were fixed.

The site gained a blog at /blog (four launch posts on query safety, streaming large results, SQLly directives, and join-aware IntelliSense, with an RSS feed) and honest comparison pages at /compare for DBeaver, DataGrip, SSMS, and TablePlus.

Every page now gets consistent social-sharing previews, and page titles and descriptions were rewritten to fit what search results display (the Download page previously had none). The sitemap now lists every docs topic, blog post, and comparison page with update dates where known. The in-browser demos are kept out of search results, the Reviews page no longer publishes a self-assigned star rating that search engines penalize, www. and trailing-slash URLs redirect to one canonical address, and pages load faster thanks to compression, long-lived caching of versioned assets, and fixed image sizes that stop the layout jumping while screenshots load.

Release builds no longer fill the build agents' disks

The release pipeline keeps each platform's Rust build output between runs so unchanged code isn't rebuilt, but it never removed old output. Every release left a full extra copy of the app's compiled code behind, and the Linux build box reached 98% disk usage. The Linux, Windows, macOS and browser builds now delete whatever the current run replaced and cap each build directory at 40 GB. The browser build also always runs on the same Linux agent now, so it reuses its previous work instead of rebuilding from scratch whenever it lands on a different agent.

Checkout fails gracefully when the payment processor is misconfigured

When Stripe rejected a checkout — for example because a plan's configured price didn't exist on the Stripe account — the billing page crashed with a raw error that exposed the price ID. Buyers now stay on the billing page with a plain message saying the payment processor is misconfigured (or, for a Stripe outage, that it couldn't be reached and to try again shortly). The price, customer and Stripe error details go to the server log instead, where they're needed to fix the configuration. The same handling covers cancelling auto-renewal, and a missing invoice PDF now shows "not found" instead of an error page. Accounts whose saved Stripe customer doesn't exist on the current Stripe account (as after moving to a new Stripe account) now get a new customer automatically at checkout instead of failing.

CSV and TSV exports are now safe to open in a spreadsheet

Text that begins with =, +, -, @, a tab, or a carriage return used to be written verbatim into CSV and TSV exports, where Excel, LibreOffice, and Google Sheets run it as a formula — a value stored in a database could execute on the machine of whoever opened the file. Every delimited export and copy now writes such text with a leading apostrophe so it lands as visible text: Save Results, the Export… dialog, Export Tables, explorer Save Data As, the result-diff and MCP activity CSV exports, Copy / Copy as CSV / Copy as TSV, and the command-line CSV output. Numbers, booleans, dates, binary values, and plain numeric text such as -12.5 are left exactly as they were, and Excel workbooks already store these values as text cells. The protection is on by default and can be switched off with "Protect against spreadsheet formulas" under Settings ▸ Results ▸ Export or in the Export Tables dialog.

Duplicate a saved connection

Right-click a server in the explorer and choose Duplicate Connection…, or select a connection in Manage Connections and press the new duplicate button in the list footer, to open the connection editor on an independent copy named "<name> (copy)" (numbered when that name is taken) with every setting carried over: server, database, engine, authentication, TLS, SSH/proxy/Kubernetes hops and named tunnel, safety rules, environment and grouping, startup SQL, timeouts, and masking. The copy gets its own saved password rather than sharing the original's keychain entry; if the original's password cannot be read, the copy opens with an empty password and tells you so. Nothing is written until you press Save, so cancelling leaves your connections untouched; saving places the copy right after the original and selects it in the explorer. The Manage Connections form also no longer drops SSH/proxy/Kubernetes hops, named tunnels, masking rules, or the production override when you save a plain edit.

AI and agent queries are now read-only at the database, not just on your machine

Every query the AI drawer's Agent mode, the MCP server, or the agent command line composes on its own now runs inside a read-only mode the database itself enforces, on top of SQLLY's existing SELECT-only check. On PostgreSQL and its compatibles and on MySQL and MariaDB the query runs in a read-only transaction that is always rolled back, on SQLite the session is switched to query-only for the statement, and on SQL Server and Azure SQL — which have no read-only transaction — it runs inside a transaction that is always rolled back plus a screen that refuses EXEC, stored and extended procedures outside a small read-only allowlist, SELECT INTO, OPENROWSET, OPENQUERY and BULK. A SELECT that quietly writes through a function or a data-modifying common table expression is now refused by the server, and the AI sees a plain "blocked: AI queries run read-only" message instead of a raw driver error. Connections that go through a bridge carry the same mode; a bridge older than this release keeps the SQL check alone. Your own editor runs are unaffected. Also fixed: PostgreSQL errors now show the server's actual message instead of a bare "db error".

Saved chart library and chart dashboard

Result charts can now be kept, not just exported: with the Chart tab in front, "Save chart to library…" (results toolbar or grid right-click) stores the chart layout together with its query, connection, and database under a name of your choice and an optional auto-refresh interval. Tools > Saved Charts… lists the library and lets you open a chart (it re-runs on its saved connection with the Chart tab already applied), rename, duplicate, copy the SQL, change the refresh interval, or delete it. Tools > Chart Dashboard… shows every saved chart as a live tile in a grid that adapts to the window width, with per-tile Refresh, Refresh all, last-refreshed stamps, clear error states when a query fails or its connection no longer exists, timed auto-refresh, and a pop-out into its own window; dashboard tiles only run read-only SELECT queries.

Schema snapshots and a Schema History timeline

You can now save a point-in-time snapshot of a database's schema and compare it later — against another snapshot or against the live database — to see exactly what drifted without needing a second server. Right-click a database and choose Save Schema Snapshot… (with an optional label such as "before release 2.4"), or press the new Snapshot button beside either side inside Schema Compare. Schema History…, on the same menu and in the command palette, shows every snapshot for that database on a timeline with a list you can rename and delete; pick two points, or one point and Live, and press Compare to open the full Schema Compare view between them, with the same color-coded differences, risk grading, rename suggestions, and deploy and rollback scripts as a live-to-live compare. Snapshots also appear as "Snapshot: label @ time" choices in the Schema Compare source and target pickers. Snapshots are kept on your machine and are a desktop-app feature; the browser preview reports that instead of failing.

One-click maintenance from the explorer

Database, table, and index nodes in the Server Explorer now carry a Maintenance submenu with the engine's own commands — DBCC CHECKDB / CHECKTABLE, UPDATE STATISTICS and index REBUILD / REORGANIZE on SQL Server and Azure SQL; VACUUM, VACUUM ANALYZE, VACUUM (FULL), ANALYZE and REINDEX on PostgreSQL; ANALYZE, OPTIMIZE, CHECK and REPAIR TABLE on MySQL and MariaDB; VACUUM, ANALYZE, REINDEX and an integrity check on SQLite — with database-wide actions expanding to one statement per table where the engine has no single form. Every action has a Script item that opens the statement in a new query tab bound to that server and database, and a Run item that first confirms the object and exact SQL, warns when the operation takes locks or can run for a long time, and then executes through the normal query path so progress and messages show in the results pane; SELECT-only and locked production connections cannot Run and say why, and engines without these commands show the reason instead of a broken item. The Index Analyzer also gains a Script REBUILD button beside Script DROP INDEX.

Generate data-model code from a table's schema

Right-click a table or view in the explorer and choose Generate Model to turn its live column metadata into a TypeScript interface, Zod schema, Prisma model, Go struct, Python dataclass, Java record, C# record, Rust struct, Kotlin data class, or SQLAlchemy model, opened in a new tab named after the table with the right file extension or copied to the clipboard from the nested Copy to Clipboard submenu. The generated types honour nullability, primary keys, identity columns, defaults, and lengths or precision as your engine reports them — SQL Server, PostgreSQL, MySQL/MariaDB, and SQLite spellings are all understood — and each language follows its own idiom, keeping the original column name in a tag, attribute, @map, or comment when the property name is cased differently; Prisma output includes @id, @default(autoincrement()), @@map, and @db native types. Schema and database nodes gain Generate Models for All Tables, which writes every table into a single file, and the results pane's export menu offers the same model languages built from whatever columns a query returned.

v0.93.2
Sep 25, 2026 · 1 section
Sep 25, 2026
1 section

Browser build fixed after the clause-aware IntelliSense change

The v0.93.1 browser (wasm) build failed. The new GROUP BY / clause-aware completions call the model engine's cursor-context and fuzzy-matching helpers, which were compiled only into the desktop app. The browser build now includes those text-only helpers; value completion, which needs a live connection pool, stays desktop-only.

v0.93.1
Sep 25, 2026 · 1 section
Sep 25, 2026
1 section

Real screenshots across the /docs site

The documentation pages now show 56 real screenshots in place of their "Screenshot needed" placeholders. They cover the database and server tools, chart kinds, directives, results formatting and layouts, IntelliSense, the explorer, and connection organization. They are captured from a sandboxed SQLly against a Northwind sample database, so no private data appears.

A new harness in scripts/docs-shots/ does the capturing. It seeds a throwaway profile and a Docker SQL Server, drives the app to each screen, crops the result, publishes the PNG and WebP pair, and wires it into the docs. It saves progress after every shot and resumes where it left off. The screenshot checklist is now generated from its shot list.

Eight shots are still pending. Six are blocked by app issues the harness surfaced: a Data Compare crash, a Schema Compare collation error, the Canvas layout, empty SQL Server plan diagrams, overlapping tab-switcher rows, and FK name lookup. The other two need Windows and Linux machines.

v0.93.0
Sep 25, 2026 · 9 sections
Sep 25, 2026
9 sections

Docs: column provenance

The website docs gained an IntelliSense → Column provenance page explaining how SQLly tracks where each result column's values come from, and why that matters: header tooltips name the real source column, and grid actions (WHERE from a cell, Match row, promoting interactive filters to SQL) write predicates against that source instead of the display heading. It works through worked examples - a select-list alias, a derived table, a CTE with and without an explicit column list - and is explicit about the boundaries, including views, deeper nesting, computed expressions, unions, and ambiguous stars, where SQLly deliberately says nothing rather than guessing.

IntelliSense Improvement: FROM leads after a finished SELECT list and picking a table writes the clause

After a finished SELECT list with no FROM yet (SELECT⏎⇥*⏎f▏), the popup now leads with FROM (and INTO where it is allowed), follows with the tables and views, and offers no columns. The app's popup used to show a wall of columns there, and once the typed text stopped matching FROM it offered nothing useful. Typing narrows the tables by the same name, schema and acronym rules as after FROM, and accepting one inserts FROM, a newline, one indent in the editor's indent style, and the table name. Detection of this position is also tighter: a trailing comma or operator, an open parenthesis or CASE, TOP n, or a DISTINCT/AS/ELSE still expects a column, so none of those get the FROM treatment. The fix covers both the editor's completer and the model engine, with regression tests in feedback_ifb_9f91.rs, the services pipeline, and the editor's accept path. The cookbook's select-list tests now expect the FROM-clause tables after the keywords (feedback ifb_9f915d558e3c42229a643194dc6be04f).

IntelliSense Improvement: WHERE leads after a FROM table and picking a column writes the clause

Right after a FROM source on its own line (FROM⏎⇥Shared.Settings⏎wh▏), or once the source's alias is in place, the popup now leads with WHERE, follows with the rest of the clause keywords (GROUP BY moves to just after WHERE when the select list aggregates or there are no JOIN keywords, where it used to lead), and then offers the query's in-scope columns, alias-qualified when the source has an alias. No tables, views, or procedures appear there. Typing narrows the columns, and accepting one inserts WHERE, a newline, one indent in the editor's indent style, and the column. Before this, the app read a word typed on the next line as the table's alias and offered nothing. A word on the table's own line with no alias yet is still left alone, since it may be the alias. The editor pipeline now admits columns at this position despite its FROM-source intent, and the post-alias and post-FROM tests in the completer and the cookbook were updated to the new contract. Covered by feedback_ifb_92d9.rs and a services pipeline test (feedback ifb_92d92370cece4b7ab77e9e6d39634539).

IntelliSense Improvement: WHERE and HAVING conditions offer only the query's columns

Inside a WHERE or HAVING condition (where⏎⇥▏, WHERE a = 1 AND ▏), completion now offers only the query's in-scope columns, the same way ORDER BY and GROUP BY lists already did in the model engine. Tables, views, and procedures no longer appear there; the app's popup used to list every table in the database after the columns. A subquery inside the condition is still its own query, so its FROM offers tables as usual. In the editor, ORDER BY and GROUP BY lists also stop offering tables now: the earlier fix for those (feedback ifb_2592) only reached the model engine. Columns of an aliased table are now offered alias-qualified only (s.Name, not both s.Name and a bare Name), which also removes the ambiguous bare name when two joined tables share a column. Covered by feedback_ifb_56ee.rs (feedback ifb_56ee3b8b03364ed0bc4bf6ab0e1fc186).

Feedback reports always include the objects behind the suggestions

When you report a completion with F2, every table, view, routine, or snippet behind a suggestion the popup showed is now always included in the report. That covers the table a column suggestion came from, the tables an FK join names, and the routine behind a parameter, even when your statement never names them. These items are marked required and locked checked in the data-model tree. Hovering or clicking one explains that it was part of the suggestions you were offered and that tests built from the report need it to be valid. The submission path enforces this too, and size trimming drops these objects last, so a report can no longer arrive missing the schema its popup came from.

IntelliSense feedback page asks you to save before leaving

The IntelliSense feedback detail page on the website now keeps you from losing unsaved work. If the suggestion's fields, the admin status picker, or the comment box hold changes and you click a link (another suggestion in the list, the nav) or submit a different form, a prompt offers Save and continue, Discard changes, or Stay on this page. Save and continue saves each changed form and only then moves on; if a save fails, the prompt shows why and keeps you on the page. Reloading, closing the tab, or typing a new address gets the browser's standard leave-page warning instead, since browsers do not let a page offer to save there.

IntelliSense Improvement: value suggestions fit the column's type and nullability

After column =, the value templates now match the column as the connected database defines it. A bigint NOT NULL column (Id = ▏) gets only a number, with no '' text literal and no NULL, IS NULL, or IS NOT NULL. A text column drops the number, and NULL and the IS NULL tests stay only for columns that allow NULL. Before, column types were only known for tables defined in the project's .sql files, so a live-database table always got every template. The editor now hands its schema model to completion so the value path can read the column's type and nullability; when nullability is unknown, NULL is still offered. Covered by a services pipeline test and feedback_ifb_9b71.rs (feedback ifb_9b711298052d442a8539f84ed7d94948).

Ungrouped columns are flagged as you type, with a one-click GROUP BY fix

A query that aggregates (COUNT, SUM, …) or has a GROUP BY, and selects a plain column it neither groups nor aggregates, now gets that column underlined in the editor as you type. This is SQL Server's error 8120, which you used to see only after running the query. Hovering the underline explains why the column is invalid and shows a Fix this link naming the columns it will add. Clicking it writes them into the GROUP BY as one undoable edit: it appends to an existing GROUP BY, or inserts a new clause after FROM/WHERE that matches the query's layout, indentation, and keyword case. It is an error on SQL Server, PostgreSQL, and Oracle, a warning on MySQL and MariaDB (where ONLY_FULL_GROUP_BY decides), and silent on SQLite. Only plain column references are flagged; expressions, stars, literals, subqueries, and window functions are left alone. Validate SQL lists the finding too, and the website docs have a new Query → GROUP BY check page.

IntelliSense Improvement: required GROUP BY columns come first and are marked required

Inside a GROUP BY list, the select-list columns the query still needs to group now lead the completion list, drawn bold with a tinted ● required by SELECT detail, followed by the rest of the query's columns. The same analysis drives the new GROUP BY check. A column stops being marked required once it is grouped, a partly typed key (GROUP BY Cat▏) still shows the column first, and an aliased column keeps its alias (s.Category). Tables and views do not appear in the list (feedback ifb_75bd6749274a414d9125fe380f654605).

v0.92.0
Sep 23, 2026 · 6 sections
Sep 23, 2026
6 sections

IntelliSense Improvement: ORDER BY and GROUP BY offer columns only

A user reported that pressing the completion shortcut after SELECT TOP 10 * FROM Shared.Terminal ORDER BY filled the popup with tables, stored procedures, and functions alongside the columns — none of which can legally appear in an ORDER BY item list (feedback ifb_259215da6d9f480c9bcea6960b00d974). The caret is now recognised as an ORDER BY / GROUP BY item position and the popup there offers only the columns in scope for the statement the caret sits in, alias-prefixed when the source table carries an alias. The restriction is scoped to the item list itself: it lifts inside a subquery opened within the clause, and again past OFFSET/FETCH or a statement boundary, where object names are legal once more. A new regression test file in the gated IntelliSense verification suite pins all of it, including the parts the engine already had right — the column set, alias prefixing, and ignoring sibling statements in the same editor.

Query editor scrolls past the last line

The editor's scrollable content stopped exactly at the final line, so the end of a query was always pinned to the bottom edge of the pane — awkward when you are working on the last few statements. The editor now carries five blank lines of scroll slack past the last line, the same "scroll beyond last line" behaviour editors like VS Code have. Nothing paints in that space, clicking there still places the caret on the last line, and caret-follow scrolling never drifts into it on its own.

One-click IDENTITY_INSERT fix in the editor

The editor has warned for a while when an INSERT writes to a SQL Server identity column without SET IDENTITY_INSERT turned on, but acting on that warning meant typing both SET statements yourself and getting the table spelling right. Right-clicking the underlined identity column now offers "Turn IDENTITY_INSERT ON for &lt;table&gt; around this INSERT", which writes the ON above the statement and the OFF below it, matches the statement's indentation, adds the missing semicolon if there wasn't one, and leaves your caret where it was. It wraps only the statement you clicked in, so the rest of the script is untouched and several inserts in one script can each get their own pair — which is what SQL Server wants, since only one table at a time may have the switch on. The offer is deliberately limited to inserts that name the identity column in an explicit column list: a positional INSERT needs a column list too, so flipping the switch alone would not actually fix it. Documented on the site under Features → Safety → Unbounded-change guard.

The editor no longer tints the lines that produced the current result

After a run, the lines that produced the result grid were washed in a faint green tint. The tint is gone, along with its Preferences → Editor → Display checkbox. Which statement produced the results you are looking at is still shown, more quietly: the play control in the gutter beside that statement turns green or red depending on how the run finished.

Save changes stays disabled until you change something

On the IntelliSense feedback page, the Save changes button was always live, so it was easy to submit a suggestion you had only opened and read — re-stamping its updated time with no edit behind it. It now starts disabled and only lights up once a field actually differs from what loaded, and it goes back to disabled if you undo the edit. The form still works if scripting is unavailable.

IntelliSense Improvement: SET offers the engine's session options

A user reported that typing SET IDEN gave nothing at all, and that SET filled the popup with the table catalog, when what they wanted was the platform's own options — IDENTITY_INSERT in their case (feedback ifb_978be77d5919462e9abbab3f2cddb288). A statement-initial SET is now its own completion position and offers the session options the connected engine accepts: the documented T-SQL list for SQL Server (IDENTITY_INSERT, NOCOUNT, XACT_ABORT, TRANSACTION ISOLATION LEVEL, the ANSI and STATISTICS options, and the rest), run-time parameters for PostgreSQL, system variables for MySQL and MariaDB, and nothing for SQLite, which has no SET statement. Matching ignores case as you type, so iden, IDEN and Iden all reach IDENTITY_INSERT. The two lookalike positions are deliberately left alone: UPDATE … SET still offers the target table's columns, and SET @name = … is still treated as a variable assignment. A new regression file in the gated IntelliSense verification suite pins all of it, including those two guards.

v0.91.1
Sep 21, 2026 – Sep 22, 2026 · 4 sections
Sep 22, 2026
2 sections

Docs page split into per-topic partials

The website's /docs page was a single 3,300-line Razor file whose entire body was one switch statement over ~120 hard-coded node paths. Every doc topic now lives in its own file under Pages/Shared/Docs/, in a folder structure that mirrors the doc tree, and the page itself is down to ~170 lines of tree/article scaffolding. Adding a topic is now: add the node, drop in a file at the matching path. Two articles that had quietly become unreachable — the detailed JSON/XML cell viewer guide and the fuller relationship-graph guide, both filed under paths no longer in the tree — were found by the split and merged back onto their live topics.

Fuller docs articles across the site

Every thin doc article was rewritten with real substance instead of a paragraph and a link, and checked against the code rather than from memory: the safety guardrails (what the SELECT-only gate actually refuses, how the rollback wrap is phrased per engine and where it refuses rather than pretending, when a confirmation fires), identity gates, the multi-modal schema model (provenance, fidelity, incomplete regions, file-wins-with-shadow drift), streaming and spill-to-disk results, the relay's zero-knowledge properties and its honest metadata limit, private-by-design with the complete list of connections the app can make, file-based storage with the real per-platform paths, value introspection, and new index pages for Tools, Server tools, and Database tools. Corrected two articles that described behaviour the app does not have: the canvas layout directive syntax (-- sqlly result: 1|1, with ranges for spans) and the file-vs-database conflict rule, which is deliberately not timestamp-based. Per-crate test counts on the Platform pages refreshed to the current 7,413.

Sep 21, 2026
2 sections

Exporting results and diagrams now tells you what happened

Copying a result set from the Export button at the right of the results tab strip used to put the rows on the clipboard and say nothing at all, so a copy that worked looked exactly like one that never happened. It now confirms what it copied, naming the format and how many rows went across. Asking to export on a tab that has no results — one that returned only messages, or where you opened History or DDL before running anything — used to do nothing whatsoever; it now says there is nothing to export instead of leaving you clicking a dead menu item.

Saving the relationships diagram had the same gap and a worse one: saving to a folder you cannot write to failed silently, leaving no file and no explanation, while a successful save was equally quiet. Saving now reports where the diagram was written, and a save that fails says so and why, matching the confirmation the diagram's Copy actions already gave. Picking a save format before the diagram has finished loading now says so rather than quietly ignoring the click.

The results Export button highlights on hover and explains itself

The Export button at the right of the results tab strip was the only control on that row that did not light up when you pointed at it — it shifted the label a shade brighter and nothing else, so it read as inert next to neighbours that fill behind the cursor. It now takes the same highlight as every other control there. Pointing at it also shows what it does, including the one part that was impossible to discover: holding Shift while picking a text format saves it to a file instead of copying it to the clipboard.

v0.91.0
Sep 21, 2026 · 7 sections
Sep 21, 2026
7 sections

Profile pane grows a detail pane and shows its source query

The results Profile tab ("Profile All Columns" / single-column profile) is now a working surface rather than a bare stats dump. The grid up top is easier to read: a Rows column joins the stats, the mean is rounded to four decimals instead of trailing float noise, and the "Top values" cell shows a short three-value preview instead of flooding the row. Selecting a column in the grid opens a detail pane below it with the full statistics line (rows, nulls with percentage, distinct, min/max/mean, length range) and every retained frequent value with its count and share — selection follows the grid even after sorting or filtering. Under the detail rows, the SQL body of the query the profile was taken from renders syntax-colored, so a profile stays attributable after the editor moves on. A row of semantic quick filters sits between the caption and the grid — All columns, With nulls, Mostly null (≥50%), Constant, Unique (candidate keys), Low cardinality, and Numeric — each chip showing how many columns match; shapes no column has are visible but not clickable, and columns whose distinct count overflowed the profiling cap are never claimed by a cardinality filter.

Admin panes detach into their own windows

Server Dashboard, Activity Monitor, Query Insights, and Disk Usage now carry the same "Pop out ⤢" affordance the relationship diagram and row editor have: the pane moves into a standalone, resizable OS window, keeping its grid, filters, and live refresh ticker intact, so a monitor can sit on a second display while you keep working. Closing the window closes the pane. The dashboard's links to its sibling tools keep working from the detached window.

Consistent footer buttons on the admin panes

Activity Monitor's footer buttons were the only small-size buttons in the app and sat a size below the chrome's own Close button; they now match every other dialog, with "Open as Query" promoted to the primary action in the last slot the way Query Insights already did it. The Server Dashboard's three cross-tool links moved out of the right-hand action cluster into the footer's leading utility slot, where navigation belongs. The Query Insights window is now titled "Query Insights" rather than "Query Store", matching its menu entry, command-palette name, and its own subtitle.

GPUI Kit adoption pass over stock controls

Settings radio buttons now use the GPUI Kit radio widget (real focus ring, built-in keyboard toggling); keyboard-shortcut keycap chips on the welcome screen and the empty results pane render through the kit's Kbd component with platform-correct symbols; the ER diagram's table picker runs on the kit's virtualized searchable list (smoother with very large schemas); and the templates dialog's category list moved to the kit list with sections, gaining arrow-key navigation. The accent-pill button treatment is now a single shared helper instead of per-site copies. Surfaces that deliberately stay custom (command palette, project file picker, run/connect pickers, feedback tree) now carry doc comments explaining exactly why the kit widget doesn't fit.

Unconstrained-mutation warnings are now opt-in

Validation no longer flags "unconstrained operation" on UPDATE/DELETE statements that aren't pinned to a single row by primary key (or TRUNCATE/DROP/MERGE) by default — editor diagnostics and the validation summary stay quiet for hand-written SQL. A connection whose safety settings enable "Confirm data changes" opts back in, and the same choice rides the engine wire so remote and bridged validation match. AI-generated SQL and explicit AI safety reviews are unaffected: model-written mutations always stay behind the human confirmation gate.

Unbounded-statement execution protections are opt-in

The pre-execution "Confirm Data Changes" stop for UPDATE/DELETE statements with no WHERE clause is now off by default: new connections (created in the editor, imported from bundles, or migrated from other tools) start with "Allow unbounded mutations" ticked, and older saved profiles from before the setting existed decode the same way. Untick it on a connection to bring the confirmation back; connections where it was explicitly saved keep their saved choice, and explicit safety opt-ins ("Confirm data changes", SELECT-only, production write-unlock, AI review) still confirm as before. No environment, client, or project entity turns the protection on by default.

AI query summary readable in dark mode

The AI query summary strip above the results tabs rendered its text in the toolkit's default near-black, unreadable on dark themes. Theme syncing now also rebuilds the toolkit's Base-layer text defaults, so markdown-rendered text (the summary strip, notebook cells) follows the active theme's colors — including the inline-code chips.

v0.90.2
Sep 19, 2026 – Sep 21, 2026 · 3 sections
Sep 21, 2026
2 sections

IntelliSense feedback dialog pre-selects the tables the query names

The "Improve IntelliSense" dialog used to seed its data-model tree with every object the capture pulled in — and because mentioning a schema anywhere in the query drags that schema's entire catalog into the capture, a query like SELECT * FROM Accounting.Account started with the whole Accounting schema ticked (or, on older builds, nothing at all). The default selection now ticks exactly the objects the query names — Inventory.ItemCategory and Accounting.Account in the example above — falling back to all captured objects only when the query names none of them, so an untouched dialog still never sends an empty model. Unticking "Include all" with nothing hand-picked also lands on that query-referenced seed instead of an empty selection.

Unterminated statements no longer bleed into each other in IntelliSense

Two statements separated only by whitespace — no ; or GO between them, the normal state of a query tab mid-edit — were treated as one statement. That broke value introspection in every statement after the first: with SELECT * FROM Inventory.ItemCategory above SELECT * FROM Accounting.Account WHERE Name LIKE , completing after LIKE found two candidate tables, declared the unqualified Name ambiguous, and offered nothing. The statement splitter now recognizes that a new top-level SELECT/INSERT/UPDATE/DELETE keyword starts a new statement, while leaving legal continuations alone (UNION SELECT, INSERT … SELECT, FOR UPDATE, ON CONFLICT DO UPDATE, subqueries, and procedure bodies). This also sharpens statement bounds for validation and mutation review of unterminated scripts.

Sep 19, 2026
1 section

Migrated to GPUI Kit (gpui-component 0.6, registry-only toolkit)

The UI toolkit moved to the reorganized GPUI Kit distribution: the app and the sqlly-datatable grid (new v6.0.0 tag) now build against crates.io releases of gpui-component 0.6.4 and the gpui-pre 0.3.5 publication of the current gpui API, with icons from gpui-kit-assets and the platform layer (including the browser's WebAssembly backend) from gpui-pre-platform. All the zed and longbridge git patch workarounds are gone from both workspaces, so builds are reproducible from the registry alone and the datatable can be published to crates.io again. No user-visible behavior changes; the full test suites, the wasm browser bundle, and a sandboxed app launch were verified on the new toolkit.

v0.90.1
Sep 19, 2026 · 1 section
Sep 19, 2026
1 section

Docs section catches up with recent releases

The website's documentation section now covers everything the last hundred app releases shipped. New pages describe the decision model (the optional fast AI check behind several smart suggestions), result-set charts with suggested first charts, personal-data detection and masking, and TLS modes with custom CA and client certificates for PostgreSQL and MySQL. Existing pages gained the run summary and busy-tab indicator, the Minify SQL command, bulk tab closing, risk grading in Schema Compare, attention ranking in Activity Monitor, inferred joins in the Query Builder, the identity-column INSERT warning, and the searchable time-zone picker in date formatting.

v0.90.0
Sep 18, 2026 – Sep 19, 2026 · 24 sections
Sep 19, 2026
2 sections

The Chart tab opens with a suggested first chart

With the opt-in decision model enabled, opening the Chart tab on a result nothing has configured no longer starts from a blind guess: the model picks the column that best labels the categories, up to four numeric columns that look like measures worth plotting (never the ID-shaped ones), and a fitting chart kind, and the chart opens with that layout under a "Suggested chart · Revert" caption. One click on Revert restores the chart's own automatic detection and stops suggestions for that result. A chart layout you saved in the sidebar or asked for with a -- sqlly chart: directive always wins over a suggestion; only column names and database types are sent, and with the decision model off (the default) nothing runs and the Chart tab behaves exactly as before.

Activity Monitor can rank rows by attention

A new Rank by attention switch in the Activity Monitor — present only when the opt-in decision model is configured with result data sharing allowed, and off on every launch — adds an Attention filter that asks the model for an act / watch / ignore call on each visible row and shows the rows needing intervention first, the unusual ones next, and hides normal activity. Durations are pre-computed into readable text before anything is sent (the model is not asked to do date arithmetic), a failed ranking falls back to showing all activity with a status-line note, and ranking is advisory only: no session is ever touched from a model answer. While the switch is on, the visible rows — SQL text of other sessions, logins, hosts, and wait info included — are sent to TypeSafe AI on each ranking pass, at most once every five seconds; with the switch off or the model unconfigured nothing runs and the pane behaves exactly as before.

Sep 18, 2026
22 sections

Inferred join suggestions for tables without declared foreign keys

When a table added to the query builder has no declared foreign key to the tables already on the canvas — common in legacy schemas, MyISAM databases, and data warehouses — the opt-in decision model now judges likely join columns (type-compatible, key-like or name-matched pairs, pre-filtered in code) and offers up to three as dimmed "Inferred (…%)" rows in the Joins panel, each carrying its probability. Unlike declared-FK joins, an inferred join is never applied automatically: it joins the query only when its Add button is pressed, and it can be dismissed instead. Only the two tables' names and column names are sent; with the decision model off (the default) nothing runs and the builder behaves exactly as before.

Suggest masking for personal-looking result columns

With the opt-in decision model enabled, every result's columns are classified by what they appear to hold — email, phone, person name, address, government id, payment card, IP address, secret, and a dozen non-personal types — and a toolbar chip offers to mask the unmasked columns that look personal, closing the gap where name-based masking misses a column called contact or c17 full of email addresses. Accepting masks those columns for the current result through the same override mechanism the masking rules use, Always appends global masking rules for them, and dismissing hides the suggestion for that result. By default only column names and database types are sent; up to eight distinct values per column (truncated to 64 characters) are sampled only behind the separate "Allow result data to be sent" switch, columns already masked by rules are never sampled, and with the decision model off (the default) nothing runs and results behave exactly as before.

Schema Compare grades every difference by risk

Schema Compare gained a Risk column — data loss, breaking, review, safe, cosmetic — and now sorts the riskiest differences to the top, so the handful of drops hiding in a several-hundred-row compare are the first rows on screen. Target-only drops, renames, and unsupported objects are graded instantly from what the compare already knows; changed definitions start as unknown, and with the opt-in decision model configured an Assess risk button classifies each changed pair, downgrading low-confidence answers to review and leaving the deterministic grades untouched if the model errors or is absent. The deploy and rollback scripts are unaffected by the new column and ordering.

Optional decision model provider for fast AI checks

SQLLY gained a seam for a new class of AI model that answers small typed questions — pick one of N, rate on a scale, yes/no with a probability — in a fraction of a second, alongside a client for the first such provider (Jev by TypeSafe AI) and an adapter that lets any configured chat model answer the same questions, uncalibrated, when no decision provider is set up. The feature is opt-in and off by default: a new Settings ▸ AI ▸ Decision model pane holds the enable switch, endpoint, pinned model, API key (stored in the platform keychain, never in settings files), a connection Test, and a separate off-by-default switch for allowing result data to be sent. Nothing user-visible consumes the seam yet; upcoming features build on it.

Named display time zone for date/time columns (fixes an ignored preference)

The Results ▸ Date "Specific time zone" preference finally does what it says: datetime columns (and epoch formatters) now render in the chosen IANA zone — Europe/Berlin, Asia/Tokyo, any of them — instead of silently falling back to UTC. Values that carry an offset (timestamptz, datetimeoffset) convert exactly; values stored without one are treated as UTC before converting, and date-only or time-only columns are never shifted. "Native (as stored)" keeps the engine's text untouched. The Date pane gained a searchable zone picker over the full IANA database, a live "UTC+hh:mm now" offset preview, and an inline error (with explicit UTC fallback) when a typed identifier does not resolve. Epoch formatters migrated from their UTC/local checkbox to the same four-way zone mode; stored preferences keep their old meaning.

PostgreSQL/MySQL TLS modes, CA file, client certificates

PostgreSQL and MySQL connections graduated from the two encryption checkboxes to a four-way TLS mode — Disable, Require (no verification), Verify CA, and Verify full — plus a custom CA bundle and an optional client certificate/key pair for mutual TLS. Managed and private-CA servers (RDS, Cloud SQL, Azure, self-hosted) can now verify against their own CA instead of choosing between "trust any certificate" and "cannot connect". Connection strings contribute sslmode/ssl-mode and the CA/cert/key path keys, exported bundles carry the paths (never key material), profiles saved before the change keep their exact old behaviour through a legacy mapping, and encrypted private keys are refused with a clear error rather than prompting for a passphrase. SQL Server keeps its existing checkboxes.

Viewing malformed geometry cells no longer crashes the app

The WKB and WKT geometry parsers behind the cell viewer now validate element counts against the bytes actually remaining before allocating, and cap GEOMETRYCOLLECTION nesting at 64 levels in both formats. Previously a corrupt or hostile geometry blob could request a multi-gigabyte allocation or overflow the stack — either one killing the whole process just from viewing a row; both cases now surface as an ordinary parse error.

Grid WHERE actions and provenance see through derived tables and CTEs

The statement introspection behind the grid's WHERE actions now understands one level of derived tables and WITH-clause CTEs: it can trace a result column through a subquery or CTE body back to the base table and column that produced it, including positional column-alias lists. Predicates keep using the reference that is valid in the outer statement, and the resolution contract is now pinned end to end for Postgres and MySQL alongside SQL Server. The provenance module moved into the shared SQL-intelligence layer so other surfaces can consume it.

Feedback reports include the referenced model by default

The Improve IntelliSense dialog now opens with every object the statement references already checked, so an untouched report carries the data model the user was actually working against instead of an empty one. Unticking still narrows the report to exactly what you opt into.

Feedback sharing toggles are screen-reader friendly

The Improve IntelliSense dialog's "Include all" and per-kind visibility checkboxes now announce what they do ("Show views in the object list") instead of a bare word, alongside the checked state the checkbox role already reports. Tab reaches every enabled toggle and Space/Enter flips it, with no focus traps.

Opt-in relationship chains in IntelliSense feedback

The Improve IntelliSense dialog gained a "Relationship chains" toggle (off by default) that adds the multi-hop foreign-key paths behind the reported tables — Orders → Customers → Regions, not just the direct hop — so reviewers can see how the model hangs together. Chains are capped in depth and count, only chains rooted at tables actually in the report are sent, and the reviewer-facing report renders them as readable paths.

Feedback reports respect a size budget and say what was cut

Serialized feedback context now has an explicit size budget. A report that would exceed it is trimmed — relationship chains first, then catalog objects — and carries a truncation note stating the budget, the original size, and exactly what was dropped, so an incomplete report can never masquerade as a complete one.

Server health dots survive a restart as last-known state

Explorer connection dots no longer reset to gray on every launch. Each server's persisted last-known health comes back as a dimmed "last known" dot — faded green for reachable, faded red for unreachable — with a tooltip saying when that was last true, and the first real connect, probe, or failure of the session replaces it with a solid live dot. A dimmed dot never claims a live connection, and an explicit disconnect stays gray for the rest of the session.

Connection Health dashboard shows the explorer's live truth

Each row in Tools → Connection Health now leads with the same live status dot the explorer tree shows for that server — connected, unreachable, dimmed last-known, or not connected — instead of only the persisted test history, and the header adds a one-glance overview of all servers ("2 connected · 1 unreachable · 5 not connected"). One source of connection truth, two views.

Lost relay connections turn the server red immediately

When a Via-Relay session dies mid-flight — the bridge went away, the relay dropped the link — the app now learns it from the connection lifecycle itself instead of the next failed query: the server's explorer dot flips to red on the spot, the failure lands in Connection Health, and the status bar says the relay connection was lost. Deliberate disconnects and session replacements are never mistaken for a lost connection.

Hover a result column header to see its source column

Resting the pointer on a result grid's column header now shows where that column's values really come from — "Source: p.Name from Shared.Permission p" — resolved from the executed statement, including columns that arrive through an alias, a derived table, or a WITH clause. Columns without a single certain source show no tooltip rather than a guess. (Grid support ships in the results-grid component v5.6.0.)

Stop rapid scrolling from cycling through result sets

Flicking a trackpad in a multi-result grid could ricochet through every result-set tab: the edge check behind the two-finger "switch result set at the grid edge" gesture always reported the grid as at its bottom edge, every wheel event above the activation threshold triggered its own switch (momentum events included), and the switch wrapped around past the last tab. The edge test now asks the grid's real viewport math, a per-gesture latch admits one switch per flick — the momentum stream must die down or reverse before another — and switching stops at the first/last result set instead of wrapping.

Make the bulk tab-close menu items actually close tabs

"Close Other Tabs", "Close Tabs to the Right", and "Close All Tabs" in the tab context menu silently kept any tab with unsaved changes — and an untitled scratch tab counts as unsaved from its first keystroke, so in practice the three commands appeared to do nothing. Clean tabs now close immediately, and tabs with unsaved changes raise a single batch confirmation ("N tabs have unsaved changes. Close them too?") instead of being skipped without a word. Pinned tabs are still always kept.

Stop flagging MERGE's UPDATE SET as an unknown table

Inside a procedure or function body, the reference harvester treated every UPDATE as UPDATE <table>, so a MERGE statement's WHEN MATCHED THEN UPDATE SET … clause grounded the word SET as a table name and the editor reported a false "unknown table or view SET" error. Clause keywords are no longer harvested as table sources, and MERGE targets (MERGE [INTO] <table> [AS alias]) plus USING sources are now harvested properly, so alias completion works inside MERGE bodies too.

Audit the reference harvester for false "unknown table" errors

A systematic audit of table-source harvesting across all supported dialects (SQL Server, PostgreSQL, MySQL/MariaDB, SQLite) found and fixed a family of constructs the live validator misread as table names, each one a false "unknown table or view" squiggle: the argument of EXTRACT/TRIM/ SUBSTRING/OVERLAY after their embedded FROM keyword; table-valued and set-returning functions (STRING_SPLIT, OPENJSON, generate_series, unnest, json_each, pragma_table_info, user TVFs) — now treated as opaque sources while T-SQL legacy hints like FROM t (NOLOCK) still ground the table; LATERAL and ONLY source modifiers; MySQL's DUAL; UPDATE STATISTICS (which also no longer parses as a data update); and, in procedure bodies, ON DUPLICATE KEY UPDATE assignments, FOR UPDATE OF/NOWAIT/SKIP LOCKED locking clauses, ON UPDATE CASCADE/ON UPDATE CURRENT_TIMESTAMP actions, cursor names after FETCH … FROM, INTO OUTFILE, and index methods after CREATE INDEX … USING. Along the way the harvester learned constructs it previously ignored: top-level MERGE statements, INSERT … SELECT sources, and REPLACE INTO now ground and complete like other statements. Two dialect-matrix regression tests pin the whole behavior.

Runs started during a sign-in token refresh stay on their own tab

Executing a query on a connection whose sign-in token needs refreshing (Entra browser, Entra device-code, or cloud-IAM) involves an asynchronous round-trip, and the pending run used to remember its tab by position. Closing, opening, or reordering tabs while the token was being minted could land the run — its spinner, timer, history entry, and manual-transaction bookkeeping — on whichever tab had slid into that position. The run now re-finds its tab by stable id once the token arrives, and if that tab was closed in the meantime it quietly aborts with a status note instead of running on a neighbour.

Minify SQL, run summary in the status bar, running dot on tabs

Three small editor conveniences. A new Minify SQL action (Edit/Query menu next to Format SQL, Cmd+Alt+Shift+F / Ctrl+Alt+Shift+F, command palette) collapses the selection, the statement at the caret, or the whole buffer to one line for pasting into logs, JSON, or a CLI — dropping ordinary comments while keeping string literals, -- sqlly directives, /*+ hints */, and GO lines intact, and leaving unparseable SQL untouched. A finished run now reports a one-line summary in the status bar — "3 statements · 128 rows affected · 0.42 s" — counting statements, rows returned, and rows affected instead of rows only. And the tab whose query is running paints a static accent dot beside its title (no animation, per the design system), so the busy tab stays findable after switching away.

v0.89.0
Sep 18, 2026 · 3 sections
Sep 18, 2026
3 sections

Per-server connection health in the server explorer

Connection state is now tracked per server instead of through a single global "active server". Any number of servers can show a green dot at once; green means the app verifiably talked to that server, red means a connect or probe attempt failed to reach it, and the default gray means disconnected. Server and database nodes gained a right-click Disconnect that returns just that server to the default state, the full schema refresh now targets the server it was invoked on, and quick-open ranking follows the focused query tab's bound server.

Foreign keys in IntelliSense feedback reports

Improve IntelliSense reports now carry the foreign key constraints of every reported table — both outgoing and incoming — whenever the table is included, whether picked whole or via a single column. The feedback dialog gained per-kind visibility checkboxes for its object list (tables and views shown by default; procedures, functions, individual columns, and types opt-in), and the defaults are configurable on the Editor settings pane.

Grid WHERE actions target the real source column

Right-clicking a result cell and choosing a WHERE operator now writes the predicate against the column that actually produced the value, resolved from the executed statement — p.[Name] <> N'CREATE' instead of the invalid [PermissionName] <> N'CREATE' when the SELECT list aliased the column. Applies to all operators, Match Row, and "Add Active Filters to WHERE"; when a value has no single source column (computed expressions, multi-table *), the previous behavior is kept rather than guessing.

v0.88.3
Sep 18, 2026 · 1 section
Sep 18, 2026
1 section

Refresh implementation planning backlog

Reconciled plans 228–286 against the latest code and added decision-model research plans 287–303 so future implementation work starts from current APIs and product constraints.

v0.88.2
Sep 18, 2026 · 1 section
Sep 18, 2026
1 section

Fix the browser build after the sidebar redesign

The sidebar redesign left the browser (WebAssembly) build broken. The new header icon buttons and the "no connections" empty state draw their icons through gpui_component's Icon, but that import was still marked desktop-only, so the browser build could not find it. The import now applies to every platform. Desktop is unaffected — it already had the import — and the browser app builds again.

v0.88.1
Sep 18, 2026 · 1 section
Sep 18, 2026
1 section

Dependency refresh

Updated the Rust dependency graph to the latest available versions. Six crates moved across a major version — rand 0.9 → 0.10, brotli 8 → 9, dirs 6 → 7, comfy-table 7 → 8, winreg 0.55 → 0.56, and rmcp 3.1 → 3.4 — alongside a patch-level refresh of roughly 120 transitive crates in the lockfile. Nothing is user-visible; the CLI's agent tables, the IntelliSense feedback compressor, the Windows URL-scheme registration, and the MCP server all behave as before.

Two follow-on cleanups fell out of the bump. The obsolete getrandom 0.2 pins in the gpui app and sqlly-services were removed — rand 0.10 draws from getrandom 0.4, which the browser build already configures, so the old pin was dead weight and a standing wasm build-break hazard. And the cargo-deny ignores for RUSTSEC-2026-0194/0195 were dropped because the refreshed lockfile no longer carries a vulnerable quick-xml.

Three dependencies were deliberately left behind: keyring 4, gpui-component 0.6, and the gpui git pin. Each is an architectural migration rather than a version bump, and each is tracked separately.

v0.88.0
Sep 18, 2026 · 3 sections
Sep 18, 2026
3 sections

Redesigned welcome screen

The screen you see before connecting has been tightened up. Saved connections are now compact cards instead of plain rows: each shows a database icon, the engine name in the detail line, a coloured chip naming its environment (production, staging, development; hidden when local or unlabelled), a star if it is a favourite, and how long ago you last connected. The old trailing "/ Unset" on connections without an environment is gone.

The SQLLY wordmark sits on a small accent-tinted database glyph, the New Connection button follows the list directly, and the three main shortcuts run along one line as keycap chips. Cards ease into view as the screen opens. If you have not saved any connections yet, three small tags for notebooks, charts, and MCP take the place of the empty list.

Tidier Explorer sidebar header

The left sidebar header now has two rows, laid out like the AI panel's. The first row holds the Explorer/Library tabs and a single options menu for whichever tab is active; the grouping title that used to sit there has gone. The second row is an action bar that changes with the tab: Filter and Add server for Explorer, Filter, Save query and New folder for Library. Opening the filter replaces that row with the filter field. The Library's own second toolbar has gone, its menu gained Refresh, and "Save current tab here…" is now the Save query button.

Tidier empty results pane

Before the first query runs, the results pane now shows a smaller outline table glyph, a short "Run a query to see results here" line, and the shortcut list with each key drawn as a keycap chip — the same chips the welcome screen uses, so "Cmd+Enter, F5" reads as two keys. The AI summary strip above it no longer paints its own bottom rule on top of the results divider, which had shown as one line twice as thick as the others.

v0.87.0
Sep 17, 2026 · 2 sections
Sep 17, 2026
2 sections

Update rustls to 0.23.45

Bumped the bundled rustls to 0.23.45, which fixes RUSTSEC-2026-0285 — earlier versions accepted TLS 1.3 handshake messages sent at the wrong encryption level when they followed a key-changing message in the same record. All TLS connections SQLLY makes (Postgres, MySQL, relay/bridge, HTTPS) pick the fix up.

Draggable scrollbars everywhere

Every custom-painted scrollbar in the app now supports the standard click-and-drag interaction: pressing a track jumps to that position and holding the button drags the thumb, with the drag continuing even when the pointer wanders off the narrow track. This covers the SQL editor (both axes), the Settings dialog, the connection manager's tree and list rails, the entity manager's details form, the connection editor form, and the IntelliSense feedback query preview. Previously these bars were paint-only or supported only a single click-to-jump.

v0.86.0
Sep 17, 2026 · 5 sections
Sep 17, 2026
5 sections

Datepart units highlight as constants

The bare unit in a T-SQL date call — the MINUTE in DATEDIFF(MINUTE, a, b) — now paints as a language constant instead of being left as plain text (or, for DAY, MONTH, and YEAR, mis-painted as a function call). It applies to the first argument of DATEADD, DATEDIFF, DATEDIFF_BIG, DATENAME, DATEPART, DATETRUNC, and DATE_BUCKET, covers every documented abbreviation (mi, dd, wk, …), and is slot-aware: DAY(OrderDate) is still a function and a column named minute is still a column.

Excel export writes numbers as numbers

decimal, numeric, money, and smallmoney values travel from the server as text so no digits are lost, and the XLSX writer was passing that text straight through — every such cell opened in Excel as a left-aligned label that would not sum, average, or chart. Cells in a numerically typed column now go out as real number cells, carrying their original digits verbatim rather than a rounded round-trip. Text columns are untouched, so SKUs and zip codes keep their leading zeros, and anything that is not a plain number ($1,234.56, N/A) still exports as text.

Comments read as comments, in every dialect

Comments are now green — the convention every SQL tool shares — across all themes, replacing the grey that sat too close to ordinary text (and, in Nordic Dark, was the same near-white as body text).

Block comments also stay comments past their first line. The editor shapes one line at a time, so a /* … */ spanning several lines used to highlight only its opening line and paint the rest as live SQL; the open-comment depth is now carried across line boundaries.

Comment syntax follows the connected engine: # opens a line comment on MySQL/MariaDB but still names a temp table on SQL Server, MySQL's -- needs the space it requires, and /* /* */ */ nests on SQL Server and PostgreSQL while closing at the first */ on MySQL, SQLite, and Oracle.

Along the way, a string containing -- no longer turns the rest of the line into a comment: strings and comments are now found in a single pass, so neither can open inside the other.

The DDL viewer highlights in the dialect of the connection the definition came from, instead of always assuming T-SQL.

Strings and comments no longer share a color

Strings moved to the warm red that Visual Studio, SSMS, and VS Code all use, leaving green to mean "comment" and nothing else. Previously both were green, so a commented-out literal looked exactly like a live one. Three themes that had pinned comments to their dim-text color — Nordic Dark, Solarized Dark, and Sepia Dark, where comments were nearly indistinguishable from body text — now carry a green that suits their palette. A test keeps the two inks apart in every theme, present and future.

Status-bar messages stay on one line

A SQL Server error arrives as two lines (Msg 8134, Level 16, State 1, Line 1 then the message), and the embedded newline made the status strip grow a second row — an ellipsis can't truncate a line break. Messages are now flattened to a single line for the strip.

Clicking a failure still copies it, and now also opens it in a dialog with the engine's own line breaks intact, so a long error is readable rather than merely copyable.

v0.85.2
Sep 16, 2026 – Sep 17, 2026 · 4 sections
Sep 17, 2026
3 sections

Pivot save works for rows-only (and columns-only) layouts

The pivot sidebar's save button silently did nothing for the everyday "group by several columns" layout: the -- sqlly pivot: directive round-trip required both a Rows and a Columns field. A pivot with a single populated axis now saves and re-loads correctly — the directive simply omits the empty axis.

Pivot save no longer depends on grid-creation order

The pivot and chart save handlers captured the query editor when the grid was first built; a grid created before the editor attached made the save button a permanent silent no-op. Both handlers now resolve the editor at save time.

Pivot and Chart tabs lock on spill-backed results

Results large enough to spill to disk run the grid in windowed mode, where the interactive pivot silently aggregated only the resident rows and its double-click drill-through could not filter the grid at all. Both tabs now lock with a "Result too large" status on spill-backed result sets, matching how they lock while a query streams.

Sep 16, 2026
1 section

Notifications open in a standard dialog

The notification bell now opens a regular app dialog (title bar, close button, movable, scrim) instead of a small popover. Clear all and the system-notification toggle sit in the dialog footer.

v0.85.1
Sep 11, 2026 – Sep 17, 2026 · 3 sections
Sep 17, 2026
1 section

Platform docs section

The website docs gained a new top-level Platform section with two articles: "How the app is built" covers the workspace layout, the native interface over a headless service core, the engine subprocess, remote access, supported platforms, and the testing story; "The crates" gives every one of the fifteen workspace crates its own overview - purpose, what lives inside it, and its current test count from scripts/count-rust-tests.sh.

Sep 16, 2026
1 section

Docs: Tools moved under Features and every tool page expanded

The website docs tree now nests the Tools section under Features (old #tools/... links redirect), and all 22 tool pages were rewritten from the actual client code: real tab names, grid columns, buttons, script-first contracts, engine differences, and scope behavior for the server tools (Server Dashboard, SQL Agent, Security, Connection Health, Disk Usage) and database tools (Activity Monitor, Backup/Restore, Designers, Import/Export, Schema/Data Compare, Data Transfer, Query Insights, Index Analyzer, Test Data Generator, Relationship Graph, Query Builder, Document Schema, Tenant Scoping, Query History, Extended Events).

Sep 11, 2026
1 section

Rust test counter script

Added scripts/count-rust-tests.sh, a repo-root helper that counts Rust test functions across every sqlly-* crate and prints a per-crate table plus a grand total. It counts line-leading #[test]-family attributes (including #[tokio::test], #[gpui::test], and tests with attribute arguments) and proptest! suites, while ignoring doc-comment mentions so the total reflects real tests.

v0.85.0
Sep 11, 2026 – Sep 16, 2026 · 4 sections
Sep 16, 2026
3 sections

INSERT value ghost labels: clipping, typing stability, and clearer styling

The column: ghost labels shown before each value in INSERT … VALUES statements had three problems, now fixed. The editor's horizontal scroll range ignored the width the labels add, so the right end of long hinted lines was clipped and could not be scrolled into view. Every keystroke also blanked all labels for a debounce interval and then popped them back, making the text and caret visibly shuffle sideways while typing near a hint — labels now refresh synchronously for normal-sized buffers, so they stay put through edits. Finally, the labels are now unmistakably decoration rather than query text: they render in italics with a faint rounded chip behind each label, in addition to the existing dimmed color.

IDENTITY_INSERT warning for identity-column INSERTs

On SQL Server connections, an INSERT that supplies a value for an identity column — either by naming it in the column list or positionally with no column list — now gets a live warning squiggle when no SET IDENTITY_INSERT <table> ON precedes it in the script, explaining exactly which column is the identity and what to run. The check tracks ON/OFF switches per table through the script (an earlier ON silences it, a later OFF re-arms it), matches schema-qualified and bracketed spellings case-insensitively, and stays quiet on engines that have no such switch, on unknown tables, and on DEFAULT VALUES inserts.

Improve IntelliSense dialog no longer clips to the editor pane

The "Improve IntelliSense" feedback dialog used to render inside the query editor, so on a small editor pane the dialog clipped and mis-centered. It is now hosted above the whole workbench like the app's other dialogs, and its title bar gained a pop-out button that detaches it into a standalone resizable window — useful for writing a longer note while still seeing the query underneath.

Sep 11, 2026
1 section

Rust test counter script

Added scripts/count-rust-tests.sh, a repo-root helper that counts Rust test functions across every sqlly-* crate and prints a per-crate table plus a grand total. It counts line-leading #[test]-family attributes (including #[tokio::test], #[gpui::test], and tests with attribute arguments) and proptest! suites, while ignoring doc-comment mentions so the total reflects real tests.

v0.84.0
Sep 11, 2026 · 9 sections
Sep 11, 2026
9 sections

Fix empty General page in SQL Server object properties

The server-properties General page came back blank ("No properties were returned") on SQL Server. Its query selected bare SERVERPROPERTY(...) values, which the engine returns as sql_variant — a type the driver could not read, so the query aborted mid-stream and the page loaded zero rows. Every property is now cast to text (as the server-info probe already did), so the page shows machine name, product version, edition, engine edition, product level, update level, and authentication mode.

Fix "Invalid column name 'state_desc'" on object-properties Permissions pages

The Permissions page of the server, database, and object property editors errored out with Invalid column name 'state_desc'. The queries selected state_desc/class_desc from sys.fn_my_permissions, which only exposes entity_name, subentity_name, and permission_name. The effective-permission state and class are now reported as literals (GRANT, and SERVER/DATABASE/OBJECT for the scope), so the page lists the current principal's permissions instead of failing.

Scope database tools to the right-clicked database; add server Disk Usage

Tools opened from a right-clicked database now show only that database, matching where they were launched from:

Disk Usage opened on a database shows just that database's files, and Disk Usage now also appears under a server's Tools menu, where it keeps the all-databases overview.
Activity Monitor opened on a database lists only sessions connected to that database; from the server it still shows every session.
Extended Events opened on a database lists that database's own event sessions; from the server it lists the instance's server-scoped sessions.

Fix crash on results grid "Copy" / "Copy with Headers"

Right-clicking a results cell and choosing Copy or Copy with Headers crashed the app. That menu action fires while the grid is mid-render, and the copy routine read the grid back while it was still being updated — which aborts. The copy now runs a beat later, after the grid finishes updating, so it copies without crashing.

Fix results pane stuck on "Preparing grid…" for a restored result

Reopening the app (or otherwise showing a cached result set) left the results pane wedged on "Preparing grid…" forever: the result set was present but its grid had never been built, and nothing rebuilt it. The pane now builds the missing grid when it draws, so a restored result shows its rows immediately — or an empty grid if the rows are no longer available — instead of hanging.

Fix crash opening Query Insights (and other tool grids) on multi-line text

Opening Query Insights crashed the app. Its result grid shows each query's full SQL, and the grid paints every cell as a single line — an embedded newline made the text shaper abort the whole process. Tool-dialog grids now collapse newlines to spaces for display (the untouched original rows still back "Open Selected Query", so the full multi-line SQL is never lost). This also protects the other tool grids that surface SQL, such as Extended Events.

Even out SQL Agent's footer button sizes

The SQL Agent dialog's footer buttons were smaller than its Close button, which stayed the standard size — the buttons no longer match in height. They now use the same size as Close and as every other tool dialog's footer buttons.

Fix explorer Tools submenu items doing nothing

Every item in an explorer node's Tools submenu (Server Dashboard, SQL Agent, Security, Activity Monitor, and the rest) silently did nothing when clicked — the tool never opened. The context menu carried its own outside-click dismiss handler, and because a submenu is drawn as a separate popover beside the menu, clicking a submenu item counted as a click *outside* that handler's region and tore the whole menu down before the item's own click could run. The menu now relies on the popup's built-in dismissal (which already knows to stay open while a submenu is active), so submenu items launch their tool. Launching a tool this way also connects to the clicked node's server first when it isn't already the active connection.

Fix Properties dead-end on database nodes

Right-clicking a database in the explorer and choosing Properties reported "Properties not available for this node" and opened nothing. The handler read the node's name, which is unset for database nodes (their name lives in the database field). It now reads the database name correctly, so the paged editor opens with the General, Files, Objects, and Permissions pages.

v0.83.1
Sep 11, 2026 · 2 sections
Sep 11, 2026
2 sections

Status bar messages truncate with an ellipsis

A long status-bar message (typically a database error) now truncates to a single line with an ellipsis instead of wrapping and clipping inside the bottom strip. Clicking an error or warning still copies the full text to the clipboard.

Closing the last query keeps the server explorer

Closing the last open query tab now shows the "No query open" screen in the query area only — the server explorer, tab bar, and status bar keep their places instead of the whole window being replaced. Dialogs and tool panes opened in that state also render in the normal overlay stack.

v0.83.0
Sep 11, 2026 · 7 sections
Sep 11, 2026
7 sections

A Server Dashboard per connection, and a real dashboard on every engine

The Server Dashboard was a single window: opening it for a second server did nothing, because the first one was already up. Dashboards are now one per connection and non-modal — open one for each server, arrange them side by side, and watch them all refresh while you keep working behind them.

The tiles themselves are rebuilt around what an operator actually looks for, grouped into Sessions, Throughput, Health, and Resources. New readings include running, blocked, and idle-in-transaction sessions, the longest running query and oldest open transaction, rows read and slow queries per second, lock waits, memory in use, total storage, and each engine's own signals — SQL Server's page life expectancy, PostgreSQL's shared buffers, MySQL's buffer-pool usage. SQLite gained a dashboard for the first time, reporting the file's size, reclaimable space, and contents.

Metrics are also collected far more defensively. Each group of readings is now its own query, so a view this login cannot read — or one a managed service like Azure SQL Database simply does not expose — costs only the tiles it feeds; the header names what was unavailable and every other tile keeps reporting. Previously any one missing permission blanked the whole dashboard.

Server and database tools moved to the explorer's right-click menu

The server- and database-scoped tools used to sit in the application Tools menu, where they always opened against whatever connection happened to be active — so pointing one at a different server meant switching connections first and remembering to switch back. They now live in a Tools submenu on the explorer's right-click menu: servers offer the Server Dashboard, SQL Agent, Security, and Connection Health, and databases offer Security, Backup / Restore, Activity Monitor, Designers, Import / Export, Schema Compare, Data Compare, Data Transfer, Query Insights, Disk Usage, Index Analyzer, Test Data Generator, Relationship Graph, Query Builder, Document Schema, Tenant Scoping, Query History, and Extended Events. Picking one switches to that node's server and database first, so the tool opens on the object you actually clicked. Every one of them is still reachable by name from the command palette.

The tab switcher waits for the mouse

Moving the pointer onto the Ctrl+Tab overlay now highlights the row you are over — the preview follows along — and stops the overlay from closing when you let go of Ctrl, which previously made the listed tabs impossible to click. A click selects that tab and closes the overlay; Escape or a click outside still cancels and leaves you where you were.

Server and database properties moved into the object Properties editor

The server had no properties dialog of the modern kind at all — right-clicking it opened an older viewer that only generated SQL for you to run yourself — and a database's properties stopped at a single General page. Both now open the same live, paged Properties editor that tables, columns, and stored procedures use: the server gets General, Databases, Configuration, and Permissions pages, and a database gets General, Files, Objects, and Permissions. Every page reads the connected engine's own catalogs, so they work on SQL Server, PostgreSQL, MySQL/MariaDB, and SQLite alike, reporting an empty page where an engine genuinely has no such concept rather than failing. The old properties viewer has been retired.

Object metadata now answers for the browser preview's SQLite database

Object property pages and the explorer detail queries failed against the in-tab SQLite database with "Unsupported capability: run_metadata_query" — the browser metadata service answered only the hand-rolled tree listings and left the generic metadata-query path unimplemented. It now generates the same SQLite catalog SQL the desktop app uses for .db files and runs it against the in-tab connection, so the property dialogs, object definitions, and primary-key metadata work in the preview as they do on the desktop.

The Messages tab survives runs with no results

A query that finished without producing any result sets or server messages used to collapse the output area back to the pristine shortcut list, hiding the tab strip — and with it the Messages tab and the run's timing lines. Once a query has run, the output tabs now stay up and the pane lands on Messages, so the run's log is always reachable. The "Query started" and "Completed" stamps in that log now include the date alongside the clock time, each is set off from the run's output by two blank lines, and the completion line's elapsed milliseconds use your thousands-separator formatting so long runs stay readable.

Admin user details page with one-click license issuing

Understanding one user used to mean hopping between the admin Users, Subscriptions, and IntelliSense triage pages. Every row on the admin Users page now links to a per-account details page that shows everything in one place: the profile (name, email, company, sign-in methods, roles), every license with an issue-a-license form and revoke for comped grants, all registered devices and relay bridges against their install allowances, and every IntelliSense submission from that user's devices, each linked into the triage view. An Issue license shortcut on each Users row jumps straight to the license form.

v0.82.1
Sep 11, 2026 · 1 section
Sep 11, 2026
1 section

Library crates now carry their own version, bumped from a content hash

Every crate in client/crates and every non-GUI app in client/apps used to inherit the application version, so all fifteen of them jumped together on every release whether or not a single line inside them had changed. They now each carry their own semver, seeded at 1.0.0, and advance only when their own source actually changes. sqlly-gpui and sqlly-browser still track the application version — browser is a wasm shell over gpui and must always report the same version as the app it wraps.

The release script decides which crates moved by content-hashing each one and comparing against a .versionhash file committed alongside it. A crate whose hash changed gets a minor bump, and every path reference to it — in the workspace root and in any crate that path-references it directly — is rewritten to the new version before the build gates run. Documentation and image files are excluded from the hash, so a README edit no longer looks like a code change. A crate with no .versionhash yet is recorded at its current version without a bump, so adding a crate never fabricates a version jump.

v0.82.0
Sep 10, 2026 · 13 sections
Sep 10, 2026
13 sections

Clearer row editing with adjustable related rows

The row editor now separates editable values from related child rows with clear headers and a change count, so it is easier to scan what you are changing. Drag the divider between the two areas to give the form or the related-row preview more room, including after popping the editor into its own window.

Consolidated the Rust workspace from 38 crates to 10

The client workspace had accumulated a crate per feature rather than a crate per boundary — nine of them under 1,500 lines, and four separate crates for the four database backends that implement the same traits and are always used together. Everything is now grouped by what the code is about: sqlly-lang (SQL model, lexers, parsers, formatter, tokenizer), sqlly-intel (metadata, completion, query safety, model engine), sqlly-engines (SQL Server, PostgreSQL, MySQL, SQLite, and the SSH/proxy tunnelling they dial through), sqlly-ai (schema catalog, agent model, Ollama and MLX providers), and sqlly-services (the shared service layer plus the admin, alter, security, scripting, plans, data-compare, cloud-auth, and remote-engine domains). Every former crate survives as a module of the same name, so this is a rename, not a rewrite — no behavior changed.

Two boundaries were deliberately kept: sqlly-wire stays standalone because the relay links it and nothing else, which is what proves the relay cannot decrypt the traffic it forwards; and sqlly-testkit stays standalone because every crate dev-depends on it. Modules that were native-only stayed native-only, so the browser build's dependency graph is unchanged. Recorded as [ADR-023](docs/architecture/adr-023-crate-consolidation.md).

Connection Management: wider, detachable, and it asks before discarding your edits

The dialog opens 120px wider so the form and the connection tree both have room. It can now be popped out into its own window like the other big dialogs, keeping every in-progress edit as it moves.

It also stopped closing by accident. Escape and clicks on the backdrop no longer dismiss it — too easy to lose a half-typed server and password that way — and closing it with unsaved edits now asks what you want: Save commits and closes, Discard throws the edits away, Keep editing puts you back in the form. That replaces the old "click Close again to discard" behavior, which never offered to save.

Messages tab now reports row counts and when the query ran

The Messages tab reads as a log of the run: the time it started, anything the server said, the row count of every result set, and the time it finished with the elapsed duration. Times are local, to the second.

Plan and DDL tabs appear only when they have something to show

Results, Messages, and History are always there. The Plan tab now shows up only for a run that actually asked for an execution plan, and the DDL tab only when there is DDL loaded — instead of both sitting in the tab strip empty on every run.

Double-click a JSON or XML cell to open it

SQLly already recognised when a cell holds a JSON or XML document; now double-clicking that cell opens it in the structured-data viewer — pretty-printed, syntax-highlighted, with a collapsible tree and search. The gesture is deliberately narrow: it fires only for cells that really do hold a document, so double-clicking anything else behaves as before. Every cell can still be opened from the right-click menu. Documented at Docs → Results → JSON & XML cells.

Long cell values no longer stretch the grid's right-click menu

A cell holding a JSON document made the context menu as wide as that document, pushing every other item off screen — the WHERE-clause items inline the cell's value in their labels. Those labels now collapse whitespace and stop at 80 characters with an ellipsis. Only the label is clipped; the predicate SQL is still built from the full value.

Fixed &#xa; showing up in execution plan statement text

SQL Server escapes the newlines and tabs inside a plan's statement text as XML numeric character references. The plan parser only understood the five named XML entities, so a multi-line statement appeared in the plan header as SELECT&#xa;&#x9;@Offset = …. The parser handles numeric references (decimal and hex) now, and the plan's statement header flattens whitespace so a formatted statement still reads as one line instead of being cut off at its first newline.

ER diagram: checkbox view switches, resizable cards, and full column lists

The relationship graph's view options are checkboxes now — Columns, Types, Nulls, FKs — instead of highlighted toggle buttons, and Nulls is new: it shows NULL / NOT NULL per column, independently of the type display. A column whose nullability the engine did not report says nothing rather than guessing.

Column rows are laid out in two columns: the name on the left, the type and nullability right-aligned against the card edge, so a long column name can never run into its type. Cards are sized to show every column of the largest table — the old twelve-row cap and its "+N more" line are gone. Width buttons resize every card, and the layout pitch follows so widening spreads the grid instead of overlapping it.

Foreign-key constraint names no longer float over the canvas; the arrow already says which table is referenced, and the labels were noise on any diagram with more than a handful of relationships. The help line at the bottom is the same size as the header text instead of noticeably smaller. All of it is documented at Docs → Server Explorer → Relationship graph, including what each export format carries.

Popped-out dialogs drop their redundant Close button

A dialog that has been detached into its own window relies on the OS title bar's close button. The in-dialog Close is now hidden there — two close buttons inches apart read as though they did different things.

Object properties for databases and columns

A database's properties page used to show three fields scraped from the database list. It now queries the database itself: state, collation, recovery model, compatibility level, creation date, size on disk, and owner. Engines with no concept of a recovery model or an on-disk size say "not reported" rather than dropping the line, so the page reads the same whatever you are connected to.

Columns have a properties page for the first time — right-click a column in the explorer. It shows position, the assembled type (nvarchar(400), decimal(18,2), nvarchar(max)), nullability, default, and the identity/computed/collation flags. Both work on SQL Server, PostgreSQL, MySQL/MariaDB, and SQLite.

Explorer, tab switcher, and run history read better

The explorer header drops its grouping word ("Clients", "Environments", …) when the rail is dragged too narrow to hold it, instead of clipping it under the action buttons — the icon carries the meaning on its own.

The tab switcher's connection trail was one run-together client|project|env|server|database string, which made the separators look like part of the names. Each part is now its own element: clients, projects and environments render as their real identity chips with their assigned colour and logo, separated by a clear divider with room around it.

In the run-history strip under the results, the run you are looking at is now filled rather than merely tinted, so it is obvious which of six chips is on screen.

Font pickers are searchable, and the AI drawer starts closed

Both font dropdowns in *Settings → Appearance* have a search box — the list is every font installed on the machine, which is not a list to scroll. And the AI drawer no longer reopens on launch just because it was open when you last quit; its width is still remembered for when you reopen it. Opening a saved workspace still restores the drawer, since that is a deliberate act.

v0.81.0
Sep 10, 2026 · 9 sections
Sep 10, 2026
9 sections

More useful object properties

Object Properties now opens live, focused pages for databases, schemas, triggers, and constraints alongside tables, views, routines, indexes, statistics, and types, making definitions, permissions, and schema inventory available where they matter.

Run SQL File closes the easy way

The Run SQL File dialog now closes on Escape or a click outside it, like the other browse-style dialogs. Mid-run that acts exactly like its Close button: the run is cancelled first.

Dialogs know when you have unsaved changes

Every dialog can now flag unsaved changes, and while that flag is up, Escape and click-outside dismissal are suspended so edits can't be lost by accident — the ✕ and the buttons still work. The connection editor uses it: it closes the easy way until you change any field, then holds your work until you close it deliberately. The row editor does the same while a row has pending edits, while the read-only row viewer, Object Properties, and the Create/Drop Database dialog always close the easy way.

Export menu items light up on hover

The results toolbar's Export dropdown now highlights the format under the pointer (and shows a pointer cursor). The old hover tint was nearly the same color as the menu surface in the dark palettes, so nothing visibly changed as you moved over the items.

Row editor: script updates, real checkboxes, date pickers, and a louder delete

The row editor gained a Script Update button that writes your pending edits as a ready-to-run UPDATE statement (literals inlined, wrapped in a transaction) into a new query tab — nothing executes, and the editor stays open. Boolean columns now edit with a checkbox instead of a dropdown, date columns get a calendar picker that writes the same ISO text the form always accepted, and once you arm a deletion the Confirm Delete button turns red so the destructive second click is unmistakable.

Relationships diagram shows columns, types, and column-level foreign keys

Table boxes in the relationships diagram now list their columns, with toolbar toggles for the column list, data types with nullability (a ? marks nullable columns), and the foreign-key arrows themselves. With columns shown, each foreign key points at the exact column it relates to on both tables instead of just the boxes. Shift-scroll over the diagram now zooms in deliberate 5% steps — scroll a real notch to change it — so a high-sensitivity mouse can't rocket the zoom.

Conditional formatting became a floating, non-blocking dialog

Conditional Formatting now opens as a dialog inside the app instead of a separate window, and it doesn't block anything: the grid behind stays fully interactive while you build rules, and Escape or a click outside always closes it. The rule picker is a proper dropdown instead of a button that cycled to a different rule on every click, and a Pop out button still moves it to its own window when you want it beside the app.

Masking is manageable from the grid, and re-masking no longer crashes

Right-clicking any column in the results now offers masking controls: masked columns keep their reveal and re-mask actions, unmasked columns gain "Mask this column on this connection", and every column menu links to "Masking Rules…", which opens Settings on the masking pane. "Re-mask this result" also crashed the app the moment it was clicked — the rebuild it triggers ran inside the grid's own update cycle; it is now deferred a beat and the action works. The masked-columns toolbar chip also swapped its emoji padlock for the app's own security icon, so it matches the rest of the toolbar.

Rounding options only where they make sense

The results column menu now offers the rounding presets only on columns whose type can actually carry decimals (decimal, numeric, float, money, and friends). Integer, text, and date columns no longer show them; columns with no reported type keep the option.

v0.80.1
Sep 10, 2026 · 1 section
Sep 10, 2026
1 section

Fix disabled database right-click actions

Right-clicking a database in the explorer left every menu item between Copy Name and Refresh — New Query, Browse Objects, Run SQL File, Search, the exports, Import, and Drop Database — greyed out whenever that database's server was not the single globally active connection (including right after launch, before you had clicked the server). A right-click now assumes the server and database the item lives on: the items enable whenever that server has a saved profile, and choosing one retargets to it — connecting if nothing was active for that server, or switching database if only the database differed — before it runs. New Query in particular used to open against whatever connection happened to be active; it now opens against the database you clicked.

v0.80.0
Sep 10, 2026 · 4 sections
Sep 10, 2026
4 sections

Roomier row action gutter in the results grid

Picked up sqlly-datatable 5.5.1, which widens the gap between the View and Edit icons in the row action gutter and reclaims the dead space next to the row number. The two icons now read as separate targets, and the gutter keeps its overall width so the data columns don't shift.

Catalog schema tests no longer trip over leftover temp files

Three sqlly-catalog schema tests named their scratch directory after the process id alone. On a long-lived CI agent, a run that was killed or failed before its cleanup left the directory behind, and a later run that reused the pid inherited a half-built catalog file — the v1 migration test then failed with "table catalog_object already exists" even though nothing was wrong with the code. Each test now clears its scratch directory before creating it.

Pop-out windows are the dialog now, and dialogs got calmer close behavior

Popping a dialog out no longer floats a second dialog inside the new window: the window itself is the dialog — its content fills the window and the window's own edges resize it. The row editor's pop-out works this way now, and Data Transfer gained a pop-out of its own that keeps a running transfer alive while you work in the main window.

Dialogs also stopped closing by accident: pressing Escape or clicking outside a dialog now only closes it where that makes sense (read-only browsers like the tool dialogs, dashboards, Settings, and pickers). Form dialogs — connection editor, transfers, exports, editors — keep your work until you close them explicitly with the ✕ or a button. Confirmation dialogs still answer to Enter and Escape. Dialog bodies also never overlap the button bar anymore: overly tall content clips or scrolls above it, and the Data Transfer form itself now scrolls instead of spilling over its buttons.

Tooltips vanish once the pointer wanders

A tooltip now stays up only while the pointer rests near where it appeared: move more than 50 pixels away and it hides, instead of trailing along while you traverse a large row or panel.

v0.79.0
Sep 10, 2026 · 1 section
Sep 10, 2026
1 section

Every dialog in the app is now movable and resizable, with one consistent frame

All sixty-plus dialogs — from the connection editor and Settings to the confirm prompts, admin tools, import/export wizard, ER diagram, and row editor — now share a single dialog frame. Each one has a visually distinct themed title bar that doubles as a drag handle, a red close ✕, resizing from every edge and corner with the proper horizontal/vertical/diagonal resize cursors, a consistent footer with a stock Close button, and Escape-to-close everywhere. Confirmation dialogs (delete connection, deploy schema, restore, trust host key, import connections) use a built-in confirm/reject mode where Enter confirms, Escape cancels, and clicking outside can never trigger the action. Dialogs remember where you dragged them for the rest of the session.

The ER diagram, row editor, and row form keep their pop-out button — now in the title bar — for detaching into a standalone window on desktop; in the browser everything stays inside the app window. The Connections & Settings workspace traded its single-corner resize for full 8-way resizing, and the Settings window is now movable and resizable too.

v0.78.0
Sep 10, 2026 · 3 sections
Sep 10, 2026
3 sections

Copy Name on every server explorer node, and Cmd+C from the keyboard

Every row in the server explorer can now hand back the name it displays. Servers, databases, schemas, grouping folders ("Tables", "Programmability"), Azure account/subscription/server rows, group headers, and error rows previously had no way to copy their name — the action existed only on data-model objects like tables, views and columns. Their right-click menus now lead with Copy Name, and rows whose kind carries no name of its own copy the title the user actually sees.

The same copy is now on the keyboard: with the explorer focused and a row selected, the platform copy chord (Cmd+C on macOS, Ctrl+C elsewhere) copies that row's name, writing exactly what the menu item writes and reporting it in the status bar. Copy Qualified Name is unchanged — it stays on the objects that have a qualified form.

Tooltips wait two seconds, and never sit on top of a context menu

Tooltips used to appear after half a second, so crossing the explorer or a toolbar of icon buttons flickered chips at you on the way past. Everything in the app now waits a full two seconds of hovering before any tooltip appears.

Right-clicking also used to leave a tooltip stranded on top of the menu it had just opened: the tooltip's timer was already running when the click landed, and neither framework cancels a pending tooltip on a mouse press. Now any open context menu suppresses tooltips for as long as it is up — in the server explorer, the connections sidebar, the SQL library, the AI panel, the query editor, and the results grid — and the editor's hover card is dismissed when a context menu opens over it. Button tooltips also now dismiss on a right-click, not only a left one, and every tooltip in the app is drawn in the same style.

The results grid follows the same rules through sqlly-datatable 5.5.0, which gained a host-settable tooltip delay and a context-menu open/close signal for exactly this.

More pronounced drop shadow on the server explorer context menu

The explorer's context menu carried the widget kit's stock popover shadow, which barely separated it from the dense tree of rows behind it. It now paints a deeper two-layer shadow — a wide soft lift plus a tighter near-edge shadow — with more ink in dark palettes, where the menu surface and the sidebar behind it are closest in value.

v0.77.0
Sep 9, 2026 · 4 sections
Sep 9, 2026
4 sections

Clearer missing-server feedback

Azure SQL connection failures caused by an unresolvable server name now explain that SQLLY could not find the hostname and ask you to check it for typos, rather than showing a low-level lookup error.

Pop out row editing

The row editor can now move into its own resizable window without losing your draft, and its Save button now uses the app's standard primary-button colors for clear contrast.

Pop out row viewing

View Row as Form can now move into a resizable window while keeping its neighboring-row navigation, and releasing a drag inside the card reliably finishes resizing or moving it.

A kinder license reminder

The occasional free-tier license reminder now uses a warm, playful invitation instead of calling people cheap, while keeping the same direct purchase option and reminder schedule.

v0.76.0
Sep 9, 2026 · 6 sections
Sep 9, 2026
6 sections

Friendlier documentation guide

Refreshed the SQLly documentation navigation and landing copy so every topic starts with a clear, welcoming explanation of why it matters, while preserving the detailed guides and screenshot placeholders.

Clearer documentation guides

Reworked the explanatory copy throughout the SQLly documentation so guides lead with the task at hand, keep technical detail approachable, and state planned work honestly.

IntelliSense Improvement: alias completion works inside nested subqueries

Typing j. inside a nested subquery — a FOR JSON PATH block in a stored-procedure body, a correlated EXISTS, any FROM clause more than one set of parentheses deep — produced an empty popup, even though the same caret position with nothing typed correctly listed j.JournalId and the rest of the in-scope columns. Alias member-access was resolving aliases against the scope at the *end of the document* rather than the scope the caret is actually in, so none of the nested query's aliases were visible. It now resolves against the caret's own scope and every enclosing scope, with the nearest declaration winning, so a subquery can also reference an alias declared by the outer query. Reported as feedback ifb_5e41d289a60a47e08ff0476646fec0b5.

Runs that return no rows are kept in the run history

A script that produced no result sets — only server output like row counts, PRINT text, or a database-context change — was dropped from the run-history strip entirely, so there was no way to go back and read what the server said. Those runs are now retained alongside grid-bearing ones, carry their messages with them, and reopen straight into the Messages pane since there is no grid to show. The run chip describes them by message count rather than claiming "0 rows".

Closing a tab with unsaved changes explains what is at stake

The save prompt shown when closing a modified query tab now says plainly that closing will lose any and all changes, and that saving will write the file first — choosing a location if the query has never been saved. Saving through that prompt also records the file's on-disk baseline, so reopening it no longer looks like it changed underneath you.

Browser preview documentation sets honest expectations

The browser-preview page now states up front that the WebAssembly build is illustrative rather than representative: many features are missing, stubbed, or broken because running the workbench inside a browser sandbox is hard, and nothing that breaks there should be read as how the desktop app behaves. It also notes that a properly secured hosted product may follow later, but that today this is only a quick online sample.

v0.75.1
Sep 8, 2026 · 1 section
Sep 8, 2026
1 section

Fix release build: stale row-header context-menu test

The grid_context_menu_edit_row_present_for_row_header_only unit test still asserted that "Edit Row…" appeared in the row-header right-click menu, but that action was moved to the always-visible eye/pencil gutter icons in v0.75.0. The stale assertion failed the workspace test step on all three platforms (macOS, Linux, Windows). The test now verifies that "Edit Row…" is absent from both the row-header and cell context menus.

v0.75.0
Sep 8, 2026 · 5 sections
Sep 8, 2026
5 sections

Fix crash when opening a server's Properties

Right-clicking a server in the explorer and choosing Properties crashed the app. The server-info probe was running its live query on a background thread with no tokio runtime, so the query layer's internal tokio::spawn panicked. The probe now runs through the shared tokio runtime like every other background query.

Row form and row editor are now floating, movable, resizable dialogs

View Row as Form previously rendered clipped inside the results pane (it read as being stuffed into the bottom section); it is now a true window-level modal. Both View Row as Form and the row Editor can be dragged by their title bar and resized from the bottom-right corner.

View/Edit moved from the row menu to always-visible row-header icons

Each result row now shows View (eye) and Edit (pencil) icons in the row-number gutter, replacing the "View Row as Form" and "Edit Row…" entries in the right-click menu (Duplicate, Insert, and the staging actions stay on the menu). The Edit icon appears only when the result comes from a simple single-table query, where editing is actually possible. (Requires sqlly-datatable v5.4.0.)

IntelliSense Improvement: alias-dot completion no longer breaks when the SELECT list uses that alias

Typing alias. after FROM/JOIN now offers the aliased table's columns even when the SELECT list already contains qualified references to that alias (e.g. SELECT jl.JournalId … FROM Accounting.JournalLine jl WHERE jl.). The resolver was matching the first symbol use carrying the alias, which for a qualified select-list column is a Column use whose reference is the column, not the table — resolving it as a table found nothing and blanked the popup. Alias-to-table resolution now only considers defining TableSource uses. Reported for a deeply nested FOR JSON PATH subquery where j. and jl. returned nothing (feedback ifb_5e41).

Relationship diagram: pop out to a resizable window and pick which tables are shown

The relationship diagram gains a Pop out ⤢ button that re-opens it as its own resizable, movable OS window (the modal stays available for a quick look); clicking a table in the popped-out window still reveals it in the main window's schema tree. A new Tables ▾ picker lists every table in the database with checkboxes; it defaults to the current foreign-key-connected set and lets you add or remove any table — including tables with no foreign keys, which now appear as standalone boxes. All / None / FK-connected shortcuts reset the selection, the chosen set carries over when you pop the diagram out, and an emptied selection says so instead of claiming the database has no foreign keys.

v0.73.0
Sep 8, 2026 · 11 sections
Sep 8, 2026
11 sections

Fix crash on Check for Updates

Running Help ▸ Check for Updates crashed the app the moment it tried to show the outcome toast ("you're up to date" / "check failed"). Menu-dispatched actions held an internal window lock while running, and showing a toast or dialog from inside one re-took the same lock. The dispatch path no longer holds the lock, so update-check toasts — and any dialog opened from a menu action — display safely.

Space after a trailing comma opens column suggestions

Pressing space after a comma at the end of the last column in a SELECT list now moves to a fresh, correctly-indented column line and opens IntelliSense so you can pick the next column — including the alias-qualified variants (i.Column) when the query defines a table alias. Previously, when the FROM clause (or another clause) followed on the next line, the space was silently swallowed and nothing happened. When the next line is already a blank column slot the caret simply hops onto it instead of adding another blank line.

Keyboard shortcuts for Add/Remove Line Comment

Add Line Comment and Remove Line Comment now ship with the standard IDE chords: ⌘K ⌘C to comment and ⌘K ⌘U to uncomment on macOS (Ctrl+K Ctrl+C / Ctrl+K Ctrl+U elsewhere). Previously these actions were reachable only from the Edit menu; ⌘/ still toggles. Both appear in the Help window's editor shortcut list, and the chords are rebindable in Preferences ▸ Keybindings like any other editor action.

Query history ignores whitespace-only changes

Reformatting a query — re-indenting, adding blank lines, collapsing spaces — no longer creates a new version in the query history timeline. A version is sealed only when the SQL's actual content changes; pure whitespace shuffles are treated as duplicates of the last version, keeping the history focused on meaningful edits.

Docs merged with the Features page, plus a "Why SQLly" section

The website Docs tree now covers everything on the Features page — new sections for Intelligence, Multi-modal schema, Safety & guardrails, Identity gates, Speed & access, Platforms, and Files & sync, plus added Query and Results pages (execution, formatter, command palette, keybindings, diff, JSON/XML viewer, templating, and more). Each documented feature carries the same honest status badge as the Features page — functional, in progress, planned, or in-tab demo — shown in the article header and as a dot in the tree. A new top-level Why SQLly section tells the origin story behind the tool.

Live result-grid sample moved into the docs and made load-on-demand

The interactive sqlly-datatable WebAssembly sample now lives in the docs under Results ▸ Interactive sample. It loads (and downloads its WebAssembly payload) only when you open that page and fully unloads the moment you navigate away, so the demo never sits idle in the background. The Engineering deep-dive guide now links to it instead of embedding its own always-present copy.

Guides updated to match

The Data Analyst, DBA & Power Users, and Engineering deep-dive guides picked up the features they were missing — the row editor, JSON/XML cell viewer, and result layouts for analysts; environment cues, identity gates, and remote access for DBAs — each keeping its functional/in-progress/planned badge.

Account icon in the status bar shows green when signed in

The account glyph in the bottom-left status bar now turns green with a small status dot while you're signed in, matching the account button in the toolbar. Previously only the toolbar button reflected sign-in state; the status-bar icon stayed dim regardless.

Copy your device id from the account dialog

The account dialog now shows a copy button next to your device id. Clicking it copies the id to the clipboard and shows an inline "Copied to clipboard" confirmation right there in the dialog.

Cleaner counts in the server explorer

Schema nodes in the server explorer no longer show a child count — a schema's children are always the same fixed set of standard folders (Tables, Views, Programmability), so the number only restated what's already visible. The counts on those folders themselves now clear the rail's right edge with a little breathing room instead of sitting flush against it.

Docs is a top-level link on the website

The website's Docs page is now a top-level item in the navbar instead of living inside the "About SQLly" dropdown.

v0.72.0
Sep 8, 2026 · 2 sections
Sep 8, 2026
2 sections

Chart directives: configure the Chart tab from a comment

Charts can now be preconfigured straight from the query text, the same way pivots are. A -- sqlly chart: kind:bar; label:Region; values:Sales comment sets the chart type, the category label column, the series columns, and optionally the aggregate, ordering, category cap, and click-to-navigate behavior — all by column name, so the directive survives a reordered SELECT list. Pair it with -- sqlly view: chart (on the first non-empty line) to open the Chart tab automatically on run. kind: accepts every chart type — bar, line, area, scatter, histogram, pie, and donut. Saving from the Chart sidebar writes the directive back into the query, so an interactively-tuned chart travels with the file. The directive is validated in the editor (unknown kinds, bad fields, and missing values squiggle), listed in Help ▸ Formatting Directives, and documented at /docs → Query → SQLly directives → Chart with a placeholder gallery for each chart type.

IntelliSense popup no longer hijacks Enter

The completion popup now opens with no item selected. Tab still accepts the top suggestion immediately, but Enter only accepts a suggestion after it has been explicitly highlighted with the arrow keys — otherwise Enter keeps its normal meaning and inserts a newline. Down enters the list at the first item, Up enters it at the last, and typing keeps filtering with the selection cleared. The popup also no longer auto-opens with the caret sitting directly on a trailing comma (e.g. at the end of a select-list line), so breaking the line there works as expected; it returns as soon as the next item is started with a space or a character.

v0.71.1
Sep 7, 2026 · 1 section
Sep 7, 2026
1 section

Fix crashes when right-clicking result-grid cells containing certain Unicode text

Right-clicking a results-grid cell whose text contained a multi-byte Unicode character in an unlucky position (for example a value like abcd€) could crash the app. Opening the menu classifies every cell's value to decide which actions to offer, and several of those classifiers — the data-URL/image sniffer and the spatial (WKT) recognizer — sliced the text at a fixed byte offset that could fall inside a multi-byte character. They now slice on character boundaries, so any cell value is safe to right-click. Separately, the "Add WHERE …" context-menu actions could crash (or insert the clause in the wrong place) on a query containing certain length-changing uppercase letters such as the Turkish dotted İ; the clause is now positioned correctly in all cases. A latent unguarded index in the foreign-key-label column layout was also hardened.

v0.71.0
Sep 7, 2026 · 2 sections
Sep 7, 2026
2 sections

One Export… item on the results grid menu, with a dialog for every format

The results grid's right-click menu used to list every export format directly — up to 23 items — which buried the actions users actually wanted. The menu now carries a single Export… item that opens a dialog listing every enabled format (standard plus the optional Programming code snippets, each honoring the Settings ▸ Results ▸ Export toggles), a copy-to-clipboard or save-to-file destination choice, and the row/column count being exported. Excel (XLSX) always saves because a binary workbook can't go on the clipboard. The shift-to-save shortcut on the old menu items is replaced by the explicit destination choice, and the Export dropdown at the right of the results tab strip is unchanged.

Charts moved into the result grid

Charts now live as a dedicated Chart tab on every result grid (Grid | Pivot | Chart) instead of a separate output-panel pane opened from a row snapshot. The chart's configuration moved into a pivot-style sidebar next to the grid — chart type, category, values, aggregate, order, and top-N are all picked there, and the layout can be saved per result set so it survives grid rebuilds during streaming. Clicking a bar, point, or pie slice jumps to the grid rows that drive that mark (selecting and revealing them); a checkbox in the sidebar's Behavior section turns that click-to-navigate behavior off. A legend strip above the plot toggles individual series (or pie slices) on and off and shows a "Showing …" note whenever the category, series, or source-row caps trimmed the data. Chart settings can also be supplied externally by the host app, and SVG export (copy or save) is available from the sidebar.

v0.70.0
Sep 6, 2026 · 8 sections
Sep 6, 2026
8 sections

PostgreSQL system databases and schemas in the server explorer

The server explorer now recognizes PostgreSQL's system objects the same way it already did for SQL Server, and honors the same Data Model preferences. pg_catalog, information_schema, and other pg_* catalog schemas (plus CockroachDB's crdb_internal) are grouped, hidden, or shown per the System Schemas placement setting instead of being mixed in with your own schemas. The postgres, template1, and template0 databases are now grouped under a "System Databases" folder (or inlined/hidden) per the System Databases placement setting, matching how master/msdb/model/tempdb are handled for SQL Server. Template databases, previously always hidden, are listed so this grouping can apply.

Browser build: fix symbol glyphs rendering as empty boxes

In the browser (WebAssembly) build, many small symbols the UI draws as text — the settings tree twisties, close crosses, plan warnings, keyboard-shortcut hints, and similar — showed up as tofu "X" boxes. The desktop app gets these glyphs from the operating system's font fallback, but the browser has no system fonts: only the two embedded Noto Sans subsets are ever consulted, and those subsets didn't include the symbol glyphs. The subsets now carry every symbol codepoint the UI uses (grafted from the upstream OFL Noto fonts by client/apps/browser/fonts/build-symbols.py), so they render correctly in the browser.

Resume Connection dialog now switches to the right connection

The "Resume Connection" prompt (shown when a query was last run on a connection you're not currently on) would show the correct server and database, but clicking Switch often did nothing and the Environment/Credentials rows read "Unknown". The dialog matched saved connections on an exact server *and* database, while the remembered database reflects the database actually in use at run time — which frequently differs from the saved profile, where the database is left unset and chosen at connect time. It now matches on the server, prefers an exact database match, and carries the remembered database onto the connection so Switch activates the right server/database. The Cancel and Switch buttons are also grouped together instead of being spread apart.

Fix crash when hovering a detached results window after re-running the query

With results detached into their own window, re-running the query and then moving the mouse over the detached grid crashed the app. Re-running clears the in-memory result sets before the new run streams in, but the old grid — still alive in the detached window — keeps reporting hover-driven state changes, and the column-layout bookkeeping indexed into the cleared result list. Those paths now tolerate a result set that has gone away and simply skip the update.

Browser build: detached results disabled

The browser runs in a single window — gpui's web platform cannot open a second one — so "Detach Results to Window" could never work there and only produced a "Could not open a results window" error. The command and its Re-attach counterpart are now hidden from the command palette and the keyboard-shortcuts settings in the browser build, and the action itself reports "Detached results aren't available in the browser" if reached through a custom keybinding.

Fix DDL navigation's "Panel on the right" never appearing

With DDL navigation set to open definitions in a panel on the right, modifier-clicking an object fetched the DDL (the status bar showed it loading) but no panel ever appeared. The workbench redesign gated split-out side panels behind a per-tab "opened as a split" flag that nothing ever set, so the freshly created panel was filtered out of the layout every render. Opening the DDL panel (and the query-history split panel, which had the same latent problem) now marks the tab as split, and closing the panel clears it.

Query switcher: engine color-coding and a richer preview

The Ctrl+Tab query switcher now shows a colored database-engine glyph next to each tab and in the preview header, so you can tell at a glance which connection a query belongs to — SQL Server, PostgreSQL, MySQL, SQLite, Oracle, and the other engine families each get their own hue (tuned for both light and dark themes) and an icon reflecting its connection shape (networked server, embedded file, or HTTP/cloud endpoint). The large preview pane also gains a caption line beneath the query snapshot summarizing how many rows the tab's last run returned and how long ago it ran (or "Not run yet"), with the result-set count shown when a run produced more than one.

Run history strip: labelled icon and new History settings

The strip above the results that switches between recent runs of a query now leads with a history icon, and hovering it explains what the row is for. A new Results › History settings section collects the run-history controls: a master on/off switch for the feature (on by default), how many runs to keep per query tab, and a "Store a run" choice that avoids cluttering history with identical re-runs — store on query change, on results change, on either, or only when both changed. The "Runs to keep" control moved here from Results › General.

v0.69.2
Sep 4, 2026 · 1 section
Sep 4, 2026
1 section

Fix the Windows release build's native-tool discovery tests

Two tool_discovery tests registered fake pg_dump/mysqldump binaries by their bare names, but the resolver probes for the platform executable name (pg_dump.exe on Windows), so the tests failed on the Windows release leg. They now build the on-disk names through the same suffix helper the resolver uses, so the coverage runs identically on every platform. The release test step also gains --no-fail-fast so a red run reports every failing crate at once instead of stopping at the first — platform-specific breakage no longer has to surface one release at a time.

v0.69.1
Sep 4, 2026 · 1 section
Sep 4, 2026
1 section

Fix the Windows release build's SQLite deep-link tests

Two sqlly:// / file:// deep-link tests asserted unix-only absolute paths (/tmp/...), which are not absolute on Windows and made the release build's Windows test leg fail. The tests now use a platform-appropriate path so the same coverage runs on every platform; the deep-link behaviour itself is unchanged.

v0.69.0
Sep 4, 2026 · 4 sections
Sep 4, 2026
4 sections

Browser preview: live Postgres (PGlite) with the Chinook sample

The browser demo now ships a second sample connection, "Chinook (PGlite)": a real Postgres (PGlite, Postgres compiled to WebAssembly) boots inside the tab and the Chinook sample database is loaded into it automatically while the app downloads. The connection is pre-configured as a Postgres profile, so queries, the full object-explorer metadata tree, and IntelliSense all run genuine Postgres — catalog queries included — against the in-tab engine. The SQLite Northwind sample is unchanged, and a PGlite download failure degrades gracefully to it.

Browser preview stability: fonts and clocks no longer crash the tab

The browser build could crash with wasm panics as soon as fonts resolved or a query completed. Three root causes fixed: the interface-font preference resolved to fonts that don't exist in a browser (now pinned to the embedded faces, with a Latin "Noto Sans" fallback registered so any unresolvable font degrades instead of panicking — the embedded UI face was also silently the Thin cut and is now a true Regular); several code paths on the connect/query/results path read std::time clocks, which panic on wasm (now routed through web-time, including the results grid's scroll physics in sqlly-datatable); and the resulting first panic used to poison the UI loop into an endless "RefCell already borrowed" cascade.

Browser preview defaults to smaller type

The in-tab demo renders larger than a native window, so the browser build now defaults both the interface and editor font sizes to 10 (native stays 13), and the results grid's density presets scale down to match (Normal is 10 with tighter rows). Saved preferences still win; native defaults are unchanged.

Try SQLly online, straight from the homepage

You can now run SQLly in your browser with no download: the site serves the in-tab preview at /app, and the homepage has a new "Run SQLly right here in your browser" section (plus a hero button) that launches it. The preview runs entirely in the tab against two live sample databases — real SQLite (Northwind) and real Postgres via [PGlite](https://pglite.dev) (Chinook) — and, as always, it's a labeled demo that can't reach your own servers. The deploy pipeline now packs and ships the full preview bundle with the site.

v0.68.0
Sep 4, 2026 · 3 sections
Sep 4, 2026
3 sections

Inline AI completion no longer repeats text already in the query

The FIM ghost-text suggestion sometimes restated the clause immediately before the cursor (for example, suggesting WHERE ReportingPeriodId = 47 AND ... when the query already ended with that WHERE clause). Suggestions are now trimmed against the text before the cursor: the repeated portion is removed so only the genuinely new continuation is shown, and suggestions that are pure repetition are suppressed entirely.

Schema-plan golden tests made line-ending independent

The schema deploy/rollback golden tests compared generated scripts byte-for-byte against fixture files, which failed on Windows checkouts with CRLF line endings. They now use the shared GoldenTest helper that normalizes line endings (and supports SQLLY_UPDATE_GOLDEN=1), and AGENTS.md gained a cross-platform test rule so new tests don't reintroduce the problem.

Settings dialog rebuilt: real tree navigation and full-content search

The Settings dialog no longer uses the stock two-level settings component. The left side is now a true multi-level tree: clicking a child shows only that item's content on the right (instead of scrolling within a long page, which often missed under virtualization), folders expand and collapse, and the hierarchy can nest to any depth. Search now looks through everything that appears on the right side — section headers, setting names and descriptions, dropdown options, and names inside collections like column overrides, snippets, AI profiles, and keyboard shortcuts — not just sidebar titles. Matches are highlighted in place in the content, the first match is scrolled into view, and the tree filters down to only the sections that contain a match. Clearing the search keeps the current selection, re-expands its folders, and scrolls it back into view. Closing and reopening the dialog returns to the same page and scroll positions, including across app restarts. Also fixed: the SQL-formatting preview editor stole keyboard focus mid-search when its pane appeared, silently ending the search you were typing.

v0.67.4
Sep 1, 2026 · 1 section
Sep 1, 2026
1 section

Windows schema-compare golden test line endings

Pinned the schema-compare deploy/rollback golden fixtures to LF so the byte-for-byte comparison passes on Windows, matching the other golden test directories. No behavior changes for the app.

v0.67.3
Sep 1, 2026 · 1 section
Sep 1, 2026
1 section

Windows build and query-history test-isolation fixes

Fixed the remaining Windows build error in the URL-scheme/file-association registration (a registry read used the wrong generic argument), and made the query-history store's test path override thread-local so parallel tests can no longer read or write each other's database. No behavior changes for the app.

v0.67.2
Sep 1, 2026 · 1 section
Sep 1, 2026
1 section

More build and test fixes

Further repaired the release build after the feature merge: the Windows build now compiles the URL-scheme/file-association registration and Kubernetes discovery code (a mis-scoped registry import and an unused import), and the PostgreSQL and SSH tunnel test suites no longer fail on a missing TLS crypto provider or a missing async runtime. No behavior changes for the app.

v0.67.1
Sep 1, 2026 · 1 section
Sep 1, 2026
1 section

Build and packaging fixes

Repaired the release build across platforms after the latest feature merge: the browser (WebAssembly) build now compiles again with the native-only tools (SSH/Kubernetes, cloud IAM sign-in, database dump/restore and transfer, schema-compare deploy) correctly excluded from the browser, the Windows build no longer trips on an unused import, and a background query in the tool dialogs now delivers its result cleanly so the test suite runs to completion. No behavior changes for the app.

v0.67.0
Aug 30, 2026 – Aug 31, 2026 · 52 sections
Aug 31, 2026
6 sections

AWS and Google Cloud IAM database sign-in

PostgreSQL and MySQL/MariaDB connections to Amazon RDS, Aurora, and Google Cloud SQL can now authenticate with IAM: SQLLY mints the short-lived token from your local AWS or Google credentials on every connect, forces an encrypted connection, and keeps the token out of the saved connection file. The connection form gains AWS IAM and Google IAM login methods with an AWS profile picker and a credentials status check, and the command-line tool and MCP server accept the same connections.

Dump and restore with the native PostgreSQL and MySQL tools

The Dump Database dialog can now hand the job to pg_dump or mysqldump when they are installed, producing vendor-format archives with live progress and cancel, and Backup / Restore works for PostgreSQL and MySQL by driving pg_restore or mysql after you review and confirm the exact command. Tool locations are detected automatically and can be overridden in Options. Restores are refused on production-marked connections.

Foreign-key columns show what they point to

Data pages now show the referenced row's label next to each foreign-key value, fetched in a single batched lookup per column and cached. Pick the label column per foreign key from the header menu, switch between beside and replace modes in Preferences > Results, and copy or edit the raw key exactly as before.

IntelliSense Improvement: completion is scoped to the cursor's statement

Symbol completion no longer leaks context from earlier statements in the same editor. After a ; or GO batch separator, a fresh SELECT is now a fresh FROM context: the previous statement's FROM tables are no longer offered as foreign-key join candidates, that statement's columns are no longer suggested, and an alias introduced in a prior statement no longer resolves in the current one. This fixes the case where opening a new query below an existing one still suggested joins and columns from the query above (feedback ifb_ea91). Known limitation still open: two SELECTs separated only by whitespace — no ; and no GO — are a single statement to the parser's splitter, so they cannot yet be separated; this is tracked by an ignored regression guard for a later splitter change.

IntelliSense Improvement: regression guards for join-type keyword filtering

Added gated regression tests generated from feedback ifb_f8a7 for post-FROM join-keyword completion — typing in, le, ri, inn, ou after a table in the FROM clause. Seven guards pass; two record still-open gaps as ignored guards rather than engine fixes: typing in should narrow to INNER JOIN only (today every join keyword matches because they all contain the substring "JOIN"), and there is no CROSS JOIN keyword for cr to match. These mark the behavior to fix next.

Regression-test skill scopes phantom popup items and logs improvements

The intellisense-feedback-tests skill now warns that a feedback report's captured popup can include items that are not derivable from the report's own context — tables from live metadata the capture never snapshotted, client-side re-scoring, and keyword text the client adds downstream — and that such items are out of scope, not gaps to investigate. It also now instructs that every IntelliSense change be recorded in this file under an IntelliSense Improvement: entry, including follow-up work done later in the same session.

Aug 30, 2026
46 sections

Results survive a restart

Reopened query tabs now show the results they had when you quit, under a "cached" banner, instead of an empty results pane. Results are cached locally per tab (10,000 rows or 32 MiB by default), with owner-only file permissions and masked SQL literals; Preferences → Query History lets you change the limits, turn the cache off, or clear it.

Preview images and geometry from a result cell

Binary cells that hold PNG, JPEG, GIF, WebP or BMP pictures (and text cells with an image data URL) open in a picture viewer with fit and 100% zoom. Spatial values arriving as well-known text or binary — for example PostGIS geometry — are sketched as points, lines and polygons with their type, SRID and bounding box.

Large text and binary values load as previews

Result grids no longer hold multi-megabyte text or binary values in memory. Cells above 8 KiB show a short preview with a size badge, and opening, hex-viewing, or copying the cell fetches the full value from the table by its primary key, up to a 32 MiB viewer limit. Queries that leave out the key columns still show the preview, with a note on how to fetch the rest. Very wide rows that previously failed on the engine path with a frame-size error now stream normally.

Explorer shows sequences, triggers, synonyms, extensions, events, materialized views, and constraints

Tables now have Triggers and Constraints folders, and databases show the object kinds their engine supports: materialized views, sequences and extensions on PostgreSQL, events on MySQL and MariaDB, synonyms on SQL Server, and database triggers wherever they exist. Each item has an icon, a context menu with Copy Name, Properties, Script as CREATE and Script as DROP, and materialized views, events and extensions get Refresh, Edit Event and Install Extension actions that open reviewable SQL in a new tab.

Export options and batch table export

Exports can now write NULLs as empty, NULL, or custom text, tame embedded line breaks, drop the header row, and gzip the file. A new Export Tables… command on databases and schema folders exports any set of tables in one cancellable background job with a filename pattern.

AI drawer: analyze history runs, faster tool turns, quit guard

Query History rows can be sent to the AI for analysis with their timing, outcome and plan summary. Agent runs execute several read-only lookups at once, the transcript groups each turn's steps under a collapsible header, and quitting during a run now asks whether to wait or quit anyway.

Create and drop databases from the explorer

The server context menu gains New Database… and every database gains Drop Database…, each showing the exact statement for your engine with collations and encodings loaded from the server. Drops require typing the database name, Run passes through the usual safety review, and production connections can only script.

Create a table from clipboard data

Copy rows from a spreadsheet or a text file and choose Import from Clipboard — from the Tools menu, the command palette, or a database's context menu. SQLLY detects the delimiter and header row, prefills a timestamped table name, and hands you the same reviewed CREATE TABLE and INSERT script the file import wizard produces.

Import connections from TablePlus and Navicat

Import From Other Tools now reads TablePlus connection lists (Connections.plist, or an export saved without a file password) and Navicat .ncx exports alongside DataGrip and DBeaver files. Names, engine, host, port, user, database, groups, environment, TLS posture, and SSH hosts come across; saved passwords in those files are deliberately ignored and requested on first connect.

Search a whole database for a value

The explorer's database menu and the command palette gain "Search Database for Value…". Enter a text or numeric value, choose exact or contains matching, and SQLLY searches every table (up to a limit you set) with read-only queries, showing progress and letting you cancel. Hits list the table, column, and row, and "Open Row" opens that table filtered to the match in a new query tab.

Per-connection startup SQL

Saved connections can include optional startup SQL that runs once when a session opens, split with the same dialect-aware rules as the editor (including GO on SQL Server). The connection editor and compact connection form expose a Startup SQL field; a failing batch rejects the connection with a clear error instead of leaving a half-configured session.

Execution plans can be viewed as a tree or a diagram

The execution plan pane now has Findings, Tree, and Diagram views. Tree shows operators indented under their parents with cost share, estimated and actual rows, and warnings, and collapses with the arrow keys; Diagram draws the operators as boxes from the root down, pans with the scroll wheel, and opens an operator's details on click. SQL Server plans now keep their real operator structure instead of a flat list.

Relationships diagram is interactive and exportable

The relationships diagram now supports zoom (Cmd-scroll, pinch, or the +, −, 100% and Fit buttons), dragging tables to rearrange them, a layered layout that starts from the most-connected tables, and a Focus mode from a table's context menu that shows only that table and its neighbours. An Export menu copies or saves the diagram as SVG, Mermaid or DBML.

Schema Compare suggests renames, orders the deploy, and can run it

Schema Compare now proposes renames for objects that share most of their columns, writes the deploy script in dependency order with a matching rollback script, shows changed definitions side by side, and can deploy straight to the target with progress and an optional single transaction.

Skip, snooze, or defer an update

The update dialog now lets you skip a version, be reminded tomorrow, or have SQLLY open the download only once nothing is running. Downloading a version clears any earlier skip.

Reusable tunnel profiles with proxy support

Bastion and proxy settings can now be saved once as a named tunnel profile and reused by any connection. SOCKS5 and HTTP CONNECT proxies are supported as a first hop before SSH, each profile has its own Test button, and SSH hosts can be picked from your ~/.ssh/config.

Kubernetes port-forward connections

Connections to PostgreSQL, MySQL/MariaDB, and SQL Server can now go through kubectl port-forward. Choose the context, namespace, and pod, service, or deployment in the connection form; SQLLY starts the forward on connect, reuses your existing kubectl login, surfaces kubectl's error text when something is wrong, and shuts the forward down on disconnect or quit. The command-line tool and the MCP server use the same connection settings, so they get the forward automatically.

Column formatters for JSON paths, templates, and number styles

A result column can now show one field out of a JSON document (for example the customer name inside an order payload), wrap values in a template such as a currency prefix or a fixed-width order number, or render numbers as percentages or byte sizes. Copy and export still carry the original value. A new Custom formatter… choice in the grid's Format Column menu previews the result on the clicked cell before it is applied.

Open SQLLY from links and SQLite files

sqlly:// links can now open a connection, reveal a table, place SQL in a new editor tab, or open the connection editor pre-filled from a connection string; SQLite files opened or dropped onto the window become connections. Each link shows a confirmation sheet first, SQL from a link is never run automatically, and a second launch forwards its link to the window that is already open.

Test Connection shows server details

Testing a connection now reports the server product and version, character set and collation, identifier case sensitivity, current login and database, whether the connection is encrypted, and the handshake time. The Server page of Object Properties shows the same details.

Explain options, safe profiling, plan import, and copyable index SQL

The Explain button gained an options menu with per-connection settings for PostgreSQL EXPLAIN flags and MySQL/MariaDB analyze mode, greying out flags the connected server version cannot run. A new Profile preset captures the fullest plan for SELECT statements and refuses to run anything that writes. Plans saved as files or copied from elsewhere can be opened or pasted straight into the plan pane, and index findings now come with Copy SQL and Open in new tab buttons.

Browse a database's objects in a sortable grid

A new Object Browser lists every table, view, procedure, function, and trigger with row estimates, sizes, dates, and comments, shows columns, keys, and the definition for the selected object, and can script drop, truncate, or create statements for several objects at once into a review tab.

Sensitive columns are masked automatically

Password, secret, token, API-key, SSN, card-number, and IBAN columns are now masked by default on every connection, including copies and exports. A toolbar chip shows what is masked, the grid menu can reveal a cell or column for the current result only, and each connection can add or exempt patterns. The pattern list is editable under Preferences ▸ Results.

Statement splitting follows the database dialect, and Run says what it runs

Run current statement, the gutter play button, the statement frame and error highlighting now use the same dialect-aware splitter as Run SQL File, so PostgreSQL $$ bodies and MySQL DELIMITER scripts are no longer cut at inner semicolons. The Run button now reads Run selection, Run statement, Run all (N) or Run file, and a new Run Statements picker (Cmd+Shift+Enter) lets you tick the statements to execute as one reviewed batch.

Exports run in the background with progress and cancel

Saving results as CSV, TSV, JSON Lines, or INSERT statements streams rows to disk in the background with no row cap, shows progress in a new status-bar task chip, and can be cancelled. Finished exports post a notification with a Reveal button, and database dumps and data transfers appear in the same task list.

Manual commit mode for query tabs

Each query tab can now run in Manual commit mode: the tab keeps a dedicated connection, starts a transaction on the first statement, and shows Commit and Rollback buttons with a pending-changes marker. Idle transactions roll back after a configurable timeout, and closing a tab or disconnecting with uncommitted changes asks whether to commit or roll back.

Timed unlock for read-only connections

Connections set to allow SELECT only can be unlocked for 1, 5, or 15 minutes from the command palette; the status bar counts down and re-locks automatically.

Fold previews, tab renaming, and connection quick-switch shortcuts

Hovering a collapsed block's marker now previews its first lines. Tabs can be renamed from the context menu, by double-clicking the title or with F2, and a renamed tab keeps its name through edits and restarts. Ctrl+Shift+1 to 9 switch the active tab to the matching connection in the connect picker's order, with the usual production banner and guards.

Security editor now manages PostgreSQL roles and MySQL accounts

The Security tool works on PostgreSQL and MySQL/MariaDB in addition to SQL Server: create roles and accounts, set passwords and options, and grant database, schema, and table privileges. All changes preview as SQL first, and applies now honour the connection's SELECT-only policy and the production write lock.

Explorer search, pinned objects, and drag-to-editor

The object filter now ranks exact and prefix matches first, understands abbreviations like SOH, accepts /regex/ patterns, and can be narrowed with chips for tables, views, routines, columns, and the extra kinds engines expose. Favorites are a Pinned group at the top of each server with Move Up / Move Down ordering, and dragging a table, view, or column onto the editor inserts its quoted name for the connected engine.

Grid conveniences

The result grid's header menu can now hide every empty (all-NULL) column at once and jump to a column by name (also Cmd+G in the grid). A new Row density preference offers Compact, Normal, and Comfortable rows. Keyboard shortcuts while the grid is focused stage a new row (Cmd+N), mark a row for deletion (Delete), undo and redo staged edits (Cmd+Z / Shift+Cmd+Z), and refresh the query (Cmd+R). Filtering the find bar to matching rows waits on sqlly-datatable 5.3.0.

Paste rows, duplicate without key, and batch insert in the results grid

Rows copied from a spreadsheet can be pasted into a result grid as pending inserts or over a selected block as pending edits, with values that do not fit a column reported per cell. A row can be duplicated with its key cleared, and Batch Insert stages up to 1,000 copies of a template row with constants, sequences, defaults, or sample values. Everything is reviewed and saved through the existing pending-changes flow.

Saved queries live in a folder-based SQL Library

The sidebar has a Library panel that lists .sql files from a folder you choose, organized by subfolder, with Save to Library, rename, delete, and an export of pinned history queries. Files can note which connection they target and the app switches to it when you open them.

Open files notice external changes

A tab whose file was changed or deleted outside the app shows a badge and asks whether to reload, keep your version, or close, instead of overwriting the newer file on save.

Keep previous results when re-running a query

Query tabs now keep their last few completed runs in a strip above the results, and a new Execute into New Result Tab command runs the query without discarding the previous grid. Runs can be pinned so they are never dropped, and the number kept is configurable in Settings ▸ Results.

SQL notebooks

A new notebook tab lets you mix SQL and Markdown cells in one document. Each SQL cell runs on its own and keeps its own result grid and chart, cells can reference earlier cells by name, shared parameters live in a bar at the top, and Run All runs the document top to bottom, stopping at the first error. Notebooks save as small text files without results and export to Markdown.

Filter groups, any-column search, and multi-column sort

The results filter builder gains AND/OR groups, an "Any text column" search, and is one of / is empty operators, and turns them into a properly parenthesised WHERE clause for the editor or a matching client-side filter for the grid. Column headers can add secondary sort keys, the toolbar shows the active multi-column sort, and that sort is remembered per table together with the column layout.

Type-aware cell editors and cell actions

Editing rows is no longer all text boxes. Boolean columns get a true/false/NULL picker, date and time columns get Today, Now, and Server time shortcuts, enum and CHECK-IN columns get a dropdown of allowed values, and binary columns can be loaded from or saved to a file (up to 16 MiB). Grid cells gain Set NULL, Set DEFAULT, and Set to server time actions. All of it stages through the same preview-and-confirm flow as before.

MCP server: activity log, impact estimates, rate limits and per-connection modes

SQLLY's MCP server now records every tool call and proposal outcome in a private, secret-masked activity log you can filter and export from the MCP settings. Write proposals show an estimated number of affected rows and a plan summary before you approve them, agents are throttled per minute for reads, writes and schema changes, individual connections can be locked or made read-only beneath the global mode, and install snippets now cover Claude Desktop, Windsurf and VS Code as well as Claude Code and Cursor.

Cell Details panel docked next to the results grid

A new Cell Details panel can be docked to the right of or below the results grid. It shows the full value of the selected cell — text, JSON or XML tree, hex, image or geometry — and a read-only form of the whole row, follows the selection as you move, can be pinned, resized, and opened as a separate window. The dock side is a Results preference.

Column selection and gesture zoom

Alt+drag or Shift+Alt+Up/Down now selects a column block with a caret on every line, so one edit applies to all of them. Cmd+wheel and trackpad pinch zoom the query editor and, separately, the results grid.

Results pane can be maximized or detached to its own window

The results pane gains a maximized state that folds the editor to a single line so the grid gets the whole window, alongside the existing hide/show toggle. A new Detach Results command moves the active tab's results into a separate window that keeps receiving that tab's query results, remembers its position per tab, and re-attaches when closed.

Server Dashboard

A new Tools > Server Dashboard shows live connection headroom, throughput, cache hit ratio, deadlocks, temp-file activity, replication lag, uptime, and database sizes for PostgreSQL, MySQL/MariaDB, and SQL Server, refreshed every five seconds with sparklines and one-click links to Activity Monitor, Query Insights, and Disk Usage.

Preview query results inside the AI chat

Generated SELECT statements in the AI drawer now have a Run here button that shows up to 200 rows in a small grid directly under the message, with a rows-and-time footer, Cancel while running, and an Open in results tab button for the full experience. Statements that change data keep going through the editor and its review dialog, and inline previews are not saved in chat history.

Visual query builder

A new Query Builder tool lets you compose a SELECT by picking tables from the schema, with joins suggested from foreign keys, column checkboxes, aggregates, filters, grouping, ordering and a row limit. The SQL preview updates live in your engine's syntax, and you can insert it into the editor or run it in a new tab. Tables in the explorer gain a Design Query action.

Structure editor handles dependent views, index options, and default quick picks

Scripts from the structure editor now drop and recreate views that depend on a changed column, keep each index's access method, filter and fill factor, and offer a quick-pick of engine-correct default expressions next to every default value. The SQL Server index designer gains filter, fill factor and clustered options.

v0.66.5
Aug 30, 2026 · 1 section
Aug 30, 2026
1 section

Windows release build no longer fails on golden line endings

The structure-editor (sqlly-alter) and database-dump golden fixtures now carry a .gitattributes pinning them to LF, matching the other golden directories. Without it, Windows checked the files out as CRLF and the byte-for-byte sqlly-alter golden tests failed against the generator's LF output. Test-only change.

v0.66.4
Aug 30, 2026 · 1 section
Aug 30, 2026
1 section

Query-history tests no longer race the shared store path

The query-history unit tests drive a process-global store-path override; run in parallel they could read each other's database (a legacy-log import landing in the wrong file), which was intermittently failing the release build. They now serialize on a shared lock. Test-only change.

v0.66.3
Aug 30, 2026 · 1 section
Aug 30, 2026
1 section

Release test suite hardened so builds stop failing on harness issues

The database-dump golden comparisons ignore the version stamp instead of breaking on every release bump, the module-hygiene guard recognizes include!-shared source files, and the SSH tunnel tests no longer race on shared host-key state under parallel execution. These were test-harness problems, not shipped behavior, but they were blocking the release build.

v0.66.2
Aug 30, 2026 · 2 sections
Aug 30, 2026
2 sections

Query history no longer crashes on non-ASCII SQL

Logging a statement whose text starts a keyword scan on a multi-byte character (accented identifiers, non-Latin text) no longer panics while redacting secrets for the history store.

Epoch columns reject out-of-range values instead of showing absurd dates

When a milliseconds value is formatted with the seconds unit (or any epoch value lands outside years 1–9999), the cell now stays blank rather than rendering a nonsensical five-digit year.

v0.66.1
Aug 29, 2026 · 6 sections
Aug 29, 2026
6 sections

Connection picker icons sit inside the dropdown

The command-bar server and database pickers now show their kind icons inside the field, to the left of the name, instead of as separate glyphs beside the dropdown.

Command bar lines up with the query tab

The server picker shares its left edge with the active query tab, instead of sitting a few pixels further in.

AI drawer no longer crashes when a database is connected

Opening the AI drawer after connecting to a database no longer quits the app. Generate and Ask paint their chat view without colliding with an in-progress update.

Generate and Ask have a session bar for New and History

On Generate and Ask, New starts a fresh chat and History opens your saved conversations as a list that replaces the transcript — the prompt hides until you pick a chat. Search and Joins stay as they are; Clear remains in the ⋮ menu.

AI conversation history is easier to scan

Each saved chat shows its title on one line and the mode plus a relative time on the next, with compact save and delete icons, so names no longer collide with timestamps in the narrow drawer.

AI drawer chrome matches the mode pills in a narrow panel

New, History, and the Copy / Insert / New query actions use the same compact type as Generate, Ask, Search, and Joins, and wrap instead of clipping when the drawer is narrow.

v0.66.0
Aug 28, 2026 – Aug 29, 2026 · 36 sections
Aug 29, 2026
3 sections

Freeze, hide, and reorder result-grid columns

You can pin leading columns while scrolling a wide result, hide columns you do not need, and move columns left or right from the column-header menu. Those choices, along with widths, are remembered for the same table or query and restored on the next run; Reset Column Layout clears freeze, hide, and order as well as widths.

Staged rows tint the whole grid row

Pending inserts, edits, and deletes now tint the entire result row (green, amber, or red) in addition to the marker glyph, so a staged batch is visible even when the marker column is off-screen.

Stage Delete on multiple selected rows

When more than one result row is selected, Stage Delete stages every selected row immediately without opening the row editor. A single row still opens the editor so you can review the delete there.

Aug 28, 2026
33 sections

Column hints in INSERT statements

The SQL editor now shows dim column-name labels before each value in INSERT … VALUES statements, using the typed column list or the table's column order, so long value lists are readable at a glance. Off/on under Preferences → Editor.

Hover cards and signature help in the SQL editor

Rest the pointer on a table, column, procedure, or function name to see its columns, types, and keys, and while typing inside a function call the editor shows the parameter list with the current argument highlighted. Both can be turned off in Preferences → Editor.

Parameter suggestions, script variables, and list tools

The parameter strip now shows one-click chips for recently used values per parameter name. Scripts can declare -- sqlly set name = value lines that replace ${name} placeholders verbatim before parameters run. Edit adds Paste as IN List (Alt+Shift+I) to turn clipboard columns or delimited text into a dialect-quoted IN (...) list, and Convert Delimited List (Alt+Shift+L) to reshape a selection with quoting, prefix/suffix, wrapping, and a live preview.

Remember result-grid column layouts

Result grids now remember column widths per table or per repeated query text and restore them on the next run when the column names and types still match. A Reset Column Layout item on the grid context menu forgets the saved layout, and widths you drag while a large result is still streaming no longer snap back when the run finishes.

Coding-agent CLI provider for Agent mode

Agent mode can run through a local Claude Code or Codex CLI you already use: SQLLY wires the tool to the SQLLY MCP server only, streams each lookup in the drawer, and still requires your confirmation before any data-changing SQL runs.

Table structure editor

Right-click any table in the explorer to edit its columns, indexes, and foreign keys in a designer-style dialog with a live change preview. The generated script matches your engine — ALTER statements for SQL Server, PostgreSQL, and MySQL/MariaDB, and SQLite’s full rebuild recipe with foreign-key checks — and opens in a new query tab for review; nothing runs from the dialog.

AI drawer gains @ mentions, attachments, templates, and conversation history

The AI drawer now lets you type @ to mention a table, saved query, or project SQL file so the assistant grounds on that object first, drop or paste a small CSV or text sample as an attachment, insert a reusable prompt template, and reopen earlier chats from History. Mentioned objects' schema cards, attached text, and the prompt go to whichever AI backend is selected; saved conversations stay on this machine with values in SQL masked. Cmd+Shift+A (Ctrl+Shift+A on Windows and Linux) sends the editor selection to Ask.

Agent-friendly command line

Adds doctor, connections, query, schema, and context as the primary CLI surface with a stable JSON envelope, five output formats, and fixed exit codes (0 ok, 1 error, 2 usage, 3 policy refused). Queries resolve saved connections through the OS keyring, default to read-only execution, and refuse writes on production; the legacy harness subcommands remain available but hidden from --help.

SQLLY as an MCP server

Added sqlly-cli mcp, a local stdio Model Context Protocol server so coding agents can list connections, browse schema, and run read-only SELECTs against saved connections without receiving any password. Read-only by default; a Read/write-with-confirmation mode turns agent write requests into proposals that open in SQLLY behind the usual confirmation and AI-review gate. Production connections are always refused. Settings ▸ AI ▸ MCP holds the policy and a copyable install snippet. SQLLY still does not execute other MCP servers' tools.

Wire-compatible engines in the connection picker

The connection editor now lists CockroachDB, YugabyteDB, Aurora/RDS, Azure flexible servers, Cloud SQL, Neon, Supabase, PlanetScale, and TiDB with correct default ports, TLS rules for hosted services, and honest notes about missing admin tools. Catalog routine definitions on CockroachDB use a compatibility fallback instead of unsupported PostgreSQL catalog functions.

Add SSH tunnel transport for network engines

Network connections can route through an SSH bastion with agent, key-file, or password authentication, an optional jump host, host-key trust-on-first-use against your known-hosts file, and secrets kept in the OS keyring. The tunnel opens when you connect and closes when you disconnect.

Production environment guard

Production connections are read-only by default with a warning banner, status-bar chip, timed write unlock (1/5/15 minutes), and a confirmation dialog that shows the exact SQL before any data change runs. Existing stores should enable Production environment on the Production entity in entity management; fresh installs seed it on by default. Cmd+Shift+L is already used for the log console, so Lock Production Writes has no default key chord.

Add cross-connection data transfer wizard

Adds a Data Transfer wizard (explorer right-click and Tools menu) to copy tables between connections and engines with create/append/truncate/upsert modes, cross-engine type mapping with visible lossiness notes, parameterized 1000-row batches, per-table transactions, progress and cancel, plus a CLI transfer subcommand for headless use.

Query history moves to a local database

Query execution history is now stored in a compact local database that collapses consecutive identical runs, records affected rows and whether a run came from the AI assistant, and loads large histories page by page. The previous text log is imported once on first launch and kept beside the new store as a .bak file.

AI agent mode with read-only tools

The AI drawer can run in Agent mode on Generate and Ask: the model may look up tables, columns, sample rows, and run read-only SELECT queries within a turn limit you set in Settings. Any data-changing SQL it proposes still requires your explicit confirmation through the normal safety review and runs at most once against the exact statement shown.

Import Excel sheets, detect CSV encoding, and create tables from imported data

The Tools > Import / Export wizard now reads Excel workbooks (.xlsx and legacy .xls) with a sheet picker, automatically detects common CSV text encodings (UTF-8 with or without a BOM, UTF-16, GBK, and other typical Windows code pages) and shows the chosen encoding in the dialog, and can generate a CREATE TABLE statement with engine-appropriate column types inferred from the file before the INSERT script. Nothing executes from the dialog; row and file size caps are unchanged.

Data Compare for table rows across connections

Tools → Data Compare aligns two tables or queries by key columns, streams both sides in key order through a bounded merge, shows only-left / only-right / changed rows in the familiar diff grid, and scripts a review-only sync (left→right or right→left) into a new editor tab pinned to the destination connection.

Smarter completion ranking and automatic table aliases

Completion now boosts exact matches and recently accepted items, flags FK-related tables, offers literal templates after =/LIKE, and inserts table AS alias after FROM/JOIN according to the alias preference.

Add AI provider profiles with Gemini, DeepSeek, and compatible endpoints

Settings ▸ AI ▸ Cloud now uses editable provider profiles instead of a fixed OpenRouter/OpenAI/Claude trio, with model fetch, latency test, per-vendor reasoning controls, and egress labels that name where prompts go. API keys remain in the platform keyring only; profile settings never store secrets.

Quick-open object palette

Added a Go to Object palette (Cmd+P / Ctrl+P) that fuzzy-searches every loaded connection, database, table, view, procedure and function across all servers. Enter reveals the object in the explorer without touching the active connection; Cmd+Enter opens a top-rows SELECT in a new tab on that object's own server; Alt+Enter opens its definition; a leading > searches commands.

Paste connection URLs and import from other tools

Paste a postgres, mysql, sqlserver, JDBC, ADO.NET, or SQLite path connection string to fill the connection editor in one step, with any embedded password routed only to the secure password field. File → Import From Other Tools reads DataGrip-style XML or DBeaver-style JSON, maps environments and SSH tunnel host settings onto SQLLY profiles, skips duplicates, and never writes secrets into your connection store file.

Export a whole database as a SQL dump, with optional scheduled dumps

Adds Export Database as SQL on the explorer database node and Tools menu to stream engine-matched DDL and batched INSERTs to disk with progress and cancel, plus Preferences scheduled dumps (hourly/daily/weekly with retention while the app is open) and a dump-database CLI subcommand that prints JSON and refuses production connections. Restore remains review-first: open the .sql in a query tab rather than auto-executing from SQLLY.

Add MRU tab switcher, tab history, and tab restore and disconnect policies

Ctrl+Tab now cycles query tabs in the order you last used them, Back/Forward commands retrace those visits, and Preferences → Editor → Tabs lets you choose which tabs reopen at launch and what disconnecting does to a connection's tabs.

Default copy format, SQL extractors, and mask/epoch formatters

Cmd+C uses your chosen default copy format (TSV, CSV, Markdown, JSON, JSON Lines, or INSERT). The grid can also copy a column as a SQL IN list, selected rows as UPDATE statements, or JSON Lines, and per-column Mask and Epoch formatters hide secrets on screen or show Unix timestamps as datetimes without changing stored values.

Result auto-refresh, total row count, and live elapsed timer

A query tab can re-run its last SELECT on a 5s / 10s / 30s / 1m timer from the results status strip or the command palette, pausing while you are on another tab or a run is in flight and never repeating a data-changing statement. When a result is truncated or spilled past the preview cap, Calculate total rows runs a separate COUNT(*) of that same SELECT so the strip can show shown-of-total, and the strip keeps a live Running 1s… readout for the whole time a query is streaming.

Add SQL snippets with tab stops and shortcut-bound quick actions

You can save SQL snippets with ${1:default} and $0 placeholders, expand them from the completion list or by typing the prefix and pressing Tab, then walk the placeholders with Tab and Shift+Tab. Quick actions are named templates (${selection}, ${table}, ${database}) assigned to one of nine slots, listed in the command palette, and bound to a shortcut in Keyboard Shortcuts.

Add Run SQL File streaming batch executor

Adds File → Run SQL File… and an explorer database-node action to stream a script statement by statement with stop/continue/ask-on-error policy, optional single-transaction wrap, throttled progress, cancel, and a summary in the tool output pane; production-locked connections are blocked at the gate. The same runner powers the headless run-file CLI subcommand (JSON outcome, production refuse exit 3).

Run on multiple connections

Added a Run on… dialog that fans the current statement out to selected connections, with one named result tab per connection, per-connection error messages, a single Cancel, and the destructive-change confirmation evaluated for every target.

Grid power features: filter builder, follow foreign key, transpose

Result grids can filter the in-memory set with a visual condition builder (or Apply to Query for a WHERE clause), open the parent row a foreign-key cell points to in a new tab on the same connection, and transpose a short result so columns become rows. Frozen (pinned) leading columns are not in this release: they need a sqlly-datatable crate change that could not be published from this worktree.

Staged multi-row grid editing with SQL preview and single-transaction save

Stage many row edits, inserts, and deletes from the result grid without committing each one immediately. The row-editor modal can stage changes instead of saving; the results toolbar shows pending counts with undo, redo, Preview SQL, Save, and Discard. Save runs the whole batch in one transaction behind the same production and confirmation gates as Run, and writes a rollback script into query history for manual recovery.

Vim keybindings for the SQL editor

The SQL editor now has optional Vim-style modal editing (Normal, Insert, and Visual) with the familiar motions and delete/change/yank operators. It is off by default; turn it on from Preferences or the command palette and the current mode appears in the status bar.

Underline execution errors and add qualify identifier actions

When a query fails, the editor underlines the span the database reported and the Messages pane offers a Go to error button (F8 jumps to the next error); the underline clears as soon as you edit. Right-click a column (or Cmd+Alt+Q / Ctrl+Alt+Q) to prefix it with its table alias, strip a redundant qualifier, or qualify every unambiguous column in a selection.

Statement run gutter, current-statement frame, and result-source highlight

The editor gutter now shows a play control on each statement's first line (click to run only that statement, with the usual safety checks), draws a hairline frame around the statement at the caret, and tints the lines that produced the result set you are viewing. Both the frame and the tint can be switched off in Preferences → Editor.

v0.65.2
Aug 29, 2026 · 1 section
Aug 29, 2026
1 section

Dependency upgrades across the Rust workspace

Upgraded a batch of Rust dependencies to their latest major releases and refreshed the lockfile: TLS/networking (webpki-roots to 1.0, tokio-postgres-rustls to 0.14), the relay/bridge crypto and cert stack (jsonwebtoken to 11, rcgen to 0.14, base64 to 0.23), and the Noise transport primitives (snow to 0.10, x25519-dalek to 3, blake2 to 0.11), plus every semver-compatible bump. Adjusted the code for the breaking API changes — Noise's builder now validates the private key up front, rcgen renamed a field, and jsonwebtoken 11 requires selecting a crypto provider (we use the pure-Rust one, matching the rest of the workspace). keyring and the wasm randomness crates (rand/getrandom) were deliberately held back to avoid destabilizing credential storage and the browser build.

v0.65.1
Aug 28, 2026 – Aug 29, 2026 · 3 sections
Aug 29, 2026
2 sections

Dependency refresh and gpui-component upgrade

All Rust dependencies were brought up to their latest compatible versions (~190 crates), and the gpui / gpui-component git trees were moved to current upstream. The gpui-component upgrade split its unified text input into per-mode types, so every multi-line field (AI playground prompts, IntelliSense feedback dialog, template editor body, MCP environment field, agent job step field, log console, result messages pane) now uses the new Textarea, and the DDL source viewer uses the new Editor. Two hand-rolled read-only workarounds — the log console and the result messages pane used to revert every keystroke because the input had no read-only mode — were replaced by the library's new readonly rendering, which also stops dimming read-only source in the DDL viewer. The obsolete psm wasm patch (its consumer, stacker, left the dependency tree) was dropped. The results grid moved to sqlly-datatable 5.1.0, whose only breaking change is the gpui-component floor this upgrade establishes. Its pivot sidebar now applies programmatic widths through the library's public resize path (clamping and notifying like a drag) instead of re-seeding the split state, and the grid and pivot pick up macOS-style scroll physics — rubber-band overscroll, bounce-back, and smooth wheel glides — active by default.

Toggle for results grid animations

Settings ▸ Results ▸ General adds an "Enable grid animations" switch. Off turns the results grid and pivot to instant, hard-clamped scrolling and removes cell and dialog fades — the macOS-style scroll physics and motion introduced with the datatable upgrade. Data updates stay instant regardless, and the setting is persisted with the other results-formatting preferences.

Aug 28, 2026
1 section

Release builds skip themselves when a tag has no client changes

Pushing a release tag that only contains website or server work no longer runs the full client release build: the pipeline now starts with a quick gate that diffs the tagged commit against the previous tag and skips the Linux, Windows, macOS, and WebAssembly builds when nothing under the client workspace (or the build pipeline itself) changed. Changelog publishing still happens on those runs, since website-only releases exist to ship changelog entries. Pull-request validation and manually queued builds are unaffected — they always build.

v0.65.0
Aug 28, 2026 · 7 sections
Aug 28, 2026
7 sections

Fix the weekly GeoLite2 refresh (GEO IP Update pipeline)

The weekly MaxMind geo-IP sync had been failing on every run. Two independent problems were fixed in the Sqlly.GeoSync importer:

The MaxMind license key stored as an Azure DevOps secret carried a trailing newline, which the download URL escaped to %0A and MaxMind rejected with a 401 "Invalid license key". The importer now trims the key (and the optional account id), so a stray newline in the secret no longer breaks the download.
Even after the download succeeded, the importer looked for the CSV files only at the top of the extracted folder, but MaxMind nests them inside a dated subdirectory, so nothing was found and the run failed with a misleading "empty download" error. The importer now searches recursively.

Failed downloads now surface MaxMind's own error text instead of an opaque HTTP exception, and the importer gained an optional MAXMIND_ACCOUNT_ID secret that switches it to MaxMind's recommended HTTP Basic authentication.

Admin map of download and purchase origins

A new Map page under the Admin menu shows on an interactive world map where downloads and purchases are coming from, using the locations recorded when each download or checkout resolved its visitor's IP. Downloads and purchases are plotted as separately colored points sized by activity, countries are shaded by how much activity they have, and a per-country table below the map carries the same numbers without JavaScript. A time-range control narrows the view to the last 30, 90, or 365 days or all time, and counters show how much of the traffic had no usable location. The map libraries are served from the site itself, so the strict content-security policy is untouched.

Privacy policy now spells out exactly what is recorded

The privacy policy gained a "What I do track" section that states plainly what the site records: every download captures the requester's IP address, browser user agent, SQLly version, platform, and architecture — never tied to an account or anything identifying, whether signed in or not — and every purchase records what was bought, the purchasing account, and the checkout IP, with payment data itself retained by Stripe. Surrounding text and the TL;DR were updated so the page no longer claims IPs are never logged.

Removed em dashes across the website

Every em dash in the website's pages, page code, styles, scripts, and the strings the site shows users (such as invoice descriptions) was replaced with a plain hyphen, and the tests asserting those strings were updated to match.

Cleaner download page intro

The text under "Take SQLly home" was rewritten to say plainly what the builds are (signed macOS, supported-beta Windows and Linux, keyring-stored passwords, an in-tab demo that is not the product) and now wraps at a wider, more natural line length instead of breaking every few words. A note explains that because each platform's build is produced independently, a missing platform for a version means that build did not finish yet, not that support ended - check back soon for the fresh build.

IntelliSense feedback now turns into permanent regression tests

Reported IntelliSense gaps can now be converted into automated tests that guard the fix forever. A new verification suite recreates the exact situation a user reported and checks that the completion suggestions are right; those tests run continuously on the build system, so once a gap is fixed it cannot silently come back. The suite runs on a single dedicated build leg by design and is invisible to users — they just see the improvement land and keep working in later releases. Internally this adds the sqlly-intellisense-verify crate (compiled and run only when a CI variable selects its platform — Linux x64), CI wiring in both the Azure and GitHub pipelines, and an intellisense-feedback-tests skill that pulls one submission from the store using the operator's Azure sign-in and generates the test from it.

Admin map renders instead of a browser script error

The map on the admin Map page failed to start, leaving the browser console with a "Refused to execute script" error naming the page itself and no map above the country table. The cause was a mismatch with the bundled map library: the page set the worker location through a property the library does not actually read, so the library tried to start its background worker from an empty address that resolves to the page itself. The page now sets it through the interface the library really exposes, so the map draws again.

v0.64.0
Aug 27, 2026 · 43 sections
Aug 27, 2026
43 sections

CLI engine catalog and validation use the same services as the desktop

The CLI engine subprocess now routes catalog sync, schema search, SQL validation, generation, and review through the shared catalog service the GPUI app calls in-process, instead of duplicating that logic in the protocol adapter. Wire tags and frame shapes are unchanged; fixes to catalog behavior land once in sqlly-services.

Schema catalogs use the platform data directory on Windows and Linux

Schema catalog SQLite files now default to the same platform data directory as preferences and the engine log (…/SQLLY/Catalogs), instead of a hard-coded macOS Library path. Existing catalogs created under the old mistaken path on Linux or Windows are still found on the next launch.

Fix the weekly geo-IP refresh failing to download the MaxMind database

The scheduled GeoLite2 refresh was failing with a 401 before it could update any location data: it requested the database from MaxMind's account-authenticated endpoint but sent only the license key, which that endpoint rejects. The importer now uses MaxMind's license-key download URL, which authenticates with the license key alone, so the weekly job can fetch the current database again.

Dropdowns use the same hairline as buttons

Dropdowns and text fields in Settings and throughout the app now draw their outline in the same hairline color as buttons, so a font picker or similar control reads as a dropdown instead of blending into the page.

Server and database pickers stay put and show what they are

The command-bar server and database controls are fixed-width dropdowns with a server or database icon, so switching to a longer name no longer shoves the rest of the bar sideways, and you can tell the two pickers apart at a glance.

Ctrl+Tab shows the query snapshot beside the tab list

The tab switcher lists open queries on the left and, on the right, the highlighted tab's summary with a live snapshot of the SQL underneath, so you can confirm you have the right query before you release Ctrl.

Completions copy less of the buffer on each keystroke

IntelliSense token scanning borrows spans from the SQL buffer instead of cloning every word, and rule checks lowercase only the current statement instead of the full script, so typing in long queries does less redundant work per keypress.

Mutation confirm runs the reviewed SQL

Confirming a data-changing query now executes the SQL that was reviewed when the dialog opened, not whatever is in the editor afterward. Starting another Run while a mutation review is pending no longer skips confirmation on other tabs.

Project SELECT-only enforced at run time

SELECT-only and rollback-wrap settings on a client, project, or environment are now applied to each query run and row edit the same way as the connection-level checkboxes, so inherited safety cannot be bypassed by confirming a mutation dialog alone.

Export and import connections without passwords

File → Export Connections… writes every saved connection together with its client, project, and environment grouping and safety settings to a JSON file that never contains passwords or secret references; File → Import Connections… merges that file into your saved list with fresh connection identities and asks you to re-enter SQL login passwords before the first connect.

Explorer kinds use icons, not letter prefixes

Tables, views, procedures, and functions in the Explorer no longer show TBL / VIEW / PROC / FN tags beside the name. Each kind has its own icon, and the table glyph is a data grid rather than a dining table, so it reads as a database object for everyone.

Completions stay bounded while you type without a prefix

Completion ranking caps candidate allocation at 2048 items while pushing, and procedure snippets are built only when a typed prefix matches, so empty-prefix requests no longer materialize every column and stored procedure in the catalog.

Catalog schema refresh writes fewer SQLite statements

Syncing a large schema now batches unchanged timestamp updates, tombstones, column inserts, and inferred relationship writes instead of issuing one statement per row, so full refreshes spend less time in SQLite I/O.

GitHub README states Windows and Linux supported-beta status

The root README now says Linux and Windows ship as a supported beta, unsigned until Authenticode and package signing, with links to ADR-020 and the supported-platforms doc so the public front door matches PRODUCT and Downloads.

Live-SQL docs match the just live-sql recipe

The README now explains that just live-sql only sets SQLLY_REQUIRE_LIVE_SQL=1 and that you must supply credentials via .sqlly-local-sql.json or exported SQLLY_SQL_* vars first; Azure CI still injects the full env set.

AI schema answers treat database definitions as quoted data

Schema intelligence marks catalog definitions, questions, and SQL excerpts as fenced untrusted data in AI prompts so instruction-shaped DDL in comments cannot override grounding rules.

PostgreSQL and MySQL conformance on a weekly Azure schedule

A new Azure pipeline runs PostgreSQL and MySQL driver conformance weekly (not on every PR), with just live-pg and just live-mysql for local runs when you already have those servers up; PR CI still exercises live SQL Server only.

Faster Linux live-SQL CI with shared compile cache

The Azure Linux live SQL job now uses the same cargo registry and sccache caching as macOS PR CI, and informational coverage no longer runs on pull requests so cold agents spend less time on duplicate work.

Failed Save Results and export writes are reported

A Save Results, grid export, or diff CSV export that cannot write the file — disk full, permission denied, or a path that is not a file — now shows the path and the reason in Messages and a notification instead of looking like it succeeded.

Failed reads of a spilled result page are reported

When a very large result is kept on disk and a page of it cannot be read — the file was removed or is unreadable — Messages says so instead of leaving a blank grid. A newer query or scroll that superseded that read stays quiet.

Datatable demo shows load failures as text, not HTML

If the hosted datatable demo fails to load, the error message is shown as plain text instead of being inserted as HTML, so exception text cannot run as markup.

Damaged connections file no longer wiped on save

A corrupt or unreadable saved-connections file is no longer replaced with an empty list on the next save. First launch still starts from an empty list, and successful saves now replace the file atomically so a crash mid-write cannot leave a half-written document behind.

Snapshot list no longer races with save

Listing result snapshots no longer deletes a payload file that a save has not finished indexing yet, so opening the snapshot browser while a save is in progress cannot orphan the new file.

Shared tinyint range checks match MySQL signed semantics

Model validation now treats MySQL and MariaDB signed tinyint columns as -128 through 127, while SQL Server tinyint stays 0 through 255, so shared range checks agree with the row editor.

SQLite keeps ill-formed TEXT as bytes

SQLite text values that are not valid UTF-8 are preserved as binary cells instead of being silently replaced with replacement characters, matching how MySQL already handles invalid text payloads.

Password changes revoke other sessions and relay bridges

Changing your password on the account page now requires your current password when one is already set, signs out other browsers, and disconnects relay bridges until you pair them again.

Sign-up no longer confirms whether an email is already registered

Trying to register with an email that already has an account now shows the same generic guidance on the page and API instead of saying the address is taken.

Sync server picks up August 2026 .NET security updates

The website and API host now build on .NET SDK 10.0.303 with ASP.NET Core and Entity Framework 10.0.11, bringing in the August 2026 runtime security servicing wave.

Retrying IntelliSense feedback no longer creates duplicate tickets

Signed feedback submissions must include the client-generated id, and the server treats repeats inside the signed timestamp window as the same ticket instead of inserting another row.

ViaRelay and CLI engine honor Required tenant scoping on validation

Remote and CLI engine SQL validation, review, and generation now receive the same per-database Required tenant setting as a direct desktop connection, so a missing tenant filter is an error on ViaRelay when Tools ▸ Tenant Scoping is set to Required.

Remote queries stop when a result cell is too large to stream

When a single result cell cannot fit in an engine protocol frame, the remote or CLI engine fails the query and cancels the statement instead of leaving it running after reporting the error.

Cancelling a remote row edit stops the statement

Cancel on an in-flight row edit over ViaRelay or the CLI engine now registers the same server-side cancel hook as query cancel, so UPDATE/INSERT/DELETE can be aborted before commit when the driver supports it.

Omitted trust-certificate on the engine wire verifies TLS

Engine connection JSON that omits the trust-certificate flag now defaults to verifying the server certificate instead of trusting it implicitly.

MCP config and schema catalogs are owner-only on Unix

MCP server lists and schema catalog SQLite files are created and tightened to owner-read/write permissions on macOS and Linux so shared-login hosts cannot read MCP endpoints or cached schema and AI cards from the data directory.

MCP Test blocks link-local and cloud-metadata HTTPS by default

The MCP Test handshake no longer calls HTTPS URLs aimed at link-local or cloud-metadata hosts unless you turn on the opt-in checkbox in MCP settings; private LAN HTTPS endpoints remain allowed.

Cloud plan copy describes desktop plus connector, not a browser app

Billing and pricing pages now describe SQLly Cloud as the desktop app reaching your databases through an onsite SQLly Bridge connector, with the in-tab browser preview still labeled as a separate demo. Team profile sharing is no longer promised until that feature ships.

Import wizard copy clarifies which export formats round-trip

The Import / Export dialog and feature catalog now state that CSV, JSON, and SQLLY Export can be imported back after Save Results, while Excel and other export-only formats must be saved as CSV or JSON first.

In-memory result grids keep one copy of cell text

Large in-memory result sets no longer store the same cell strings twice in the app and the grid widget; export, diff, and pivot still read the full set when needed, and spill-backed results are unchanged.

Explorer filter and row hover stay cheap while the tree is large

Object Explorer builds each row's hover group when the node is created and lowercases the filter once when you type it, so expanding or filtering a large loaded tree no longer allocates a string per visible row on every paint.

Query tab titles are rebuilt when the tab changes, not every frame

The query tab strip keeps each tab's title, close label, and status announcement until the name or status actually changes, so painting many open tabs no longer formats those strings on every frame.

Completion popup list is shared across editor paints

The editor keeps the live completion list behind a shared handle, so scrolling or blinking the caret no longer clones every suggestion on each paint.

Distinct-value lookups keep recently used columns

The distinct-value cache now drops the least recently used column when it fills, instead of an arbitrary HashMap slot, so a column you keep typing against is not evicted by a later lookup.

Editor line numbers keep their shaped glyphs while you scroll

Gutter numbers are shaped once per font and size and reused on later paints, so scrolling a long script does not reshape every visible line number each frame. Changing the editor font size still redraws them.

v0.63.0
Aug 27, 2026 · 7 sections
Aug 27, 2026
7 sections

Results grid now follows the app theme

The results grid (and the pivot, diff, profile, tool-dialog, and object-editor grids) ignored the theme selected in Preferences → Appearance: the grid widget kept re-applying its own built-in palette on every repaint, overwriting the app's colors. Grids are now themed through the widget's host-theme path, so they pick up the chosen palette immediately — including live while the Preferences dialog is open, on the View → Theme menu, and when the OS switches between light and dark in System mode. Theme changes now also reach the diff/profile/pivot grids and an open object editor, which previously kept their old colors until rebuilt.

Make golden-file tests line-ending independent

The v0.62.1 Windows release build exposed a masked failure: the build agent checks sources out with CRLF line endings, so every multi-line golden fixture mismatched the formatter's LF output byte-for-byte. Golden comparisons now normalize line endings before comparing, and the golden fixture directories pin LF in git so every platform checks out identical bytes.

Website: fix dead Open/View links on the admin IntelliSense-feedback and Subscriptions pages

Clicking "Open" on a report in Admin ▸ Triage or "View" on a subscription in Admin ▸ Subscriptions did nothing: the links were generated with a route value named page, which is reserved by Razor Pages, so link generation silently produced an empty href. The paging query parameter is now named p on the admin pages, and the links navigate to the selected item's details again.

Website: one shared IntelliSense-feedback details page, with full capture visible to the reporter

The report details page (/intellisense-feedback) now shows everyone who can view a report the full capture the app sent — the SQL around the cursor, every suggestion the popup offered, the schema metadata and stored procedures it had seen, a picture of the popup itself, and the raw capture payload — instead of reserving that context for admins. The admin Triage page is now a pure list whose Open button lands on that same details page, so the details markup lives in exactly one place; status changes, reply-as-the-team comments, and the "Build an LLM prompt" / "Run with model" tooling appear there only for admins.

Website: admin revenue dashboard with purchase origins

Admin ▸ Revenue is a new dashboard for what the store earns and how licenses are issued: monthly and annualized recurring revenue, projected renewals inside a selectable window (default next 90 days), realized revenue over the last N months (default 12) with a per-month history, a per-plan breakdown, and license counters including the share that were manually issued at no charge. Every paid checkout now also records the IP address and browser the purchase was started from, and a recent-purchases log shows that provenance next to each invoice — earlier purchases, renewals, and no-charge comps show a dash, since that data was never captured. Only admins can reach the page.

Website: admin users and subscriptions listings reworked

The admin subscriptions page now lists every subscription in the store, fifty per page newest first, showing the plan, the account it belongs to, and its status — instead of making you hunt through accounts — while the per-account panel for assigning no-charge plans, adjusting device allowances, and revoking is unchanged. The admin users page also lists fifty per page, defaults to showing just admin accounts on first load (with a "Show everyone" link to clear that), and now shows each user's subscriptions inline under their roles.

Download and purchase records now know where they came from

Every recorded download and subscription purchase is tagged with the location its IP address resolves to — city, region, country, continent, postal code, coordinates, and time zone — for both IPv4 and IPv6 addresses, and historical records are filled in retroactively. The location data is refreshed automatically every week by a scheduled job (Saturdays 02:00 GMT) that pulls the latest MaxMind GeoLite2 database into the store, so lookups always use current data and no manual step is ever needed.

v0.62.1
Aug 27, 2026 · 1 section
Aug 27, 2026
1 section

Fix the v0.61.0 release-pipeline test failures on Linux and Windows

The keyring round-trip test now skips itself when the platform has no live secret service (the headless Linux build agent has no D-Bus Secret Service), instead of failing the suite; it still runs fully wherever a real keyring exists. The ER-diagram error-surface test now waits on a wall-clock deadline instead of a fixed 2-second poll, so a loaded CI agent that briefly starves the shared background runtime can no longer flake the Windows run.

v0.62.0
Aug 26, 2026 – Aug 27, 2026 · 15 sections
Aug 27, 2026
13 sections

Verification pass over this release's new features

An adversarial review of everything new in this release caught and fixed a set of defects before they shipped: the result-grid find bar now follows you across result-set tabs and cleans up its highlights everywhere; grid search stays correct after sorting, filtering, and scrolling large spilled results, and its highlight is no longer hidden under conditional formatting; the Statistics tab stays available after switching away; "View as Hex" works on large spilled results and no longer appears on ordinary numbers; JSON imports survive delimiter and header changes; split schema-documentation links always point at the files actually written; generated test data respects binary column sizes and manual generator choices; and "Explain Last Error" always pairs the error with the exact SQL that produced it.

One-click UUID for query parameters

A query parameter declared as a UUID now has a "Generate" button in the parameter strip that fills in a fresh random UUID, so you don't have to type or paste one.

View binary cells as a hex dump

Binary and varbinary result cells now offer "View as Hex…" in the right-click menu, opening a canonical hex dump — offset, hex bytes, and an ASCII gutter — of the value's actual bytes (capped for very large values). Previously binary cells only showed a "&lt;N bytes&gt;" placeholder with no way to inspect the contents.

Import JSON files, not just CSV

The Import / Export wizard now accepts JSON files in addition to CSV: a JSON array of objects (or an object wrapping one) is turned into columns and rows, with nested values kept as JSON text and the first row treated as the header. Everything downstream — type inference, per-column overrides, preview, mapping, and the reviewed import script — works the same as it does for CSV.

Build or clear AI catalog data on demand

Two new command-palette actions make the AI catalog reachable directly: "Build AI Cache and Embeddings" prepares the AI descriptions and vector embeddings for the connected database now and reports how many objects were embedded, and "Clear AI Catalog Data" removes all AI descriptions and embeddings for that database while keeping structural catalog knowledge.

Explain an execution plan with AI

After capturing an estimated or actual execution plan, run "Explain Execution Plan (AI)" from the command palette to get a plain-language explanation of what is expensive and what could make it faster. The AI receives a bounded digest of the plan (its findings and most expensive operators, never raw plan XML) and is asked to explain rather than rewrite your SQL.

Explain a failed query with AI

When a query fails you can now ask the AI to explain the error in plain language and suggest a fix. Run "Explain Last Error (AI)" from the command palette; the AI drawer opens in Ask mode pre-filled with the error message and the SQL that produced it.

Unsaved edits are auto-saved and survive an unclean exit

What you type in a query tab is now saved automatically a couple of seconds after you stop, so unsaved edits come back after a crash or forced quit instead of only being kept at the last time you opened, closed, split, or rearranged a tab. Saved files and the workspace layout are unaffected; this only closes the gap for in-progress typing.

Client Statistics pane for query runs

A new Client Statistics pane summarizes the last run — result sets, rows returned, rows affected, and a timing breakdown of server execution, time to first response, client display, and total wall-clock time. Open it from the result grid's right-click menu; it shows up as a Statistics tab beside Results and Messages.

Find across every column of a result grid

Result grids now have a find bar that searches every column at once and highlights the matches, with a match counter and next/previous navigation. Open it from the grid's right-click menu ("Find in Grid…"), type to highlight matches as you go, press Enter or the arrows to step through them, and Esc to close.

"First" is now a pivot aggregation in the interactive view

The interactive Pivot view now supports the "first value" aggregation directly, matching the -- sqlly pivot: …:first comment hint. Previously choosing "first" fell back to a separate flattened copy of the data; now it works on the live pivot like count, sum, average, min, and max.

Finished exports show up in the notification center

Completed exports — result exports, table data exports, and generated schema documentation — now appear in the status-bar notification center, and raise a system notification when the app is in the background. Large exports finish while you've often looked away, so you now find out they're done without watching the window.

Large databases are documented in full, split by schema

Schema documentation export no longer stops after the first couple thousand objects on a big database. When the schema is large, SQLLY now writes a short linking index at the file you chose plus one Markdown file per schema beside it, so nothing is omitted and the result stays navigable. Small databases still produce a single file exactly as before.

Aug 26, 2026
2 sections

Restoring a query version can be undone

Restoring an earlier version from a query tab's History panel (or stepping through versions with Cmd+Opt+Left/Right) is now a single editor edit you can reverse with Ctrl+Z, bringing back exactly the text you had before the restore. Previously a restore replaced the whole buffer but also cleared the tab's undo history, so there was no way to step back from it.

Test data fits each column's declared size

The Test Data Generator now keeps every generated value inside its column's declared bounds: text is clamped to the column's character limit and decimals are kept within its precision and scale. This means the reviewed INSERT script no longer fails with truncation or numeric-overflow errors when you run it against tables that use tight column widths.

v0.61.0
Aug 26, 2026 · 4 sections
Aug 26, 2026
4 sections

Downloads, sign-in, and reviews load again

The public downloads page, the sign-in and register pages, and the reviews page could hang forever after a server restart: constructing billing went in a circle through the Stripe gateway and never finished. Those pages respond again, and you still only see SQL VPN / bridge files on downloads when you're signed in on a Cloud plan.

More WHERE filters from result-grid cells

Right-clicking a value in a result grid now offers a fuller set of quick filters. Numbers and dates gain greater-than and less-than comparisons (>, >=, <, <=), and text cells gain "does not contain", "starts with", and "ends with" alongside the existing "contains" match. Each menu only shows the operators that make sense for the value you clicked, and every generated fragment still uses the quoting and literal style of the engine your query ran against.

More query-parameter types

Query parameters now support four more types beyond text, int, decimal, bool, date, and datetime: time, uuid, json, and binary. Declare them the same way — for example -- sqlly param: @id uuid or -- sqlly param: @payload json — and SQLLY validates each value as you'd expect and substitutes it as the right literal for your engine, including proper hex binary literals (like 0xDEAD or X'DEAD') for binary values.

Test Data Generator handles JSON and binary columns

The Test Data Generator now recognizes JSON/JSONB and binary/blob columns and fills them with sensible sample values — small valid JSON objects for JSON columns and engine-correct binary literals for binary columns — instead of falling back to a plain word. You can still switch any column to a different generator by hand.

v0.60.1
Aug 26, 2026 · 3 sections
Aug 26, 2026
3 sections

Release pipeline: macOS job no longer runs live database smoke tests

The macOS build stage now skips the live_db_connect smoke tests the same way the Linux and Windows jobs already did, so no release job depends on databases running on the build agent. The TestDatabaseCredentials variable-group wiring was removed from the pipeline along with the per-database environment mappings.

Codebase-wide maintainability refactor

Reorganized the largest source files across the workspace — from the foundation crates up through the engine, services, and the GPUI app — into focused modules grouped by responsibility, without changing behavior or performance. The SQL formatter, parser, IntelliSense completion, catalog store and validation, query safety policy, TDS execution, row editing, streaming query pipeline, LSP server, engine subprocess, and the app's editor, results, preferences, connection-management, and main workbench views are all split along their natural seams now, which makes future changes easier to review and test. Duplicated display helpers shared by the connection trees were consolidated, and a handful of previously dead files in the TDS crate were wired back in or folded away.

Hermetic tests for developer machines

Two test groups silently depended on the developer's real local files: the live-SQL config loader test could find a real config above the repo, and the results-grid export menu tests read the developer's persisted export preferences. Both now run against injected state, so the suite passes regardless of local configuration. Also added focused unit tests around newly isolated pure helpers across the refactored crates (the workspace suite grew by roughly sixty tests).

v0.60.0
Aug 26, 2026 · 1 section
Aug 26, 2026
1 section

Dot-segment completion in IntelliSense

While the suggestion popup is open, typing . or pressing the Right arrow now accepts just the next segment of a dotted name — Acc becomes Accounting. — and the popup immediately re-scopes to what belongs under that segment, so walking a qualified path like Accounting.Account takes a couple of keystrokes instead of a full accept. When the typed prefix matches several different next segments, SQLLY only advances once you highlight a suggestion, so it never guesses the wrong name.

v0.59.1
Aug 25, 2026 – Aug 26, 2026 · 35 sections
Aug 26, 2026
1 section

Live database connect smoke tests in the release pipeline

Added basic "can we connect?" tests for PostgreSQL, MySQL, MariaDB, and Oracle that read the release pipeline's TestDatabaseCredentials variable group (<DB>_HOST/PORT/USER/PASS/DATABASE). Each test skips silently when its credentials are absent, so the suite is a no-op locally and on the hosted CI. The release pipeline runs them only on the macOS agent, which hosts all four databases on localhost; the Linux and Windows jobs pass --skip live_db_connect to cargo test to exclude them. Oracle has no native driver, so its test verifies the listener is reachable.

Aug 25, 2026
34 sections

Leftover branch follow-ups on main

MCP keychain tests run as one serialized case so parallel cargo test cannot clobber the process-wide mock keyring. Spill-backed export helpers that load an entire file are test-only. The explorer flatten cache keeps a clippy allow after the module split, and the flatten viewport tests have unique names.

Explorer tree split into modules

The schema explorer tree is now a directory module instead of a single ten-thousand-line file: context menus, the node model, async metadata loading, and flatten/render each live in their own source file. Public types and events are unchanged for the rest of the app.

Workbench module split continued

The GPUI workbench further splits large AppView clusters into focused modules: tab helpers, overlay hosting, procedure-mode run wiring, and query execution with the run gate. The main module file is substantially smaller; render and connection paths remain there for a follow-up pass.

Admin tool panes use the shared services facade

Backup, restore, activity monitoring, index and disk usage, SQL Agent, templates, security management, query insights, import/export, schema compare, and designers now generate their SQL through the in-process services layer instead of calling admin crates directly. Behavior is unchanged; relay and future frontends can swap the same ports.

Oracle dialect clarified as parser-only

The Oracle parser family in the SQL model crate is now documented in code as grammar support only — there is no native Oracle driver and the connection picker continues to ship six engines.

SQL Server TLS advisories time-boxed pending upstream tiberius

The rustls 0.21 certificate-validation advisories (RUSTSEC-2026-0098/0099/0104) remain pinned by tiberius 0.12.3 on the client→SQL Server hop. A 2026-08-25 upstream review found no crates.io drop-in on rustls 0.23; cargo-deny ignores are tracked in plan 057 with re-evaluation due 2026-11-25.

Windows and Linux labeled a supported beta

Windows and Linux desktop builds are now a supported beta: the same GPUI app as macOS, unsigned until installer signing exists, with connection passwords in the OS keyring. macOS Apple Silicon remains the polished, signed and notarized tier.

cargo-deny and lockfile gates enforced in CI

Supply-chain audit now uses cargo-deny 0.20's global --locked flag, sources policy matches rev-less gpui git deps (tightening deferred to plan 056), release clippy and live-driver GHA tests run with --locked, and just audit passes on a clean tree.

Redact SQL literals in query history and snapshots

Query history and named result snapshots now redact string literals, hex literals, comment bodies, and password-style values before they are written to disk, so secrets in SQL no longer persist in the data directory. Existing history files on disk are unchanged; only new entries use redaction.

JSON/XML tree stays scrollable for large cells

Opening a large JSON or XML cell in the structured data viewer no longer hitches while drawing thousands of tree rows. Collapse, search, and the existing row cap still work as before.

Device pairing revoked on password change

Changing or resetting your account password now signs out every paired device. Previously issued app tokens stop working as before, and pairing secrets can no longer mint a new token until you complete device pairing again in the app.

MCP environment stored in Keychain

MCP server environment variables are no longer written to the MCP JSON preferences file. They are stored in the system keychain (sessionStorage in the browser preview), keyed by each server's stable id. HTTP Test rejects non-HTTPS URLs except loopback http, does not follow redirects, and the AI panel labels discovered MCP tool text as untrusted third-party content.

Engine correlation log moved out of shared temp

The engine request/response correlation log now defaults to a private path under Application Support (or the platform data directory on Linux and Windows) instead of $TMPDIR, and is created with owner-only permissions on Unix so query text in REQ lines is not world-readable.

README architecture paragraph reflects two-tier boundary

The root README now states that GPUI Direct uses in-process Services while CLI, bridge, relay, and the frozen Swift client use the framed engine subprocess protocol, matching the amended ADR-018.

Product docs aligned with GPUI and multi-engine reality

PRODUCT.md, plans/specs/ banners, ADR-017 pointers, the AI feature catalog, and docs/wire-protocol.md now describe GPUI as the shipping workbench, six supported database engines, cross-platform releases (macOS arm64 primary; Windows and Linux released), platform keyring for secrets, and honest local-vs-cloud AI routing instead of outdated macOS-only SQL Server or AppKit-first copy.

Query parameter substitution preserves UTF-8

Parameterized SQL with non-ASCII characters in string literals and comments is no longer corrupted during parameter substitution. Multi-byte UTF-8 sequences are copied intact in every lexer state.

Windows filesystem indexer path normalization

Backslash-separated Windows paths are normalized to forward slashes before exclude-pattern matching, so bin, obj, node_modules, and target directories are pruned correctly without false positives on paths like robin.

Snapshot and template catalogs fail closed on corrupt indexes

Result snapshot and user-template indexes now refuse to save over corrupt JSON instead of replacing the file with an empty catalog. Snapshot index writes use atomic temp-and-rename, and snapshot payload paths must be a single UUID-named export file inside the store folder.

Procedure parameter types match column formatting

Stored procedure and function parameters in the object explorer and EXEC snippets now use the same type spelling as columns — nvarchar lengths are shown in characters (not bytes), (max) appears for unlimited types, and fixed types like int no longer show spurious length suffixes.

GitHub Actions quarantined from PR gate

Azure Pipelines remains the PR gate. GitHub Actions CI no longer runs on push or pull request (manual dispatch only), no longer builds the frozen AppKit reference app, and clippy now matches Azure with --all-features --locked.

Include Statistics no longer blocks SELECT-only runs

The run gate and mutation confirmation now evaluate your SQL before the Include Statistics session-counter wrap is applied, and SELECT-only enforcement ignores those known wrappers so Production connections can gather IO and timing stats on ordinary SELECT batches.

Required tenant scoping blocks unscoped queries

The Required tenant-scoping level now enables the missing-tenant-filter guard and treats a missing predicate as a blocking validation error; Run is refused when validation has flagged one. Procedure mode no longer skips mutation confirmation for parsed headers.

Closing a tab cancels its in-flight query

Closing any query tab now cancels that tab's active server task (not only the focused tab), and async query completions resolve the target tab by its stable id so late results cannot retarget another tab's state or database picker.

Align local CI recipes with Azure PR gates

just ci now runs the same contributor-facing checks as Azure macos_ci (formatting, icon drift guards, clippy, cargo-deny, check, test, and Lucide vendor check). README documents starting SQL Server from client/ with docker compose up -d --wait, the .sqlly-local-sql.example.json template, and live-test env vars; just live-sql sets SQLLY_REQUIRE_LIVE_SQL=1. Azure PR jobs no longer clone a sibling sqlly-datatable checkout now that the workspace patch is git+tag. CONTRIBUTING documents installing the commit-msg hook via core.hooksPath.

Test Data Generator type matching

The Test Data Generator now classifies column types by exact name instead of substring checks, so PostgreSQL intervals and bit-string types no longer receive integer or boolean sample values, while SQL Server int and bit columns still get the right generators.

Row editor: MySQL/MariaDB signed TINYINT range

The row editor now treats default MySQL and MariaDB TINYINT columns as signed (−128 through 127) instead of clamping them to the SQL Server unsigned range. Unsigned spellings and SQL Server tinyint are unchanged.

JWT default lifetime matches seven-day config

The server JWT AuthOptions default is now seven days (10080 minutes), matching appsettings.json, so hosts that omit the key no longer silently issue thirty-day bearer tokens. GitHub Actions CI and the relay Docker image pin Rust 1.98 via client/rust-toolchain.toml instead of floating stable or rust:1.

Faster catalog sync and validation on large databases

Catalog sync now batches object lookups and tombstone checks, skips rebuilding inferred relationships when nothing structural changed, and adds schema-generation 2 indexes for live objects and embeddings. SQL validation builds its name index from column metadata instead of deserializing every stored object body, and join-path discovery batches neighbor lookups per depth.

Snappier completions on large catalogs

IntelliSense now scores candidates with borrowed names first and builds display labels only for items that pass the fuzzy gate, so typing against thousands of columns avoids per-candidate string churn on rejected matches.

Honest labels for on-device versus cloud AI

Settings and the AI feature catalog now say which features stay on the local runtime and which send prompts and schema excerpts to a named cloud provider when you choose one as the App AI backend. Query summaries, AI review, and inline autocomplete stay on Ollama on this machine even if Ask and Generate use the cloud.

Spill-backed export stops at 100,000 rows

Copy and save of very large results that live on disk now export the first 100,000 rows (the same bound as named snapshots) instead of loading the whole file into memory. When more rows exist, Messages, a warning toast, and the Save Results status bar say so — charts still use their 10,000-row cap, and pivot still refuses spill-backed sets.

Smoother large Object Explorer trees

Scrolling, filtering, and keyboard navigation in a large Object Explorer stay responsive: a fully expanded database no longer clones the whole visible tree on every frame, and the first paint draws one screen of rows instead of thousands.

Browser WASM frozen as an in-tab demo

The browser build is labeled SQLLY browser preview (in-tab demo) on the host page, download page, and features/pricing copy, and is not marketed as a hosted Cloud app. Welcome and explorer no longer offer adding SQL Server or relay routing in the tab; wasm stays a CI compile check.

Performance preferences cached off scroll hot path

Result-set paging and stacked-scroll gesture handling no longer re-read performance or gesture preference JSON on every scroll event. Preferences load once at startup, stay in memory, and refresh only when the settings dialog saves.

v0.59.0
Aug 15, 2026 – Aug 25, 2026 · 65 sections
Aug 25, 2026
22 sections

Conditional formatting for result grids

Result grids can now color cells by rules: right-click a column header and choose Conditional Formatting… to highlight values over/under a threshold, between two numbers, NULL or not, or matching text — with your choice of text color, background, and bold — or apply a color scale or in-cell data bars driven by the column's real numeric range. Rules follow the column by name into every query that returns it, persist across sessions, apply first-match-wins, and cost nothing when a grid has no rules.

Include Statistics on SQL Server reports real numbers

The Include Statistics toggle on SQL Server and Azure SQL now reports CPU time, physical and logical reads, writes, memory, and row counts for each run in the Messages pane. The previous SET STATISTICS approach produced output the app's driver silently discarded — the new session-counter snapshots actually arrive, and were verified against a live SQL Server 2025.

Tenant Scoping joins the command palette

The Tenant Scoping tool was runnable from the menu but missing from the command palette registry; it is now searchable and runnable there like every other tool, and a stronger built-in check derives the palette/menu gating list from the command registry itself so a future tool cannot be silently missed.

Snapshot storage is crash-safe

Result snapshot saves now write their index atomically and clean up after themselves: a failure can no longer leave an orphaned snapshot file or a torn index, and opening the snapshot browser silently repairs anything a past crash left behind.

v0.58.0 release audit with live-engine proofs

A full audit of the v0.58.0 release re-ran every quality gate and proved the headline engine claims against live servers: MySQL 8.4 and MariaDB 11.8 actual execution plans with real runtime row counts, MySQL and SQL Server Include Statistics deltas, and PostgreSQL Procedure Mode deploy/CALL/INOUT with the rollback sandbox leaving nothing behind. The audit also fixed contradictory execution-plan feature documentation. Findings live in docs/validation.

Clear reason for "must be the first statement in a batch", plus a rollback-wrap bypass

Running a CREATE/ALTER PROCEDURE (or FUNCTION, TRIGGER, VIEW, …) that is not the first statement of its batch used to surface as a raw SQL Server error 111. SQLLY now recognizes that shape on the SQL Server path and returns a plain-language reason — the definition must lead its batch, put a GO before it — before the batch is ever sent. When the connection wraps runs in a rolled-back transaction (auto_wrap_rollback), the message also notes that the wrap adds its own leading BEGIN TRANSACTION and points to the new bypass. The pre-run "effective SQL" preview reports the same reason instead of showing a batch that would fail.

Added an Execute Without Rollback Wrap command (command palette, category Query) that runs the current query once with the connection's rollback wrap disabled — for deploying DDL the wrap would otherwise roll back, or a batch its transaction preamble would break. It is strictly one-shot: the bypass is consumed at dispatch and never leaves a production safety wrap disabled for the next run.

Time-box rustls 0.21 advisory ignores (tiberius)

cargo-deny ignores for RUSTSEC-2026-0098/0099/0104 (rustls-webpki issues in the tiberius→tokio-rustls 0.24→rustls 0.21 chain) now carry explicit re-evaluate-by dates and plan-013 tracking; no upstream tiberius release on rustls 0.23 exists yet.

Root task runner and build-script hygiene

Added a repo-root justfile that mirrors Azure PR CI (macos_ci + live-SQL recipes), a comment-only client/rustfmt.toml to freeze default formatting intent, replaced the build-gpui-debug-app.sh symlink with a Windows-safe wrapper, documented the test-db.zip tokenizer fixture in the README, removed the accidental root config.json, and updated the README build/dev section to lead with GPUI and just instead of the legacy Swift shell. PR CI's wasm compile step now checks -p sqlly-browser (the crate's actual name).

ADR series reconciliation

Reconciled docs/architecture/ into a single lowercase adr-NNN series with a README index, supersession status on stale UI/platform ADRs, new records for the subprocess engine boundary (adr-018) and multi-database cross-platform product target (adr-019), an osxql→SQLLY name sweep, and demotion of the historical plans/architecture.md sketch. The unused sqlly-ffi crate was removed in prior work on this branch.

Docs refresh (plan 022)

README now presents GPUI as the product with accurate apps/crate layout, per-target build prerequisites, and a historical Phase 0 note; docs/supported-platforms.md clarifies SQL Server/auth scope (not OS support); four feature briefs label GPUI vs frozen Swift reference; added CONTRIBUTING.md; deferred items recorded in plans/reports/022-docs-staleness-findings.md.

Dependency hygiene bundle (plan 025)

Pinned the Rust toolchain in client/rust-toolchain.toml (channel 1.88; workspace rust-version aligned to 1.88 because the lockfile needs ≥1.88). Corrected the cargo-deny quick-xml advisory comment to document both vulnerable copies (0.30 via xcb on Linux X11, 0.39 via zbus_xml on D-Bus). Bumped workspace rand from 0.8 to 0.9 (unified with gpui); documented holds on sha2/p256 and getrandom/wasm in client/Cargo.toml. Added .NET central package management (server/Directory.Packages.props) and SDK pin (server/global.json 10.0.302). Added root renovate.json for self-hosted Azure DevOps Renovate (git gpui/psm/datatable bumps excluded); activation still requires a scheduled pipeline + PAT. Reduced server JWT lifetime from 30 days to 7 days (TokenLifetimeMinutes 10080) — desktop clients have Entra refresh for database auth but no server-JWT refresh, so 24h would force daily cloud re-login; refresh tokens remain the follow-up to shorten further. Nightly browser build left on floating +nightly (dated nightly not verified here).

Split app.rs into focused app/ modules

Refactored the GPUI workbench god file into app/mod.rs plus sibling modules (retarget, pending_dialogs, tabs, completion_cache, toolbar, ddl_navigation, query_history, query_resume) with move-only extractions and no behavior changes; mod.rs shrank by over 3,000 lines.

Faster typing, scrolling, and schema loading

Typing, expanding the schema tree, and scrolling large result grids now stay lighter: IntelliSense catalogs are shared across tabs instead of deep-copied, the editor no longer copies the whole buffer on every keystroke for procedure-mode detection, spill paging reads performance settings from a cached snapshot instead of the disk file on every scroll, and result-grid cells reuse a per-column type/format plan instead of re-deriving it for every cell.

Test-hardening bundle (plan 023)

Added framing integration tests for the engine wire codec, revived sqlly-testkit golden/container helpers, golden suites for the SQL formatter and DDL query builders, unit tests for Azure SQL token cache skew and entity safety-toggle persistence mapping, and a report-only cargo llvm-cov step in the macOS PR pipeline (non-gating).

Zero-knowledge sync client design spike (plan 027)

Added plans/reports/027-zero-knowledge-sync-design.md — direction doc for encrypted connection-profile sync (macOS v1, no credential sync), key hierarchy, recovery codes, device enrolment, production auth replacement, dev-bearer kill-switch, and conflict strategy pending human crypto review.

Remote/browser tier positioning spike (plan 028)

Added plans/reports/028-remote-browser-tier.md — honest feature matrix for native vs browser builds, security-bar checklist, pricing hook points, and recommendation to keep the wasm shell demo-only.

Windows/Linux productize decision memo (plan 029)

Added plans/reports/029-windows-linux-memo.md — reconciles PRODUCT.md with ADR-019 and the cross-platform keyring credential path; recommends staged Win/Linux beta productization.

AI backend consolidation design spike (plan 030)

Added plans/reports/030-ai-consolidation-design.md — per-feature egress audit, Local/Cloud/Off selection model, privacy-label rewrite draft, and execution-plan explanation as the next AI feature.

Smoother frames on large trees, buffers, and result sets

Schema tree filtering and scrolling stay lighter by caching flattened rows and drawing only the on-screen window; the SQL editor keeps undo history under a memory budget with smaller snapshots; IntelliSense data-model cache is capped; result-set format hints and canvas layout are parsed once per query instead of per batch or frame.

Toolchain pin follows sqlly-datatable 4.1.2

Merged origin/main's result-grid crate bump (sqlly-datatable 4.1.2), which needs rustc ≥ 1.96. The workspace pin is now 1.98 (current stable, matching CI) and rust-version is 1.96.

Modern workbench chrome reunited with the latest tools

Merged the redesign/modern-ui workbench — one canvas, hairline splits, the named color-theme catalog, a title bar on every platform, and the AI drawer — onto main, then restyled the tools that landed in the meantime (connection health, relationship graph, snapshots, query parameters, and similar overlays) to match: sans chrome, no shadows, and the same popup surfaces as the rest of the app. Command Palette stays Cmd+Shift+P, the AI drawer is Cmd+Shift+I so Cmd+L can keep showing the execution plan, empty results advertise Filter Explorer instead of leftover Cmd+K / Cmd+P chips, and Chart is a first-class output tab next to Results.

Status bar notifications match the rest of the chrome

The notifications control uses the same icon set as the other status-bar buttons instead of an emoji bell, the title-bar pig sits one pixel lower so it lines up with the SQLLY wordmark, and the “I love SQLLY” tagline appears only once — on the left, next to the heart.

Aug 24, 2026
17 sections

Filesystem indexer directory pruning

Filesystem indexing no longer skips entire folders whose path merely contains letters like bin or obj (e.g. /Users/robin); exclusions now match whole path segments using the same delimiter-preserving rules as file-path acceptance.

Data-model cache identity collision-proofing

IntelliSense's on-disk data-model cache now keys files collision-proof per connection and database, verifies the cached database on load, and uses unique temp files for atomic saves (cache format v2; older cache files are refreshed on next connect).

Schema tree column types render correctly

Schema tree column details and scripted CREATE TABLE DDL now render types correctly (nvarchar character lengths, (max), decimal precision/scale, no bogus lengths on fixed-size types).

Identifier quoting always re-escapes ] in bracketed names

Generated SQL (table scripts, result exports, scripting templates) now unwraps and re-escapes already-bracketed identifiers instead of passing them through verbatim, so interior ] characters are always doubled and hostile names like [a]b] cannot break out of quoting.

SQL Login passwords stored in keychain only

Security: SQL Login passwords are now always stored in the system keychain (sessionStorage in the browser); existing connections.json files are migrated on launch and rewritten without the password. Users who previously saved SQL Login connections should rotate those database passwords because older backups may still contain cleartext copies.

Entra browser sign-in PKCE (S256)

Security: Microsoft Entra browser sign-in now uses PKCE (S256), protecting the authorization code against local interception.

SELECT-only policy fails closed on unclassifiable statements

The SELECT-only connection policy now blocks statements it cannot classify (for example a bare stored-procedure name with no recognized keyword) instead of silently ignoring them. The Run confirmation gate is now covered by unit tests so regressions in the destructive-change confirmation matrix are caught before they reach the UI.

Pin git dependencies and enforce locked CI builds

Git patches for stacker/psm and sqlly-datatable now use explicit rev pins, cargo-deny requires rev on declared git sources, and every Azure (and GitHub) cargo build/test/clippy/check invocation passes --locked so lockfile drift fails CI instead of silently re-resolving.

Privacy: IntelliSense feedback payload scrubbing

IntelliSense feedback now masks string and binary literals, comment bodies, and PASSWORD/SECRET-style values before upload, and by default includes only the schema objects the flagged statement references (the full catalog is opt-in).

Keychain-backed secret resolution in engine crates

Security: profiles whose password lives in the OS keychain are now resolved from the keychain everywhere (previously an environment-variable fallback), and a missing secret is a connection error instead of a silent empty password. CLI/headless setups that resolve passwords from environment variables must set the profile's secret provider to Environment — that path is unchanged.

Live SQL integration tests run in Azure PR CI

CI now runs the live SQL integration suite (TDS, metadata, completion, engine pool) against a containerized SQL Server 2022 on every PR via a parallel Ubuntu job; SQLLY_REQUIRE_LIVE_SQL=1 turns silent skips into failures when the server config is missing. GPUI live connection tests no longer embed a hard-coded admin credential — they read LiveSqlConfig like the other live suites.

GPUI is the product; Swift app frozen (ADR-017)

Recorded ADR-017 (GPUI is the product, Swift AppKit app frozen as a porting reference) and removed Swift build/test from the main Azure PR pipeline; GPUI release pipeline and Rust workspace checks remain authoritative.

Editor no longer crashes on non-ASCII text at the caret

Opening the editor context menu with the caret at the end of a query that ends in a non-ASCII character (accented letters, CJK, and similar) no longer crashes the app. Token lookup now snaps to a valid character boundary before reading the text.

Release pipeline verification and aligned CI lint flags

The release pipeline now runs fmt, clippy, and workspace tests on the macOS stage (before signing) and tests the full workspace on Linux/Windows amd64 legs; PR CI gains sccache-backed Cache@2 caching on hosted macOS agents; clippy invocations across CI and release now pass --all-features to match the tag-release gate.

Faster IntelliSense on large schemas

Completion no longer builds a qualified label string for every catalog object and column before scoring — labels are materialized only for matches, the result set is capped before the final sort, and the word-anchored fuzzy fallback avoids per-candidate allocations on the common ASCII path. The tokenizer already classifies keywords allocation-free (shipped earlier on this branch).

Mainline multi-engine, admin tools, and editor depth land on the new workbench

The redesigned chrome now carries the work that shipped on main after v0.51: PostgreSQL, MySQL, and SQLite parity, real admin-tool dialogs, AI drawer InputState lanes, charts on the output panel, folding, multi-cursor, FIM ghost text, and the registry-generated Help window. New surfaces use the same hairline splits, Theme tokens, and gpui-component inputs as the rest of the workbench rather than the old boxed toggle bar.

Advisor plans 001–030 reviewed against current main

The numbered improvement plans were checked against origin/main after this merge. Several are now PARTIAL (work landed independently), plan 026 is REJECTED because multi-engine parity already shipped, and the remaining TODOs note what is stale so the next executor does not re-implement finished work.

Aug 23, 2026
1 section

Mainline editor splits, find/replace, and IntelliSense cache land on the new workbench

The redesigned workbench now includes the editor features that shipped on main while we were restyling: split the query area vertically or horizontally, find and replace in the current script, and inspect or clear the IntelliSense cache from Settings. Split panes use the same hairline dividers and per-pane editor/results stacks as the rest of the chrome, and the find bar sits on the editor as a quiet overlay.

Aug 22, 2026
2 sections

Pixel-art pig in the title bar and as the website favicon

The title bar now uses the 16-pixel SQLLY pig (from a 32-pixel retina source) instead of shrinking the full illustration, so the mark stays sharp next to the wordmark, sitting a pixel lower so it lines up with the letters. The website serves the same pig as a proper favicon set — SVG, ICO, PNG sizes, Apple touch icon, and a web app manifest — so browser tabs and home-screen shortcuts show the pig instead of the old mascot drawing.

“I love SQLLY” uses a rose that follows the theme

The status-bar heart and tagline are no longer the accent color. Each palette has its own pink–red — raspberry on cool navy and ice, copper rose on amber and sepia, a dusty rose on graphite — so the line still reads as love, stands out from the chrome, and never looks like an error.

Aug 19, 2026
3 sections

Planning docs reorganized: advisor plans in plans/, historical specs in plans/specs/

A repo-wide advisory audit produced 30 numbered implementation plans, now living in plans/ with an execution-order index in plans/README.md. The previous contents of plans/ (product, architecture, parity, and phased implementation documents) moved to plans/specs/, and references across the repo were updated to the new paths. No application code behavior changed.

Themes are twelve Light/Dark pairs with clearer names

Every color family now has a Light and a Dark palette, including High Contrast. Classic and Neutral were too close to the brand navy look, so they are now Cobalt and Graphite with their own surfaces. Nord is Nordic Dark, Tokyo Night is Tokyo Dark, GitHub Light is Harbor Light, and Light Soft is Sepia — both Sepia palettes are photographic brown, not taupe-and-blue. Choosing High Contrast from the View menu picks the High Contrast palette for your current Light, Dark, or System mode.

Sepia Light is pale ivory, lighter than Solarized Light

Sepia Light was reading as stained manila paper. It now uses Light Soft's pale surfaces with a slight cream shift, walnut ink, and the burnt-sienna accent, so the page is lighter than Solarized Light while still looking like sepia rather than taupe-and-blue.

Aug 18, 2026
3 sections

Themes and Display: a color gallery, including SQLLY navy

Appearance settings split into Themes and Display. Themes has Light / Dark / System pills and preview cards for several light and dark palettes. SQLLY is a dark navy workbench with lighter blue trim and ice-blue accents from the app mark. Fonts and sizes live under Display. High Contrast is a dark palette rather than a fourth mode.

Theme preview cards no longer show square corners

Theme thumbnails in Settings → Appearance → Themes clip to their rounded border, so light palettes, Nord, and High Contrast no longer leave a square of color poking out at the corners.

Closing a dialog or drawer no longer traps the workbench

Closing Settings, the command palette, or a drawer (Explorer, AI) now hands focus back to your query first and drops leftover menus, so the next dialog, sidebar, or overflow menu opens immediately instead of ignoring clicks and shortcuts until you click the editor.

Aug 17, 2026
7 sections

Query workspace uses three bands: tabs, editor, output

The workbench is now tab strip plus command bar, then the SQL editor, then a unified output panel. History, DDL, and Plan live as output tabs so the query stays on screen; AI is only the right drawer (Search looks through your schema). One command bar holds query tabs, New Query, the all-tabs list, the connection chip, save, format, overflow, and a filled Run that stays disabled until you connect. The tab strip sits slightly above the canvas like a folder; dirty dots are ordinary ink. Cmd+1–5 (and Cmd+6 for Diff) switch output tabs, Ctrl+Tab cycles queries, and Cmd+B / Cmd+L / Cmd+J match the title-bar sidebar, AI, and output toggles. Tabs reorder by drag, with Close Others / Close to Right / Reopen Closed, keyboard focus, and auto-names from the first SQL object. Destructive reset moved to Preferences → Advanced.

Query tabs sit above the command bar; drawers and status bar tighten up

Query tabs and the command bar are two rows of the same height as the Explorer header. Hiding the Explorer from the title bar removes it completely. The AI drawer uses a single hairline and can be dragged to resize; its Search mode is named Search again. Status-bar Search opens the command palette (Cmd+K); click the zoom percent to resize editor text. Account sits left of Settings, Diagnostics replaces the notifications bell, a heart sits by the tagline, and “Not connected” no longer repeats on the status bar.

Drawers stay on their side of the hairline; zoom is a drop-up

Explorer and AI content stay inside each drawer, with a little space before the split so rows don’t run into the editor. Query tabs use the same quiet rounded fill as the AI segments, and New Query (+) sits next to the last tab unless the strip is overflowing. Clicking the zoom percent opens a drop-up with a vertical slider; an Update available link sits to the right of the version. The Account icon signs you in or opens your subscription again, and releasing a pane resize drops the drag cursor.

Action bar, tabs, and status bar quiet down

The server and database pickers sit on the left of the command bar without a chip border. Query tabs match the AI segments on the same canvas, with no extra border or rail. Explorer type matches the AI tabs, and disclosure uses a simple chevron. Commands (not Search) opens the command palette; update and version sit to its left; the live message sits after Diagnostics, with the tagline in between. Settings stays inside the window. The zoom percent keeps a fixed width, and the drop-up slider no longer covers the minus control.

Status bar, tabs list, diagnostics, and Explorer grouping polish

The all-tabs list opens above the command bar and editor instead of behind them. Version sits at the far right of the status bar in a fixed-width slot; click it to check for updates, and a small dot marks when a newer build is waiting. Status messages carry a small icon. The zoom slider thumb stays fully visible at both ends and no longer jumps while dragging. The diagnostic log can be resized, and the Explorer and AI split lines sit flush with no gap beside the hairline. Server rows put the environment color on the far left and a ⋮ menu on the far right. The Explorer title and icon follow the grouping you pick (Connections, Clients, Projects, Environments, or Servers), and Manage Connections lives in that same ⋮ menu.

Command bar, connection rows, and status-bar clicks

Clicking the version checks for updates. The live status message sits flush beside the tagline. Server rows put the connection-status dot on the far left and a ⋮ on the far right. Refresh schema is an icon on the command bar; those icons match the rest of the chrome. Run matches the AI Generate tab in height and sits closer to the right edge. An active server and database picker uses the same quiet rounded fill as an active query tab.

Explorer rows, status-bar spacing, and connection pickers

Server-row ⋮ lines up with the Explorer header overflow, and disclosure chevrons sit closer to the status dot at a smaller size. The status-bar tagline uses the same gap as the icons before it, with the live message following the tagline so it takes that place when the tagline is hidden; the message icon sits as close to its text as the heart sits to the tagline. Command-bar server and database are separate tab-sized pickers that fill only on hover or open, without an accent focus square, and action-bar icons match the Explorer header.

Aug 15, 2026
10 sections

Workbench chrome matches the flat workbench spec: drawer, overflow, and empty states

The AI drawer uses a compact Generate/Ask/Search/Joins control, a paper-plane send icon in the input, and Clear in a ⋯ menu. Diagnostic log actions moved into the same kind of overflow; a count badge appears on the notifications icon when the log is closed with entries. Explorer failures show a red status dot instead of the word “failed,” with a quiet selected row. Welcome and empty results use plain two-column shortcuts, and Results/Messages/Plan plus Export stay hidden until the first run. Account sits in the status-bar cluster; Run/save/format share the sub-tab row.

Workbench uses one canvas and hairline splits

Sidebar, editor, results, AI drawer, status bar, tab strip, and title bar now share one background. Regions are separated by thin hairlines instead of darker rails. Raised surfaces stay for inputs, menus, and table headers.

Query tabs are boxed folders; the title bar is on every platform

The active query tab is a boxed open-bottom folder with a quiet border, not an amber underline. Inactive tabs are dim text only. A slim title bar with the SQLLY mark and sidebar / AI / log layout toggles now sits above the whole window on every platform; macOS keeps the system menu and native traffic lights.

Tagline sits with the status-bar icons

"I love SQLLY, you will too!" moved to the left of the status bar, after the settings cluster, so the center stays for live status and errors.

Workbench theme rebuilt around dark rails and amber

Classic, Neutral, and Signature are now offered as named palettes, each with a Light and a Dark variant, all WCAG AA verified. Appearance is System, Dark, Light, or High Contrast, with a flat near-black (or light) chrome, a warm amber accent, system sans-serif UI text, and monospace kept for SQL and data. Existing saved "theme family" preferences are honored by their matching named theme.

Query tabs sit on a rail with an action bar underneath

Each query tab now sits on a flat rail with an amber underline on the active one, a connection-status dot, and a server · database chip at the right. Under the tabs, a single action bar holds a server ▸ database breadcrumb, a primary Run button, save/format, and Editor · History · DDL · Plan · AI text tabs. The Explorer sidebar still resizes and collapses, with a 220-pixel minimum when open.

Explorer header, kind tags, and overflow grouping

The Explorer opens with a Servers header: search, add, and a ⋮ menu that holds grouping (Client / Project / Environment / Server), collapse all, and refresh. Search is an icon that opens an inline filter (⌘⇧F). Tables, views, procedures, and functions show a small colored TBL / VIEW / PROC / FN tag beside the name.

Results pane uses text tabs and a shortcut empty state

Results, Messages, Diff, and Plan are now underlined text tabs with count superscripts, and Export is a text link on the right. An empty results pane shows a large table glyph and the shortcuts for Run, New Tab, Command Palette, and Search Objects.

Welcome, editor, and dialogs pick up the flat workbench chrome

The welcome screen is now a centered outline database glyph, the SQLLY name, recent connections, and a shortcut table with keycaps. The SQL editor uses the warm syntax colors (amber keywords, green strings, blue functions) with a current-line wash, and dialogs, the command palette, AI, history, DDL, and plan views use the same sans labels, mono values, popover surfaces, and amber primary buttons with dark ink.

Status bar is a thin utility strip

The status bar is a 26-pixel rail with settings / security / notifications and the connection name on the left, plain dim status text in the middle (errors in red, click to copy), and Search, zoom percent, and version on the right. The free-tier note is dim text with an accent link instead of a filled chip.

v0.58.0
Aug 25, 2026 · 28 sections
Aug 25, 2026
28 sections

Menus and the command palette share one capability matrix

The in-window menu bar's engine gating now derives directly from the same central capability matrix the command palette uses, so a tool added to one surface can never silently miss gating on the other — a test enforces the pairing. Document Schema and Tenant Scoping now correctly report "Connect to a database" when run without a connection, and a few dead internal scaffolds left over from the explorer-actions work were removed.

Go to Line and line editing verbs in the SQL editor

The editor gains Go to Line (Ctrl+G opens a small prompt that jumps to any line number), Move Line Up/Down (Alt+Up/Down), Duplicate Line (Cmd/Ctrl+Shift+D), and Join Lines (Ctrl+J). All four work on the current line or the whole selection, participate in undo, and are rebindable in keyboard shortcut settings.

Command palette remembers your recent commands

Opening the command palette with an empty search now shows the commands you ran most recently at the top, and recent commands get a small ranking edge while you type — without ever outranking a strong text match. The recency list is bounded and persists across restarts.

Profile Column opens the Profile pane

Profiling a single column from the grid's right-click menu now opens the same in-app Profile tab that Profile All Columns uses, scoped to that column, instead of only copying a Markdown summary to the clipboard. The Markdown copy is still available as its own menu item.

Pivot results export what you see

Right-clicking a pivoted result grid now exports the pivoted table itself — every format from CSV to Excel to SQLLY export reflects the pivot's rows and columns instead of silently exporting the raw source rows. Actions that only make sense on source rows (row editing, WHERE clause generation) no longer appear on the pivot grid's menu.

Upsert scripts match on the table's real primary key

Script as MERGE (and Save Data As → Update or Insert) now discovers the table's primary key and uses it — including composite keys — as the merge match key instead of blindly using the first column. When no key can be discovered, the generated script starts with a clear note that it fell back to the first column so you know to review it.

Run jumps to the parameter that needs a value

When Run is blocked because a declared query parameter is missing or invalid, the cursor now lands directly in that parameter's field in the strip, so fixing it is one keystroke away instead of a hunt.

Save Data As actually saves the data

The explorer's Save Data As no longer opens a SELECT tab and asks you to export manually. CSV, Excel, and SQLLY Export now prompt for a file and write the table's rows directly (bounded to the first 10,000 rows, using the table's own connection); Insert Into and Update or Insert generate the full script from the rows in a new tab, and a new "New Temp Table Import" script kind builds a temp-table load script the same way.

Result grid update

The result grid component was updated to its latest version, which makes the last column resizable like every other column.

Relationship graph

Tools → Relationship Graph… draws the current database's foreign-key relationships as a pannable diagram: tables as boxes, arrows pointing at the referenced table, constraint and column labels (compound keys folded into one edge), a filter that spotlights matching tables, and click-to-reveal in the explorer. Very large schemas keep the most-connected tables with an explicit truncation note.

Explorer favorites

Right-click any table, view, procedure, or function and choose Add / Remove Favorite; a collapsible FAVORITES strip above the explorer tree lists them persistently, and clicking one jumps to the object — with a clear note when its database isn't loaded yet.

Reviewed rename dialog

Rename… no longer opens a script with a <new_name> placeholder. A dialog now collects the new name with validation, shows a live preview of the exact engine-specific rename statement, and opens the finished script in a new tab on the object's own connection for review before you run it.

Connection health dashboard

Tools → Connection Health… lists every saved connection sorted problems-first with a health badge, direct/relay route, last connected and tested times, the last error (redacted), and measured connection latency, plus per-row Test and a Test All sweep whose outcomes persist into the health badges everywhere else.

Named result snapshots

Save any result set under a name with Save as Snapshot… (right-click a result grid) and reopen it later as a read-only tab from the new Tools → Result Snapshots… browser — no rerun needed. Snapshots are bounded to 100,000 rows with truncation recorded, carry their query text and engine, and can be renamed and deleted.

Diff filters, CSV export, and snapshot baselines

The result diff view gains change-class filter chips (All / Added / Removed / Changed), an Export CSV… button for the change table, and a Snapshot baseline button that compares the current run against any named snapshot instead of only retained in-memory runs.

View Row as Form

Right-clicking a row number now offers View Row as Form: a read-only record card showing every column's name, type, and value on its own line, with per-value copy buttons and previous/next navigation. It works on every engine and on very large disk-backed results.

Chart export, clickable legends, and histograms

Charts can now be exported as crisp SVG images (Copy SVG or Save SVG…), legend entries hide or show individual series or pie slices with a click (axes rescale to what remains), and a new Histogram chart type bins a numeric column into neat automatic ranges.

JSON and XML tree view with search

The structured data inspector gains a Tree mode: collapsible outline of the document, live search that keeps parents of matches for context, and hover buttons to copy any node's path or value. Raw pretty-printed text stays one toggle away, and malformed documents keep the classic view.

Write your own SQL templates

The Template Explorer now lets you create, edit, and delete your own templates alongside the built-in catalog. Placeholders written as <name,type,default> automatically become fill-in parameters with live preview, your templates persist across sessions and sort ahead of the built-ins, and the built-ins stay read-only.

Tab context menu, pinned tabs, reopen closed tab, and recent files

Right-clicking a query tab now opens a menu with Pin Tab, Close Tab, Close Other Tabs, Close Tabs to the Right, Close All Tabs, Copy Path and Reveal in Finder for file-backed tabs, and a Recent Files section listing your last opened SQL files. Pinned tabs move to the front, trade their close button for a pin, survive bulk closes and restarts; bulk closes skip unsaved tabs instead of prompting for each. Cmd/Ctrl+Shift+T reopens the most recently closed tab with its content, name, and connection.

Named saved queries and one-click re-run in the history browser

Pinned queries can now carry a name and description alongside tags; named pins show their name in the list and search finds them by it. A new Run button executes the selected history entry immediately in a fresh tab.

Compare a query version with the current buffer

The version history panel gains a Compare button that diffs the selected version against the editor's current text and opens a marked-up line-by-line comparison in a new tab.

MCP servers list their tools, and the AI knows about them

Testing an MCP server now discovers its tools and shows them (with descriptions) under the server in Settings → AI → MCP, remembered between sessions. The AI panel's Ask and Generate features are told which external tools your enabled servers provide so they can recommend them — SQLLY still never executes an MCP tool itself.

Context-scoped keyboard shortcuts

Custom keyboard shortcuts can now carry a when condition so a binding only fires in a specific part of the app — for example only while the SQL editor is focused (SqlEditor) or only in the results area (ResultsPane), with !, &&, and || supported for combinations. Invalid conditions are ignored safely with the binding staying global, and existing shortcut files keep working unchanged.

System notifications when a long query finishes in the background

When the app window is not in front, queries that run ten seconds or longer — and any query failure — now raise a system notification on macOS and Linux, so you can switch away and still know the moment your query is done. A checkbox at the bottom of the status-bar bell turns this off; it is on by default.

Include Statistics on MySQL and MariaDB

The Include Statistics toggle now works on MySQL and MariaDB: each run reports how many rows the storage engine read via indexes, read via table scans, and wrote — as a clear summary in the Messages pane — by snapshotting the session's handler counters around your query. On PostgreSQL the toggle now explains exactly why it is unavailable (collecting runtime statistics there would execute the query twice) and points at the Actual execution plan instead.

Procedure Mode on PostgreSQL

Procedure Mode now works on PostgreSQL: open a CREATE PROCEDURE or CREATE FUNCTION script (dollar-quoted bodies, IN/OUT/INOUT modes, and defaults all understood), toggle Proc, fill in the parameter strip, and Run deploys with CREATE OR REPLACE and invokes with CALL — or a SELECT for functions — with INOUT values coming back into the strip. The Rollback option wraps deploy and invoke in one transaction that always rolls back. MySQL/MariaDB remain excluded with an honest explanation: their procedure deployment commits immediately, so the rollback guarantee cannot exist there.

Actual execution plans for MySQL 8 and MariaDB

Explain Query (Actual) now works on MySQL 8 and MariaDB, not just SQL Server and PostgreSQL. MySQL captures the runtime analysis tree and MariaDB the analyzed JSON plan, so the plan pane shows real row counts, loop counts, and timings next to the optimizer's estimates. As on other engines, Actual capture executes the statement; Estimated capture remains available without running anything.

v0.57.1
Aug 25, 2026 · 5 sections
Aug 25, 2026
5 sections

External editors now negotiate live-edit sync correctly

An audit of the language server found that its editing-mode announcement used a wording real editors don't understand, so conforming editors could silently fall back to not syncing your edits at all. The announcement now uses the standard form, a regression test pins it, and the audit also added coverage for Windows line endings, emoji-heavy documents, batched edits, and out-of-date edit rejection.

Activity Monitor speaks native MariaDB

MariaDB connections previously borrowed MySQL's blocking-chain query, which depends on an optional helper schema that upgraded MariaDB servers often lack — the Blocking tab could fail outright. MariaDB now gets its own queries built on the views it always ships, and its session diagnostics follow suit. On stock MariaDB the Expensive Queries and Waits tabs show empty results until the server's performance tracking is enabled; everything else works out of the box.

SQL Agent schedules and alerts read like English

The Schedules tab now shows each schedule's recurrence in plain words ("Weekly on Mon, Wed at 09:30:00") with a locally computed next-run time, and the Alerts tab shows each alert's firing condition ("Error 1205", "Severity 17") instead of raw code pairs — no extra server queries involved.

Device-code sign-in expiry is bounded

The Microsoft Entra device-code flow now bounds the sign-in window a server can declare, closing the last unclamped value in the sign-in path: a malformed response can neither declare a code that never expires nor one that is already dead.

v0.55 work validated end-to-end, live databases included

Every capability shipped in v0.55 was audited claim by claim against the code and its tests, with the full report at docs/validation/2026-08-25-v0.55-claims-validation.md. The per-connection query timeout is now proven against real PostgreSQL, MySQL, and SQL Server servers (new live test suite), the AI review/summary/status endpoints are proven over the bridge path, and every SQLite metadata query is executed against a real database in the test suite so a syntax slip can never ship.

v0.57.0
Aug 25, 2026 · 19 sections
Aug 25, 2026
19 sections

Menus gray out tools the connected engine cannot use

On Windows and Linux, the in-window Tools and Query menus now dim engine-specific entries — like Backup / Restore on PostgreSQL or SQL Agent on SQLite — and show the reason right beside the item, instead of letting the click fail with a status message after the fact. The gating updates the moment you connect to a different engine, and dependency browsing now reports unsupported engines up front with the same clear reason.

Activity Monitor waits now show what changed since the last refresh

The Waits tab previously showed only totals accumulated since the server started, which buries what is happening right now. Each refresh now adds delta columns showing how much every wait's time and task counters grew since the previous refresh, with blanks on the first sample and after a server restart instead of misleading negative numbers.

Disk Usage tool for every engine

A new Tools > Disk Usage pane shows how much space every database on the connected server takes: data and log sizes with recovery model on SQL Server, per-database sizes on PostgreSQL, data and index totals per schema on MySQL/MariaDB, and file size with reclaimable free space on SQLite. SQL Server connections also get a Files tab listing each file of the current database with its physical path, size cap, and growth setting, so runaway growth is visible before it becomes an outage. The pane is strictly read-only and is available from the Tools menu and the command palette.

Index Analyzer finds the indexes you're paying for but not using

Tools > Index Analyzer lists every index with its real usage counters — seeks, scans, lookups, and update cost on SQL Server, scan counts and on-disk size on PostgreSQL, and read/write I/O events on MySQL/MariaDB — sorted least-used first so dead weight surfaces immediately. Primary-key and unique indexes are marked as constraint-enforcing and cannot be scripted for removal, while any ordinary index can be turned into a reviewed DROP INDEX statement in a query tab, with a reminder that counters reset on server restart.

Test Data Generator scripts realistic sample rows

Tools > Test Data Generator loads a table's real columns from the connected server, infers a sensible value generator for each — names, words, emails, numbers, dates, times, booleans, and GUIDs — and lets you adjust any column's generator, the row count, how often nullable columns get NULL, and the seed. It produces a dialect-correct INSERT script in a query tab for review, skips identity, auto-increment, and computed columns automatically, and the same seed always regenerates identical data so a test setup is reproducible. Works on SQL Server, Azure SQL, PostgreSQL, MySQL/MariaDB, and SQLite.

Export your schema as a Markdown data dictionary

Tools > Document Schema (Markdown) writes the loaded schema model to a readable Markdown document: every table with its columns, types, nullability, defaults, and primary/foreign keys, views with their columns, and procedures and functions with their parameters and return types. Save it into a wiki or a repository so the database's shape is documented without hand-writing anything.

Profile all columns in a real results tab

Right-clicking a result column already offered a copy-to-clipboard profile of that one column; a new Profile All Columns action analyzes every column at once — nulls, null percentage, distinct counts, numeric min/max/mean, text lengths, and frequent values — and opens the summary as a sortable, filterable Profile tab beside Results, Messages, and Chart. Large results stay bounded: profiling samples the first 100,000 rows and says so in the caption.

CSV import understands your data's types

The CSV import wizard now infers each column's type from the data — whole numbers, decimals, booleans, dates, timestamps, and GUIDs — and generates the reviewed INSERT script with real typed values instead of quoting everything as text. A new Column types field lets you override any column's inferred type, and rows whose cells don't match their column type are excluded from the script, listed in the dialog, and summarized at the top of the script so nothing is silently dropped.

Query history: paging, tags on pinned snippets, and pin backup

The Query History Browser now loads long histories a page at a time — a Load more control extends the list and reaches further back into the log on demand, instead of stopping at a fixed cap. Pinned snippets can carry your own comma-separated tags, editable right in the browser and searchable with the new tag: filter (or plain text), and new Export Pins / Import Pins buttons write your pinned snippets to a JSON file and merge them back on another machine without creating duplicates.

Test every connection at once, and favorites lead the list

Connection Management gains a Test All button that checks every saved connection one at a time on demand — sign-in connections are skipped so no interactive windows pop up — updating each health badge as the pass advances and summarizing passed, failed, and skipped counts at the end. Connections can now be starred as favorites, and connection pickers list favorites first, then the profiles you connected to most recently, then the rest by name; editing a connection no longer clears its recorded health history or its star.

Notification bell for the things you'd miss while looking away

A bell now lives in the status bar and quietly collects the events worth seeing after stepping away: queries that ran ten seconds or longer, query failures, and newly available updates, each with its time. The bell shows an unread count, opening it marks everything read, and the list stays bounded so it never grows without limit — nothing pops up over your work.

Generated SQL reads formatted, and validation findings are clickable

AI-generated SQL in the Generate tab now displays formatted with your own SQL formatting preferences instead of as one raw block, while copy, insert, and run continue to use the exact statement the validator checked. Validation findings that point at a specific place in the SQL are now clickable: clicking one opens the statement in a new query tab with the cursor already sitting on the offending token, so fixing a flagged reference no longer means hunting for it.

Automagic can now repair missing ANDs in WHERE and JOIN ON

Alongside the existing comma repair, an opt-in Automagic assist inserts a missing AND when you start a new predicate on a fresh indented line inside a WHERE clause or a JOIN's ON. It stays quiet whenever inserting a connector could be wrong — a line already ending in AND, OR, NOT, or a comparison, a BETWEEN still waiting for its own AND, strings, comments, and unbalanced parentheses — and every repair is a single undoable edit with a per-query override in the editor's Automagic controls.

Query parameters: declare once, fill in a strip, run safely

Queries can now declare parameters with -- sqlly param: @name <type> comments — text, int, decimal, bool, date, or datetime, with an optional default. A parameter strip appears between the editor and the results with one typed field per parameter, and Run substitutes each @name with a validated, properly escaped literal for the connected engine, leaving @name inside strings and comments untouched. Empty fields use the declared default, typing NULL sends SQL NULL, and a missing or invalid value blocks the run with a clear message instead of sending broken SQL. The directive is documented in Help ▸ Formatting Directives and mistyped declarations get an editor diagnostic.

MCP servers: real configuration replaces the placeholder

The Settings ▸ AI ▸ MCP pane is no longer a "coming soon" note: it now manages a saved list of Model Context Protocol servers — local stdio commands with arguments and environment, or remote HTTP endpoints — each with an enable toggle. A Test button performs the protocol's real initialize handshake with a strict timeout and process cleanup, reporting the server's name, version, and protocol revision or the exact failure. Tool execution by the AI remains a later step, and the pane says plainly that environment values are stored unencrypted so secrets stay out.

Cloud providers can now power the AI panel

The cloud provider keys and model lists in Settings ▸ AI ▸ Cloud finally have a consumer beyond the Playground: a new App AI backend choice routes the app's schema-aware AI — Ask, Generate, Search context, and query summaries — through OpenRouter, OpenAI, or Claude using that provider's selected model. It is strictly opt-in: the default stays on-device, keys stay in the system keychain, and a half-configured choice (missing key or model) keeps everything local with a logged reason instead of silently failing. Takes effect on the next launch.

Update checks keep running, and the what's-new dialog stays fresh

The app no longer checks for updates only at launch: it re-checks every half hour in the background, so a build released while SQLLY is running still surfaces — including a newer build than one already advertised. Opening the what's-new dialog re-queries the update service whenever its last answer is more than 30 minutes old, a new Check Again Now button forces a fresh check on the spot, and if a fresh check finds you're already up to date the stale update chip and dialog retire themselves. The dialog's list of changes — every version between your build and the latest — now shows a visible scrollbar so it's clear there is more to read.

Tenant scoping is now configurable per database, with four levels

Tools > Tenant Scoping lets each database choose how strongly SQLLY guards tenant-scoped tables: Off disables the feature entirely (including its configuration), Allowed keeps tenant filters working without ever warning, Warn — the default and the previous behavior — flags a tenant-scoped table queried without its tenant filter, and Required, which will block unscoped queries outright, is visible but disabled with a note until it is implemented. You can also name that database's exact tenant column instead of relying on the TenantId/AccountId/OrganizationId conventions, and optionally record the tenant table with its id and display-name columns for future tenant picking. The warning now appears only in Warn mode — every other level shows no indication anywhere, including AI-generated SQL review.

SQL Agent operators, alert notifications, and safe deletion

The SQL Agent tool's Operators tab now has the same guided Create/Edit experience as jobs, schedules, and alerts, including the operator's email address, and the alert editor can name an operator to notify by email when the alert fires. Every tab also gains a Script Delete button that writes the reviewed removal statement — job, schedule, alert, or operator — into a query tab instead of deleting anything directly, and schedule deletion deliberately refuses to force-detach schedules still attached to jobs.

v0.56.2
Aug 24, 2026 · 1 section
Aug 24, 2026
1 section

Fix browser build: gate the LSP cancellation helper to native

The language-server request-cancellation helper (cancelled_reply) referenced native-only internals but wasn't itself marked native-only, so the browser (WebAssembly) build failed to compile. It now carries the same native-only gate as its sibling helpers — the in-browser build doesn't run the stdio language server, so there is no behavior change on the desktop app.

v0.56.1
Aug 24, 2026 · 1 section
Aug 24, 2026
1 section

Fix release build: wasm, cross-platform keybindings, and dependency audit

Repaired the four failures that broke the v0.56.0 release build across the Linux, Windows, and WebAssembly legs:

Keybindings round-trip (Linux/Windows): the persisted shortcut descriptor serialised the logical *command* modifier as ctrl on non-macOS, which parsed back as *control* — so saved shortcuts didn't survive a reload (and didn't move between operating systems). The descriptor is now a canonical, platform-independent form (command → cmd everywhere); the platform-specific label (Ctrl) is applied only for display and for the actual gpui key registration, which is unchanged.
Browser (WebAssembly) build: the in-browser column listing wasn't updated when columns gained type, nullability, and identity information, so the browser engine failed to compile. It now reports those fields (pulling type and nullability from SQLite's own metadata where available).
Browser (WebAssembly) build: the TLS crypto provider (ring) is native-only and can't compile for the browser; it is now excluded from the browser build, which uses the browser's own networking instead.
Dependency audit: the internal SQL-formatter crate is now marked unpublished so its path dependencies pass the audit, and the results-grid crate's pinned git release tag is now on the allowed-sources list.
v0.56.0
Aug 24, 2026 · 31 sections
Aug 24, 2026
31 sections

Stabilized AI panel validation

AI panel validation no longer intermittently aborts on slower or cross-platform builders when background work finishes after the test scheduler. The test harness now uses deterministic local catalog responses and safely accommodates the panel's real background-task behavior.

Safer and more accurate AI foundations

Direct AI features now use the configured local provider, preserve cloud API keys when model lists refresh, honor response-length limits, and generate SQL in the connected database's dialect.

Native database engine selection

New and edited connections can choose any database engine backed by SQLLY's native drivers. The forms apply engine-specific ports and authentication choices, preserve custom ports, save the selected engine, and treat SQLite as a credential-free file connection.

Centralized engine-aware tool gating and truthful plan modes

Admin tools and execution-plan commands now share one engine capability policy, so unsupported actions explain why they are unavailable before opening a misleading pane. Activity Monitor and Query Insights gate their supported live sources consistently, actual plans are limited to engines that return runtime measurements, and estimated no-execution plans remain available across all four native engine families.

Regular-expression find and replace

Find and Replace can now use bounded regular expressions, show invalid-pattern feedback without disturbing the query, and reuse numbered or named capture groups in replacements. Regex searches retain the editor's case, whole-word, multiline, Unicode, match-limit, folded-region, and single-undo behavior.

Multi-cursor completion acceptance

Editor and AI completions now apply to every active cursor as one undoable edit while preserving all cursor positions. Each cursor uses its own selection or local identifier fragment, so accepting a qualified completion does not overwrite neighboring SQL.

Rectangular editor selection

Option/Alt+Shift dragging now creates tab-aware selections across every visible line in a rectangle. Typing, deletion, paste, completion, and undo reuse the editor's bounded multi-cursor path, including lines shorter than the selected column.

Compare captured execution plans without rerunning queries

Each query tab now retains a bounded history of compatible plan captures and can compare any two of them without executing the SQL again. The comparison keeps Actual and Estimated labels visible, summarizes aggregate cost, operator, and warning changes, and highlights regressed, improved, added, and removed operators while declining unsafe cross-query or exceptionally large comparisons with a clear reason.

Ephemeral AI Playground

Settings now includes a one-request AI workspace for selecting a configured local or cloud model, editing instructions and a prompt, watching bounded output, cancelling work, copying the response, and reviewing latency and token estimates without saving prompt history.

Triage live activity and review safe termination scripts

Activity Monitor now identifies blocking roots and chain depth, adds sortable normalized runtime and triage columns, and filters blockers, blocked sessions, long-running work, and idle transactions across supported engines. Selected SQL Server, PostgreSQL, and MySQL/MariaDB sessions expose copyable read-only diagnostics and engine-native termination scripts behind an explicit review confirmation; SQLLY never executes the termination itself, and permission failures now explain the relevant engine privileges.

Safer generated SQL review

Generated SQL now presents the database validator's findings beside copy, insert, and open-in-new-query review actions, while direct execution stays unavailable for invalid, warning-bearing, or confirmation-required statements so risky model output cannot bypass human review.

Bounded temporary AI conversations

The AI panel can now include a small opt-in window of recent complete exchanges for follow-up questions, with hard prompt, response, transcript, pinned-object, and context limits; Clear and database switches discard the in-memory transcript and cancel its active request.

Opt-in Automagic comma repair

The editor can now repair a missing separator when a new indented item begins in SELECT, GROUP BY, or ORDER BY lists. Each clause is independently opt-in globally or for the current query, leading and trailing comma styles are supported, and the bounded SQL-aware check avoids strings, comments, operators, and unrelated expressions.

Persistent connection health

Successful tests, live connects, and failures now update each saved connection's health without persisting hydrated credentials. Connection Management shows healthy, failed, or untested badges plus the last observation and a bounded secret-redacted error summary.

Structured query history and retention

Execution history now records terminal outcome and duration, supports composable text, connection, database, date, duration, and outcome filters, and displays those facts with each match. Configurable age and count retention runs atomically when history opens, while separately stored pinned statements remain protected.

Expanded result chart presentations

Result charts now add area, scatter, pie, and donut presentations alongside bars and lines, plus controls for category, numeric series, aggregation, ordering, and top-N limits that stay with the chart pane. Radial charts enforce honest single-series, nonnegative, bounded-category inputs with clear explanations when the selected result is unsuitable, while nulls remain gaps and chart extraction samples a clearly labeled bounded source prefix.

Query Insights across three database engines

Query Insights now reads bounded native performance metadata from SQL Server Query Store, PostgreSQL pg_stat_statements, and MySQL/MariaDB Performance Schema. The pane names its active source, uses engine-appropriate work metrics and time controls, explains collector and permission failures, and offers reviewed setup SQL only where the database can safely enable collection without pretending that server configuration changes are automatic.

Shortcut profiles and overrides now control the app

The Keyboard Shortcuts pane now saves the active platform-native, SSMS, or custom profile together with per-command overrides and applies the resolved shortcuts on the next launch. Conflicting assignments are rejected before saving, editor-specific shortcuts remain scoped to the editor, and a clear restart notice prevents menus and help text from advertising bindings that are not active yet.

Actionable schema search and editable JOIN scaffolds

Schema search results can now be copied, inserted, revealed in Explorer, opened as definitions where supported, pinned, used as AI context, or chosen as the start of a relationship search. Relationship paths distinguish declared foreign keys from confidence-filtered inferred links and produce bounded, dialect-aware JOIN SQL that users can copy, insert, or open in a new query without executing it.

Completion-time JOIN aliases

The Auto based on join preference now adds deterministic, collision-free aliases only when a plain table or view completion is accepted as a JOIN source. Alias work stays out of completion ranking and typing paths, and existing foreign-key JOIN completions remain unchanged.

Script-first SQL Agent editors

SQL Agent now provides guided create and edit flows for job properties, optional add-or-update job steps, schedules with recurrence and job attachment, and message- or severity-based alerts. Every form validates its engine-native fields and shows the generated msdb script before opening it in a query tab for review; no Agent mutation is executed from the pane.

Mapped CSV and typed SQLLY export imports

Import / Export now reads and parses bounded files away from the interface thread and accepts positional column renames and skips with duplicate and incomplete mapping validation, a configurable null token, active-engine quoting and literals, and an engine-appropriate schema default; mapping is applied to previews and reviewed INSERT scripts, while SQL Server-only bulk loading is offered only where it is valid. Portable SQLLY export snapshots can also be opened in the wizard with their declared schema and typed null, boolean, numeric, text, and binary values preserved, while malformed, oversized, or incompatible envelopes are rejected before scripting.

Bounded on-demand result profiling

Result grids can now copy a null-aware profile for a specific column from its header menu. Profiles report declared type, distinct and frequent values, numeric range and mean, and text or binary lengths, read only a bounded visible or spill-file prefix, and clearly mark results that were truncated at the profiling limit.

Dependency discovery for MySQL, MariaDB, and SQLite

View Dependencies and Find Usages now work across every native database engine. MySQL/MariaDB combines foreign-key metadata with visible view, routine, trigger, and event definitions, while SQLite combines foreign-key pragmas with stored schema SQL; both paths are bounded, escape hostile object names, label best-effort completeness limits, and explain metadata visibility or permission gaps rather than presenting partial matches as exhaustive.

Native cross-connection Schema Compare

Schema Compare can now independently select saved source and target connections and databases across SQL Server, Azure SQL, PostgreSQL, MySQL/MariaDB, and SQLite. It collects bounded native metadata, labels cross-engine, unsupported, and manual-review differences honestly, refuses partial catalogs, and opens conservative migration guidance in an editor pinned to the captured target without generating automatic drops.

Complete and safe workspace restoration

Workspace sessions now restore every open tab and its complete unsaved SQL, the focused tab, editor split, Explorer and results dimensions, resizable AI width and active lane, per-tab history and object-definition widths, editor zoom, connection targets, and the last project folder. Legacy, missing, and corrupt state falls back safely, writes are atomic, deleted files or connections cannot prevent launch, and restored connections remain disconnected until the user explicitly connects without ever running SQL automatically.

Named workspaces

File menu and command-palette actions can now save the current tabs, connection targets, project folder, and pane layout under a name, open another saved setup, or manage saved workspaces by renaming, duplicating, and confirm-deleting them. Workspace snapshots preserve complete unsaved buffers and are stored atomically in isolated files so one damaged snapshot does not hide healthy workspaces, and opening one always restores into a disconnected, non-executing state.

Compare any two retained result runs

Result Diff now keeps a bounded history of completed runs in each tracked query tab and lets either side of the comparison target any retained run. Optional composite keys turn matching rows into typed per-column updates with duplicate and null-key validation, while keyless comparisons preserve typed multiset behavior and oversized, incomplete, spill-backed, or incompatible results explain why comparison is unavailable.

Reopen portable SQLLY result snapshots

File > Open SQLLY Export and the command palette can now validate and reopen a .sqllyexport file in a fresh disconnected, read-only result tab. Typed values and the source engine are restored through the existing result surface for inspection, copying, charts, diffs, and re-export, while unsupported versions, damaged data, oversized files, and browser-only file boundaries produce clear errors without contacting a database.

Reproducible data-grid dependency

The app now pins its compatible data-grid release directly to an immutable upstream tag instead of requiring a machine-specific sibling checkout. Local and release builds therefore resolve the same source while retaining compatibility with the app's web-capable interface stack.

Accurate predicate-column validation

Model-aware validation no longer mistakes unqualified columns in WHERE or JOIN predicates for schema objects. Valid SELECT, UPDATE, and DELETE predicates therefore stay free of false missing-object errors, while genuinely missing tables remain diagnostic and edit-mode exemptions behave as configured.

v0.55.0
Aug 24, 2026 · 18 sections
Aug 24, 2026
18 sections

Object definitions come back on every engine

Edit Definition, go-to-definition, Script as ALTER, and the Properties window's Definition page previously fetched object source with SQL Server queries no matter the engine, so they failed outright on PostgreSQL, MySQL/MariaDB, and SQLite connections. Each engine is now asked in its own language: PostgreSQL rebuilds views and functions from its catalog, MySQL/MariaDB reconstructs views, procedures, functions, and triggers, and SQLite returns the exact source it stores — including a table's CREATE statement with its indexes. Tables on engines that don't store source get a generated CREATE TABLE from live catalog metadata, and objects whose source is withheld report that plainly instead of failing.

WHERE-value autocomplete speaks your engine's SQL

The IntelliSense value lookup (offering a column's real values in a WHERE clause) and the editor's distinct-values lookup previously sent SQL Server syntax to every engine and failed anywhere else. Both now build the lookup query in the connected engine's own dialect, so value autocomplete works on PostgreSQL, MySQL/MariaDB, and SQLite too — with SQL Server output unchanged.

Bridged connections run every engine, not just SQL Server

Connections routed through SQLly Bridge previously treated every server as SQL Server: the connection sent over the wire carried no engine type, so queries against PostgreSQL, MySQL/MariaDB, or SQLite servers behind a bridge spoke the wrong protocol and failed. The bridge now knows each connection's engine and runs queries with that engine's own driver — including warm connection reuse, the same stale-connection retry rules as direct connections, and true server-side cancellation (the statement stops on the server, not just in the app). Remote row editing on the new engines works through the same path.

Clearer, machine-readable remote failures and version mismatches

Failures from a bridged engine now carry a stable category alongside the human-readable message, so the app can tell "this engine doesn't support that" apart from a connection problem — remote capability gaps now surface as proper capability messages instead of generic errors. If the app and a remote engine ever run incompatible protocol versions, the session now stops immediately with a message naming both versions and which side to update, instead of failing later with baffling decode errors. Remote sessions also gained sensible patience limits: an unresponsive engine fails a ping within ten seconds and abandons a report request that has gone completely silent, and a wedged connection can no longer queue outgoing requests without bound.

PostgreSQL results stream as they're produced, and slow streams stay live

PostgreSQL queries previously buffered the entire response in memory before the first row reached the grid; rows now stream in as the server produces them, so large results start rendering immediately and memory stays flat. On PostgreSQL and MySQL/MariaDB, a query that produces rows slowly now surfaces what has arrived on a steady cadence instead of holding buffered rows hostage until the next row happens to show up.

Per-connection query timeout

Connections gained an optional "Query timeout (seconds)" setting in the connection editor's Advanced section. A statement that runs past the limit is stopped on the database server itself — SQL Server, PostgreSQL, MySQL/MariaDB, and SQLite each via their native stop mechanism — and reported as a clear timeout rather than a generic error. The limit travels with the connection, including over bridges, and an empty field means what it always did: no limit.

The schema catalog can no longer lose knowledge to an incomplete refresh

The schema intelligence catalog previously treated every refresh as the complete truth: if a refresh came back empty or partial — a failed metadata read, an interrupted scan — everything it didn't mention was marked deleted, taking cached knowledge and AI context with it. Refreshes now declare how much they actually covered, and the catalog only forgets objects a refresh provably observed to be gone. Objects that reappear after being marked deleted are restored with their history intact.

Catalog files heal themselves and clean up after themselves

A corrupted catalog file no longer breaks schema intelligence: it is detected on open, preserved beside itself for inspection, and rebuilt fresh — costing one re-sync, never your data (the catalog is a cache of your database, not a store of record). Catalogs also refuse to be half-migrated backwards by an older app version, and a new maintenance operation can purge long-deleted entries, drop orphaned search vectors, and compact the search index on demand — including for catalogs living behind a bridge.

Schema-grounded AI review, query summaries, and provider status — bridge-ready

The AI foundation gained three engine-level capabilities that work locally and across bridged connections alike: a SQL review that runs the deterministic validator first and then explains the statement grounded in only the relevant slice of your schema (never the whole thing), a plain-language query summary grounded the same way, and a provider status check that reports which AI providers are configured and active, why anything fell back, whether the local runtime answers, and which models it has installed. Semantic schema search can now also be narrowed to a specific database or schema instead of always searching everything.

Validation catches misspelled and ambiguous columns — and understands CTEs

Pre-execution validation and live diagnostics now catch a column name in a WHERE clause or join condition that doesn't exist on any table in the statement, and warn when a name exists on several tables and needs qualifying. The checks are engineered never to cry wolf: function arguments, subqueries, casts, and special registers are excluded, and any statement using a CTE or derived table the validator can't see through stays quiet rather than guessing. WITH clauses are now genuinely understood — CTE names no longer read as unknown tables, and tables referenced inside CTE bodies participate in validation and navigation. A connection's default schema now also disambiguates unqualified table names that exist in more than one schema.

Tenant-scope guard: never forget the tenant filter again

When a table carries a tenant discriminator column (tenant, account, or organization id), running a query against it without that column in the WHERE clause or join conditions now raises a warning before execution — catching the classic mistake that reads or changes every customer's rows at once. Tables keyed by the tenant column (the tenant registry itself) are exempt, so the guard stays quiet where it should. It can be turned off per validation options.

Cross-database checks match each engine's real rules

Cross-database references are now judged the way each engine actually behaves: PostgreSQL flags them as errors (one connection genuinely cannot query another database), MySQL/MariaDB's database-qualified names warn without falsely reporting the table as unknown, SQLite stays quiet (attached databases are session state), and SQL Server keeps its existing warning. On MySQL, # comments are now recognized by the safety scanner, so a commented-out WHERE can no longer make an unconstrained DELETE look safe.

Property pages work on every engine, and the schema model knows column types

The Properties window's General, Index, Statistics, Permissions, and Type pages previously queried SQL Server system views on every engine and failed anywhere else; each page now speaks the connected engine's own catalog language on PostgreSQL, MySQL/MariaDB, and SQLite too, with concepts an engine lacks shown as honestly empty rather than erroring. The database list, server capability info, and statistics listings gained the same treatment (MySQL surfaces its per-index statistics; SQLite reports none rather than failing). Separately, the schema model the editor builds on connect now retains each column's data type, nullability, and identity status instead of just its name — improving completion detail and join inference — and a later column refresh no longer wipes that knowledge.

The language server grew up: incremental edits, real highlighting, outlines, and formatting

For people using SQLLY's SQL intelligence from external editors (VS Code, Neovim, and friends): the language server now applies edits incrementally instead of re-sending whole documents, handles closing and saving files properly, and ignores out-of-order edits from a confused client. Syntax highlighting is now driven by a real SQL lexer — a keyword inside a string or comment is never mis-colored, and strings, comments, numbers, parameters, and function names each classify correctly, including across multi-line comments. A document outline lists every statement with what it touches, and Format Document is now available, running the exact same formatter as the app — which itself moved into a shared component so the two can never drift apart.

Activity Monitor and query insights on PostgreSQL and MySQL/MariaDB

The Activity Monitor's four views — sessions, blocking chains, expensive queries, and waits — now speak each engine's own monitoring language: PostgreSQL's activity and statement statistics (including who-blocks-whom via the server's own blocking detection) and MySQL/MariaDB's process list, lock waits, and statement digests, alongside the existing SQL Server views. Engines without live activity, like SQLite, say so plainly. Top-query insights equivalent to SQL Server's Query Store are now available for PostgreSQL (via pg_stat_statements) and MySQL/MariaDB (via statement digests), session-kill statements map to each engine's native mechanism, copy-only log backups are no longer refused, SQL Agent schedules now describe their recurrence in plain words with a locally computed next-run time, and agent alerts parse into readable conditions instead of raw code pairs.

The browser demo database now supports editing data

The in-browser sample database is no longer read-only: INSERT, UPDATE, DELETE, CREATE TABLE, and DROP TABLE now work against the in-memory Northwind sample, so the demo can show the full edit-and-query loop — safety confirmations included — without installing anything. Changes live only in the page and reset on reload.

A tougher safety net under remote connections and encrypted sessions

Behind-the-scenes hardening proof grew substantially: remote sessions are now exercised against interleaved concurrent queries, hostile duplicate completion signals, and mass-cancellation storms — each stream must settle exactly once, every time — and remote row editing gained fully offline test coverage. The encrypted-tunnel handshake is now tested against truncated, corrupted, and oversized bytes (they can never complete a session), and schema-catalog search gained a performance tripwire that fails the build if search ever regresses by an order of magnitude.

Microsoft Entra sign-in is sturdier against bad server responses

Token responses from Microsoft Entra are now validated more strictly: a malformed or hostile response can no longer crash the app with an absurd expiry value, non-Bearer token types are rejected instead of silently accepted, and sign-in polling intervals are bounded so a bad response cannot stall sign-in indefinitely. Tokens also now refresh slightly before their server-side expiry, so a small clock difference between your machine and Microsoft's servers no longer risks a query failing with an expired token.

v0.54.0
Aug 24, 2026 · 28 sections
Aug 24, 2026
28 sections

Same-buffer editor splits (two views of one query)

The editor split can now show the same query in both panes: with a single tab open, View ▸ Split Editor Vertically/Horizontally splits the buffer itself instead of cloning it into a new tab, and new Split Same Buffer Vertically/Horizontally commands do so explicitly at any time. Both panes edit one document — typing in either appears instantly in the other, undo/redo is a single shared stream, diagnostics and the unsaved-changes marker stay in step — while each pane keeps its own cursor, selection, multi-cursor carets, and scroll position, so you can edit the top of a long script while watching the bottom. The split follows you when you switch tabs, survives an app restart, and closing it keeps your text plus the focused pane's cursor; syntax highlighting and line indexing are computed once per document, not per pane.

Result-set charts

Right-clicking a result grid now offers "Chart…", which opens a bar or line chart over that result set's currently visible rows (active sorting and filtering respected). The first text column supplies the category labels and every numeric column becomes a series — up to four, with theme-derived colors and a legend — while the value axis picks nice rounded steps with K/M/B tick labels and NULL values chart as gaps rather than zeros. Very large sets chart their first 200 rows with an explicit note, a result with no numeric columns says so instead of drawing an empty chart, and a Chart tab beside Execution Plan flips between the chart and the grid until the next run.

Multi-cursor editing in the SQL editor

The editor now supports command-driven multi-cursor editing. Cmd+D (Ctrl+D on Windows/Linux) selects the word under the cursor and then adds a cursor on each next occurrence, Cmd+Shift+L (Ctrl+Shift+L) puts a cursor on every occurrence at once, and Cmd+Alt+Up/Down (Ctrl+Alt+Up/Down) stacks cursors on neighboring lines at the same column. Typing, Backspace, Delete, and paste apply at every cursor — a paste with one line per cursor distributes the lines — each multi-cursor edit is a single undo step, and Escape (or any click, movement, or non-editing command) collapses back to a single cursor.

Validation follows your connection's dialect

Pre-execution validation and the editor's live diagnostics previously judged every query by SQL Server's rules regardless of the connected engine, producing false warnings on PostgreSQL, MySQL/MariaDB, and SQLite. Both now analyze your SQL in the connected engine's own dialect.

Explorer scripts speak every engine's SQL

Every SQL script the explorer generates — View Data, Count Rows, Delete All Rows, Truncate, Drop, Create-table DDL, INSERT and upsert scripts, Filter Data, and procedure/function invocations — now renders with the target engine's own identifier quoting, row limiting, literals, and statement forms instead of always emitting SQL Server syntax. Engines that lack a statement get their closest native equivalent: Truncate on SQLite scripts a DELETE with an explanation, upserts use ON CONFLICT on PostgreSQL/SQLite and ON DUPLICATE KEY on MySQL/MariaDB instead of MERGE, and procedure invocations use CALL where EXEC is SQL Server-only. SQL Server output is unchanged byte-for-byte.

Result-grid WHERE actions and filters match your engine

The WHERE-builder context-menu actions (equals, not-equals, IN, LIKE, BETWEEN, Match Row) and the filters-to-WHERE generator previously always emitted SQL Server bracket quoting and N'…' literals. They now render in the dialect of the engine the results came from — correct quoting, string and boolean literals, and LIKE escaping per engine (including MySQL's backslash rules, and no invalid bracket escapes on engines that reject them).

SQL exports match your engine, and a new SQLLY Export format

Exporting results as INSERT statements, a temp-table script, or SQL values now generates scripts for the engine the query ran against: correct quoting and literals everywhere, real temporary-table syntax per engine (the SQL Server form now actually creates a #temp table), and a portable UNION ALL form where an aliased VALUES table isn't supported. A new SQLLY Export format (.sqllyexport) saves a result set as a self-describing document carrying the source engine, column schema, and typed rows — nulls, booleans, numbers, text, and binary data all preserved — and the explorer's Save Data As ▸ SQLLY Export now opens the query that feeds it instead of reporting "not yet available".

Result diff compares real values and bounds its memory

The result-set difference tracker previously compared rows by their displayed text, so an integer 1, the text "1", and a true/false flag rendered as 1 all counted as the same value — a column silently changing type between runs never showed up as a difference. Diffing now compares typed values, so those cases are reported as real changes, while runs whose only difference is floating-point NaN noise still compare as identical. Difference tracking also now caps how much it holds in memory: results over one hundred thousand rows are no longer kept for comparison, and the diff quietly steps aside for them the same way it already does for disk-spilled results.

Every query tab keeps its own connection

Each query tab now pins itself to the server and database it was created or last used with. Switching tabs switches the workbench connection with them — toolbar, explorer, IntelliSense, validation, and execution always match the tab you're looking at — and the pins survive restart, so a restored session brings each tab back pointing at its own server.

Explorer scripting: ALTER, DROP, Rename, and Edit Definition

The explorer's context menus grew real script-first object actions. Script as ALTER opens a view, procedure, or function's live definition rewritten in the engine's alter form (tables get an editable scaffold of common changes), Script as DROP produces a guarded drop with the engine's syntax, Rename scripts the engine's own rename statement with a placeholder, and Edit Definition opens the object's current server source in an editable tab. Everything opens in a query tab targeted at the object's own server, and nothing runs until you run it.

View Dependencies and Find Usages

Right-clicking a table, view, procedure, or function now offers View Dependencies — what the object references and what references it, from the server's own dependency tracking on SQL Server and PostgreSQL — and Find Usages, which searches stored view and routine source for the object's name. Results show in a grid dialog with an "Open as Query" escape hatch, and engines without dependency metadata say so plainly.

Connect to Current Query and Open in New Tab

The explorer's Connect to Current Query and Open in New Tab actions now work: the first switches your active query tab's connection to the clicked server and database, the second opens a fresh tab already targeted there — both previously reported "not yet available".

Auto-navigate the explorer to what you're working on

The "Auto navigate schema into view" preference is now real: going to an object's definition from the editor expands the server explorer down to that object, selects it, and scrolls it into view. Previously the checkbox existed but did nothing.

Engine-specific actions disable themselves honestly

A new capability layer answers "is this feature available on this engine, and why not" in one place. Procedure Mode and Include Statistics now disable on non-SQL Server connections with a tooltip naming the reason (previously the proc toggle appeared to work everywhere and Include Statistics silently did nothing), and opening the row editor on an engine that doesn't support it yet explains itself immediately instead of failing mid-dialog.

The AI review gate for data changes is real, and it fails closed

The "Require AI review for data changes" safety flag previously collapsed to the ordinary confirmation dialog with no review — and on the actual execution path it could never even trigger, because entity-level safety settings never reached query execution. Both halves are fixed: a connection's effective safety now includes the safety features of its assigned client, project, and environment entities, and when AI review is required the confirmation dialog generates a plain-language explanation of what the batch will change on your local AI runtime before Execute unlocks. If the review cannot be generated — runtime unreachable, no model, timeout, empty answer — the query does not run.

Estimated execution plans are back, captured safely

Explain Query (Estimated) returns as a dedicated menu and command-palette action that never executes your SQL: SQL Server compiles the batch in isolated setup steps and returns the predicted plan (the old capture was retired because its setup shared a batch with your SQL and always errored — that underlying capture is fixed everywhere it was used), and PostgreSQL is asked without ANALYZE. MySQL and SQLite plan captures never executed anyway and are unchanged. Plans open in the plan pane clearly labeled estimated.

Object Properties speaks your engine's SQL, and a rename bug is fixed

The Properties editor's Apply previously generated SQL Server commands no matter the engine. Each staged change now renders in the connected engine's own form — native renames, nullability changes, and statistics refresh mapped to each engine's analyze — and changes an engine cannot express are refused with a plain reason instead of firing foreign SQL. Also fixed: changing a column's nullability on SQL Server generated an invalid statement (a missing dot between schema and table) that would have failed on every apply.

Query summary settings are real, and the On device page checks your AI runtime

The AI ▸ On device settings page now carries working query-summary controls — an on/off switch and an optional model override that persist and take effect — replacing the coming-soon placeholder that stood where inert toggles once lived. The page also shows the local AI runtime (Ollama) endpoint that summaries run against, with a Check button that probes it, lists the installed models, and gives one line of setup guidance when the runtime isn't reachable.

Auto Format is a real toggle, and a dead debug toggle is gone

The query toolbar's Fmt toggle now does what it says: with it on, Run formats the whole buffer (as a single undo step) before executing, and it steps aside when you've selected text to run so the selection still covers what you meant. The "Include result set debug" toggle, which was never wired to anything, has been removed entirely rather than left hidden.

Removed the editor indentation steppers that did nothing

The Editor ▸ Formatting page's four indentation steppers (Section, Keyword, Join, Column) wrote preference values that no feature ever read, so the controls and their stored fields have been removed rather than left implying control they never had. The formatter's real indentation settings live in the SQL ▸ Formatting section, the page now points there, and old preference files carrying the removed values still load cleanly.

Row editing over relays is actually reachable now

The previous release wired row editing through relay connections engine-side, but the app still blocked the row editor up front on any relay connection and would have routed edits over the wrong path — so the capability was unreachable in practice. The gate is gone and edits now follow the connection's route, so a relay connection's row edits run on the bridge with the same key-only identification and conflict detection as direct ones.

SQL Agent job control, script-first

The SQL Agent browser's Jobs tab now offers Script Start, Script Stop, and Script Enable/Disable for the selected job. True to the script-first contract, each button opens the exact statement in a query tab for review — nothing executes from the dialog, and running the script still requires the msdb permissions you actually hold.

Schema Compare covers indexes, foreign keys, and constraints

Schema Compare now diffs indexes (key columns, includes, uniqueness), foreign keys (columns, referenced table, delete and update actions), and check and default constraints alongside the existing object definitions and table column signatures — so a target database missing an index or carrying a different FK action shows up as a real difference.

Query Store ranking and time windows

The Query Store viewer can now rank its top queries by average duration, execution count, or average logical reads, and restrict the view to the last hour, 24 hours, or 7 days of captured runtime statistics — with average reads and last-execution time added to the grid.

Activity Monitor operator controls

The Activity Monitor gained the controls an operator actually needs mid-investigation: pause and resume auto-refresh, a selectable refresh interval (2, 5, or 15 seconds), a status line showing how long ago the data refreshed, and — when a selected row carries query text — Copy SQL and Open as Query buttons that carry the investigation into a real query tab.

Drop SQL files onto the window

Dragging .sql files from Finder or Explorer onto the main window now opens each one as a file-backed query tab — a file that's already open focuses its tab instead of duplicating, and non-SQL files are declined with a short notice.

In-grid row editing on PostgreSQL, MySQL/MariaDB, and SQLite

Editing, inserting, duplicating, and deleting rows directly in the results grid previously worked only on SQL Server connections. The same experience now works on PostgreSQL, MySQL/MariaDB, and SQLite with identical safety guarantees: the edited row's source table is identified by the database itself rather than guessed from column names, rows are addressed only by their real primary key, and every save runs as a single guarded transaction that detects when someone else changed or deleted the row first. Foreign-key dropdowns, related-row panels, and clear explanations for rows that cannot be edited safely carry over to the new engines too.

Query History Browser

The execution log the app has always kept is now browsable. A new Query History dialog lists your past runs newest-first with a live search across the SQL text, server, and database, shows each entry with where and when it ran, and opens the full statement into a new editor tab with a double-click. Statements you keep returning to can be pinned; pinned entries stay in their own section at the top of the list and survive restarts.

v0.53.0
Aug 23, 2026 · 44 sections
Aug 23, 2026
44 sections

Passwords no longer land in the engine diagnostic log

The engine's request/response correlation log previously recorded each request body verbatim — including connection passwords and access tokens. Every credential value is now scrubbed to a redaction marker before a line is written, while the request id, type, server, username, and full SQL text are preserved, so the log keeps its post-mortem debugging value without ever putting secrets on disk. The general-purpose secret redactor was also rewritten to handle JSON bodies, repeated occurrences, bearer tokens, and hydrated secret references rather than just the first password= pair in a string.

PostgreSQL connections honor encryption, fail closed

PostgreSQL profiles that ask for encryption now actually negotiate TLS instead of connecting unencrypted with a buried warning. Certificates are validated against the standard trusted roots by default, the existing "trust server certificate" toggle covers self-signed development servers, and a connection that cannot establish TLS fails with a clear error rather than silently downgrading to plaintext. Server-side query cancellation uses the same encryption as the query connection, and the behavior is pinned by live tests covering both the successful encrypted path and the fail-closed rejection of an untrusted certificate.

MySQL connections honor encryption, fail closed

MySQL and MariaDB profiles that ask for encryption now negotiate TLS instead of silently connecting unencrypted. Certificates are validated by default, "trust server certificate" covers self-signed development servers, and a server that cannot establish TLS produces a clear connection error rather than a plaintext downgrade — verified live against MySQL 8.4 (encrypted, plus fail-closed rejection of its self-signed certificate) and a TLS-disabled MariaDB 11 (encrypted profiles refused, plaintext profiles unaffected). Server-side query cancellation inherits the same encryption. All TLS across the app now runs on one crypto provider, removing a latent conflict that could have crashed encrypted relay connections at runtime.

Two MySQL data-corruption fixes caught by live conformance runs

Running the driver conformance suite against MySQL 8.4 and MariaDB 11 surfaced two real value-corruption bugs, both now fixed: binary parameter values echoed back through text-typed columns were mangled by lossy text conversion (they now come back byte-for-byte intact), and re-executing a cached prepared statement with a different parameter type on MySQL 8.4 silently corrupted values (a bytes value after a numeric one came back as 0.0) — statement caching is disabled so every parameterized call re-prepares, trading one round trip for correctness.

One credential-resolution boundary, and honest missing-credential errors over relays

Four separate copies of the secret-resolution logic (SQL Server, PostgreSQL, MySQL drivers, and the remote engine serializer) were consolidated into a single shared boundary. The remote path previously turned a missing local credential into an empty password sent to the server — surfacing as a baffling server-side login failure; it now fails immediately with a clear local error naming the missing credential.

Spilled result files are private, crash-orphans get cleaned up, and paging reads faster

Large result sets that spill to disk are now written owner-only (query results are data — they were previously created with default permissions, world-readable under a permissive umask), and the spill directory itself is locked down the same way. Files orphaned by a crashed or killed session — which previously sat on disk forever — are swept on the next launch once they are an hour old. Reading a page of spilled rows now seeks once and streams forward instead of seeking per row, which keeps the read buffer warm across an entire page.

Read-only mode now blocks server-side file and command access

The SELECT-only connection policy previously waved through read-shaped statements that reach the server's file system or shell — OPENROWSET(BULK ...), pg_read_file, COPY ... TO PROGRAM, load_file, load_extension, CALL, and friends. These are now refused with a message naming the artifact and an editor highlight on exactly where it appears. Mentions inside strings and comments stay inert, quoted and bracketed spellings are still caught, and the MySQL executable-comment trick can't smuggle one past the gate.

Test Connection and database listing use the right driver per engine

Testing a connection and listing databases previously spoke the SQL Server protocol no matter what engine the profile targeted, so PostgreSQL, MySQL, and SQLite profiles failed with protocol gibberish. Both operations now route to the native driver for the connection's engine family, engines without a native driver report a clear "not supported" message, and the SQL Server transport joined the same engine-neutral driver contract the other three drivers already shared.

Explorer metadata speaks every engine's native catalog

The queries behind the server explorer and schema intelligence — schemas, tables, views, columns, primary keys, foreign keys, routines, parameters, indexes, triggers, constraints, and types — previously existed only in SQL Server's system-catalog dialect, so other engines couldn't populate the tree or feed completions. Each engine now answers from its own catalog (PostgreSQL via pg_catalog, MySQL/MariaDB via information_schema with database-as-namespace, SQLite via its schema pragmas) in exactly the shapes the app already consumes, with per-engine literal escaping and engine-truthful details like SQLite's implicitly non-null INTEGER PRIMARY KEY. Concepts an engine lacks return an honest empty list or a clear "not supported" — never SQL Server SQL fired at a foreign server. Verified by a shared behavioral matrix run hermetically on SQLite and live against PostgreSQL 16, MySQL 8.4, and MariaDB 11.

Completion snapshots fold identifiers with the connected engine's rules

Schema snapshots built from live metadata previously always used SQL Server's identifier rules regardless of the connected engine, which would mis-fold quoted and case-sensitive names on PostgreSQL, MySQL, and SQLite now that those engines feed real metadata. The snapshot builder and the incremental column/parameter merge paths now derive the dialect from the connection's engine family, so lookups and completions fold names the way the target server does.

Live schema collection from SQL Server, PostgreSQL, MySQL/MariaDB, and SQLite

The schema intelligence engine could previously collect a live database model only from SQLite; the other three engines are now first-class sources, closing the long-declared architecture gap. One shared collector reads any engine through the same dialect-dispatched catalog queries the explorer uses — so the explorer and the model can never disagree about a schema — while each driver contributes only a thin pooled connection that runs SQL and returns rows. Tables arrive with ordinal columns, primary keys, and regrouped composite foreign keys; views with their columns; routines with their kinds; and a source that partially fails is marked incomplete rather than published as authoritative. A conformance matrix drives the identical fixture through the full engine pipeline on every dialect: hermetically on SQLite and live against PostgreSQL 16, MySQL 8.4, MariaDB 11, and SQL Server 2025.

MySQL queries reuse warm connections per tab

MySQL and MariaDB connections join the same warm-connection machinery SQL Server and PostgreSQL already had: each tab keeps its server connection between runs (preserving temp tables and session variables and skipping the reconnect cost), an errored or cancelled connection is never reused, a stale idle connection retries once on a fresh one only when provably nothing reached the server, and closing or editing a connection evicts its warm clients. Verified live: sequential runs in one tab keep the same server connection id while a second tab gets its own.

Execution plans for PostgreSQL, MySQL/MariaDB, and SQLite

The Explain button (⌘L) now works on every supported engine, not just SQL Server. Each engine's own plan format — PostgreSQL's analyzed JSON plans with real row counts, MySQL/MariaDB's optimizer JSON, SQLite's query-plan steps — is parsed into the same plan pane with the same cost, row, and operator detail, and plan findings carry over: a full table scan is called out by name on every engine. Malformed plan documents produce typed parse errors, never crashes. Verified end-to-end against live PostgreSQL 16 and MySQL 8.4 and hermetically on SQLite, with real captured outputs pinned as parser fixtures.

Remote sessions are bounded against hostile or broken peers

Two memory-exhaustion vectors in remote engine sessions are closed. The protocol client's cross-request frame buffer — which a peer could grow without limit by addressing frames to request ids that were never issued — now enforces id and byte caps with stalest-first eviction, and its memory of completed requests is capped too. The session demultiplexer's per-request queues — previously unbounded so a peer ignoring flow control could flood one request's stream — are now bounded: a flooded request's stream is closed with a clear terminal error while every other request on the session keeps streaming, and the reader never blocks. Both bounds are pinned by hostile-peer tests (unknown-id floods, oversized-frame floods, per-route floods alongside a healthy request).

Relay credentials are single-use, and relays can pin their issuer

A captured app relay credential is now worthless after its first use: apps fetch a fresh credential for every session, so the relay treats a repeat presentation as a replay and rejects it, while bridge credentials keep their by-design ability to serve several concurrent sessions and reconnects. Relay operators can also pin exactly which sign-in service a relay accepts credentials from (issuer pinning, opt-in per deployment), so signing material shared across environments can never cross between them. The replay cache retires entries as their credentials expire and is hard-capped. The remote-relay status document was also brought up to date — headless bridge secret storage shipped some releases ago and is now recorded as resolved.

One TLS crypto provider everywhere

All HTTPS clients across the app, engine, bridge, and relay now install the same single TLS crypto provider at startup. This completes the provider unification begun with the database TLS work and removes a class of startup crash where a client could be built before any provider was selected.

One codec for every engine request layout

The binary layouts of the engine's core operations — request headers, ping/shutdown/cancel, query execution, catalog sync (including its allocation-bomb guards), and row batches — previously existed as hand-mirrored encoder/decoder pairs in two different components, annotated "must match byte-for-byte". Each layout is now encoded and decoded by a single protocol-owned implementation with round-trip and version-mismatch tests, so the pair can no longer drift; the engine's performance-tuned row encoder is pinned byte-for-byte against the protocol reference by a tripwire test. Real-subprocess round-trip suites confirm nothing changed on the wire.

Row editing works over relay connections

Editing table rows — double-click edits, inserts, deletes, foreign-key autocomplete, and related-row browsing — previously reported "not available over a relay connection". Every operation now travels through the secure relay session to the bridge and executes with exactly the same guarantees as a local edit: rows identified only by real keys, optimistic conflict detection when someone else changed the row first, and transactional single-row statements. Proven end-to-end against a live SQL Server through a real engine session: open, guarded update, stale-edit conflict, insert, fetch, and delete.

Row edits no longer trip SQL Server's transaction-count check

The guarded update/insert/delete path opened and committed its transaction through the parameterized RPC path, which SQL Server rejects with "Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements" — surfacing as a failed edit on live servers. Transaction-control statements now run as plain batches, and the whole edit lifecycle is covered by a live end-to-end test.

Schema-collection connections hand back instantly, plus a hygiene sweep

The schema engine's metadata connections previously returned to their pool through a background task, so an immediately following read could miss the idle connection and dial a fresh one; the hand-back is synchronous now, and the pool's own test proves back-to-back reads reuse one connection with no waiting. Housekeeping alongside: five more library crates now forbid unsafe code outright (the one intentional unsafe block — a cryptographic key wipe — is explicitly scoped), the catalog's vector-search interface uses the crate's typed errors instead of a bolted-on generic error type, and a workspace-wide dead-code exemption that no longer covered anything real was removed.

Consistent browser-preview messaging

The last few browser-limitation messages that said "browser" or "browser build" without pointing anywhere now use the same shape as everywhere else: "not available in the browser preview — use the desktop app" (folder reveal, relay connections, store sign-in). An audit confirmed every remaining placeholder surface is honest: the nine former admin-tool placeholders are all real tools now, and the AI Playground pane explicitly explains its status rather than showing dead controls.

Find/replace and folding proven to cooperate

Two new acceptance tests pin the interaction between the shipped find/replace bar and the new code folding: navigating to a find match hidden inside a collapsed region automatically reopens that fold on the next paint, and a replace-all that destroys a folded region (for example replacing away its END) reconciles fold state instead of leaving live lines hidden. Existing coverage already validated Unicode replacement, single-undo replace-all, match wrapping, and bounded large-script scans.

Performance smoke guardrails

Four hot paths gained always-on performance smoke tests with deliberately generous bounds (they catch accidental quadratic blowups, not percent-level regressions): command-palette search over the full registry, schema-tree filtering across a 9,000-node tree, fold-region scanning of a near-cap buffer, and current-statement span scanning of a 400KB script.

Test coverage for previously untested UI modules

The sidebar, version-history panel, DDL viewer, entity management, and store sign-in modules — previously carrying zero tests — gained focused unit tests over their pure logic (grouping labels, timestamp formatting, viewer states, entity/safety tables, registration URLs), and the AI panel gained a full suite earlier in this release. The sign-in panel's registration URL was extracted into a pure helper to make it testable.

Dead-code cleanup: twelve orphan modules deleted

Twelve source files inherited from the Swift-era port (azure_auth, azure_overrides, biometric, intellisense_error_log, keychain_status, local_ai, metadata_lifecycle, playground_engine, project_files, schema_gating, server_error_parser, table_reference — 2,439 lines) were never compiled into the app and referenced nothing that runs; they are now deleted, with git history preserving them. The orphan-module hygiene guard's legacy allowlist is empty, so any future undeclared source file fails CI immediately.

Run Current Statement and Include Statistics toggles

Two more query-toolbar toggles came back to life: Run Current Statement executes only the statement under the caret (split on top-level semicolons and GO lines, string- and comment-aware; a selection still wins, and undelimited scripts run whole), and Include Statistics wraps SQL Server / Azure SQL runs in SET STATISTICS TIME, IO so timings and logical reads land in the Messages pane. Both were previously hidden because they did nothing.

Table, index, and foreign-key designers

Tools > Designers now opens real guided builders instead of printing sample DDL: design a table with dynamic column rows (type, NULL, identity, and primary-key options), an index with key/include columns and a unique flag, or a foreign key between two tables — each with a live preview of the exact statement your choices produce. Script to Editor puts the DDL in a query tab for review; nothing executes from the dialog. This completes the set: all nine Tools-menu placeholders are now real, working tools.

Extended Events browser

Tools > Extended Events now lists real event sessions with their startup and running state — server-scoped on SQL Server, automatically falling back to database-scoped sessions on Azure SQL Database (and labeling which you're seeing). Select a session to inspect its events and targets side by side, and Script Start / Script Stop put the ALTER EVENT SESSION statement into a query tab for review instead of running it directly.

Schema Compare between databases

Tools > Schema Compare now performs a real comparison instead of printing a sample diff: pick two databases on the connected server, and views, procedures, functions, and triggers are diffed by definition while tables are diffed by column signature. Differences show in a grid, and the migration script — which annotates every change and never auto-drops objects — opens in a query tab for review.

CSV import wizard

Tools > Import / Export is now a real wizard: pick a CSV file, choose the delimiter and header handling, preview the parsed rows in a grid, name the target table, and script the import as per-row INSERTs (with a clearly-announced row cap for big files) or a server-side BULK INSERT. Scripts open in a query tab for review — nothing executes from the dialog — and the export path scripts a SELECT and hands off to File > Save Results, which already writes every supported format.

SQL Agent browser

Tools > SQL Agent now opens a read-only Agent browser instead of printing a sample query: Jobs, History, Schedules, Operators, and Alerts each load from msdb into live grids. The pane first checks that the server actually has SQL Agent — Azure SQL Database doesn't, and the pane says so (pointing at Elastic Jobs) instead of showing empty grids — and msdb permission problems are explained in terms of the SQLAgent roles they usually mean.

Query Store viewer

Tools > Query Store now detects whether Query Store is enabled for the connected database and shows its top queries by average duration in a live grid — select a row and open that query's full SQL in a new editor tab. When the store is off, the pane offers to script the enabling ALTER DATABASE statement; when it's unsupported or permission is missing, it explains why instead of showing sample text.

Template Explorer with parameter substitution

Tools > Template Explorer now opens a real template browser instead of printing a template list: script templates for tables, views, indexes, functions, stored procedures, triggers, and schemas are organized by category with a filter box, each template presents a form for its parameters, a live preview shows the exact SQL your values produce, and Insert into Editor opens the finished script in a new query tab. The built-in catalog also grew from two templates to eight.

Activity Monitor is live

Tools > Activity Monitor now opens a real monitoring pane instead of printing a sample query: Sessions (with current SQL), Blocking chains, Expensive Queries, and Wait statistics each render in a live grid that auto-refreshes every five seconds while open. Row counts are bounded, only one refresh runs at a time, closing the pane stops all polling, and permission problems (like missing VIEW SERVER STATE) are explained in plain language.

Backup / Restore is a real dialog now

Tools > Backup / Restore opens an actual dialog instead of printing sample SQL: it lists the server's databases live, offers full/differential/log backup with compression, checksum, and copy-only options (or restore with REPLACE/RECOVERY), previews the exact T-SQL, and scripts it into a new editor tab for review — nothing executes from the dialog itself.

Save Results in any format, not just CSV

File > Save Results (previously "Save Results as CSV") now honors the file extension you pick in the save dialog: .json, .xlsx, .md, .html, .xml, .tsv, .sql, and the code-generation formats all work, with Excel files written as real binary workbooks. The suggested filename follows the first format enabled in your Export preferences.

Filter Data works from the explorer

Right-clicking a table or view and choosing Filter Data now opens a new query tab containing a SELECT TOP 1000 with a commented, type-aware predicate suggestion for every column (LIKE for text, range comparisons for dates, equality elsewhere) — uncomment, fill in values, and run. The query opens against the table's own server and database and never modifies your current editor. The menu item was previously disabled.

Run procedures and functions straight from the explorer

Right-clicking a stored procedure and choosing Run now opens a new query tab with a ready-to-edit EXEC statement — including typed placeholder values for each parameter when IntelliSense knows the procedure — targeted at the procedure's own server and database. Copy EXEC Script puts the same script on the clipboard, and functions get an invocation scaffold with both table-valued and scalar forms. These menu items previously showed "not yet available".

Command palette now covers the whole menu bar

The command palette grew from about 20 commands to full menu-bar coverage: file open/save, new window, themes, zoom, results pane, schema search, admin tools (Backup/Restore, Activity Monitor, SQL Agent, Security, Designers, Import/Export, Schema Compare, Query Store, Extended Events, Template Explorer), connection management, external editor, tab cycling, and help. Rows now show each command's current keyboard shortcut, an explicit "No matching commands" state replaces the silent empty list, keyboard navigation scrolls the selection into view, and a new test guarantees every palette entry actually dispatches to a handler.

Help window now shows your real shortcuts

The Help window's keyboard-shortcut listing is now generated from the live command registry and your actual keybinding configuration — including any customizations from Preferences > Keybindings — instead of a hardcoded list that had drifted from reality. It also gained a SQL Editor section covering folding, find/replace, formatting, completions, and AI suggestion shortcuts.

Explorer AI actions now open the AI panel

Right-clicking an object in the server explorer and choosing Ask AI, Generate SQL, Find Join Path, or Pin to AI Context now opens the AI panel on the matching lane, pre-seeded with the object's schema-qualified name and targeting the object's own server and database (not whatever tab happens to be active). Pinned objects appear as removable chips above the chat input and are automatically included as grounding context in every prompt. Previously these menu items only showed a "not yet available" status message.

AI panel inputs actually work now

The AI panel's four text fields (Generate SQL prompt, Ask Schema, schema search, and the join-path From/To inputs) were placeholder boxes that could not accept typing; they are now real input controls with placeholder text, Enter-to-submit, and buttons that disable while a request is running. Each lane also tracks its requests independently, so a slow schema search can no longer overwrite a newer chat answer (or vice versa), and switching connections cancels everything in flight.

Code folding in the SQL editor

The editor now folds BEGIN…END blocks (including TRY/CATCH), CASE expressions, multi-line subqueries and parenthesized groups, multi-line comments, and GO batches. Chevrons in the line-number gutter collapse and expand each region, collapsed headers show a dim ⋯ chip, and Fold/Unfold/Fold All/Unfold All keyboard shortcuts work at the cursor. Folding is display-only — running, copying, searching, and editing always see the full script — and moving the cursor into a hidden line reopens its fold automatically.

Inline AI ghost-text completion is now fully hooked up

The AI autocomplete sparkle toggle on the query toolbar is back and now genuinely controls inline fill-in-the-middle suggestions per tab, on top of the global preference switch. The manual trigger mode preference is now honored — in manual mode suggestions only appear on demand via Alt+\ (which also works in automatic mode), and new tabs respect the "enable for new queries" preference. Suggestions are also suppressed while text is selected or the completion popup is open, so ghost text never fights the IntelliSense popup.

v0.52.0
Aug 23, 2026 · 13 sections
Aug 23, 2026
13 sections

SELECT-only policy can no longer be bypassed with MySQL executable comments

MySQL (and MariaDB) actually execute SQL hidden inside version-gated comments like /*!50003 DROP TABLE t */, but the safety classifier treated every block comment as inert — so a mutation smuggled that way sailed through a SELECT-only connection policy and could also slip DDL past the rollback wrapper's implicit-commit protection. The classifier now conservatively sees the payload of executable comments on every backend (ordinary comments, optimizer hints, and comment-looking text inside strings are untouched), blocking hidden mutations with an accurate highlight span. The architecture decision record for lexer ownership was amended to record this fail-closed rule.

The invariant tokenizer no longer allocates per word

The dialect-invariant tokenizer behind safety checks, parameter detection, and paren diagnostics allocated an uppercased copy of every word token just to test keyword membership. It now case-folds into a stack buffer against prebuilt lookup sets, making raw tokenization about 24% faster (197µs to 150µs on a 250-table script). A new raw-tokenize benchmark isolates this path so future scanner regressions are visible, and a benchmark-notes document now records which bench every published performance number came from.

Parameter hint comments are no longer confused by dashes inside strings

Default and lookup hints (-- name = expr, -- {table} {col}) were found by scanning each line for the first --, so a -- inside a string literal hijacked the line and silently swallowed a real hint after it, and hint-looking text inside block comments was misread as a hint. Hint parsing now walks the tokenizer's actual comment tokens, which also keeps hint highlight offsets correct in files with Windows line endings.

Orphan source files in the app can no longer masquerade as shipped

A new hygiene test fails the build when a source file in the app is never declared as a module — the failure mode where code (and its tests) silently never compile while looking present in the tree. Known legacy orphans from the porting era are pinned in an explicit burn-down list that only shrinks.

Cancelling a query now stops it on the server for SQL Server and MySQL too

Cancel already told PostgreSQL and SQLite to stop the running statement server-side; SQL Server and MySQL runs kept executing invisibly after a cancel. MySQL now gets a KILL QUERY from a short side connection (works for your own connections, no extra privilege), and SQL Server gets a KILL of the session (best-effort — it needs the KILL permission, and quietly falls back to client-side cancel without it; the session was discarded after a cancelled run anyway). On SQL Server the session-id probe doubles as a liveness check for warm pooled connections, catching stale ones before any of your SQL runs. Verified live against a real SQL Server: a cancelled 30-second wait ends in under a second.

Remote protocol hardened against hostile or corrupted frame streams

A new chaos suite drives the remote-engine frame path through malformed frame bodies, frames addressed to unknown requests, duplicate terminal frames, both orders of the cancel/completion race, mid-frame disconnects, and oversized length prefixes. Every scenario now has a pinned guarantee: no panic, no hang, exactly one terminal event per query, and a poisoned request never takes down the rest of the session.

Go to Definition and Find References in external editors

The SQLLY language server now answers go-to-definition and find-all-references: definition jumps to the CREATE [OR ALTER] statement declaring the object under the cursor (across all open documents), and references lists every occurrence of that name — recognizing bare, bracketed, double-quoted, and backticked spellings as the same identifier. Both work purely on script text with no database connection required.

Test hardening from the release review

The SQLite vector store's unsupported-filter error path, the persistent IntelliSense cache's compatibility with the new function-subtype object kinds (old caches load, unknown future kinds are treated as a clean miss), and a raw-tokenize benchmark isolating the invariant scanner all gained direct regression coverage.

The language server now validates in your database's dialect, not always T-SQL

External editors telling the SQLLY language server which engine they're connected to had no way to say so — the connection request silently assumed SQL Server, so diagnostics, completion syntax, and safety semantics were T-SQL no matter what you were actually running. The connection request now accepts the engine name (with each engine's conventional port filled in automatically), rejects unknown names loudly instead of guessing, and cancelled requests are now skipped before their validation work runs rather than computing a result nobody will see.

Editor keyword highlighting survives emoji, accents, and extra spaces

Semantic keyword highlights from the language server drifted out of place on lines containing multibyte characters, tabs, or runs of spaces, because columns were counted in bytes with an assumed single space between words. Positions are now counted in the encoding editors actually use, and keywords straight against punctuation (like FROM() highlight too.

SQL Server joined the driver conformance matrix

The shared driver-behavior suite (streaming order, multi-result batches, parameter round-trips, error surfacing, affected-row counts) now runs against live SQL Server too — previously the harness docs named the TDS driver but no such test existed. Writing it immediately caught a real cross-engine difference: temp tables created inside parameterized batches vanish at statement scope on SQL Server, unlike PostgreSQL and MySQL. The remote-cancel regression test was also tightened to demand exactly one Cancelled outcome instead of accepting any terminal state, and a vendor-directive corpus pins that DELIMITER scripts, SQL-mode switches, and quote-mode lookalikes can't slip mutations past the SELECT-only policy.

Architecture rules and performance floors are now enforced by tests

A mechanical tripwire fails the build if the safety classifier ever gains a dependency on the dialect-aware lexer or a dialect parameter (the merge the lexer-ownership decision forbids), and coarse latency-budget tests for parsing, slot resolution, and completion catch order-of-magnitude regressions in plain test runs without noisy benchmark gates. Duplicate Swift-era architecture decision records that collided with current numbering moved to a legacy folder with their successors mapped.

Live-database driver tests run on a weekly schedule

A new opt-in CI workflow stands up real PostgreSQL, MySQL, and SQL Server servers weekly (and on demand) and runs the driver conformance suites against them — and it fails outright if any test self-skips, so a broken environment can never quietly report green while testing nothing.

v0.51.2
Aug 23, 2026 · 1 section
Aug 23, 2026
1 section

Release pipeline: h2 advisory fix, take two

The v0.51.1 release still failed its Linux dependency audit: the h2 upgrade that clears RUSTSEC-2026-0258 had been silently undone before it was committed, because the release script's failure rollback restores the whole lockfile — discarding any uncommitted dependency updates along with the version bump it means to revert. The h2 0.4.18 upgrade is now actually committed, and the audit passes.

v0.51.1
Aug 23, 2026 · 1 section
Aug 23, 2026
1 section

Release pipeline: dependency advisory and new-toolchain lint fixes

The v0.51.0 release run failed on the Linux leg. Two causes, both fixed: the h2 HTTP/2 dependency was raised to 0.4.18 to clear RUSTSEC-2026-0258 (unbounded empty DATA frames, low severity — flagged by the release audit), and three byte-decoding helpers now carry an explicit allowance for clippy 1.98's new chunks_exact lint, whose suggested replacement needs a newer Rust than the workspace's declared minimum. Verified on both the current stable toolchain (Linux container and macOS: full test suite, lint gate, dependency audit) and the local toolchain.

v0.51.0
Aug 23, 2026 · 2 sections
Aug 23, 2026
2 sections

Find and Replace in the editor

The SQL editor now has a real find/replace bar: Cmd+F (Ctrl+F on Windows/Linux) opens it, Cmd+H / Ctrl+H opens it with the replace row showing, and it seeds itself from your current selection. Every match is highlighted as you type with a live "N of M" count, Enter and Shift+Enter step through matches with wrap-around, and case-sensitive and whole-word toggles refine the search. Replace steps through matches one at a time, and Replace All lands as a single undoable edit, so one undo restores the whole document. Matching is capped and debounced so even very large scripts stay responsive, and all the keys are remappable from the keyboard-shortcuts editor.

Split Editor (two panes)

View ▸ Split Editor Vertically / Horizontally now actually splits the workspace: the active query is cloned into a second pane, and each pane is a full editor-plus-results stack showing its own tab. Click a pane to focus it — menus, shortcuts, and the query toolbar follow the focused pane — drag the divider to resize, and the split (including divider position) is restored after a restart. Closing a tab a pane depends on, or choosing View ▸ Close Split Editor, returns to a single pane; the same commands are available from the command palette.

v0.50.0
Aug 23, 2026 · 22 sections
Aug 23, 2026
22 sections

Lexer ownership decision: no merge, dialect invariance guaranteed

Reviewed and decided the long-open question of merging the SQLLY lexer stack. The dialect-aware lexer (sqlly-sqllex) and the dialect-invariant tokenizer (sqlly-tokenizer) stay separate by design: safety classification, parameter scanning, and paren diagnostics must behave identically no matter which database SQLLY is connected to, and a merged lexer cannot guarantee that (the same text can legitimately tokenize differently per dialect). Recorded as ADR-016; ADR-011 flipped to Accepted with its stale "tokenizer retired" consequence revised. The unused tokenizer "Full mode" placeholder API was removed, and tripwire tests now pin the dialect-invariant behavior (temp-table names, # comments, dollar-quoted bodies, backticks) so a future merge attempt fails loudly instead of silently changing safety behavior per backend.

SQL parsing is ~32% faster: statements are lexed once, not twice

The parser used to tokenize a script once to find statement boundaries and then tokenize every statement again to parse it. The splitter now hands the parser each statement's tokens directly from a single pass over the document, cutting parse time on a 300-statement script by about a third (919µs to 621µs in the benchmark). Statement splitting behavior (GO batches, routine bodies, MySQL DELIMITER scripts) and every span in the output are unchanged, verified by equivalence tests and the full parser/model/catalog suites.

Slot resolution stopped re-lexing every statement

Value-slot scanning (the machinery behind typed value completion and slot validation) used to re-tokenize each INSERT/UPDATE/SELECT statement on every pass. It now lexes the document once and hands each statement its token range — the exact tokens the parser consumed — so slot spans and parse-tree spans can never drift apart, and repeated per-keystroke slot lookups do less work.

SQL lexing is ~60% faster: keyword lookup no longer allocates

Every word token used to allocate a lowercased copy of itself just to check whether it was a keyword. Keyword lookup now case-folds into a stack buffer and hits a prebuilt hash set, cutting raw lexer time from 434µs to 176µs on the 500-statement benchmark. Together with the single-pass split, end-to-end parsing is now twice as fast as before this session (919µs to 459µs).

Fixed a crash when MySQL scripts contain non-ASCII text

The MySQL statement splitter walked the script byte by byte and could slice mid-character, crashing the engine on any accented or non-Latin character outside a string literal (for example SELECT * FROM tabellé). Found by the new property-test suite; the delimiter scan now matches on bytes, which is always character-boundary safe.

Property tests now prove the SQL pipeline never panics

Added a proptest suite over the lexer, parser, splitter, and slot resolution: arbitrary input (including malformed and non-ASCII text, unterminated strings/comments, 100,000-deep paren nesting, and 5,000-statement scripts) must never panic or hang, tokens must exactly tile the input, and all emitted spans must stay inside the source. This is the safety contract for editor-fed SQL; the parser needs no recursion depth guard because it has no recursive descent cycles.

IntelliSense completion scoring is up to 2x faster

Fuzzy candidate scoring allocated several lowercased strings per candidate on every keystroke. Scoring now case-folds in place with no per-candidate string building (in both the completion engine and the model engine), cutting table-name completion from 448µs to 223µs and keyword completion from 985µs to 432µs on a 1,000-table catalog, and model-engine symbol completion from 1.55ms to 0.95ms on 2,000 tables. Ranking behavior is unchanged and covered by the existing scoring tests.

Symbol completion only sorts the results it returns

Model-engine symbol completion sorted every scored candidate before cutting the list to 200. It now partitions the top page out in linear time and sorts just that page — another 20% off large-catalog completion (1.55ms before this session, 0.77ms now on 2,000 tables). Ordering is byte-identical, including tie-heavy catalogs, pinned by a new determinism test.

Unqualified name lookup is indexed

Resolving a bare object name (no schema qualifier) against the data model scanned every name in the index on each lookup. The model now keeps a dedicated bare-name index — candidate lists are found directly, ambiguity reporting is unchanged, and the result order is now deterministic instead of hash-map order. The index is rebuilt when a cached data model loads, so the on-disk cache format is unchanged.

Completions now know scalar, table-valued, and aggregate functions apart

Function suggestions previously treated every function as scalar. The data model now carries each function's real kind end to end: table-valued functions are offered as row sources after FROM and JOIN (and validate as legal table sources), while scalar and aggregate functions stay in expression positions. Functions defined in project .sql files are recognized as table-valued from their RETURNS TABLE / RETURNS SETOF shape.

Objects defined in project .sql files complete as file-sourced objects

When the data model is fed from project files, tables, views, procedures, and functions defined in those files now surface in autocompletion as file-defined objects (with their defining file attached) instead of being mixed in as generic database objects. This closes the long-standing gap where file-driven models produced no filesystem-aware suggestions.

Engine response encoding unified onto the shared protocol writer

The engine's response frames were hand-encoded with raw byte pushes while the shared protocol crate already provided the canonical writer. The response body encoders now use the shared writer (the streaming rows path keeps its size-aware fast path), removing the last duplicated encoding logic; the wire bytes are unchanged, proven by the existing byte-layout and subprocess protocol tests.

One shared selector for local AI providers

The rules for choosing the local AI providers (Ollama, MLX, or the built-in deterministic fallback) lived only inside the engine subprocess, so anything constructing services directly always ran on the fallback. The selection logic — including model configuration, graceful fallback, and the "you asked for MLX but it isn't available" notes — now lives in the shared services layer, used by the engine and available to every other consumer, so provider behavior can never drift between paths.

Test-suite hardening: one safe door for environment variables

Tests across six crates each hand-rolled their own locking (or none) around process environment changes, which can race under the parallel test runner and leak values between tests. A single shared guard now serializes every environment change and automatically restores prior values, and all test sites were migrated onto it. The test toolkit also dropped four source files that were never compiled into the crate.

Database drivers now share one conformance test matrix

Added a shared driver conformance harness: the same behavioral checks — streaming order and multi-result batches, parameter round-trips across every value type, error surfacing without wedging the connection, and affected-row reporting — now run identically against SQLite on every test run and against live PostgreSQL/MySQL servers when configured. Behavioral drift between database backends now fails a test instead of surfacing as a bug report.

Cancelling a query now stops it on the server too (PostgreSQL, SQLite)

Cancel used to stop delivering results but left the statement running on the server — a runaway query kept burning server resources invisibly. On PostgreSQL, cancel now sends the server's native cancel request; on SQLite, it interrupts the executing statement immediately. Verified by tests that interrupt an infinite query and see it end within milliseconds. SQL Server and MySQL keep the existing stop-receiving behavior for now, with the plumbing in place to add their server-side cancels next.

Warm connections for PostgreSQL

PostgreSQL queries used to open a fresh connection on every run. Each query tab now keeps its PostgreSQL connection warm and reuses it — repeat queries skip the whole connect/auth handshake, and tab sessions gain continuity (temporary tables and open transactions survive across runs, matching SQL Server tabs). Stale idle connections retire quietly and retry on a fresh one, never surfacing as an error, and cancelled or failed runs always discard the connection instead of reusing it.

Fixed schema-search vector filters that could never match

Filtering vector search results by object kind compared the kind against the object's full name, so any kind filter returned nothing, and the result count ignored its filter entirely. Kind filters now match the catalog's real object kinds, counts honor the same filter, and filter dimensions the local store cannot answer are rejected with a clear error instead of silently matching everything.

Schema-search performance is now benchmarked

Added benchmarks for catalog text search, vector similarity search, and hybrid ranking at 100/1,000/5,000-object catalog sizes. The baseline shows brute-force vector search costs under a millisecond even at 5,000 objects, confirming the current approach comfortably covers supported catalog sizes and giving future scaling work hard numbers to justify itself against.

Language server correctness: no stale squiggles, real error positions, cancellation

Four fixes to the SQL language server. Diagnostics are now version-tracked and debounced: a burst of edits validates once against the final text, and a slow validation can never paint stale squiggles over newer code. Diagnostics land on the actual offending token (line and column, correct even with non-ASCII text) instead of always pointing at the top of the file, and validation runs in the connected database's own dialect instead of assuming SQL Server. Editors can now cancel in-flight requests, and edits that arrive bundled are all applied instead of only the first.

Fixed: cancelling a remote query could hang the results stream forever

New remote-engine parity tests (streaming, catalog search, error decoding, cancellation against a real engine process) uncovered that cancelling a query over the remote connection never delivered the "cancelled" answer to the right listener — the results stream stayed open until the whole connection dropped. Cancellation confirmations are now delivered to the query they cancelled, so a cancelled remote query ends promptly with an honest "cancelled" status.

Workspace hygiene: dead code removed, one source of truth for shared values

Removed the vestigial FFI crate left over from the retired Swift integration (nothing referenced it) and a never-compiled legacy driver file whose "cancel" did not actually cancel anything. The row-streaming cadence constants that were duplicated between the driver contract and the SQL Server driver now come from one place, and stale comments claiming the wire protocol was "mirrored by the Swift client" (or that real AI providers were "a later phase") now describe reality.

v0.49.0
Aug 21, 2026 · 5 sections
Aug 21, 2026
5 sections

Connection safety settings: no more false "unbounded UPDATE/DELETE" triggers on procedure scripts

Full review and overhaul of the per-connection safety checks. Deploying a CREATE/ALTER PROCEDURE, FUNCTION, or TRIGGER no longer trips the unbounded UPDATE/DELETE confirmation or the editor's "unconstrained operation" diagnostics — a definition's body is deployed, not executed. The statement splitter now keeps a T-SQL routine definition intact through the semicolons in its body (previously body statements after the first ; were analyzed as free-standing DML). The mutation scanner also stops false-flagging FK cascade actions (ON DELETE/UPDATE CASCADE), MERGE WHEN … THEN UPDATE/DELETE clauses, SELECT … FOR UPDATE, upsert clauses, trigger UPDATE(col) tests, and #temp/@table targets — and it now correctly catches unbounded statements whose only WHERE lives in a subquery or a following statement.

Editor validation no longer errors on multi-statement scripts

Script buffers with multiple statements were flagged "expected a single statement" as a blocking error by editor and LSP diagnostics. Multi-statement buffers are now validated as scripts; the single-statement rule still guards the AI SQL-generation flow.

Rollback-wrap connections can now deploy procedures, and refuse what MySQL can't undo

With "wrap execution in transaction + rollback" on, CREATE/ALTER PROCEDURE/FUNCTION/TRIGGER/VIEW batches used to fail with a server error (these statements must lead their batch); they now run through EXEC inside the transaction, so the deploy executes and still rolls back. On MySQL, DDL implicitly commits — instead of silently persisting inside the wrap, such statements are now refused with a clear explanation (temporary tables remain allowed).

Safety settings survive the inline connection editor

Saving a profile in the inline connection form no longer silently resets "confirm data changes", "skip confirmation for unbounded mutations", engine type, routing, or provenance/status — fields the form does not expose are carried forward from the saved profile.

Connection error summaries: crash fix in secret redaction

Redacting multiple secret-like tokens (password/pwd/token/…) from a driver error could mangle the text or panic when an earlier replacement shifted offsets; redaction now re-locates each key in the updated text and catches repeated keys.

v0.48.0
Aug 16, 2026 – Aug 17, 2026 · 12 sections
Aug 17, 2026
9 sections

Pricing page readability and accessibility overhaul

The pricing page no longer goes blank for visitors with JavaScript disabled: the reveal-on-scroll styling is now correctly gated so content is visible by default and animation is purely an enhancement. Several small-text elements on the page (plan tags, plan subtitles, renewal pills, eyebrows, terms markers, and timeline stamps) now meet WCAG AA contrast in both light and dark mode, and inline links in dark mode are legible across the whole site. The site theme toggle also gained proper accessible labels and a full 44px touch target on mobile.

Pricing page drops vague urgency from the Lifetime card

The Lifetime plan no longer carries a "limited-time offer" tag with no stated end — the scarcity claim contradicted the page's own "zero ways to get got" promise. The card now simply states what buyers get: no renewals, no expiry dates, no clocks.

Pricing page licensing section made scannable, page easier to navigate

The "whose laptop does the license cover" section is now two short, parallel cases — you bought it, or your company bought it — followed by a single highlighted rule for business use, instead of two dense paragraphs. The hero also gained quick jump links to the plans, Cloud, and FAQ sections, the nag-policy section now stands out with a tinted backdrop, and signed-in visitors see a link to the relay design from the coming-soon Cloud cards instead of a dead-end note.

Home page copy clarified end to end

The home page headline now carries the actual message — "SQL your databases will thank you for" replaces a bare "SQLly" that merely repeated the nav logo, with the pronunciation gag kept right beside it. Jargon like "SQL RDBMS tool" gave way to plain "SQL client", a muddled humans-pig-AI credit line now reads cleanly, and the "built by people who care" card was corrected to the honest one-person story. The feedback form's success message and the security card now use active voice, heading casing is consistent across the feature cards, and em dashes were swapped for colons, commas, and parentheses across the page's copy and social-preview meta tags.

Marketing pages now share one style foundation

About a dozen page-specific stylesheets (home, features, changelog, cloud relay, on-device AI, about, terms, privacy, licenses, 404) each carried a stale copy of the site's shared style rules, and the copies had drifted out of sync with the fixed shared versions. The duplicated rules are removed so every page inherits the same buttons, links, and labels — which also repairs dark-mode link legibility and small-text contrast on several of those pages. Pages with deliberate, documented custom themes keep their own look.

Pricing page shows the free tier beside the paid plans, and Lifetime's break-even

The plans section now opens with a slim $0 "Free forever" strip so comparison shoppers see the free tier alongside the paid options instead of hunting for it, and the Lifetime card states the math plainly: at $300 versus $100 then $50 a year, it pays for itself in year five.

Free tier re-aimed at hobbyists with two concrete limits

The free tier is no longer "the full app for everyone" — it is now explicitly built for hobbyists and side projects, with two stated limits: you can save up to two servers (with unlimited databases on each) and connect one RDS IAM or Azure cloud account. Any paid license lifts both limits, and the Annual and Lifetime cards now list unlimited servers and cloud accounts as paid features. The pricing page, billing page, terms of service, and plan catalog all tell the same story, the FAQ gained a "what are the free tier's limits" answer (also in the page's structured data), and the standing rule that organizations over 100 employees may not use the free tier is unchanged. Payment reminders for unlicensed installs continue exactly as before.

"Guides" nav link now lands on a real guides hub

The top nav's Guides link used to drop visitors straight into the first of three persona guides with no sign the other two existed. It now lands on a new guides page that lays out all three — Data Analyst, DBA & Power Users, and the engineering deep dive — with who each one is for, so visitors pick the guide that matches them. The link was updated in the desktop and mobile menus of every page on the site, including the templates that generate the changelog and licenses pages.

Manage the IntelliSense schema cache from Settings

SQLLY keeps an on-disk copy of each database's schema so IntelliSense and the server explorer are ready the instant you reconnect, instead of waiting on a fresh metadata pull. A new "IntelliSense Cache" section in Settings now makes that cache visible and manageable: a button reveals the cache folder in Finder (Explorer on Windows, the file manager on Linux), and every cached database is listed under its server with the size it takes up and when it was last refreshed, along with a running total for all of them. Each database has a Clear button, plus a Clear all that asks for a second click before wiping everything. Clearing a cache reclaims its space and simply means the next time you open that database, SQLLY rebuilds its schema from a fresh server pull — the database you're actively working in keeps its IntelliSense the whole time.

Aug 16, 2026
3 sections

Features page layout redesign

The marketing features page traded its uniform two-column card grid for an asymmetric six-column layout: cards now span by content weight so marquee features (execution plans, identity gates, the row editor) get full-width treatment while small features cluster together. Group headers are left-aligned with spec-sheet index numerals, the hero is left-aligned with the badge legend as a side panel, and icon tiles were replaced by a clean meta row (glyph, tag, status). No content changed, only layout and rhythm.

Identity gating and WASM copy clarified on the features page

The former Touch ID section now presents the feature as OS-native secure identity verification (Touch ID on macOS, Windows Hello on Windows, or the platform equivalent) instead of a Mac-only biometric story, and all section copy, examples, and audit/fallback language were updated to match. The browser (WebAssembly) feature card now states clearly that connecting it to your own database will require a small proxy component that ships later, and that the preview stays sample-only until then.

Downloads page gates the bridge connector behind Cloud plans

The bridge connector downloads on the downloads page are now headed "SQL VPN" and appear only for signed-in visitors whose plan includes Cloud Relay access — anonymous visitors and non-Cloud accounts see just the desktop app downloads. For subscribers the section is collapsed by default so it stays out of the way until needed.

v0.47.0
Aug 14, 2026 · 1 section
Aug 14, 2026
1 section

IntelliSense: persistent data-model cache, instant database switches, fully searchable explorer

Switching the connected database now reloads IntelliSense reliably and instantly. Every freshly pulled schema snapshot is persisted to a local per-(connection, database) cache file alongside the validation catalogs, so a database switch — or a fresh app launch — seeds completions and the explorer tree from disk immediately while a background refresh revalidates against the server. A switch to a never-seen database clears the previous database's catalog instead of leaving it lying (stale completions after a switch), and a slow pull that lands after the user has already switched again can no longer clobber the newer database's IntelliSense.

The explorer tree is also hydrated from the same snapshot: schemas, tables, views, procedures, and columns all exist in the tree the moment the data model loads, without expanding anything — so the sidebar filter now finds any object or column in the database, not just the nodes that happened to have been expanded. Server-loaded subtrees always win; hydration only fills levels the lazy loader hasn't fetched, and index/statistics/FK folders stay lazy.

v0.46.0
Aug 14, 2026 · 8 sections
Aug 14, 2026
8 sections

Every release is downloadable without an account

The downloads page used to show only the current version to signed-out visitors and asked them to create a free account to reach earlier builds. Every published version is now listed for everyone, and a quiet note explains that downloads never require an account — accounts exist for Cloud Relay, devices, and billing.

Download buttons say what they do, and picking an architecture is no longer a guess

The buttons on the downloads page were labeled only with a processor type ("ARM64", "x64"), never with the word "Download". They now read "Download · ARM64" (and carry the full file name to screen readers), and platforms offering more than one architecture show a one-line hint for anyone whose device couldn't be detected automatically — for example, that almost all PCs are x64.

Site content now renders even when JavaScript is off

Pages across the site faded their content in via a script, which meant visitors with JavaScript disabled — and link-preview crawlers — saw a blank page below the heading. Content now renders by default and the fade-in only applies when JavaScript is actually running. The same pass gave the downloads page a real heading structure for screen-reader navigation, hid decorative emoji from assistive technology, and stopped smooth-scrolling for people who prefer reduced motion.

Green badges and notices are easier to read

The green used for "current", "your platform", and "recommended" badges — and for success notices site-wide — sat at about 3:1 contrast on its pale background, below the readability floor for small text. The green is now darker, bringing those badges and notices comfortably past WCAG AA while keeping the same hue.

Download controls are easier to tap

The small download buttons and the per-version "changelog" pill links were shorter than the 44px comfortable touch target on phones. Both now meet that size without changing how they look on desktop.

Collapsed versions show they open, and keyboard focus is visible everywhere

Each version bar on the downloads page now ends in a small chevron that rotates when the section opens, so earlier releases are discoverable instead of looking like static rows. Across the site, links, buttons, and expandable sections now show a consistent blue outline when reached by keyboard, and hover animations no longer play for people who prefer reduced motion.

Accented text in SQL no longer crashes the app

Typing or opening SQL that contains accented or other non-ASCII characters — for example a Spanish name in a comment — could crash the app while it scanned the text for completions, located the WHERE clause, split MySQL scripts, or parsed a typed color. All of those scans now step through text one full character at a time instead of one byte at a time, and regression tests cover the reported crash.

Release commits describe what shipped

The release script now writes a one-line summary of the release's changes as the commit message instead of a bare "release: vX.Y.Z", falling back to the old format automatically if summarizing isn't available.

v0.45.0
Aug 14, 2026 · 6 sections
Aug 14, 2026
6 sections

Update-check API: "what have I missed" and "latest build for my platform"

Added two lightweight public endpoints so the app (and any tooling) can answer update questions precisely instead of downloading the whole changelog and diffing it locally.

The first takes the version you're currently running and returns every release published since then, newest first, along with the actual list of changes in each — so an update notice can say exactly how many releases you're behind and what changed, rather than just "an update is available." If you're already on the newest release it comes back with an explicit "up to date" answer and no changes.

The second reports the latest downloadable build for your operating system and architecture — the version number and a stable download link — without redirecting you at the installer, so a client can check whether it's on the newest build in a single request. It understands the same set of OS names as the existing download link and cleanly reports when a platform is unsupported or has no published build yet.

App update check is lighter, and the About link points at the licenses page

The desktop app now checks for updates using the new purpose-built endpoints instead of downloading the entire changelog every time. The routine "am I on the newest build?" check is a single tiny request, and the full list of what changed is fetched only when there's actually a newer build to show — so the common up-to-date case does far less work. What you see is unchanged: the "update available" chip and the "what's new" dialog still list every release since the one you're running.

The About window's "full license breakdown" link now opens the Licenses page — which credits every open-source project SQLly is built on — instead of the pricing page.

Formatting-directives reference: filter, keyboard copy, and copy confirmation

The reference window (Help ▸ Formatting Directives…) now has a filter box at the top that narrows the directive families as you type, searching their headings, explanations, parameters, and samples — and the field is focused when the window opens, so typing filters immediately.
Samples are now reachable without a mouse: Tab to any sample and press Enter (or Space) to copy it, with a visible focus ring and an "enter to copy" hint on the focused row instead of a hint that only appears on hover.
Copying a sample now confirms it right where you clicked — the row shows a brief "copied" label instead of leaving you to wonder whether the clipboard actually changed.
Family headings and samples render in body ink rather than accent/syntax colors, so color in the window once again means something, and the sample hover highlight uses the app's standard row-hover color (with the focus ring's contrast now covered by the palette test suite).
The window enforces a minimum size so it can't be squashed into unreadability, the duplicated in-window title is gone (the titlebar already says it), and the intro text correctly warns that the canvas layout directive — not just view: — only works on the first line.

Changelog deep links open the version you clicked

Following a "changelog" link beside a version on the downloads page now opens that version's notes on the changelog page: the page scrolls to the section, expands it, and folds every other version away. Previously the link scrolled to the right spot but the target version stayed collapsed while the latest release stayed expanded, so you had to find and open the version yourself.

Formatting-directives reference: layout polish

Long samples and parameter descriptions now wrap inside their rows instead of overflowing when the window is narrow, and the copy hint docks at a consistent right edge on every sample row instead of floating at a different spot per row.
The filter's no-results message now tells you how to recover — clear the filter to see every family again — and very long filter text is shortened in the message rather than blowing it out.
The intro copy reads more naturally and makes clear that 2x2 is one example canvas layout, not the only one.

Formatting-directives reference: hardening pass

The filter is now covered by tests with extreme input — emoji, CJK text, right-to-left scripts, and ten-thousand-character queries — so unusual typed input can't panic or confuse the window, and the no-results message never splits a multi-byte character when it shortens long filter text.
Sample rows now have collision-proof identities, so adding more samples to a directive family in the future can't make the window mix up one row's feedback with another's.
v0.42.0
Aug 13, 2026 – Aug 14, 2026 · 18 sections
Aug 14, 2026
8 sections

Builds no longer expire

The prerelease era is over: the app no longer stops working a year after it was built. The startup lockout screen, the status-bar countdown ("Prerelease build — stops working …"), and the "pay to keep using it" messaging are all gone, product-wide. Subscriptions and licensing are unchanged — this only removes the time bomb.

Checking for updates now always answers

Picking Check for Updates from the menu now pops a toast with the outcome — "You're up to date" or "couldn't reach the update service" — in addition to the status-bar message, which was easy to miss. Finding an update still opens the what's-new dialog directly.
The status bar, the update dialog, and the About window now share one update-check state, so the surfaces always agree and simultaneous checks collapse into a single request.

Store admin: refresh cached site data on demand

The store's changelog feed is cached for 90 minutes and its downloads listing for 10, which could make a fresh release invisible for a while. The admin Settings page now has a Cached data panel with "Refresh changelog now" and "Refresh downloads now" buttons that clear those caches immediately — the next request (including the desktop app's update check) sees fresh data.

Explorer actions always run where you clicked

Yesterday's fix made double-click open its SELECT against the table's own server and database; today every context-menu action follows the same rule — Select Top N, View Data, Count Rows, INSERT/MERGE scripting, and the DDL scripts all connect or switch to the clicked object's home first, waiting for slow connections (Azure sign-in, relay) and opening nothing if the connection fails.

Formatting directives: typos now squiggle

A -- sqlly comment the app would silently ignore now gets a warning when you validate the query: unknown directive keywords, the --sqlly format: no-space spelling trap, and a view: directive that isn't on the first line (the only place it works) are each called out with a specific message.
The directive reference now also documents the stacked-results layout directives (-- sqlly 2x2 and -- sqlly result: 1|1), and hovering them in the editor works like the other families.

Verified against a real SQL Server

A live integration test now runs the explorer's system-schema queries against an actual SQL Server: it proves sys and INFORMATION_SCHEMA list their views and columns, that those schemas count as non-empty (so "Only If Not Empty" shows them), and that ordinary schemas stay free of system noise.

Formatting directives: one grammar everywhere

The -- sqlly directive language is now defined in a single place shared by the editor's hover, the validation warnings, autocomplete, and every result parser — the surfaces can no longer quietly disagree about what counts as a directive.
Unifying them surfaced and fixed real disagreements: comments with extra dashes, a result-set name numbered zero, an empty name, and signed or absurdly large canvas sizes are now rejected (and warned about) everywhere, and a tab after sqlly in a stacked-layout directive now works everywhere instead of only in the hover.
New warnings on validation: a directive written after SQL code on the same line is called out as ignored (result-set names excepted — those work anywhere on a line), and a view: value other than grid or pivot warns instead of silently falling back to the grid.
A directive commented out inside /* … */ on a single line no longer shows a hover or a misleading warning.
Directive autocomplete now appears for every accepted spelling — --sqlly with no space, a tab after the dashes, any casing — and picking a new column format now replaces an existing loosely-spaced rule in place instead of adding a second rule that the stale one silently overrode.

Downloads page links to changelog

Each version on the downloads page now has a changelog link that jumps directly to that version's section on the changelog page. Changelog version sections now have stable anchor IDs (#v0.42.0, #v0.44.0, etc.) so the links land exactly on the right entry.

Aug 13, 2026
10 sections

A cleaner, more useful Object Explorer

Rows that never had a real icon — views, procedures, functions, and the Tables/Views/Programmability folders — no longer reserve an empty slot or show a stray "T"/"V"/"P" letter. The name simply slides left into that space, so the tree reads as one tidy column. Tables, columns, indexes, servers, and databases keep the icons they already had.
The INFORMATION_SCHEMA and sys schemas finally show their contents. Their objects are system-owned and were invisible to the query the explorer used; expanding them now lists the catalog and information-schema views (and their columns) like any other schema.
Double-clicking a table to open a SELECT TOP 1000 now opens it against the host and database that table actually lives on. Previously the new query ran against whatever connection happened to be active; now SQLLY retargets to the double-clicked server/database first, so the query runs where you meant.

Formatting-directive comments now explain themselves

Hovering a -- sqlly … comment (the special comments that tell SQLLY how to format results) pops up a note that it's controlling how SQLLY handles the query, with a link to the details. Clicking it opens a full reference of every directive — format:, name, view:, and pivot: — with what each does, its allowed values and parameters, and copy-ready examples.

About screen: current, honest, and actionable

Dropped the "prerelease… pay to keep using it" messaging.
Added a link to the full license breakdown on the SQLLY website.
The screen now shows whether you're up to date, and a Check for Updates button re-checks on the spot — reporting a newer build (with a download link), "you're up to date", or a brief note if the update service can't be reached.

Tidier Tools menu

Removed the Connection Preview item, which did nothing.
Connect and Disconnect are now mutually exclusive: you only ever see the one that applies — Connect when nothing is connected, Disconnect when a connection is live. (The menu updates itself the moment you connect or disconnect.)

Smaller touches that add up

The IntelliSense popup now offers the "to improve this, press F2" feedback affordance even when there are no suggestions — which is exactly when telling us what was missing helps most. F2 and a click both open the feedback dialog.
The AI query summary in the results bar is now selectable, so you can copy and paste it.
A "view source" side panel (go-to-definition on a table, view, or routine) now scrolls both horizontally and vertically, with scrollbars when the source runs wide or tall, and its text is read-only but selectable.

Safer, quieter validation

ALTER TABLE … ADD — adding a column, a constraint, or a foreign key — is no longer flagged as an "unconstrained destructive change". Only an ALTER TABLE … DROP COLUMN (which actually discards data) now asks for confirmation, alongside DROP, TRUNCATE, and MERGE.

# 2026-08-13 (v0.43.0)

Double-click retargeting now truly waits for the connection

Double-clicking a table on another server opens its SELECT TOP 1000 after that server's connection is fully established — including the slow parts, like Azure Entra sign-in and relay sessions. Previously the query tab opened immediately while the connection was still switching, so a quick Run could execute the query against the old connection, and the tab's validation and autocomplete stayed wired to it too. If the connection fails, no query tab opens at all — you get the connection error instead of a misleading tab.

System schemas no longer hide when counted

With schema visibility set to "Only If Not Empty", the sys and INFORMATION_SCHEMA schemas could still vanish: the object count the explorer consulted only counted user-defined objects, so system schemas always looked empty even though expanding them (fixed in v0.42.0) showed plenty. Counts now draw from the same catalog the folders enumerate, so the two features finally agree.

Formatting directives: easier to find, harder to mislead

A new Help ▸ Formatting Directives… menu item opens the directive reference directly — previously the only way in was hovering an existing -- sqlly comment, which required already knowing the syntax.
The hover now appears only on directive spellings SQLLY actually accepts, and no longer triggers on directive-looking text inside string literals.
The reference now says that -- sqlly view: must be the first non-empty line of the query, matching how it really behaves.

Sharper destructive-change detection

ALTER TABLE … DROP COLUMN[Name] (no space before the bracket — valid T-SQL) is now correctly flagged as destructive, while dropping a *constraint* whose name merely starts with "column" is no longer mistaken for a column drop.

v0.41.0
Aug 13, 2026 · 7 sections
Aug 13, 2026
7 sections

Check for updates whenever you want

There's now a Check for Updates… command — in the SQLLY menu on macOS, and under Help on Windows and Linux. It re-checks on the spot instead of only once at launch: if a newer build is available it opens the "what's new" dialog, and if you're already current it tells you so ("You're up to date"). If the check can't reach the network it says so briefly and gets out of your way, never blocking.

Object Explorer copy actions now copy the right thing

"Copy Name" and "Copy Qualified Name" in the Object Explorer produce correct, ready-to-paste identifiers for every kind of node:

Columns copy as [schema].[table].[column], procedure and function parameters as [schema].[routine].[parameter], and indexes, statistics, and foreign keys as [schema].[table].[name].
Every part is bracket-quoted, so names with spaces, dots, reserved words, or even a ] in them stay a single valid SQL Server identifier instead of breaking the paste.
"Copy Name" now copies the object's own name rather than the schema-prefixed label shown in the tree.

Foreign keys with the same name in different schemas stay separate

A table's Foreign Keys folder no longer collapses two different constraints that happen to share a name across schemas into one row — they're shown as the distinct relationships they are, and each keeps a stable identity so expanding and selecting them survives refreshes and unrelated foreign-key changes.

Faster Object Explorer filtering

Typing in the Object Explorer filter is now noticeably lighter on large, fully-expanded trees: the match work grows with the number of loaded nodes instead of ballooning as the tree gets deeper. Filtering still only ever searches what you've already loaded and never quietly goes off to enumerate the database while you type.

Safer connection reuse after an error

When a query fails, the failure now reports the actual server error (for example, "Invalid object name") instead of a generic "the query completed with errors", which also makes the engine's diagnostic log far more useful.
If a batch leaves a transaction open — most often a runtime error inside BEGIN TRANSACTION … — SQLLY now rolls it back before returning the connection to the pool, so the next query can never inherit a stray open transaction (and its locks). If the connection can't be cleaned, it's dropped and the next query gets a fresh one.

Sturdier generated scripts and safer edit warnings

"Script as CREATE" for a table degrades more gracefully: when a section (indexes, foreign keys, or triggers) can't be read, the others still render, and the explanatory -- … unavailable note can no longer spill a multi-line server error into the script as runnable text.
The unsaved-changes guard when closing tabs is more robust against tabs being closed or reordered mid-prompt, so a Save/Don't-Save prompt always acts on the tab you meant.
The single-row-safety check for UPDATE/DELETE no longer treats a primary key compared to another column (like WHERE id = id) as pinning a single row — those can match many rows, so they now correctly prompt for confirmation.

Under the hood

Update availability is now release-safe end to end: the download service always points at the newest build that actually has an installer for your OS and architecture (walking back to an older release if the very latest one doesn't), unsupported platforms get a clear error instead of a silent redirect, and the changelog feed's "latest" is computed from real version numbers rather than heading text.
Platform and architecture reporting is centralized so the update check, downloads, and feedback can't drift apart.
v0.40.0
Aug 13, 2026 · 1 section
Aug 13, 2026
1 section

Changelog page now shows full details under each header

The website's changelog page and the desktop app's "update available" dialog now render the full story under each feature header — paragraphs and bullet points — instead of just a flat list of header titles. The page sections are also up to 60% wider, making long descriptions easier to read while staying responsive on smaller screens.

v0.39.0
Aug 13, 2026 · 5 sections
Aug 13, 2026
5 sections

IntelliSense you can always open, scroll, and click

The suggestion popup always opens now. Press the IntelliSense shortcut and the window appears even when there's nothing to suggest yet — it says "No suggestions" and stays open as you keep typing, so it can start suggesting the moment there's a match. No more pressing the key and having nothing happen.
Scroll it with your mouse. Hovering the suggestion list and scrolling now moves through the suggestions instead of scrolling the SQL underneath.
Click to pick. Click any suggestion to select and insert it — you're no longer limited to the arrow keys.

Join suggestions follow the join type you're typing

When SQLLY offers a foreign-key join after a table, it now leads with the join type you're typing: type i and the suggestions become INNER JOIN …, l gives LEFT JOIN …, r gives RIGHT JOIN …, o gives FULL OUTER JOIN …, and with nothing typed you get a plain JOIN …. The whole, ready-to-use join (table, alias, and ON clause) comes with it.

Tidier results controls

The grouping / filtering / sorting clear buttons now appear only in the Stacked results layout — where per-set headers make them most useful — and they sit inline on the right of the results header instead of the left. Tabbed and Canvas layouts stay uncluttered.

Azure & connection polish

The "ask my admin" message names the account you picked. When you copy the Azure Reader-permission request, it now always names the Entra account this connection actually uses, even if you have several accounts registered.
The info button next to the "type your server" hint is now a proper icon button with a tooltip, consistent with the rest of the app.

Under the hood

A query that can't confirm its database afterward (a USE, then a hiccup reading the current database) no longer turns a run whose results already arrived into a failure — it warns and quietly reconnects for the next query.
Confirmed and documented that a USE inside dynamic SQL (EXEC(N'USE …')) is scoped to that dynamic batch and doesn't change your session's database, so SQLLY correctly leaves your database indicator alone in that case.
v0.38.0
Aug 13, 2026 · 7 sections
Aug 13, 2026
7 sections

SQLLY now tells you when there's a new version

A new indicator appears in the bottom status bar when a newer build is available. Click it and SQLLY shows you everything that changed between the version you're running and the latest one, version by version. A Download button takes you straight to the right installer for your operating system and processor (Apple Silicon vs Intel, Windows x64 vs Arm, and so on).

For now this is a heads-up, not an installer: the dialog says so in plain terms — fully automatic in-app updates are on the way, but until then you download the newest version and replace your current install yourself.

A friendlier Server Explorer filter

The filter now includes more, not less. Type a table's name and you'll see the table *with all of its columns*, not just the columns that happen to match your text. Type a column's name and the table that owns it still shows up, carrying the matching columns. Either way, you find what you're looking for without losing its context.
Matches are highlighted so you can see exactly what your text hit in each name.

Copy names from any object

Right-clicking a table now offers Copy Name and Copy Qualified Name, and so does every other data-model item in the Server Explorer — views, procedures, functions, types, columns, indexes, and more. The right-click menus also share one tidy standard width instead of resizing to each menu's longest label.

Scripts you can actually run, for everyone

Table definitions no longer fail on a permission you don't have. If your account can't read one part of a table's definition (indexes, foreign keys, or triggers), SQLLY now notes that section as unavailable and shows you the rest, instead of failing the whole view.
Generated table scripts execute cleanly. Covering indexes keep their INCLUDE (…) columns, and a GO is placed before the triggers so the assembled script runs as-is. (Columnstore, XML, and spatial indexes, plus storage/filegroup options, are still out of scope and noted as such.)

A clearer Foreign Keys folder

The Server Explorer's Foreign Keys folder now shows a composite (multi-column) key as a single relationship — ColA, ColB → RefA, RefB — instead of one confusing row per column pair. Expanding many tables' foreign keys is also faster (the database's key list is fetched once and reused), and each entry keeps its place when unrelated keys change.

Fewer false alarms on "unconstrained change"

The primary-key–aware warning for unconstrained UPDATE/DELETE statements got smarter: it no longer misfires on WHERE (id = 1) (parenthesized), on a key column named with a reserved word like [order], and it no longer treats a filter on a *different* table's same-named column as if it pinned your target row.

Safer tab closing

Closing a query tab that prompts you to save now tracks the exact tab by identity, so if tabs are closed or reordered while the save dialog is open, the right tab is always the one that gets saved and closed.

v0.37.0
Aug 13, 2026 · 1 section
Aug 13, 2026
1 section

Connect to an Azure SQL server even when you can't list them all

Choosing Microsoft Entra sign-in asks Azure for the list of SQL servers your account can see. If your account doesn't have permission to enumerate them, that list comes back empty or forbidden — and the connection dialog used to treat that as a hard error, popping open an empty picker with a red message that made it look like you were stuck.

You were never actually stuck: connecting by server name has always worked. This release makes that obvious.

Discovery is now best-effort. The server picker still fills in when Azure will let it, but a permission failure no longer blocks the form. The server field stays front-and-center with the hint *"SQLLY couldn't list your Azure SQL servers — type the server name above."* Type myserver.database.windows.net, pick your database, and connect.
A one-click request for your admin. Next to that hint is an info button. Click it and SQLLY copies a ready-to-send message to your clipboard — *"Could you give my account '…' the Reader role on the subscription so that I can find the servers that I have access to instead of having to type them in by hand"* — and shows you exactly which permission is needed (the Reader role on the subscription, i.e. Microsoft.Sql/servers/read). Paste it to whoever manages your Azure access and the picker will start finding your servers for you.
v0.36.0
Aug 13, 2026 · 1 section
Aug 13, 2026
1 section

Azure sign-in no longer goes stale outside the query window

If you use Microsoft Entra sign-in for an Azure SQL connection, you may have hit this: a query window works fine, but expanding the server in the Server Explorer, or clicking Test Connection / the Databases dropdown in the connection editor, fails with "token expired" or "failed" — even though it's the same server you were just querying.

The cause was that only the query and connect paths refreshed the Azure access token (which lasts about an hour). Every other place reused whatever token was minted when you first connected, so once it lapsed those buttons broke while queries kept working.

Now a single shared token minter backs all of them. Test Connection, the Databases dropdown, and expanding a server or database in the Server Explorer each refresh the Entra token the same way a query does — reusing a still-valid token, or quietly re-minting one that's near expiry — so they keep working for as long as your sign-in is valid. When your sign-in really has expired, they now point you to Manage Entra Accounts ▸ Add User instead of showing a raw connection error.

v0.35.0
Aug 13, 2026 · 1 section
Aug 13, 2026
1 section

GO and USE now work in a query window

You can finally paste a script the way SQL Server hands it to you — with GO batch separators and USE statements — and run it as-is.

GO splits the script into batches. Each batch runs in order on the same connection, so temp tables, SET options, and other session state carry from one batch to the next, and the results grids are numbered continuously across the whole run. A GO only separates batches when it sits alone on its line, and a GO inside a string or comment is left untouched.
A failed batch no longer stops the rest. If one batch hits an error — a bad column name, a constraint violation — SQLLY reports it in the Messages pane the way SSMS does (Msg 207, Level 16, …) and carries on running the batches that follow, instead of failing the whole script.
USE OtherDb switches the session. Running USE retargets the current database for that query: the database picker, the status bar, and IntelliSense all follow along, and your next query runs against the new database. The change stays with the session — your saved connection is untouched. (USE is also no longer refused on read-only connections, since switching databases changes nothing.)
v0.34.0
Aug 13, 2026 · 5 sections
Aug 13, 2026
5 sections

Unconstrained UPDATE / DELETE / TRUNCATE are now called out

SQLLY now knows each table's primary key and uses it to spot data changes that aren't pinned to a single row. An UPDATE or DELETE is flagged as an unconstrained mutation / destructive operation unless its WHERE clause matches the full primary key (an equality on every key column, with no broadening OR); TRUNCATE, DROP, ALTER, and MERGE are always flagged. The notice is an inline indication — it no longer promises a confirmation dialog it wasn't going to show — and it stays quiet on tables whose primary key isn't known, so a normal filtered statement doesn't nag you.

Under the hood, primary-key columns now travel with the rest of the schema catalog (extracted from the database, cached, and change-detected alongside columns and foreign keys).

A cleaner object explorer

The server explorer trades its two-letter text badges for real icons: a tower for servers, a cylinder for databases, a grid for tables, columns for a column, and a contents list for indexes. Schemas are shown without an icon so the tree reads more quietly.

Foreign keys, finally visible

Every table in the explorer now has a Foreign Keys folder listing both directions of its relationships — the keys the table declares (→ references out) and the keys other tables point back at it (← referenced by). The table Properties dialog gains a matching Foreign Keys section covering both directions.

Richer table definitions

Opening a table's definition now shows more than the columns. Below the generated CREATE TABLE, SQLLY appends its indexes, foreign-key constraints, and triggers, so a single view describes the whole table.

Closing query tabs

Closing a query tab with unsaved changes now asks first: Save, Don't Save, or Cancel. "Save" writes the file (opening a save dialog for an untitled tab) and then closes; "Don't Save" discards and closes.
You can now close the last query tab.
With no query open, the query area shows the SQLLY logo and a reminder of how to start a new query (⌘N).
v0.33.2
Aug 12, 2026 · 4 sections
Aug 12, 2026
4 sections

Pick a database and the data model follows

The query bar's database picker now tracks the server you choose. Switching servers repopulates the database list for that server instead of stranding the previous one's databases, and picking a database switches IntelliSense to that database's data model automatically — no more reaching for the refresh button.

Data model snapshots are now cached per server + database, so returning to a combination you've already loaded is instant rather than a fresh round-trip to the engine. The cache is kept honest: columns and stored-procedure parameters you load by expanding the tree fold back into it, editing a connection drops its stale snapshots, and the refresh button still forces a fresh pull whenever you want one.

Hovering the refresh-data-model button now tells you how old the current snapshot is — e.g. 2026-08-12 05:59 (12 minutes ago).

Detailed query timing is now opt-in

The results status bar no longer shows the full First / Query / Server / Paint / Wall / Latency breakdown (and its latency-subtract and hh:MM:ss toggles) by default — just the row counts. A new setting, Results ▸ Performance ▸ Show detailed query timing stats, brings the whole breakdown back for when you're measuring.

Improve IntelliSense dialog polish

The "What will be sent" notes now wrap inside the dialog instead of running off the right edge.
The dialog has a proper title bar with an ✕ to close it, alongside Cancel and Escape.

Status bar & supporter link

The connection/route indicator in the bottom-left corner now paints its glyph in a contrasting ink (white on the green/red circle) so it's legible instead of a dark smudge.
Once you're licensed, the "I love SQLLY, you will too!" chip — and the other upgrade prompts — take you to My Account to manage your subscription rather than back to the pricing page.
v0.33.1
Aug 12, 2026 · 1 section
Aug 12, 2026
1 section

SQLLY for Windows on ARM

There is now a Windows arm64 download, alongside the existing x64 one. It runs natively on Windows 11 on Snapdragon and other ARM laptops — no x64 emulation, so it starts faster and drains less battery than running the x64 build under translation. The on-prem bridge connector ships for Windows arm64 too.

The arm64 build had been switched off in the release pipeline because it never linked. Two pieces were missing from the build machine's cross-compile setup, and with those in place the Microsoft ARM64 toolchain builds and links it natively.

Every Windows download is now checked against its own architecture before it is published, so an arm64 download can never turn out to contain an x64 program.

v0.32.0
Aug 11, 2026 · 3 sections
Aug 11, 2026
3 sections

Improve IntelliSense: submit and keep working

Submitting the "Improve IntelliSense" dialog no longer holds the dialog open while the report uploads. Submit closes it immediately and the upload continues in the background; the bottom status bar reports the outcome ("IntelliSense feedback sent — thank you!", or the failure).

A failed upload is no longer lost. The app keeps a local record and retries on a slowing schedule — 30 seconds, then 2, 5, and 10 minutes, then every 20 minutes — for up to 5 hours from submission (surviving app restarts) before giving up and saying so. Every submission carries a client-generated GUID and the store deduplicates on it, so a retry after a half-delivered upload can never double-post your report.

Improve IntelliSense: a friendlier dialog

The query preview pane now scrolls both ways when the query is bigger than the pane — with proper scrollbars — instead of clipping long lines off the edge, and it opens with your cursor position already scrolled into view.
A new Editor setting, Include full data model in IntelliSense feedback by default, pre-ticks the dialog's "Include all" box so heavy reporters don't have to tick it every time. As always: object names only, never data.
The "to improve this press F2" hint under the completion popup now spans the popup's full width, draws F2 as a proper keycap so it reads as a key press, and is clickable — clicking it opens the dialog just like pressing F2.

One status bar, one voice

Every piece of text in the bottom status strip — connection trail, status messages, zoom readout, badges — now uses the same size as the result grid's message bar, so the band reads as one uniform surface.
The supporter message is now simply "I love SQLLY, you will too!", and it lives only in the bottom status bar: the full-width yellow banner above the editor is gone.
v0.31.0
Aug 11, 2026 · 2 sections
Aug 11, 2026
2 sections

The status bar now shows how you are connected

The bottom status strip gained a route indicator at its far right, after the client / project / environment badges. A direct connection shows a small "direct wire" mark (outward chevrons around a dotted line); a connection riding a relay shows a satellite. Hovering the indicator spells out the full path a query travels:

Directly connected to {server} for direct connections.
Connected via {relay host} → {bridge name} → {server} for relay connections, naming the relay endpoint the session actually landed on (selection can override the profile's advisory hint) and the bridge's display name as configured in the account.

Before a relay session is fully open the indicator still tells the truth about the transport, degrading to the profile's advisory relay hint and the raw bridge device id rather than pretending the connection is direct.

Toolbar and status-bar icons say what they mean

Several query-bar buttons wear new Lucide icons so their pictures match their jobs:

Run now uses the standard play triangle.
Result layout toggle shows a tabbed window when result sets are in tabs, and full-width stacked rows when they are stacked.
Execution plan (both the capture button and the actual-plan toggle) uses a framed Gantt-style chart.
Result diff uses a file marked with plus and minus.
Procedure mode uses a database with a lightning bolt.
Version history uses the classic clock-with-arrow history mark.
Refresh schema metadata uses a database with a restore arrow.

The new artwork ships as SQLLY-bundled glyphs (the component library's icon set does not include them), so they theme and scale exactly like the rest of the toolbar.

v0.29.1
Aug 11, 2026 · 1 section
Aug 11, 2026
1 section

The bridge now runs on any Linux machine, desktop or server

sqlly-bridge register failed on headless Linux hosts with a Platform secure storage failure: DBus error — the bridge stored its Noise identity and relay token exclusively in the OS keyring, and a server without a desktop session has no Secret Service on DBus to provide one.

Secret storage is now layered, favoring the most secure backend the host actually offers:

1. OS keyring — macOS Keychain, Windows Credential Manager, or the Secret Service on Linux — used whenever it is reachable. 2. Owner-only files — hex-encoded secrets under ~/.config/sqlly/bridge-secrets with 0700/0600 permissions — the fallback when no keyring is available.

The bridge probes the keyring once at startup, writes to the best layer available, and reads fall through the layers so an identity registered under the file store keeps working if a keyring appears later. register and status now print a secret storage: line stating exactly which backend is in use (and, when falling back, why the keyring was unavailable).

v0.29.0
Aug 10, 2026 · 2 sections
Aug 10, 2026
2 sections

IntelliSense feedback now asks before sharing your data model

Pressing F2 over a completion popup used to capture the entire data model of the active connection — every schema, table, column, procedure, and parameter name — and ship it silently with the feedback. That was a quiet deviation from our commitment to never take your data, and it is gone.

The Improve IntelliSense dialog is now an explicit disclosure screen:

Left side: a searchable tree of your data model (databases, schemas, tables, views, procedures, functions, columns, parameters, snippets). Searching filters live and expands the path to each match. Every node has a checkbox — tick a column and its table and schema show a greyed box, meaning only their *names* ride along so the column has a home; tick a schema or table and everything inside it is included. An Include all toggle at the top restores the old full-catalog behavior, greying out the search and the tree while it is on.
Right side: the query being flagged with an exact cursor-position indicator, plus a plain statement of what is always sent (your note, the query, the suggestions exactly as the popup showed them, your IntelliSense settings, app version and platform) and what is never sent (query results, table data, credentials, or anything left unticked).

Nothing is serialized until Submit, and only the parts you opted into ever leave the machine. By default nothing in the tree is ticked — a submission with no boxes checked sends no data-model names at all.

The completion popup itself now carries a small note — "to improve this press F2" — so the feedback path is discoverable instead of buried in the keybinding list.

A warmer first run, and a livelier workbench

Opening the app used to drop you into a grey wall: a sidebar saying "No connections yet" in small print, a results pane telling you to press a shortcut, and nothing that invited a first step. The disconnected workspace now opens on a welcome canvas — the SQLLY mark, a plain invitation, and your saved connections as clickable rows that connect on a single click (or a New Connection button when there are none yet). It appears only while there is nothing to work on; the moment you connect, or start typing a query of your own, it steps aside.

The smaller dead ends got the same treatment. The sidebar and schema-tree empty states now explain what to do next and offer the button that does it, instead of describing a "+" somewhere else on screen. The AI panel's disconnected placeholder says what unlocks it. The results pane's "no results" message now names your actual run shortcut — it reads your keybinding overrides, so if you remapped execution it says so — and is correct instead of hardcoded.

The workbench also moves a little: new results fade in, switching query tabs crossfades, the welcome hands over to the editor softly, status-bar messages arrive with a gentle fade rather than a pop, and the completion popup fades in as it appears (a hand-rolled ~120ms fade in the custom paint path — the steady-state typing loop still paints one frame per keystroke). While a query runs, the results pane shows a breathing loader with a live elapsed counter, and the status strip's busy dot pulses. When it finishes, the status bar tells you what happened — rows and duration on success ("Query complete — 1,234 rows in 0.42s", thousands-separated), the server's message on failure (a failed run used to be flatly reported as "Query complete"), and an honest "Query cancelled" when you stop it. Picking a connection from the welcome canvas or the server dropdown now shows "Connecting to …" immediately instead of staying silent until the connection lands.

Finally, saved servers in the explorer now wear their environment's color as a small dot, so "which environment is this?" is answered at a glance in the rail — the same identity the status bar chips and group headers already carried.

Under the hood: a new welcome canvas, plus updates across the app shell, editor, results pane, schema tree, sidebar, AI panel, and keybindings, and a revived connection picker for the welcome rows (trimmed to just what it needs).

v0.28.2
Aug 10, 2026 · 1 section
Aug 10, 2026
1 section

Dialogs reopen reliably from the menu bar

Fixed an intermittent bug where dialogs such as Preferences and Connection Management would open the first time but silently fail to reappear when reopened from the menu bar after being closed. Closing one of these dialogs left keyboard focus pointing at the dismissed dialog, which knocked the main window's menu actions out of the focus chain until the user clicked back into the query editor. Every dialog now hands focus back to the active editor when it closes, so the menu bar keeps working without an extra click.

v0.28.1
Aug 8, 2026 · 1 section
Aug 8, 2026
1 section

Relay connections work from any paired machine

Connecting through a relay no longer appears to require the SQLLY Bridge on the same machine as the app. The connection editor now offers "Authorize" on each device that has not paired with a bridge yet (instead of silently borrowing another device's pairing), relay profiles resolve their authorization from the signed-in device itself so they work on any machine, and the bridge accepts a secure handshake from any authorized device rather than only the first one that ever paired.

v0.28.0
Aug 8, 2026 · 1 section
Aug 8, 2026
1 section

Bridge fixes: canonical Noise key and product-tagged downloads

The SQLly Bridge now sends its Noise static public key as canonical base64 rather than hex, matching what the relay and app expect during pairing and remote connection setup.

Download events are now tagged with a product code (App, Bridge, or Relay) parsed from the artifact filename, so download counts can be attributed per product. The relay server binary continues to publish to storage but is never exposed as a downloadable item on the consumer downloads page.

v0.27.0
Aug 5, 2026 · 1 section
Aug 5, 2026
1 section

Download the SQLly Bridge from the downloads page

The on-prem SQLly Bridge — the connector you run next to your databases so you can reach them through a relay — is now published with each release and shown on the downloads page. When a version ships a bridge build, its download page gains a dedicated SQLly Bridge section with the right file for each platform: Windows (x64), Linux (x64 and ARM), and macOS (Apple Silicon). The desktop app downloads are unchanged and still lead the page; the bridge sits just below them.

Behind the scenes the release pipeline now also publishes the Linux relay server binary to storage alongside the app and bridge, so the whole remote-access set (app, bridge, relay) ships from one release.

v0.26.0
Aug 5, 2026 · 3 sections
Aug 5, 2026
3 sections

Relay security hardening

Following a security review of the remote relay, the relay boundary got a round of hardening. Relay-connection tokens are now signed with their own dedicated key instead of sharing the one that signs your account's app tokens, so a compromised relay can no longer be used to forge account access — deployments must now configure a distinct relay signing key, and the API refuses to start with a shared or missing one once relays are in use. The relay itself now fails to start (rather than quietly accepting anyone) if it is launched without a signing key, bounds slow or stalled connections before they can tie up resources, enforces the per-connection rate limits the design always promised, caps how much it will buffer for a slow client, and only talks to the control plane over HTTPS. On the control-plane side, the relay APIs and public relay directory are now rate-limited, a crashed relay drops off the public directory instead of lingering as "online," admin relay endpoints are gated by a single admin policy, and relay control credentials must meet a minimum strength. The full findings and status are tracked in an internal security report.

Website/API security hardening

A full security review of the store website and API was completed and its findings fixed the same day; the report with per-finding status lives in an internal security report.

The three critical fixes: password-reset links (which contain a sign-in token) are no longer written to server logs — real reset emails are now delivered through Azure Communication Services, and production refuses to start without email delivery configured; billing can no longer silently fall back to the free "stub" checkout if Stripe configuration goes missing — that misconfiguration now stops the site from starting instead of giving paid plans away; and signing in with Google/GitHub/Microsoft/Apple no longer attaches to an existing account just because the provider reports the same email address — the provider must prove the email is verified, closing an account-takeover path.

Beyond those: password changes and resets now sign out every previously issued app token within about a minute; login and password-reset attempts are throttled per email address (not just per IP, which Cloudflare made ineffective) and the sign-in web pages are rate-limited for the first time; Stripe webhook deduplication survives restarts and scale-out so replayed events can't double-apply; cross-site request forgery protection was fixed (a junk Authorization header could previously switch it off) and extended to all browser-reachable mutations; encrypted sync storage now has free-tier and paid-tier quotas plus rate limits; and the two-installs-per-platform device limit is enforced by the server rather than trusted to the app.

Smaller hardening: a stricter HTTPS pin (one-year HSTS), a report-only content-security policy as the first step toward enforcement, host-header allowlisting, checkout redirect URLs restricted to the site itself, contact-form bodies kept out of logs, timing fixes that stop the login endpoint and the password-reset request from revealing which emails have accounts, and a security.txt disclosure pointer for researchers.

Deploy note: the next production deploy requires new app settings (ACS email connection string and sender, and the host allowlist — the latter is set by the deploy pipeline) or startup will fail fast by design; a database migration adds the token-version column and webhook-event table automatically.

Pricing page: no redundant sign-up push for the signed-in

The coming-soon SQLly Cloud cards on the pricing page no longer ask signed-in visitors to create an account they already have. Signed-in users now simply see that they'll be notified when Cloud leaves beta testing, while anonymous visitors still get the Create account button.

v0.25.0
Aug 5, 2026 · 5 sections
Aug 5, 2026
5 sections

Relay fleet management for admins

The website now gives administrators a Relays page — under the Admin menu — for operating the hosted relay fleet. Admins can register a new relay server (its control credential is stored only as a hash), force a server's advertised status (for example, drain one before maintenance), remove a server from the fleet, and audit every bridge pair across every account in one list. Matching admin API endpoints back the page, so the same operations can be scripted.

Manage your own relay bridges from your account

Signed-in users now have a Relays page — under the account menu — showing the relay servers available to route through (with location, status, and capacity), the bridges (on-site connectors) registered to their account, and the pairs that authorize a device to reach each bridge. From there you can revoke a pair you no longer want, or disable a bridge entirely; disabling a bridge also revokes all of its pairs, so relay servers immediately stop routing traffic to it.

The pay dialog lost its guilt-trip opener

The periodic "have you paid yet?" dialog no longer opens with the paragraph about the thousands of queries you've run and the hours it saved you — it gets straight to the point (and the pig). The running query counter stays.

Export menu is now configurable, and speaks eleven programming languages

Settings → Results gains an Export page that controls exactly which formats appear in the results-grid right-click menu. Each of the ten existing formats (CSV, TSV, JSON, Markdown, HTML, XML, INSERT, Temp Table, SQL Values, Excel) can be switched off individually, so the menu stays as short as you want it.

A new Programming section (off by default) adds code-snippet exports: pick C#, Python, Rust, JavaScript, TypeScript, Kotlin, Swift, Java, Go, PHP, or C++ and the grid copies a ready-to-paste snippet for the current result — a record/type definition (a C# record, Python dataclass, Rust struct, TypeScript interface, and so on) plus a collection literal holding every row, with nullability, types, string escaping, and reserved-word column names handled for you. A master switch shows or hides the whole group, and each language has its own toggle that stays put while the section is off.

Website security review documented

A full security review of the website and API was completed and documented in an internal security report. It identifies three critical issues (password-reset tokens landing in application logs, a misconfiguration path that could grant paid plans without payment, and external sign-in auto-linking that doesn't prove the email is verified), plus higher- and medium-priority hardening items around rate limiting, CSRF consistency, token revocation, webhook deduplication, and storage/device abuse controls. Each finding includes evidence, impact, and a fix plan; fixes land in a follow-up change.

v0.22.4
Aug 4, 2026 · 21 sections
Aug 4, 2026
21 sections

Microsoft Entra connections are dramatically faster on repeat queries

Running queries against a Microsoft Entra ID (Azure AD) connection used to be far slower than the same queries over SQL authentication. The reason was that the desktop app opened a brand-new connection for every query, and every new Entra connection pays for Azure AD token validation at the Azure SQL gateway — a cost that dominates the time of a small query.

The app now keeps a live connection warm and reuses it for subsequent queries on the same tab, skipping the network handshake and, for Entra connections, that repeated token validation. Each query tab keeps its own warm connection, idle connections are retired after five minutes, and a warm connection that turns out to have been dropped by the server while idle is transparently reopened before the query runs — so reuse never surfaces as a stale-connection error. This mirrors the connection reuse the command-line engine already had.

Row editing reuses connections too, on its own dedicated lane

In-grid row editing now reuses warm connections the same way queries do, on a separate connection lane so its transactional sessions never mix with ordinary query connections. Editing a row no longer pays a fresh connection cost each time.

Fixed: warm connections could be shared across different Entra accounts

A latent correctness issue is now fixed: connections were identified by server and database only, so two different Entra accounts signed in to the same server and database could have been handed the same warm connection — leaking session state across accounts. Connections are now keyed by the signed-in account as well, so accounts never share a session. (On-disk schema catalogs deliberately keep their existing account-agnostic naming, so no existing cached schema or AI data is affected.)

Faster local builds with sccache

Local Rust builds now route through sccache, so unchanged crates are pulled from a compilation cache across both cargo check and cargo test runs.

Connect to databases you can't reach directly — remote engine connections

SQLLY can now run your queries against SQL servers that your laptop cannot reach on its own network — a database behind an office firewall, in a private cloud subnet, or at a customer site. Each remote connection picks its route automatically: Direct when the machine running the engine is reachable from where you are, and Via-Relay when it isn't, in which case traffic travels through a SQLLY relay server on the public internet.

The relay can't read anything — end-to-end encryption by design

Traffic that crosses the relay is end-to-end encrypted between your copy of SQLLY and the connector inside the database's network, using keys that only those two machines hold. The relay — and SQLLY's own servers — see nothing but opaque encrypted packets and the addressing needed to pass them along. Your queries, credentials, schemas, and results are never visible to us or to the relay, and there is nothing on our side that could decrypt them. Before any data flows, both ends also verify each other against the device identities registered to your account, so a relay (even a compromised one) cannot impersonate either side.

A small on-site connector brings your database to the relay

The remote end is a lightweight background service you run on any machine inside the database's network — no inbound firewall holes, no VPN. It dials out to the relay, waits for your app, and runs the same query engine the desktop app uses locally. Install it once next to the database and every device signed in to your account (and authorized to pair with it) can query through it.

Relay connections are part of Cloud plans

Connecting through the hosted relay is a Cloud-plan feature: Cloud and Cloud Business include an on-site connector in the plan's allowance. Direct connections on your own network remain available on every plan and never touch the relay.

Fixed: release website build failure

A stray trailing comma in the download page's cache attribute broke the server build in CI. Removed, and the build passes again. The download and changelog pages are now output-cached for one hour, cutting repeat page-rendering and release-catalog work.

Website spacing polish

Two layout fixes on the marketing site: on the on-device AI page, the "in progress" / "planned" status pills on feature cards sat flush against the card title — they now have breathing room matching the surrounding badges. And on the guide pages, the closing call-to-action band ("That's the whole workflow.") had no internal padding, so the text crowded the band's edges; the band now pads its content like the features page CTA does, including on small screens.

Releases now publish whatever built, even when one platform fails

The release pipeline used to publish downloads and the changelog only when every platform (Windows, Linux, macOS) built successfully — one red leg suppressed the working builds. Publishing is now partial: the downloads and changelog stages wait for the platform builds and run as soon as at least one of them succeeds, uploading whatever artifacts exist. The jobs that used to cancel the entire run when one platform failed were removed for the same reason.

Fixed: macOS release build couldn't find the data-grid crate

The macOS release job failed because it relied on a pre-existing sibling checkout of the data-grid crate on the build machine that was no longer there. The job now materializes that checkout itself, the same way the Linux and Windows jobs already did, and the build files carry a note explaining the arrangement so it isn't removed again.

Fixed: Linux arm64 release build broke on a stale zig install

The Linux arm64 leg failed inside the cross-linker with "unable to find zig installation directory" whenever an earlier interrupted run left a partial zig download on the shared build machine — the install step saw the directory and skipped re-downloading. The step now checks for the zig binary itself, downloads into a staging folder, and moves it into place atomically, so a killed run can never poison later builds.

Fixed: supporter banner text spilling off the left edge

The banner above the query editor was centered, so on narrower windows its overflowing text pushed out past the left edge of the banner instead of clipping. The banner is now left-aligned and clips whatever doesn't fit (with an ellipsis), and hovering the clipped message shows the full text in a tooltip.

Fixed: Settings sometimes wouldn't reopen

After closing Settings, the window's keyboard focus was left pointing at the now-destroyed dialog, so focus-routed shortcuts like ⌘, silently did nothing and Settings appeared to ignore reopen attempts. Closing Settings now hands focus back to whatever had it before it opened, and a stray late "closed" notification from a previous Settings instance can no longer dismiss a freshly reopened one.

Lifetime license is now $300

The one-time Lifetime plan dropped from $500 to $300, everywhere it appears: the plan catalog that drives checkout, the pricing page, and the server documentation. (The actual Stripe charge follows the price configured in the Stripe dashboard, so the Stripe price needs the same update when this ships.)

Status pills on feature cards now sit properly above their titles

The "functional" / "in progress" / "planned" badges on feature cards had two layout problems depending on the page. On the Engineering Deep Dive, Data Analyst, and DBA Power Users pages the badge touched the card title with no gap at all; and on every page — those three plus Features and On-Device AI — the badge stretched the full width of the card instead of hugging its text like the neighboring tag chip. The badges now keep a consistent gap above the card title and size to their content on all five pages.

Billing page: clearer Cloud pricing, friendlier free tier, aligned buttons

The SQLly Cloud section on the billing and pricing pages now states the real shape of the offer: Cloud and Cloud Business are priced per user per month, each plan includes one onsite connector license, and extra connector licenses will cost $5/month each. Instead of a waitlist form, the cards now explain that creating an account is all it takes to be notified when Cloud ships — and that early accounts get their first two months free.

The free plan also grew up: the "currently in development" badge, the "while it's in the oven" tagline, and the one-year build expiry line are gone. In their place, the card simply says the free tier is the full app, forever, with a loving reminder every few hours that the pig has to eat — and its call to action now links straight to the download page.

Finally, the action buttons at the bottom of every plan card — paid plans, free tier, and the Cloud coming-soon cards, on both the billing and pricing pages — now line up at the same height across a row and sit centered within each card, instead of sticking to the left at uneven heights.

Fixed: download stats recorded Cloudflare's IP instead of the visitor's

The website tracks each release download with the caller's IP address, but since the site sits behind Cloudflare it was recording the Cloudflare edge server's address for everyone — making the IP data useless. The tracker now reads the real visitor IP from Cloudflare's connecting-IP header (which Cloudflare overwrites, so it can't be spoofed), only falling back to the direct connection address when the header is missing or malformed.

Fixed: sign-out button was invisible in the store sign-in window

The "Sign out / switch account" button in the SQLLY Store sign-in window rendered with nearly invisible dark-on-dark text. The app builds against a newer snapshot of the UI component library whose buttons read a new family of theme colors, and the app's theme mapping predated it — so button labels fell back to the library's built-in light-theme ink no matter which app theme was active. The global theme mapping now covers the full button color family (plus the other newer widget color slots), so every stock button renders in the app's own palette, dark or light.

The yellow banner no longer shows "Sign in" when you're signed in

The yellow free-tier banner always offered a "Sign in" link, even after signing in. The link (and its separator) now only appear while you're signed out; the supporter message and pricing link still show either way. The banner's text-clipping estimate also stops reserving space for the sign-in link once it's gone, so the message gets the room back.

v0.22.0
Aug 3, 2026 · 25 sections
Aug 3, 2026
25 sections

Downloads page groups by platform and points you to the right build

The downloads page no longer lists one flat row per file. Each platform (macOS, Windows, Linux) is now a single group headed by its logo, with a separate Download x64 / Download ARM64 button per architecture. Every build's download count moved onto its own line beneath the file name and size, rather than being dot-joined into one string.

The page now also detects the visitor's OS and CPU architecture in the browser and calls out the build that fits. Using the browser's platform-hint API (with a User-Agent string fallback), a banner at the top reads e.g. "Looks like you're running macOS on ARM64 — we've highlighted the build that fits.", the matching platform group gets a your platform pill, and the matching architecture card is highlighted with a recommended pill. It is pure progressive enhancement: when detection is unavailable the page still lists every build normally.

Moved the account menu under the signed-in username

The signed-in navigation no longer has a separate Account dropdown in the link row, and the standalone Sign out button is gone from the bar. The username pill on the right is now itself the menu trigger — clicking it opens a dropdown anchored beneath the name, right-aligned so it never clips off the viewport edge: Billing, Devices, Invoices, Feedback, Profile, then a separator and Sign out as the last item. The dropdown highlights when any account page is active, still closes on outside click/Escape/link selection, and is hidden on narrow screens where the hamburger menu already lists the same links (including sign out).

Grouped the signed-in top navigation into dropdown menus

The website header overflowed once a user signed in — admins saw up to nine top-level links (Changelog, Pricing, Billing, Devices, Invoices, Feedback, Triage, Users, Subs) alongside the brand, theme toggle, account name, and sign-out button. The signed-in links are now grouped into two dropdown menus: an Account menu (Billing, Devices, Invoices, Feedback, Profile) and, for admins, an Admin menu (Triage, Users, Subscriptions). Changelog and Pricing stay top-level, and a group highlights when one of its pages is active. The dropdowns reuse the JS-free disclosure pattern from the mobile menu, close on outside click or Escape, and the signed-out and mobile navigation are unchanged.

Admin AI settings + one-click OpenRouter runs for IntelliSense feedback

Admins can now run an IntelliSense-feedback report through a hosted model without leaving the store. A new Admin → Settings page (visible and reachable only to admins) hosts an AI section for the IntelliSense-feedback test generator: an autocompleting model picker backed by a cached OpenRouter catalog, a Refresh model cache button that pulls the live list, an editable system prompt, and editable copies of each fixed block that wraps the generated prompt (intro, schema-builder reference, harness reference, deliverables). Leaving a prompt field blank resets it to the built-in default.

On the IntelliSense feedback page, the existing "Build an LLM prompt" panel gains a Run with model button next to "Generate prompt". It assembles the same prompt from the checked sections, sends it to OpenRouter under the configured model and system prompt, and shows the model's answer inline with a copy button — the manual copy-into-an-LLM round trip is now optional. The button is disabled with an explanation when OpenRouter is unconfigured or no model is picked.

Configuration and storage: OpenRouter is reached with a small typed HTTP client (no heavyweight SDK) using its OpenAI-compatible models-list and chat-completions endpoints. The API key comes from a new configuration setting, wired for the App-Service-setting-bound-to-Key-Vault pattern the store already uses. A new general-purpose key/value settings store (with JSON and in-memory backends to match the other stores, plus a database migration) persists every AI knob, including the cached model list, so nothing resets on restart.

Security hardening pass on the store API

A full security review of the store API surfaced a handful of real exposures, all now fixed with regression tests (180 tests passing):

The auto-billing sweep was anonymous. Anyone could hit the auto-billing endpoint and trigger the renewal run across every subscription. It now requires a store admin, backed by a new centralized admin authorization policy instead of ad-hoc checks.
Stripe webhooks could skip signature verification. When no webhook secret was configured, forged webhook bodies were parsed and acted on. Verification is now mandatory outside Development, startup refuses a Stripe secret without a webhook secret, and the webhook payload is size-capped. The webhook route also now honors configuration supplied by host overrides.
Password-reset links trusted the Host header. Reset emails are now built from a configured canonical origin (required in production) so a forged Host header can't redirect reset tokens to an attacker's domain. Development keeps the request-origin fallback.
Session cookies weren't explicitly hardened. Both the main and external auth cookies now set HttpOnly, SameSite=Lax, and Secure (the latter pinned to always-on outside Development). Access tokens now carry unique IDs and issued-at timestamps.
No abuse throttling. Sign-in, registration, password reset, passkey assertion, contact, waitlist, device pairing, and feedback submission endpoints now have per-user/per-IP rate limits (configurable, returning 429 with Retry-After).
Missing browser defenses. Responses now include HSTS (outside Development) plus X-Content-Type-Options, Referrer-Policy, X-Frame-Options, and Permissions-Policy headers.
CSRF gap on cookie-authenticated API calls. Passkey registration calls from the browser must now present an antiforgery token (the account page issues one and its script sends it); bearer-authorized app clients are exempt.
Unsafe production configuration failed open. The server now refuses to start outside Development when the JWT signing key is missing or weak, Stripe lacks a webhook secret, passkey origins point at localhost, the canonical origin isn't HTTPS, the storage provider name is unrecognized, or in-memory persistence is selected.
API metadata leaked in production. The OpenAPI document endpoint and its generation services are now Development-only; outside Development the document isn't served at all.

Home page: "Human designed. AI assisted. Human verified."

The home page gains a new section right below the hero that explains, in plain terms, how SQLly is actually built: LLMs write much of the code, but every feature runs a gauntlet first — a hand-written spec, an adversarial review of that spec (extra edge cases, security issues, second-order effects), hand-written tests that define "done", a committee of models implementing, adversarial reviews and a verification pass where the models pick each other's work apart, and a final human review before anything ships. The section also credits a home-grown review plugin that haggles over proposed solutions and verifies implementations, and notes that plenty of code is still written entirely by hand. It appears as a dark navy band between the hero and the features grid, with a numbered six-step layout that collapses on smaller screens and joins the existing scroll-reveal choreography.

Scroll progress rail on every page

The gradient progress bar that fills horizontally as you scroll — previously a homepage-only touch — now appears at the top of every page on the site. The rail's markup moved into the shared layout and its styles into the shared stylesheet, so all pages (Features, Pricing, Changelog, account pages, legal, everything) get it for free. It remains a pure CSS scroll-driven animation: no JavaScript, and visitors without scroll-timeline support or with reduced-motion enabled simply never see it.

Home page: trim "how it's built" step 4 copy

The "committee of models" step in the home page's build-process section no longer ends with the "whichever is sharpest for the job" aside; it now simply states which models implementation is split across.

Pricing page: who pays, and a real free-tier limit

The pricing page now leads with the permanent deal instead of the development-phase one: the hero headline is "Free forever. Judgy and disappointed if you don't pay." (moved up from the nag-policy section, which is retitled "The judgment starts on day 91."), and the "pricing for the 1.0 release" line is gone from the paid-plans intro. A new "Company laptop? Company card." section spells out who should pay: SQLly on a company-provided device means the company should buy the license, while a personally bought device is yours to use however you like within your license's device installs. It also states the one hard rule — organizations with more than 100 employees may not use the free tier inside the organization.

The terms of service now say the same thing in binding language: the free-tier section gains the 100-employee prohibition (a terms violation, not a joke), the company-device expectation, and the personal-device freedom, and the TL;DR carries both rules too.

Clarified device licenses for personal and company hardware

The pricing page and terms now make hardware ownership irrelevant to license validity: a personally purchased license may be used on a company-owned device, and a company-purchased license may cover a personally owned device when company policy allows it. They also state the business-use boundary directly: each work computer consumes a registered device install, and a device license cannot be treated as a user license that follows an employee across every computer or rotates among a team. The earlier suggestion that the company must pay simply because it owns the device has been removed.

Corrected the free-tier reminder schedule

The pricing page now consistently explains that payment reminders begin on the first day of unlicensed use and return about every five hours while the app is open. All references to a 90-day grace period or a day-91 start have been removed, including the page metadata, hero, example dialog, build-expiry explanation, and FAQ.

Made the permanent free-or-paid model explicit

The pricing page no longer describes SQLly as temporarily free during development or suggests that paid pricing arrives later. The current-build expiration badge, build-expiration FAQ, and future-pricing button are gone. The hero now presents the permanent choice directly: use the complete app for free with reminders, or buy a license to remove the reminders.

Removed the navbar download button

The signed-out navbar no longer shows the Get SQLly button. Download links remain available in the page content where they have context.

Refined the footer sign-off

The site footer now reads “Made with oinks, rust, and a passion for SQL.”

Fixed the changelog activity calendar layout

The activity calendar now places each week in its own column with Sunday through Saturday running vertically. It previously flowed dates across rows, so the weekday labels did not correspond to the cells beside them and the calendar was difficult to interpret.

Nav: Features first in the About SQLly menu

The signed-out "About SQLly" dropdown now lists Features before On-Device AI, matching the order the hamburger menu already used.

Reviews are open to every account, and now have their own page

Writing a review no longer requires a paid plan — any signed-in account gets one, edited in place, with the paid-plan gate removed from the profile page. Reviews now have a public home at /reviews: twenty per page, newest first, with a left-hand sidebar that filters by star rating (with counts) and a call-to-action that invites signed-in visitors to write their own (or asks anonymous ones to sign in). Each review shows its stars, date, whatever sections the author wrote, and the author's name, company, and job title — but only when the author explicitly consented to share their identity; everyone else is simply Anonymous. The homepage testimonial section now links to the real reviews.

Feedback moved to its own page

The contact form that lived at the bottom of the homepage now lives at /feedback, with the same honeypot-protected flow posting to the existing contact endpoint. The homepage section is gone, and every link that pointed at it (the footer, the pricing waitlist cards, the terms page, the on-device AI page, the engineering deep dive) now points at the new page.

Review cards put a face to the name

Each review on the reviews page now leads with the reviewer's identity — name in a larger font, with job title and company stacked on their own lines beneath it — instead of tucking a single attribution line at the bottom. The review sections (liked, didn't like, would improve) now show their label on its own line with the body below, and a horizontal rule separates each section so longer reviews are easier to scan. Anonymous reviewers still show simply as Anonymous, and identity remains consent-gated.

Profile saves now reach your published review

Saving your profile details (name, company, job title) on the account page now updates the attribution on your published review too. Previously the review kept the snapshot from the moment it was written, so details added later never appeared on the reviews page — it looked like the profile save had not worked. The review's consent choices are untouched, and a profile save does not count as a review edit, so it cannot bump the review to the top of the list. Also locked in that the local JSON-backed store persists the profile fields across restarts.

Downloads are counted per platform, architecture, and version

Every download link on the downloads page now routes through a tracking endpoint that records the download and then redirects to the file in blob storage. Each event is stored in a new download-events store with the caller's IP address, the platform as a single letter (W = Windows, L = Linux, A = Apple), the architecture (amd64 or arm64), the version, and the UTC time — so every exact file has its own counter, shown next to its download button. SAS download links are now minted on demand when a file is actually requested instead of for every artifact on every page view, and unknown or malformed file requests return 404 without touching the counters. As with the other stores, the download events persist to SQL Server in production, a local JSON file by default, and in-memory for tests.

Platform logos replace emoji on downloads and devices pages

The downloads and devices pages no longer use emoji (🍎 🪟 🐧) for platform indicators. Each platform now shows its official logo as an inline SVG: the Apple logo, the Windows 8-era four-pane window, and Tux the penguin for Linux. The SVGs are served as static files and rendered at 24px, scaled to fit, so they stay crisp at any DPI and align consistently with the surrounding text.

Local development can read release storage with a connection string

Running the site locally previously showed no downloads and no live changelog even though the storage account is full of release artifacts. Both features read from Azure Blob Storage using the deployed app's managed identity, which has no local equivalent, so the reads silently returned nothing. There is now an optional connection-string setting for each feature that, when present, is used to reach blob storage with shared-key access instead of managed identity. It is meant to live in local user secrets and stays empty in production, where managed identity continues to be used. When the downloads feature uses a connection string it also mints its per-file links with the shared key rather than a user-delegation key, since the latter requires an Entra ID sign-in.

Release builds now publish the changelog straight to blob storage

The changelog page on the store stopped showing new entries: the release pipeline was still packaging the changelog as a build artifact for the old website pipeline, but that website is retired and nothing ever delivered the artifact anywhere. The release pipeline now uploads every entry from the repo's changelog folder directly into the changelog container on the release storage account on every non-PR release run. The store's changelog page already reads that container live, so new entries appear as soon as a release build finishes — no site redeploy, no stale page. The upload is cumulative and overwrite-only, matching how the changelog folder grows in the repo.

Blocked by an Azure SQL firewall? SQLLY now offers to fix it

Connecting to an Azure SQL server from an address the server has never seen fails with error 40615 — "Client with IP address '…' is not allowed to access the server". Until now that message just landed in the results pane, and the two fixes Azure suggests are both awkward from a SQL client: open the Azure portal, or run sp_set_firewall_rule on master — the very database the firewall is refusing to let you reach.

SQLLY now recognises the block and asks whether to open the firewall for you. The prompt names the server, the exact IP address the server reported as blocked, and which signed-in Azure account the change would be made with; you choose the rule name (pre-filled as SQLLY-<you>-<date>) and, if you want a range rather than a single address, the start and end IP. Nothing is sent to Azure until you press Add Firewall Rule. Re-using a rule name updates that rule instead of piling up duplicates, and the confirmation notes that Azure can take up to five minutes to apply the change, so an immediate reconnect may still be refused.

The change is made as you, with your own Azure permissions — no extra credential is involved. If your account is not allowed to modify the server's firewall, SQLLY says so plainly: you do not have this permission, and you will need to ask an Azure administrator (or anyone with the SQL Server Contributor role on that server) to add the rule. It never retries or works around the refusal. If the connection uses SQL authentication rather than a Microsoft Entra account, there is no Azure credential to act with, and the dialog explains that instead of offering a form that could only fail.

The prompt appears from whichever operation hit the block — running a query, running a procedure, or expanding a server in the explorer — and names the server that actually failed, which need not be the one the current query tab is connected to. Only one prompt appears at a time, since a blocked connection usually fails several operations at once. The existing manual command (Tools ▸ Add My IP to Firewall) is unchanged.

v0.18.3
Aug 2, 2026 · 15 sections
Aug 2, 2026
15 sections

The app can now sign in to the SQLLY store and register itself as a device

The desktop app learned how to authenticate against the store: a new sign-in window (reachable from the "Sign in" link in the free-tier banner) takes the user's store email and password, exchanges them for a JWT, and immediately registers the machine on the account. Registration sends the OS (macOS/Windows/Linux), the hostname as the device name, the app version, and a one-way machine fingerprint — a SHA-256 hash of the OS's stable machine identifier. The raw identifier never leaves the machine; only its hash is transmitted. The signed-in session (token + device id, never the password) persists in the OS keychain, so the app stays registered across launches. The store base URL defaults to https://store.sqlly.app and can be overridden for local development.

Device registration now keys on the machine fingerprint, not the hostname

The store's device model gained a required machine-fingerprint field, and registration is now idempotent per physical machine: the same machine re-registering (relaunch, reinstall) refreshes its existing record instead of consuming another install slot, while two different machines that happen to share a hostname correctly count as two devices — closing the "rename every laptop the same" allowance loophole. Ships with a database migration adding the machine-fingerprint column and an (account, machine) index, so the Azure SQL schema upgrades itself on next deploy.

Tests. 100 .NET tests (new: same-machine re-registration dedupes, same hostname on different machines counts twice, blank machine id rejected, unregistered machines can re-register fresh) and 1737 Rust tests (fingerprint is a deterministic 64-char digest, request/response JSON matches the server's camelCase schema, session serialization round-trips, expired sessions are detected).

Release v0.19.0

Version bumped from 0.18.3 to 0.19.0 across the Rust workspace, tagged as v0.19.0. This release ships the free-tier messaging (status-bar chip and query banner), the SQLLY store sign-in window, and machine-fingerprint device registration on both the app and the store server.

"Improve IntelliSense" feedback: F2 captures the whole completion context

The editor can now report a bad completion popup straight from the keyboard. Pressing F2 while the IntelliSense popup is open freezes everything the completion engine saw — the SQL before and after the caret, the suggestions it offered (with kinds, sources, and scores), the user's IntelliSense rule configuration, a summary of the schema catalog, and how the popup was triggered — Brotli-compresses it, and opens a small dialog asking what should have been offered instead. Submitting sends the capture to the store.

Every submission is signed with an ECDSA P-256 key baked into the app, and the server verifies it against the matching public key — proof the request came from a genuine app build, so anonymous junk stays out. The checks fail fast: missing or stale signature headers are rejected before the request body (which can be up to 100 MB) is ever read. The app version travels as a signed header. Sign in to the store first and the submission is linked to the account, with a snapshot of the current subscription attached so reviewers can see the plan context; signed-out submissions work too, just anonymously.

On the store site, a new Feedback page lists the user's own suggestions with their status — submitted, reviewing, planned, rejected, implemented, or withdrawn — and both the user and the SQLly team can discuss each one in comments. Withdrawing is always available to the owner. A new admin triage page (gated by an admin allow-list) lets the team move suggestions through the pipeline, reply in comments, and inspect the captured context, which the server Brotli-decompresses for display. Ships with a database migration adding the feedback and comments tables.

Tests. 110 .NET tests (new: unsigned/stale/tampered submissions are rejected, anonymous and subscriber submissions are accepted, the subscription snapshot is attached for subscribers and absent for free accounts, only the owner can see/comment/withdraw, admins can change status and comment but not withdraw, bodies past the web server's 30 MB default are accepted) and 1747 Rust tests (new: caret splitting including mid-character clamping, suggestion and catalog capture with truncation, Brotli round-trip, request signing verifies against the baked public key, request body shape, F2 keybinding wiring).

macOS releases now build on a self-hosted agent with sccache

The release pipeline's macOS job moved off the hosted macos-14 image onto a self-hosted agent (the maintainer's Mac). Because the machine is already fully provisioned, the job no longer installs anything: the rustup target-add step is gone, and the data-grid crate checkout step is gone — that script wipes and re-clones the sibling crate checkout, which would have destroyed the agent's live development copy. The Rust build now compiles through sccache, so even though checkout: clean wipes the cargo target directory every run, unchanged crates never recompile and the stage stays fast.

Release v0.20.0

Version bumped from 0.19.0 to 0.20.0 across the Rust workspace, tagged as v0.20.0. This release ships the "improve IntelliSense" feedback pipeline (F2 capture in the app, signed submissions, feedback and admin triage pages on the store site) and the self-hosted, sccache-accelerated macOS build.

Store site nav: Sign in moves with the hamburger, rightmost on wide screens

On the store website, the "Sign in" button now follows the same responsive rules as the rest of the navigation: on narrow screens where the nav links collapse into the hamburger menu, "Sign in" disappears from the header bar and lives at the bottom of the hamburger panel instead. On wide screens it moved to the far right of the header, after the "Get SQLly" download button, so the account action sits in the conventional top-right spot. Updated the shared layout and navigation styling.

Store admin access is now role-only, and the feedback pages are linked

Two related changes on the store site. First, the admin-email config allow-list is gone entirely: it used to seed the admin role at startup and at registration, but admin membership now lives exclusively in the roles table and is managed from the admin Users page — anyone with the admin role can open the triage and user-management pages, no config entry required. Existing admins keep their role (it was already persisted in the database); on a brand new deployment the first admin is granted by flipping the role directly in the database. Second, the IntelliSense feedback pages are now actually reachable: signed-in users get a "Feedback" link in the wide top nav (it was previously only inside the mobile hamburger menu) and in the footer's Account column, and admins additionally get "Triage" and "Users" links in both the wide nav and the hamburger menu. All 132 .NET tests pass (the bootstrap-email registration test was removed with the feature).

Store sign-in is now browser device pairing (no password in the app)

The app no longer collects a store email and password. Instead it pairs like a streaming-TV device: the sign-in window asks the store for a short pairing code (XXXX-XXXX-XXXX) the moment it opens — creating an unclaimed device row and reusing a keychain-stored pairing on later launches — and shows the code with a "Log in to activate" link. That link opens a device-registration page in the browser, where the user can sign in with any provider (Google, Microsoft, GitHub, Apple, a passkey, or email) and claim the device to their account. The app quietly polls the store with an opaque secret until the claim lands, then stores the issued JWT in the keychain. Because the secret keeps working while the device is active, it also silently re-issues tokens after the JWT expires; unregistering the device on the web revokes it. The store's device record now allows an unclaimed state (the row exists before it's claimed), with new pairing-code, secret-hash, expiry, and claimed-at fields and a browser claim page.

IntelliSense feedback is keyed by device, not subscription

Feedback submissions now carry the paired device id and drop the subscription snapshot: the feedback records lose their subscription-plan fields and gain a device reference. A submitter's "my submissions" view still works — it resolves the account's devices and lists the feedback owned by them — and the admin triage page shows the originating device id in place of the old plan label. Ships with one database migration covering both the device-pairing fields and the feedback change.

Tests. 137 .NET tests (new: pairing code issuance/format, claim links the account and consumes the code, expired codes can't be claimed, unregistering stops token issuance, code normalization, device-column round-trips, feedback resolves through the claimed device) and 1755 Rust tests (new: pairing request/response JSON contract and keychain round-trip, feedback body carries the device id).

Release v0.21.0

Version bumped from 0.20.2 to 0.21.0 across the Rust workspace, tagged as v0.21.0. This release ships browser device-pairing sign-in in the app and device-keyed IntelliSense feedback on the store.

IntelliSense feedback page fixed and now shows admins every submission

The "Feedback" link on sqlly.app led to a 404: the page only answered at one URL while the nav pointed at the hyphenated /intellisense-feedback. The page now claims the hyphenated route like its sibling pages, so the link works. It also got smarter about who sees what: admins browsing it now see every submission in the store (each with the originating device id and a jump into the triage page), while everyone else sees only the feedback tied to their own account as before. Owner-only actions (withdraw, comment) stay hidden on submissions that aren't yours.

Tests. 140 .NET tests (new: the hyphenated route resolves instead of 404ing, a signed-in user sees only their own submissions on the page, an admin sees everyone's).

IntelliSense feedback page becomes a master-detail workbench

The feedback page on sqlly.app grew up from a flat list into a full workbench. The page is no longer width-capped: a 300px master list sits on the left and the detail pane takes the rest of the screen, defaulting to the newest submission. Each suggestion gained four new fields — a short name, affirmative test instructions, negative test instructions, and additional details — and together with the original description they're editable right on the page. Editing follows the triage lifecycle: the submitter can keep polishing while the suggestion is still open (Submitted or under review), and once the team has decided (planned, rejected, implemented, or withdrawn) only admins can make changes. Ships with a database migration adding the four fields.

For admins, the detail pane hides a gem: a "Build an LLM prompt" panel with checkboxes for every piece of the submission (the five fields, capture metadata, the SQL at the caret, what the popup offered, the schema catalog snapshot, and the raw rules JSON). Generate, copy, and paste into your favorite model — the prompt asks for a schema-builder function that recreates the needed schema plus positive and negative completion tests written in the style of the IntelliSense test cookbook, with the harness cheat-sheet baked in so the model gets it right the first time.

Tests. 158 .NET tests (new: owner/admin/stranger edit-permission matrix across every status, field validation and blank-to-null normalization, prompt section include/omit coverage, master-detail rendering with first-item selection, the edit form disappearing after triage decides, end-to-end edits and prompt builds through the page with antiforgery, and the prompt builder always carrying the task framing and cookbook references).

Admins can comp subscriptions

The store learned about no-charge subscriptions: a new admin page (/admin/subscriptions, linked as "Subs" in the admin nav) lets an admin search for an account and grant it any paid plan for free — optionally with an expiry date (end-of-day UTC), a reason, and a record of who assigned it. Comps coexist with an organic Stripe subscription (an account can now hold several subscriptions, so the per-account uniqueness was dropped), and revoking a comp deletes just that record without touching anything the customer paid for. Ships with a database migration for the new subscription fields.

Tests. 158 .NET tests total, including new comp coverage (assign, expiry date handling, revoke, organic subscription untouched, listing per account).

Release v0.22.0

Version bumped from 0.21.0 to 0.22.0 across the Rust workspace, tagged as v0.22.0. This release ships the IntelliSense feedback workbench on sqlly.app (master-detail layout, editable suggestion fields, admin LLM-prompt builder) and admin-comped subscriptions on the store.

v0.17.1
Aug 1, 2026 · 8 sections
Aug 1, 2026
8 sections

The sync server is now a real customer portal: accounts, billing, devices, invoices

The server area grew from a storage stub plus a demo subscription page into the full backend the pricing page promises. A customer can now sign up, sign in, buy a license, register their machines, and read their invoices — all in a portal that looks like the marketing site, because it shares the site's stylesheets, fonts, nav, and footer.

Accounts and sign-in. Email/password registration and sign-in work out of the box, with password reset via emailed tokens. Google, Microsoft, GitHub, and Apple sign-in are wired and appear automatically once their client credentials are configured — unconfigured providers stay hidden. Passkeys (WebAuthn) are supported end to end: register a passkey from the account page, then sign in with it from the login page. API clients authenticate with JWT bearer tokens; the desktop client's development bearer header still works in Development so nothing breaks while it migrates.

Billing that matches the pricing page. Free, Annual ($100 first year, then $50/yr), and Lifetime ($500 once) plans, with Cloud and Cloud Business marked Coming Soon and joinable through a waitlist. Stripe is fully wired — Checkout Sessions, customer management, cancel-at-period-end, and a webhook that records renewals, generates numbered invoices (SQLLY-2026-0001 style), switches annual subscriptions from the first-year price to the renewal price after year one, and marks lapsed payments past due. The only setup required is dropping the Stripe keys and price IDs into Azure Key Vault (or local configuration); without keys the server falls back to a stub gateway with a local pretend-to-pay page so the whole flow stays exercisable in dev and CI.

Registered devices. Each license allows two installs per platform (macOS, Windows, Linux) on the honor system. The Devices page lists every registered machine, flags any beyond the allowance, and the sync endpoints update a device's last-seen time whenever it syncs.

Encrypted sync is now account-scoped. The zero-knowledge storage endpoints require a real account token instead of trusting any header, keeping the model intact: the server still only ever sees ciphertext.

Persistence. Everything — accounts, passkeys, devices, licenses, invoices, waitlist — persists to atomic JSON file stores by default, switches to in-memory for tests with one config flag.

Tests. 65 tests pass, including story tests that drive the whole server end to end: sign up → buy annual → hit the device honor limit; renew at $50 a year later; cancel and keep access until expiry; lifetime buys once and never renews; password reset never leaks whether an email exists; forged passkey assertions are rejected; the marketing shell renders on every portal page.

The sync server now stores its data in Azure SQL and deploys itself

The portal's data — accounts, passkeys, devices, licenses, invoices, waitlist — used to live in JSON files on the app server's disk, which a cloud App Service would lose on every reimage. It now runs on the provisioned Azure SQL database, authenticating with the web app's managed identity, so there are no database passwords anywhere. JSON files remain the zero-setup default for local development, and tests run on in-memory stores; switching is one config setting.

The schema is code-first: EF Core migrations are checked in with the app, and the app applies them itself on startup (one-time db_ddladmin grant required — documented in the server README). A new Azure DevOps pipeline builds and tests the server whenever the server code changes, and from main deploys it straight to the store Web App and stamps its production settings. One manual step remains: create the ARM service connection and put its name in the pipeline's service-connection variable.

Portal logo links back to the marketing site

Clicking the SQLly logo/title in the portal's top-left nav now goes to https://sqlly.app instead of the portal's own home page.

Passkeys and social sign-in fixed on store.sqlly.app

Two production config bugs surfaced after the first deploy. Passkey sign-in failed with "relying party ID is not a registrable domain suffix" because the deploy pipeline stamped the Fido2 domain and origins under setting names the app never reads — production kept the localhost defaults. The pipeline now stamps the correct names. And the Google, Microsoft, and GitHub buttons were missing from the sign-in/register pages because the OAuth client credentials weren't reaching the app; the README now documents the exact Key Vault secret names and the app logs which providers it detected at startup so a misnamed secret is easy to spot in the log stream. The "skip to content" link was also removed from the portal chrome.

Portal reads the Key Vault secrets as they actually exist, and tidies its chrome

The OAuth credentials in the Key Vault were stored under names the app didn't read — a legacy naming scheme for GitHub and Google, and a separate Azure AD naming for Microsoft — so no social sign-in buttons appeared. The app now accepts those names directly (the canonical external-provider names still win when both exist), honors the callback-path values stored with them, and uses the stored tenant id to target the right Microsoft tenant — required for single-tenant app registrations. The SQL connection string likewise falls back to a default-connection setting name.

Portal polish from the same pass: the Billing/Devices/Invoices/Account links now only appear once you're signed in (desktop nav, mobile menu, and footer alike), and the footer's Legal-ish links (Privacy, Terms, Licenses) point at the sqlly.app pages instead of dead # anchors.

Half-configured social providers no longer take the site down

After the vault-alias support deployed, every request to store.sqlly.app started failing with The value cannot be an empty string (Parameter 'ClientSecret'): a provider was registered on the strength of its client ID while its client secret resolved empty (in this case the Microsoft secret had been pasted as a Key Vault secret *name* instead of the value of the Microsoft client-secret setting). OAuth validates that pairing on first use and threw inside the authentication middleware. A provider whose credentials are incomplete is now skipped at startup — with a warning in the log stream naming the provider and what's missing — instead of crashing every request.

Google/Microsoft/GitHub sign-in callback no longer 500s

Completing a social sign-in returned a 500 (The given key 'LoginProvider' was not present in the dictionary). The callback read the provider name from an authentication-properties item that only ASP.NET Identity writes — and this app doesn't use Identity, so the key was never there and the dictionary lookup threw before the fallback could run. The callback now looks the item up safely and falls back to the handler's authentication type, with a friendly error page instead of an exception if the provider can't be identified at all.

Free-tier messaging: the app now gently shames non-paying users

The desktop app gained a licensing flag (a paying-user flag, hard-coded false until the entitlement store lands) and two surfaces that appear while it is false. The status bar shows a vibrant chip confessing "I am too cheap to pay for an app I clearly love", and the query window grew a loud full-width banner under the server/database toolbar saying the same, with a link to https://www.sqlly.app/pricing.html. Both the chip and the banner link open the pricing page in the browser. When a real purchase flow arrives, only the flag's body changes — the messaging turns itself off.

v0.17.0
Jul 31, 2026 · 9 sections
Jul 31, 2026
9 sections

Browsing the server explorer no longer moves your query's connection

Clicking a server or one of its databases in the explorer used to connect to it — silently retargeting the active query tab, its completions, and whatever you ran next. Opening one server to look up a column name could send the next execution somewhere else entirely.

Clicking now only selects and expands. The explorer still loads each server's databases and objects on demand, using that server's own saved credentials, so browsing several servers side by side works exactly as before — it just leaves the query tab alone. Connecting is now a deliberate act: press Enter on the selected server, choose Connect from its right-click menu, or pick it from the server dropdown in the query toolbar.

Discovered Azure databases follow the same rule. Clicking one still adopts it as a saved connection — that is what makes it usable — but no longer connects to it, so a newly discovered database cannot hijack the query tab either. Its Link & Connect menu item does both, as its name says.

Connection lists show your names, not hostnames

The connection tree in the Connection Management window labelled each server with its hostname, so a tree full of sql-prod-03.internal said nothing about which connection was which. Server rows now carry the connection's own name, falling back to the hostname only when a connection has no name. Two differently-named connections pointing at the same machine now get a row each, instead of being merged under one label that matched only one of them.

Saved accounts fill in and lock the credential fields

Choosing a saved SQL account in a connection dialog now fills in its username, locks both the username and password fields, and shows a fixed-length masked stand-in for the password — which lives in the system keychain and is never loaded into the form. Previously the password box simply sat empty, which read as "no password saved". Clicking the chosen account again releases the fields if you want to type a one-off login instead. Both connection dialogs behave the same way.

Production connections may trust the server certificate

Saving, testing, or connecting with a production profile that trusts the server certificate was blocked outright. Plenty of real production servers sit behind internal-CA or self-signed certificates a client machine cannot chain, so this is now a choice rather than an error: the connection preview still warns that TLS identity is not being verified, but nothing refuses to save or connect.

Client, project, and environment chips carry their colours

The colour and icon you assign to a client, project, or environment now show up on the matching chips in the status bar: the colour fills the chip, the icon sits beside the name. The chip label automatically switches between light and dark ink so it stays readable on any colour you pick. Entities with nothing assigned keep the plain outlined chip.

One way to capture an execution plan

Estimated execution plans are retired. SQL Server requires SET SHOWPLAN_XML to be the only statement in its batch, so the estimated capture failed on every real query it was pointed at — a control that could not work is worse than a missing one. Its toggle is gone, as is the estimated/actual switch that went with it (there is no longer a kind to choose).

The Explain button is gone too, replaced by a single plan button in the toolbar's icon cluster that captures the actual plan for the current query. ⌘L, the command palette, and the Query menu still do the same thing.

Copy an error straight off the status bar

Error and warning messages in the status bar are now clickable: one click puts the full text on the clipboard and confirms it did. Database errors are exactly the messages worth keeping — for a ticket, a search, or whoever runs the server — and the status strip both truncates them and cannot be selected with the mouse.

Tidier query toolbar and a tighter explorer

Everything in the query toolbar is now one height: the server and database dropdowns line up with Run and Explain instead of sitting a few pixels short, and every icon button matches them.

The toolbar's icons were text characters, and a font draws each character at its own size and weight — so even in identically sized buttons, no two icons matched. They are now proper drawn icons on a shared grid, at one size derived from the height of the Run button, so the strip finally reads as a single row of controls. Run, plan capture, result diff, procedure mode, and the result layout toggle wear SQLLY's own artwork; the rest come from the icon set the component library ships. All of them are compiled into the application.

The result layout button now shows the layout you are in rather than the one a click would switch to: stacked panels when result sets are stacked, a tabbed panel when they are in tabs, with a tooltip that says both.

The server explorer's per-level indentation is 25% tighter, so deep schema paths spend less of the rail on empty gutter.

v0.17.1 — housekeeping

No behaviour changes. A workspace-wide format and lint sweep (with warnings treated as errors) came back clean apart from some stale formatting in the execution plan pane, which this release tidies.

v0.15.0
Jul 28, 2026 · 4 sections
Jul 28, 2026
4 sections

Execution plan viewer (SQL Server)

The workbench can now capture and explain SQL Server execution plans. A new Explain button sits next to Run in the query toolbar, with ⌘L as its shortcut (Ctrl+L on Linux/Windows), plus command-palette and Query-menu entries. Explaining opens a new Execution Plan pane in the results area, alongside Results and Messages, so the plan lives where the results already are.

The pane leads with plain-language findings, ranked by cost: missing indexes (with a ready-to-adapt CREATE INDEX skeleton), table scans, implicit conversions, lookups, tempdb spills, join-predicate warnings, and — for actual plans — estimates that diverged wildly from reality. Each finding says what is wrong, why it matters, and what to do next; expanding a row reveals the full technical detail. Below the findings, a compact operator list ordered by cost offers the same expand-for-detail treatment. Clean plans say so explicitly instead of showing an empty list.

Estimated plans (the SSMS default) are captured without running the query; a toolbar toggle switches to actual plans, which run the query under SET STATISTICS XML and power the estimate-vs-reality findings. The toggle pair is mutually exclusive, matching SSMS. Multi-statement batches get a statement switcher in the pane header with per-statement findings, and the footer toggle shows the finding count. The pane is fully keyboard-navigable (up/down/enter/space).

Explain is enabled only for SQL Server and Azure SQL connections; on other databases the button stays visible but disabled, with a tooltip explaining why. Server errors (for example a missing SHOWPLAN grant) surface verbatim in the pane, and a re-capture button makes retrying one click.

Under the hood the ShowPlan parser now handles multi-statement batches in one document and extracts richer warning detail — missing-index column lists, scan/lookup object names, tempdb spills — which is what makes the findings actionable rather than generic.

Procedure mode: edit, deploy, and run stored procedures in one gesture

Query tabs now understand stored procedure scripts. When a script begins with CREATE PROCEDURE, ALTER PROCEDURE, or CREATE OR ALTER PROCEDURE, the new Proc toggle in the query toolbar glows to offer the mode; turning it on opens a parameter strip between the editor and the results grid.

The strip reads the procedure header and builds one row per parameter: name, data type, a NULL checkbox, and a value editor matched to the type (bit parameters get a checkbox, everything else a validated text field with per-type placeholders and inline error messages). Parameters with defaults can be left blank to use the default. Output parameters show their returned values in a trailing cell, and the strip header shows the procedure's return code after a run.

Running in procedure mode deploys first, then executes. If the script says CREATE but the procedure already exists on the server (or says ALTER and it does not), the deploy verb is corrected automatically, so the same script works against fresh and existing databases without manual edits. The run then invokes the procedure with the supplied arguments; result sets flow to the grid as usual, while return code and output values land in the strip.

A Rollback checkbox in the strip wraps the whole deploy-and-invoke cycle in a transaction that is always rolled back, so nothing persists: the procedure definition, its side effects, everything. That makes iterating on a procedure against a shared database safe, since repeated runs leave no trace.

Editing the script re-reads the header as you type and keeps entered values for parameters that still exist, so refining a procedure body does not wipe the arguments you were testing with. Selection is ignored in procedure mode: the whole buffer is always the unit of work, matching how procedures are authored.

### Under the hood

New script detection, header parsing, deploy rewriting, invocation batch building, per-type literal validation (14 tests).
Module registration.
New per-tab mode state, parameter row reconciliation, run orchestration with probe, deploy, invoke, rollback, and sentinel peeling (11 tests).
Tabs carry procedure mode state.
Proc toolbar toggle with detection glow, parameter strip rendering between editor and results, run dispatch.
Added the query crate dependency.
New design brief.

Procedure mode — hardening and polish pass

Procedure mode now validates parameter values as you type, so a malformed date or an out-of-range integer shows its message under the input before you ever press Run (blank stays quiet, since blank means "use the default"). The parse-failure note now includes the actual parser reason, the Rollback checkbox only appears when a procedure header was really parsed, and non-editable parameters explain themselves precisely: read-only table parameters say an empty table is passed, and unsupported types say whether the declared default will be used or no value can be supplied at all. Long procedure names truncate in the strip header instead of pushing controls off screen, datetime values accept fractional seconds up to the server's seven-digit precision and reject trailing dots or junk fractions, the detection glow keeps its hover feedback like every other toolbar toggle, and the glow now also appears when a procedure script arrives without typing: opening a .sql file, restoring a session, replaying a history entry, or running SQL from the AI panel.

Prerelease builds now carry a build date and expire after 365 days

Every build now stamps its UTC build date into the binary at compile time. A prerelease build keeps working for 365 days from that date; on the expiration date the app no longer starts the workspace — it shows a small panel explaining what happened, when the build expired, and where to go, with a Quit button.

The About window (SQLLY menu ▸ About SQLLY, or Help ▸ About on Linux/Windows) now leads with the version and build date and clearly states that this is a prerelease version that will stop working on the expiration date, with a link to sqlly.app for getting a newer build or paying to keep using it.

During the last 7 days before expiration, a centered warning in the status bar announces the cutoff date; clicking it opens the About window. Clicking the Dock icon on macOS after expiry reopens the expired-build panel instead of the workspace.

v0.13.9
Jul 27, 2026 · 5 sections
Jul 27, 2026
5 sections

Query version history

Every query tab now keeps a restorable timeline of its editor text. A version seals automatically each time the query runs, and whenever the user edits the text and then pauses (five seconds by default, configurable down to one). Versions are listed newest-first in a new per-tab History side panel — opened from the query toolbar or with ⌘⌥H — where run versions are marked with an accent dot and edit snapshots with a hollow one. Double-clicking a version (or selecting it and choosing Restore) is never lossy: the current editor text is sealed as a version first, then the chosen version's SQL loads into the editor. ⌘⌥← / ⌘⌥→ step backward and forward through versions without opening the panel.

Optionally, each run's result sets are captured with its version (binary-encoded and gzipped to keep the store small; off by default). Restoring such a version re-renders its results in the grid read-only, under an informational banner so restored data is never mistaken for live results; running the query or dismissing the banner clears it.

History lifecycle is deliberate: closing an untitled tab deletes its entire history (SQL index and result payloads), while tabs backed by a real file keep their history across sessions and tab closures. File-backed tabs can also show a read-only git local-history section — recent commits touching the file — in the same panel (opt-in). Storage is bounded by configurable caps (100 versions and 25 MiB of results per tab, 1 MiB per version), and when caps are exceeded result payloads are evicted before SQL entries.

All of this is configured from the new Preferences > Editor > Query History pane. The full design brief lives in the project's feature docs.

### Under the hood

New storage engine for query version history.
New timeline side panel.
Sealing triggers, restore flow, panel wiring, tab-close purge, toolbar toggle, keybinding actions.
Read-only historical-results rendering with the informational banner.
Per-tab history state.
Settings model and the new Query History pane.
Shortcuts and command registration.
Contrast coverage for the banner marker.
Gzip support for result payloads.
New design brief.

Query version history — hardening and polish pass

The new history store is now crash-safe and failure-honest. Index and result payloads are written atomically (temp file + rename), so a crash mid-write can no longer corrupt them. A previously corrupted index is preserved as a marked-corrupt copy instead of being silently discarded, and restoring a version whose stored results are missing or undecodable now restores the SQL anyway and tells the user the results couldn't be loaded — likewise, restoring a version that was evicted between click and restore explains itself in the status bar instead of doing nothing. New tests cover the atomic-write, corrupt-index, and corrupt-payload paths.

The History panel's chrome was aligned with the DDL panel so the two right-side panels read as one family: matching header padding and close button, a version-count subtitle that also indicates when result storage is on, and footer actions restyled to the house button treatment (accent-pill Restore, quiet Delete, both clearly disabled with nothing selected). The "no results" annotation on run rows now only appears when result storage is actually enabled, and the historical-results banner's Dismiss action gained a hover state.

### Under the hood

Atomic writes, corrupt index preservation, three new tests.
Restore-failure status messages.
Panel chrome alignment.
Banner Dismiss hover state.

Object editor dialogs (Properties, rebuilt as a true editor)

Right-click → Properties in the server explorer now opens a live editor instead of the old read-only template dialog — for tables, views, procedures, functions, and (new) indexes, statistics, and user-defined types. Every page runs its metadata query against the server when first opened and renders real results: property sheets for scalars, sortable/filterable data grids for columns, indexes, statistics, constraints, triggers, parameters, and permissions, and a syntax-highlighted source pane for view/procedure/function definitions. Pages lazy-load with skeleton placeholders, offer Retry on failure, and show kind-appropriate empty states.

The explorer grew the nodes to reach every one of these objects: tables now have Indexes and Statistics folders listing their children (marked IX and ST in the gutter), and Programmability gained a Types folder (TY), each with its own right-click → Properties.

Editing is deliberately scoped and always reviewed. Rename the object, rename columns, flip a column between NULL and NOT NULL, toggle a statistic's auto-update (NORECOMPUTE), or queue an immediate statistics update — edits stage locally with a pending-change count and dirty dots on the affected pages. Apply diffs the staged changes into the exact T-SQL batch (sp_rename, ALTER TABLE … ALTER COLUMN, UPDATE STATISTICS) and shows it in a review sheet — with a plain-English summary, the highlighted statements, and a Copy button — before anything executes. Confirming runs the batch and refreshes; closing with unapplied changes asks first. Everything outside that facet set still Scripts to a query tab. Server and Database nodes keep the previous read-only dialog.

### Under the hood

New dialog shell, live fetching, apply review flow.
New pure page/parsing/DDL logic (19 tests).
Index/Statistic/Type nodes, folders, loaders, context menus, and connection profile carried in the action context.
Properties dispatch routing to the editor, event handling, overlay rendering.
New metadata queries (types, table/index/statistic detail, object definitions, permissions).
List-types and a generic metadata-query service method.
Statistic object kind.
New design brief.

v0.15.0 version bump

Bumped the workspace version from 0.14.0 to 0.15.0 and tagged the release as v0.15.0, so the client workspace, CLI, and desktop app all report the new version.

Security management dialog (SQL Server logins, users, roles, database access)

The Security… menu item now opens a real management dialog instead of a static SQL snippet, covering the day-to-day access work a DBA does against SQL Server: creating logins and database users, editing them, granting role memberships, and mapping logins into databases.

The dialog has two tabs. Logins lists server logins with their type and enabled state; the editor creates SQL-password or Windows (DOMAIN\user) logins, resets passwords, enables or disables the login, grants fixed server roles, and offers an SSMS-style database-access checklist where checking a database creates its user for the login and unchecking drops it. Users lists the users of any selected user database with their mapped login, and the editor sets the default schema and database role membership.

Trust is the design center: everything the dialog will do is shown first as the exact T-SQL batch in a live statement preview that updates as the form changes, and Apply runs only the statements that represent an actual difference from server state (membership and access changes are diffed, not replayed). Each statement is attributed in errors, so a mid-batch failure says exactly which change failed; the dialog then reloads truth from the server, preserves the form so nothing typed is lost, and re-selects the principal being edited. Validation catches overlong identifiers, duplicate names (case-insensitively, matching the server), malformed Windows logins, and missing passwords before anything runs, and all identifiers and literals are quoted through the same escaping helpers the rest of the app uses. While a batch is applying, the dialog refuses to close or mutate.

The full design brief lives in the project's design docs.

### Under the hood

New catalog queries (user databases, server roles, per-login memberships, login-to-database user mapping, users with their logins) and new writer statements (Windows logins, password reset, enable/disable, server role membership, default schema, drop user), with tests.
New dialog view plus the pure parsing/validation/diff model (24 tests).
Security… opens the dialog, overlay rendering and event wiring.
Module registration.
New design brief.
v0.12.0
Jul 18, 2026 · 2 sections
Jul 18, 2026
2 sections

Browser preview opens on a real sample database

The WebAssembly browser build now boots with a database already connected. The Northwind SQLite sample (from [jpwhite3/northwind-SQLite3](https://github.com/jpwhite3/northwind-SQLite3)) is staged into the site by the build and fetched on load; the explorer shows a seeded "Northwind (sample)" connection, already open, so tables, views, columns, foreign keys, and indexes are browsable and every query runs for real against in-tab SQLite from the first paint. If the sample can't be fetched, the app falls back to the emulated demo database (which also carries stored procedures), so the preview is never left without a backend.

The sample database is large (~24 MB) and is never committed: the browser build script downloads it once into a git-ignored cache under the browser app's web assets — skipping the download when the file is already present — and copies it into the distribution output for the app to fetch same-origin at runtime.

### Under the hood

Download-if-missing of the Northwind sample into a cached, git-ignored path and staging into the distribution output.
Ignore the cached sample database.
Fetch the sample before boot and hand its bytes to the browser entry point.
The browser entry point opens the bundled SQLite sample (or the demo database when it is absent) before the first connection.
Seed an idempotent, fixed-id sample connection profile on the browser build.
Seed the profile before the explorer loads and auto-connect it on first launch when there is no session to restore.

Browser preview labels illustrative surfaces

Surfaces whose real work a browser tab cannot do now say so plainly instead of silently failing. A "Preview" notice sits at the top of the New/Edit Connection dialog, the Connections & Settings manager, the Microsoft Entra accounts dialog, and the project-file picker, explaining that the surface is illustrative in the web version — connections can't reach SQL Server, Azure sign-in can't authenticate, and files can't be read from your computer; the app runs on the bundled sample database and the desktop app is where the real thing happens. "Add My IP to Firewall" reports the same up front rather than walking into an Azure call it cannot complete. Every notice compiles out entirely on the desktop build.

### Under the hood

New. The shared browser-only preview banner (a no-op on desktop).
The banner at the top of each dialog.
The firewall action short-circuits with the illustrative notice on the browser build.
Register the browser-preview module.
v0.11.0
Jul 17, 2026 · 7 sections
Jul 17, 2026
7 sections

Row editor: foreign keys, constraints, related data, insert & delete

The Edit Row dialog (Phase A) grew the remaining phases from its design plan.

Foreign-key autocomplete (Phase B). Foreign-key columns now render a searchable dropdown that queries the referenced table server-side (bounded to 50 matches, filtered on a Name-like display column when one exists and falling back to the raw key otherwise). Picking a candidate fills every column of the key structurally, so composite foreign keys stay consistent, and the raw key — never the display text — is what gets written. The current value's display label is resolved on open. Debounced, with keyboard navigation and stale-result guarding.

Check constraints & server preflight (Phase C). A small three-valued evaluator locally pre-checks the common CHECK shapes (comparisons, IN, BETWEEN, IS [NOT] NULL, and AND/OR combinations over columns and literals) and flags a violation on the offending field before a round trip; anything it cannot decide is deferred to the server. When the server rejects a save, the named CHECK or FOREIGN KEY constraint is mapped back to the specific field(s).

Related data (Phase D). Incoming foreign keys surface a read-only, lazily-loaded panel of child rows (bounded preview) with an "Open query" action that drops the full parameterless query into a new editor tab.

Insert, duplicate & delete (Phase E). New row-header actions "Insert Row..." and "Duplicate Row..." open the same form in an insert mode (identity/computed columns become read-only, a non-identity primary key is user-supplied, required fields are enforced, OUTPUT INSERTED.* returns the generated row). "Delete Row" lives in the edit dialog with a two-step confirmation that names the tables referencing the row. Inserts and deletes run as guarded, transactional, parameterized statements with the same optimistic-concurrency and mutation-policy handling as updates.

### Under the hood

FK/check/incoming-FK metadata loading, foreign-key search, related-rows, insert, delete, and insert-session operations, and the pure SQL builders/parsers behind them.
New. FK dropdown state, request shaping, debounce/staleness, keyboard navigation.
New. Three-valued CHECK evaluator.
FK fields, check evaluation, DB-error mapping, insert/duplicate mode, insert/delete request assembly.
FK controls, related panel, insert & delete flows, intent-aware titles/buttons.
Insert/Duplicate row-header actions.
Thread the edit intent through and open related-rows queries in a new tab.

Query workspace chrome, distilled

The query toolbar reads more quietly. Its controls are grouped by spacing rather than vertical separators — Run, then the Server/Database dropdowns, then the query toggles, then the schema-refresh and result-layout buttons — and the two dropdowns now carry "Server" and "Database" as their own placeholders instead of standing labels beside them. The editor footer bar, which used to sit under every editor solely to hold the zoom control, now appears only when AI summaries are turned on; the editor zoom stepper moved down into the bottom status bar beside the connection badges, so the common case is one fewer horizontal band.

### Under the hood

Toolbar cluster grouping and dropdown placeholders, the AI-summary bar made conditional, and the zoom control relocated into the status bar.

Server explorer filter field

The "Filter objects" field above the server explorer is now the app's standard text field. It scales with the interface font like the rest of the rail — previously it was frozen at a fixed size — and it gains the full set of text-editing shortcuts the old field silently dropped: Shift+arrows and Shift+Home/End extend a selection, Cmd/Ctrl+A selects all, and click-drag and double-click select with the mouse. Its height now matches the query bar's compact Server/Database dropdowns.

### Under the hood

The filter now renders the standard text field styled through the shared text-box style, wired to the tree via an observer (which keeps Escape-to-clear re-entrancy-safe), and sized to match the query-bar selects.

Maintenance

Applied rustfmt and fixed all clippy (warnings as errors) lints across the workspace (a redundant format reference and a ?-operator rewrite).

UI toolkit migration

The entire widget layer of the desktop app moved from the retired in-house widget toolkit (and the separate password-field crate) to a shadcn-inspired component library. Every stock control — text inputs, password fields, checkboxes, buttons, the Server/Database dropdowns, the query tab bar, the editor zoom slider, color pickers, and the explorer/editor context menus — is now the library's native widget, themed once through a global palette bridge instead of per-widget color overrides.

What this means in practice:

One theming path. The app's palette (all families and light/dark/high contrast modes, plus the user's font preferences) is pushed into the component library's global theme and re-synced automatically whenever the theme or OS appearance changes. Widgets can no longer drift out of theme individually.
Real input state. Text fields are stateful editor entities with proper focus, selection, undo history, and (new) a show/hide toggle on password fields — which let the separate password-field crate be removed entirely.
Better dropdowns and menus. Selects own their popup lifecycle (click-outside and Escape dismiss them without the old full-screen overlay hacks), and the explorer's context menu gained real nested submenus in place of flattened prefixed labels.
Icons. The library's Lucide icon set ships embedded via its bundled asset package, giving tab-close buttons and input adornments proper glyphs.
The pane splitters (Explorer / Results / DDL) keep their exact drag, double-click-collapse, and labeled-strip behavior via a small in-app replacement, since the component library's resizable panels use a different interaction model.

The visible behavior of every dialog and surface is unchanged by design; the full test suite (1,595 tests) passes against the new toolkit.

### Under the hood

Swapped the in-house widget toolkit and password-field crate for the new component library and its asset package.
Component init, asset source, root-wrapped windows, global-action routing through the new root view, stateful select/slider/tab-bar ports, dialog buttons/checkboxes.
The palette-to-component-theme bridge replacing all the old per-widget theming shims.
New. In-app splitter element.
Every dialog/view with widgets: the command palette, object properties, connection editor and management, entity management, preferences, project-file picker, row editor, sidebar, log console, results, editor, schema tree, structured-data viewer, about, and open-source panels.
The UI-widget ground rules now document the new component library.

Browser build: SQLLY in WebAssembly

The pipeline grew a WebAssembly leg that packages a browser demo of SQLLY. The page runs entirely in the browser — there is no server component and no SQL Server connectivity (browsers cannot open direct SQL Server connections; the page says so up front). On load it downloads the Northwind SQLite sample database and runs it with SQLite compiled to WebAssembly, so real SQL executes locally: browse tables and views in a schema rail, run query batches, and read result grids and messages. Users can also open their own local SQLite files — the bytes never leave the machine — or switch to an emulated demo database that additionally demonstrates stored procedures (which SQLite lacks).

New crates:

The emulated demo database: a small Northwind-flavored catalog of tables, views, and stored procedures with a read-only SQL subset (SELECT with WHERE/ORDER BY/TOP/LIMIT, views, EXEC), fully unit-tested and dependency-light so it compiles for every target.
The web app: the wasm module (SQLite FFI wrapper + JSON contracts, native-tested) and the static page.

Pipeline: the release pipeline gained a wasm-build stage producing a self-contained static-site artifact, and the PR pipeline compile-checks the wasm target. A local build script builds the bundle.

### Under the hood

New demo-database crate (engine, parser, demo data).
New web-app crate and static site.
New wasm-build stage in the release pipeline.
Wasm compile check on PRs.
New local build script.

Data table 4.1.0 and release v0.12.0

The result grid moved to the data table's 4.1.0 release — the port built on the new component library, published to the package registry again. Publishing had been blocked because the datatable's 4.0.x migration pinned the UI framework and component library as git dependencies (which the registry rejects); with compatible registry releases available, the datatable was retargeted to them, its publish pipeline restored, and 4.1.0 released. The app consumes it with no API changes — the toolkit theme it needs is already installed by the app's component-library init — and the full test suite passes unchanged. With the toolkit port, the wasm pipeline, and the datatable upgrade landed together, the workspace version moves to 0.12.0.

### Under the hood

Pinned the data table component to 4.1.
Bumped the workspace version to 0.12.0.
Updated the datatable example version in project docs.
v0.10.0
Jul 17, 2026 · 1 section
Jul 17, 2026
1 section

Result grid improvements

Upgraded the data table component to v3.1.2, which fixes scrolling behavior with pivot view fields and adds support for row operations without hierarchy. The result grid now handles complex pivoted data layouts more smoothly and provides greater flexibility for configuring row display and interaction patterns.

### Under the hood

The data table component bumped from 3.0.2 to 3.1.2.
v0.9.0
Jul 16, 2026 – Jul 17, 2026 · 12 sections
Jul 17, 2026
8 sections

SQL formatting

SQLLY now has a real SQL formatter. Format the current selection (or the whole buffer) with Alt+Shift+F, Query menu "Format SQL", or the command palette; optional format-on-save and format-on-paste apply the same engine, with paste formatting only when the pasted text reads as one or more complete statements. Formatting is token-safe: the output is re-lexed and compared token-by-token to the input, and if anything would change meaning — or the SQL doesn't lex — the text is returned untouched. Comments are never dropped, formatting is idempotent, and every format lands as a single undoable edit.

Preferences → SQL → Formatting configures keyword casing and layout (including CASE expression style, IN list style, ORDER/GROUP layout, semicolon style, blank-line limits, and column alias alignment) with a live "Try it" preview: pick a sample or type your own SQL and see the formatted, syntax-highlighted result as you adjust settings.

### Under the hood

The formatter engine over the SQL lexer's tokens, with token-safety and idempotency test coverage.
Formatting preferences (now actually read), new layout options, and the live preview.
The Format SQL action and binding, menu/palette entries, and the format-on-save / format-on-paste triggers.

Query editor keyboard conventions

The query editor now follows the keyboard conventions of mainstream editors on every platform. Escape dismisses things in the expected order — dialog, context menu, completion popup, ghost text, then the selection — and query cancel keeps its own shortcut (Cmd+Esc on macOS; now Shift+Esc on Windows and Linux, because Ctrl+Esc opens the Windows Start menu). Executing with a selection runs only the selected SQL, matching SSMS, so highlighting one statement and pressing F5 never runs the rest of the script.

Navigation and selection fill in the remaining standards: Shift+click extends the selection; Up/Down remember the caret's column through short lines; Home toggles between the first non-blank character and column 0; macOS gains its native Ctrl+A/Ctrl+E/Ctrl+K text-field bindings; Windows and Linux gain the classic Ctrl+Insert / Shift+Insert / Shift+Delete clipboard chords; Linux supports middle-click primary-selection paste. Copy and Cut with nothing selected act on the current line. Undo now groups typing into word-sized steps instead of one character at a time.

Keyboard access reaches the mouse-only corners: Shift+F10 (or the Menu key) opens the editor context menu at the caret, and the column-format dialog can be driven entirely with Up/Down/Enter/Esc — and is now truly modal, so typing can no longer edit the buffer behind it. Editor shortcuts are scoped to the editor's focus context, ending stroke conflicts with app-level bindings, and tabs can also be switched with Cmd+Shift+[ / ] (macOS) or Ctrl+PageUp / PageDown (Windows/Linux). The caret now stays in view horizontally as well as vertically when typing or navigating long lines.

### Under the hood

The dismiss and open-context-menu actions, scoped bindings, smart Home, sticky column, shift+click, line copy/cut, undo coalescing, modal format dialog, and scroll-follow, with regression coverage.
The Windows/Linux cancel rebinding and the additional tab-switching shortcuts.
Execute-selection routing.

International text and IME input

Fixed a crash: moving the caret up or down through text containing accented characters, non-Latin scripts, or emoji could land it inside a multi-byte character and panic on the next edit. Vertical movement is now measured in characters, never bytes.

The editor now supports IME composition properly. Chinese/Japanese/Korean input and dead-key accents show underlined preedit text, place the candidate window correctly, suppress auto-completion while composing, and commit as a single undoable edit. Pasted or loaded text with Windows CRLF line endings is normalized so line numbering and caret math stay correct, and completion suggestions can no longer be inserted at a stale position after the caret has jumped (Home/End/PageUp/arrows now close the popup, as other editors do).

### Under the hood

Char-based vertical movement, the marked-text (preedit) implementation, CRLF normalization, stale-completion guards, and multibyte regression tests.

Editor responsiveness

Large scripts scroll and edit noticeably faster. The editor's line index, widest-line measurement, and per-line syntax shaping are now cached and only recomputed when the text (or font/theme) actually changes — previously the whole document was rescanned several times per frame and on every mouse move. IntelliSense analysis now runs off the UI thread, so the editor never hitches after a typing pause, with stale results discarded if you have typed or moved on. UTF-16 position conversions used by input methods now walk one line instead of the whole document.

### Under the hood

Revision-keyed line index, max-width and shaped-line caches, background IntelliSense with staleness guards, and UTF-16 prefix indexing.
Clonable tab maps for the shape cache.

Editor theming and contrast

The column-format dialog dims the editor with the shared scrim color so every modal in the app dims identically in both light and dark modes, and the diagnostic hover tooltip drops its colored side stripe in favor of its full severity border. The WCAG contrast test matrix now also asserts text over the editor selection, text over the highlighted completion row, warning and info gutter dots and tooltip borders, the caret, and DDL link-hover text — all passing with the existing palettes.

### Under the hood

New contrast requirements.
Scrim usage, side-stripe removal, and a hover state on the dialog's Cancel control.

Server explorer credentials, keyboard access, and hardening

The server explorer now loads for connections backed by a saved SQL account. Saved profiles are secret-free by design, and the tree was handing the engine an account id where a password belonged; it now resolves the password from the system keychain off the render thread, so a locked keychain or permission prompt can no longer freeze the app mid-expand. When the password truly cannot be found, the row and its tooltip explain that the account has no stored password instead of naming an internal key.

The explorer is fully keyboard operable: ⌘⇧B focuses it from anywhere, Home/End/PageUp/PageDown, Escape, and type-ahead navigate it, Shift+F10 opens the context menu, and the selection always scrolls into view. Selected rows use full-contrast ink, connection actions are reachable without a mouse hover, and long names ellipsize cleanly at any interface font size.

Refreshing a slow-loading node can no longer paint stale results over fresh ones — each load carries a generation, and late answers from a superseded load are dropped. Extreme identifiers (128-character names, CJK, emoji, RTL, embedded newlines) display safely without overflowing or hiding the real name, and row tooltips wrap at the standard width.

### Under the hood

Explorer redesign, credential hydration, keyboard access, load-generation guards, identifier and layout hardening, and regression coverage.
Removed the old servers-explorer module, superseded by the schema tree.

Interface typography tracks the user's font

Enlarging the interface font now grows every label in the app chrome — menus, tabs, status bars, dialogs, panels — instead of leaving text frozen at the default size, and the rows and bars holding that text grow with it so nothing clips. Fixed-size icon glyphs keep their size so they stay centered in their boxes, and the last two bold headers were retired in favor of the app's single-weight type system.

### Under the hood

App-wide sweep routing text sizing through the user font and deriving text-bearing heights from it.

Optional confirmation skip for unbounded UPDATE/DELETE

UPDATE and DELETE statements without a WHERE clause always asked for confirmation before executing, even on connections with every other safety feature turned off. A new, separate connection setting — "Skip confirmation for UPDATE/DELETE without WHERE" — lets a connection opt out of that last prompt so any query runs unimpeded. It is off by default, independent of "Confirm data changes before executing" (which still confirms everything when enabled), and survives profile export and import.

### Under the hood

The new allow-unbounded-mutations profile flag, preview field, and export round-trip.
Safety-policy wiring, the editor checkbox, and regression coverage.
Profile construction updated for the new flag.
Jul 16, 2026
4 sections

Fresh-install reset

The Tools menu now offers a confirmed, irreversible reset that removes every registered server, preference, saved workspace detail, query history, cached catalog, signed-in account, and stored credential. Secure credentials are removed before local files, and SQLLY quits after a successful reset so the next launch starts with clean first-run state. The confirmation explicitly warns that unsaved queries will be lost, and the app remains open if any data cannot be deleted instead of reporting a partial reset as successful.

### Under the hood

Added the reset command, confirmation, secure-store cleanup, filesystem cleanup, and regression coverage.

Connection account and Azure server selection

Connection Management is now wider and its Accounts section can create, edit, and remove reusable SQL authentication accounts while keeping passwords in the system keychain. New and edited connections no longer offer Entra device-code authentication. Choosing Microsoft Entra now refreshes the Azure SQL servers available to the signed-in account and presents them in a dropdown, while keeping manual server entry available and showing a clear message when server discovery cannot be completed.

### Under the hood

Added the account manager, secure SQL credential storage, Entra server selection, failure handling, and regression coverage.

Connection management organization and appearance

The connection tree disclosure controls are easier to see, and Accounts now uses the same list-and-details organization as Connections. Client, project, and environment colors can be selected with an interactive color picker rather than entered as raw hex values. Those records can also use custom icon images selected from a file picker or dropped directly onto the editor, with an immediate preview.

Connection lists now keep their scrollbars visible, including a two-axis scrollable server tree with tighter hierarchy spacing. When exactly one Microsoft Entra account is registered, choosing Entra authentication selects it automatically in both connection editors.

### Under the hood

Refined the management layout, added appearance controls and icon persistence, and expanded regression coverage.

Edit Row from the results grid

Right-clicking a row header in the results grid now offers "Edit Row...", which opens a form for editing that row's values. The editor resolves the row's source table from the server (never by guessing from column names), loads authoritative column metadata, and re-fetches the current row by its primary key before editing. Results that cannot be edited safely — joins, expressions, views, missing key columns, later result sets of a batch, or SELECT-only connections — explain why instead of failing silently.

Fields validate as you type (required values, type parsing, string length, decimal precision and scale, integer ranges, GUID and date/time formats) and keep NULL distinct from the empty string with an explicit NULL toggle. Primary key, identity, computed, and rowversion columns are shown read-only. Saves run as parameterized, transactional single-row updates guarded by the row's key plus its rowversion (or the original values of the changed columns), so a row changed or deleted by someone else is never overwritten silently — the form reports the conflict and reloads the current values. Connections configured to confirm data changes require an explicit confirmation step, and a successful save re-runs the originating query so the grid shows current data.

### Under the hood

Parameterized query/execute helpers over the warm client (a SQL-parameter type, plus routines that run parameterized queries and statements over the warm client).
New row-editing service: provenance analysis via the server's result-set metadata introspection, metadata load, keyed row fetch, guarded transactional update with optimistic concurrency.
Modal editor and its pure, unit-tested form model.
Row-header menu action, edit-row event routing, modal hosting, and post-save refresh.
v0.8.0
Jul 15, 2026 · 10 sections
Jul 15, 2026
10 sections

Reliable repeated result renaming

Starting an inline result-name edit now resets any cached cursor and selection state from an earlier edit of the same result. Typing a replacement therefore consistently replaces the selected label instead of occasionally prepending to the old name. The edit no longer synthesizes a platform shortcut that Linux interprets as moving the cursor instead of selecting the label.

### Under the hood

Reset rename input state and cover consecutive edits of the same result.

Portable project-relative paths

Project file picker paths now use forward slashes consistently on macOS, Linux, and Windows. This keeps displayed and copied paths stable and lets nested gitignore rules match the same way on every platform while filesystem access continues to use native paths.

### Under the hood

Normalized project paths and retained recursive scan coverage across platforms.

IntelliSense keybindings: Ctrl+Space / Ctrl+Shift+Space on macOS

The macOS default editor keybindings buried the two manual IntelliSense triggers behind chords that either never reach the app (cmd-space is Spotlight) or are hard to press (shift-ctrl-alt-space):

The manual completion trigger is now Ctrl+Space (with Cmd+Space kept as a secondary stroke), matching the Linux/Windows default.
The value-lookup trigger (distinct-column-value introspection when the cursor is in a WHERE-clause comparison) is now Ctrl+Shift+Space, replacing the previous Shift+Ctrl+Alt+Space chord.

Users with their own editor-keybinding overrides for these actions are unaffected (override semantics unchanged).

Manual completion trigger always opens the popup

The manual completion path no longer lets render-suppressing IntelliSense rules close the popup for manual requests (Ctrl+Space / value lookup). Rules with render: false now only gate automatic-typing popups; an explicit user request always renders when there are items. Kind filtering, blocked kinds, and max_items from rules still apply to manual requests.

### Under the hood

macOS defaults for the completion and value-lookup triggers.
The manual trigger bypasses render-suppressing policy decisions.

Pivot control text supports grouped axes

Pivot directives now use semicolons between the row, column, and value groups, with commas separating multiple fields inside the row and column groups. For example, row:Region,Country; column:Year,Quarter; value:Revenue:sum preserves the full axis layout when loading a directive or writing the current pivot configuration back to the query.

### Under the hood

Grouped parsing, serialization, native pivot configuration mapping, and round-trip tests.
Updated pivot directive completion text.
Updated test fixture for grouped axis fields.

Native pivot Sum/Avg blank for decimal/numeric/money columns

SQLLY intentionally decodes decimal/numeric/money cells into canonical strings to preserve the exact wire value. Those strings flowed into the grid as text cells, and the native pivot's accumulator skips text cells for Sum/Avg (they still count for Count/Min/Max) — so pivoting e.g. a decimal(28,9) amount column showed blanks for sum/average while count/min/max worked.

Fix: the grid bridge now coerces text cells in numeric-kind columns (decimal/numeric/money/float, per the column's inferred result kind) back into integer/decimal grid cells at conversion time. Unparseable strings stay text. Side benefit: number-format preferences (decimal places, thousands separators, red negatives) now actually apply to decimal columns in the grid — previously they only applied to float/real because text cells bypassed number formatting.

The comment-directive pivot transformer was never affected — it already parses numeric strings.

Verified against a live database: a decimal(28,9) amount column arrived as text before the fix.

### Under the hood

Numeric-text coercion applied across the grid-data build and row-conversion paths; four new tests.

Restored connections load metadata on startup

The last-selected server and database are now reactivated when the app starts, including token refresh, schema-tree loading, IntelliSense/model snapshot construction, validation-catalog sync, and latency measurement. Previously the workspace restored an active profile for display only, but the rest of the app treated it as a live connection; queries could run while completion and model data remained empty until the user manually refreshed metadata.

### Under the hood

Token-aware startup activation and regression tests for restoring the saved server/database selection.
Documented the active startup reactivation behavior.

Grid bridge lint cleanup

The borrowed-row conversion helper is now compiled only for tests, matching its remaining usage and keeping production builds free of dead-code warnings.

### Under the hood

Gated the test-only helper behind a test-only compilation flag.

Results UI regression coverage

Results UI coverage now follows the data grid's viewport-aware context-menu positioning and explicitly activates test windows before checking inline-edit focus. Stacked result dividers also expose stable debug bounds without changing their resize behavior. This keeps the end-to-end UI checks reliable with the published data table 2.3.0 release.

### Under the hood

Updated context-menu, focus, and divider rendering coverage.

Independently rerunnable release builds

The release pipeline now presents Linux, Windows, and macOS as separate, parallel stages. A failed operating-system build can be rerun without rebuilding the successful platforms, while website publication still waits for every platform attempt and runs even when one fails.

### Under the hood

Separated platform builds, changelog packaging, and website publication into explicit stages.
v0.7.0
Jul 14, 2026 · 18 sections
Jul 14, 2026
18 sections

Query-bar server + database dropdowns restore their last selection on launch

What changed. When the app reopens, the query-bar Server and Database dropdowns now come back prepopulated with the values they were set to when the app was last closed. This is a display-only restore: the app does not reconnect, mint Entra/SQL tokens, load databases, or refresh intellisense on launch. The user reconnects explicitly by interacting with the dropdowns.

Why. Previously both dropdowns reset to the empty placeholder ("—") on every launch, forcing the user to re-pick their server and database each session.

How it works.

Both dropdowns render their selected value from the active connection profile (server name and database), so restoring that profile is sufficient to prepopulate them.
The workspace session file now persists the selected connection id and database name.
On startup the saved connection id is matched against the loaded connection profiles; when found, the active profile is set (with the saved database) for display only.
The dropdown control only renders a selected label when that value is present in its options. The database options are empty until a connection loads its database list, so the render path now injects the active database into the option list to guarantee it displays after a no-connect restore.

Under the hood.

Added optional active-connection-id and active-database fields to the persisted layout state, both defaulted for backward-compatible decoding of older session files. Updated the existing full-literal tests and added round-trip, backward-compat, and skip-when-none tests.
Saving the workspace session now writes the two new fields from the active profile; startup restores the active profile (display-only, no connect) from the session; the query-bar render injects the active database into the database dropdown options when absent.

Tests. The workspace-session tests pass (24), with clippy clean and formatting applied.

Azure SQL discoveries are cached locally and restored without re-auth on launch

What changed. Discovered Azure SQL servers and databases are now cached to disk and reloaded on startup, so they appear in the sidebar immediately when the app opens. The app no longer performs a live Azure discovery automatically on launch. Azure is only contacted when the user explicitly asks for it — the Explorer Refresh action on an Azure node (account/subscription/server/ database), adding or removing an Entra account, or completing a new Entra sign-in. Each of those overwrites the cache with the fresh results.

Why. Azure discovery is an auth-gated network round-trip that cannot run until the Entra sign-in/token flow completes. On a cold launch that left the sidebar empty of Azure servers for the first few seconds, and made it impossible to restore an active Azure connection until auth finished. Caching the last known discovery makes the Azure tree available instantly and offline, and stops the app from silently hitting Azure on every launch.

How it works.

A new caching layer persists the combined all-accounts Azure SQL discovery list as JSON in the app's data directory. Loads are fault-tolerant (missing/corrupt → empty); saves are atomic (temp write + rename), mirroring the workspace-session store.
On startup the app loads the cached discoveries, which populate the in-memory discovery list, the sidebar tree, and the connection-management tree from the cache with no network calls and no token access.
The Azure discovery routine (still invoked only by the explicit refresh paths) writes its fresh results to the cache after a successful live discovery.
When the last Entra account is removed, the discovery routine now clears the in-memory discoveries and the cache so removed accounts' servers do not reappear on the next launch.

Under the hood.

New Azure discovery caching module (load/save/encode/decode) with unit tests (round-trip, corrupt→empty, missing-file→empty, disk save/load, path).
Startup loads discoveries from cache instead of triggering live discovery on launch; the discovery routine persists results to the cache and clears it when no accounts remain.

Tests. The Azure discovery cache tests pass (5), with clippy clean and formatting applied.

Editor undo, redo, and completion acceptance

What changed. The SQL editor now owns undo and redo as configurable editor actions. Their standard shortcuts are Cmd+Z / Cmd+Shift+Z on macOS and Ctrl+Z / Ctrl+Y (plus Ctrl+Shift+Z) on Windows and Linux. Pressing Enter now accepts the currently selected IntelliSense suggestion, including the default first suggestion and FIM ghost text, rather than inserting a newline.

Why. Undo and redo were only registered as application-wide actions, so they were not exposed through the editor shortcut configuration. Enter required the user to first navigate the completion popup before it would accept a suggestion, making the default completion unnecessarily difficult to use.

Under the hood.

Registered editor-local undo/redo action handlers and made Enter accept visible completions and FIM ghost text; added coverage for both Enter acceptance paths.
Exposed undo and redo in the editor action registry, display names, bindings, and platform defaults.
Removed duplicate application-wide keyboard bindings now owned by the editor keymap; Edit-menu actions remain available.

Tests. The editor tests (143 passing) and editor-keybindings tests (24 passing) pass, with formatting and clippy (warnings as errors) clean.

Pivot directive completion

What changed. SQL comment lines now offer completion templates for the existing result-view directives. Typing -- can insert a pivot-ready pair of directives, and partial --view: or -- pivot: lines offer the corresponding structured directive.

Why. Pivot configuration is expressed with zero-based result-column indexes in SQL comments. A valid template makes the feature discoverable and avoids hand-typing the required row, column, and value fields.

Under the hood.

Added local completion handling for result view and pivot directives, avoiding a SQL IntelliSense request for comments; added unit coverage for the generated directive and non-comment guard.

Tests. The editor tests (145 passing) and result-pivot tests (36 passing) pass, with formatting, clippy (warnings as errors), and whitespace checks clean.

Native interactive result pivots

What changed. SQLLY now uses the data table's native pivot view for configured result sets. A -- pivot: row:0, column:1, value:2:<aggregate> comment seeds the data table's interactive pivot configuration, while --view:pivot opens that view by default. The data table now owns the Grid / Pivot tabs, field sidebar, grouping, totals, filters, and drill-through rather than SQLLY flattening result rows into a separate static grid. avg is now a supported directive aggregate alongside count, sum, min, and max.

Why. The packaged data table has a complete, virtualized pivot engine. Passing it the original result snapshot preserves its interactive features and avoids duplicating a less capable pivot implementation in SQLLY.

Under the hood.

Bumped SQLLY to 0.8.0 and updated the data table component to its published 2.0.0 release.
Configures the native pivot tab from the parsed query directive and opens it for --view:pivot.
Translates directives to the pivot configuration and added avg support.
Supplies SQLLY theme and formatting values for the data table's new null and pivot surfaces.
Advertises avg in pivot directive completion help.

Pivot tab availability

What changed. Every in-memory result grid now exposes the native Pivot tab. When there is no -- pivot: directive, the pivot sidebar starts unconfigured so users can assign rows, columns, and values interactively.

Why. Pivot support was previously enabled only for queries containing a directive, leaving ordinary result grids with no visible way to open the pivot workspace.

Under the hood.

Always enables the native pivot tab and verifies it in the rendered results-grid integration test.

Tests. The results-grid cell-selection test passes, with formatting, clippy (warnings as errors), and whitespace checks clean.

Unconstrained Workspace Splitters

What changed. The splitter controls between Explorer and the query workspace, and between the query editor and results, now resize without arbitrary minimum or maximum dimensions. Persisted explorer widths are likewise restored unchanged.

Why. The workspace splitters should let users allocate the available window space freely instead of stopping at preset 150–600px and 100–800px ranges.

Under the hood.

Removed sidebar-width and results-height drag clamps while retaining splitter drag/collapse behavior.
Removed the persisted sidebar minimum and updated its regression test.

Tests. The workspace-session tests (24 passing) pass, with formatting, clippy (warnings as errors), and whitespace checks clean.

Diagnostics panel remains user-selected

What changed. Query validation now updates the Diagnostics tab without automatically opening it.

Why. A background or explicit validation result should not replace the results view the user is currently inspecting. The Diagnostics tab remains available whenever validation reports findings and opens only when selected.

Under the hood.

Decoupled diagnostic updates from panel selection and added regression coverage.

Tests. The diagnostics-do-not-auto-open test passes, with formatting, clippy (warnings as errors), and whitespace checks clean.

Tab switching, deferred pivots, and Entra credential refresh

What changed. The Ctrl+Tab switcher is now centered at 40% of the window width and 60% of its height. Its left half lists the open query tabs and its right half previews the highlighted tab's editor text. It now displays the long AI summary SQLLY actually generates, with legacy short summaries as a fallback. Result grids keep both Grid and Pivot tabs visible while a query streams, but Pivot is locked and labelled Loading... until a terminal query event arrives. The Azure SQL account dialog also provides a Refresh action on every listed account to replace its keychain credentials through tenant-scoped browser sign-in.

Why. The previous switcher was top-inset, list-only, and read an AI summary field that SQLLY never generated. Recomputing pivot snapshots while batches arrived caused visible lag. Registered Entra accounts also had no direct way to renew expired or revoked credentials.

Under the hood.

Redesigned the switcher preview and routed per-account credential refresh sign-in.
Locks native Pivot views during streaming, unlocks them on completion/error/cancellation, and preserves Pivot when a grid must be rebuilt.
Added per-account Refresh controls and event coverage.
Updated to the data table component's published 2.3.0 release, which provides the locked Pivot-tab state, loading label, and pivot formatting controls.

Tests. Focused Ctrl+Tab, result Pivot lifecycle, Entra dialog, and data-table Pivot-lock tests pass. Data-table and SQLLY formatting and clippy checks pass.

Scalable editor image previews

What changed. The Ctrl+Tab switcher's right pane now shows a generated SVG image of the highlighted editor viewport instead of laying out its SQL as plain preview text. The image retains the current scroll position, line-number gutter, editor font, application theme, and SQL syntax colors, and scales to the available pane while preserving its aspect ratio.

Why. A visual editor thumbnail makes tabs easier to recognize and remains legible and proportionate as the switcher or application window changes size. SVG generation is limited to visible lines so large query buffers do not add unbounded work while cycling tabs.

Under the hood.

Generates the bounded, XML-safe editor viewport SVG and tests its scalable image metadata and escaping.
Renders the generated image with contained, aspect-ratio-preserving scaling in the Ctrl+Tab preview pane.

Tests. The focused editor image test and existing Ctrl+Tab layout/summary tests pass. Clippy passes for the requested code and whitespace checks are clean; full formatting remains blocked by unrelated concurrent project-file-picker changes.

Name-based pivot and query formatting directives

What changed. Pivot directives now identify row, column, and value fields by result-column name instead of fragile zero-based indexes. The native Pivot context menu can write its current configuration back into the query with Update query pivot comment. Query-editor right-click menus now offer schema-aware actions to expand * or an aliased wildcard, add a foreign-key LEFT JOIN, replace a foreign-key expression with the referenced table's Name column when available, and configure a column's display format. Format choices write structured -- sqlly format: comments and support date/time, decimal and integer, boolean, binary, and text values. Directive IntelliSense provides name-based pivot and format templates.

Why. Column indexes change whenever a query's projection changes and could silently pivot the wrong data. Keeping configuration in structured query comments makes result presentation portable with the SQL, while schema-aware context actions remove repetitive and error-prone query editing.

Under the hood.

Parses, resolves, generates, and updates name-based pivot directives.
Synchronizes the native Pivot state back to the query and applies name-resolved pivot/format configuration to result grids and streamed rows.
Adds the editor context menu, wildcard and foreign-key actions, datatype-specific format dialog, and directive completions.
Parses and applies structured format hints, including hexadecimal and Base64 binary rendering.
Propagates the typed schema snapshot to editors and registers query-format support.

Tests. All 39 pivot, 150 editor, 24 result-menu, 30 grid-bridge, and 2 query-format tests pass; the full desktop-app test run passes 1,341 tests with one unrelated rendered-menu coordinate test failure. Clippy passes for the requested code when excluding the two pre-existing project-file-picker warnings, and whitespace checks pass. Full workspace formatting remains blocked by concurrently edited files outside this change.

Alias-aware wildcard and display-name expansion

What changed. Query-editor wildcard expansion now reuses an existing table alias for both qualified and bare * expressions. A bare wildcard over multiple tables qualifies every generated column; tables without aliases receive collision-free aliases in their FROM or JOIN declarations, and existing table-qualified references are updated to those aliases. The foreign-key Show referenced display name action now replaces the complete qualified expression, so a.BusinessUnitId becomes b.Name AS BusinessUnitId_Name rather than leaving an invalid a.b.Name prefix.

Under the hood. Implements the bounded SQL identifier edits and regression fixtures. Existing aliases, synthesized multi-table aliases, qualified references, and foreign-key display-name replacement are covered by editor tests.

Custom Open Project File dialog

What changed. Cmd+O (macOS) and File → Open Project File... now open SQLLY's own project-file picker instead of the native open panel. The dialog has a Folder mode (browse one directory at a time, .. navigation) and a Project SQL mode (every .sql file under the project root, recursively). Each SQL file row shows metadata chips — schema, object type, and a confidence source (indexed when the object also exists in the loaded completion catalog, otherwise scanned) — plus its relative path, size, and modified date. The right pane shows a line-numbered, syntax-highlighted preview (bounded to 256 KB / 1,000 lines, with Load Full File), and Reveal / Copy Path actions. The filter box supports a prefix language: table:/t:, view:/v:, trigger:/tr:, index:/i:, sproc:/proc:/p:, function:/f:, type:/ty:, schema:/sch:, plus "quoted" literal search; matches are scored across file name, object name, schema, and path, and each hit is labelled with its match reason. Choose Root... and New SQL File... round out the dialog; Escape clears the filter, then closes.

Why. The desktop app only had a basic OS file dialog, while a richer picker understands SQL projects: it finds files by the object they define, filters by object type and schema, and previews content before opening. This is a port of that functionality with a cleaner split between logic and UI.

How it works.

A pure model layer holds all logic — directory/tree scans (gitignore-aware with negation rules, symlink-escape protection, hidden-file toggle, built-in ignore list, 10k-file cap), the filter parser and scorer, a bounded SQL header scanner (first top-level CREATE/ALTER [OR ALTER] object, bracketed/quoted identifiers, index/trigger schema inferred from ON), a size+mtime metadata cache, and the preview loader. No UI-framework dependencies, fully unit-tested.
The overlay view is state + rendering only. Scans and preview loads run on the background executor with generation counters so superseded results are dropped.
A single shared file-open path is now used by both the native Open SQL panel and the picker (previously duplicated inline in two handlers), and the chosen project root is remembered across openings.

Under the hood.

New pure model module.
New dialog view module.
Picker wiring and a deduplicated file-open path.
Rebound Cmd+O from the native panel to the picker (the native panel remains on File → Open SQL...).
Syntax token coloring shared with the picker's preview pane.
Added a temp-file dev dependency for scan tests.

Not yet available (no supporting app infrastructure yet): live filesystem watching (rescans happen on navigation/mode/root changes instead), "Open to Side" (editor splits are unimplemented), and "Open at Object" (no editor cursor-positioning API).

Tests. The project-file-picker tests (32 passing) pass, with clippy clean for these files and formatting applied.

Open Folder and data-model objects as openable files

What changed. A new File → Open Folder... action prompts for a directory and opens the project-file picker rooted there in Project SQL mode, listing every .sql file beneath it recursively. Inside the picker, a new DDL toggle treats the loaded data model as openable files: every table, view, stored procedure, and function from the completion catalog appears as a virtual schema.name row (chips: object type, data model, indexed) that participates in the same prefix filters and scoring as real files. Opening one fetches its definition server-side — sp_helptext-style DDL generation through the existing DDL service, per object type — and shows it in a read-only DDL viewer tab with syntax highlighting. Without an active connection the status bar explains a connection is needed.

Why. Users work against databases whose objects have no local .sql file; treating the data model as a browsable, openable file tree makes every known object reachable from one dialog. Open Folder gives a one-step way to point the picker at any SQL tree.

Under the hood.

Added a row-source distinction (filesystem vs. data model) and model-object rows ranked just below real SQL files.
DDL toggle, model-row preview/actions, and an open-DDL event.
Open Folder action + handler; maps picker object kinds to navigation targets; the DDL fetch previously inline in go-to-definition is now a shared path used by both.
Open Folder... menu item.

Tests. The project-file-picker tests (32 passing, including model-row openability/sorting and kind-prefix filtering) pass, with clippy clean for these files.

Result naming, stacked panes, and SQLLY formatting directives

Result-set rename fields now focus automatically with the existing label selected, and the rename pencil sits directly beside the current name.
Increased the result-set rename pencil by 50% so the inline rename affordance is easier to identify.
Replaced the stacked/canvas result-count label with an icon toolbar for clearing grouping (☷×), filters (▽×), and sorting (⇅×) across every result grid. Buttons disable when their corresponding state is already clear and include descriptive tooltips.
Fixed an immediate query-editor crash when pasting emoji or other multibyte Unicode at a cursor produced by tab-expanded hit-testing. Reverse tab mapping now returns only valid UTF-8 boundaries, and the shared edit path defensively expands invalid ranges to complete characters before slicing SQL text.
Formatting override dialogs now close on Escape without applying a formatting directive or changing the query.
A single stacked result fills the available results area. Multiple stacked results are separated by splitter controls that resize both adjacent result sections.
SQL editor horizontal and vertical scrollbar tracks remain visible and expose thumbs only when their axes can scroll.
Added result-grid column context-menu actions that open the same datatype-aware formatting dialog as editor column actions and write -- sqlly format: overrides.
Namespaced SQLLY-owned query directives as -- sqlly ..., including view, pivot, result name, result layout, placement, and formatting comments; unnamespaced lookalike comments are ignored. Including the product name makes these otherwise unusual comments self-identifying and searchable, so someone encountering one can search for SQLLY and discover what generated it and how the directive works.
Updated directive IntelliSense, parsers, writers, tests, and result-grid formatting integration across the editor, results, and directive-handling modules.

Mutation confirmation is now a per-connection setting

What changed. The pre-execution confirmation dialog ("WE WILL MAKE INSERT/UPDATE/MERGE… CHANGES.") no longer appears for every data-changing query on every connection. It is now controlled by a new per-connection setting — Confirm data changes before executing — in the connection editor, off by default. Unbounded UPDATE/DELETE statements (no WHERE clause) still require confirmation regardless of the setting, as does a SELECT-only-locked connection.

Why. The safety-features logic hardcoded the confirmation dialog on, so it fired for all queries on all databases. Earlier versions derived this from per-scope safety configuration; the flat connection profile had no equivalent field.

Under the hood.

The connection profile and its preview gained a confirm-data-changes flag (defaulted, so existing saved profiles decode with it off).
The secret-free export bundle round-trips the new flag (backward-compatible default).
The safety-features check reads the profile flag instead of defaulting the dialog on; tests updated and added (unconfigured profile skips bounded confirmations, configured profile confirms, unbounded still confirms).
New checkbox wired through the connection-editor form and profile assembly.
Profile constructors populate the new field.

Tests. The mutation-review tests (32 passing), connection-editor tests (11 passing), and profile-store tests (9 passing) pass.

Project-file picker root survives app restart

What changed. The folder the "Open Project File" picker was last rooted at (via Open Folder... or Choose Root...) is saved in the workspace session and restored on launch, so Cmd+O reopens to the same project folder after quitting the app. Roots that no longer exist on disk are ignored at restore time.

Under the hood.

The persisted layout state gained a project-file-picker-root field (backward-compatible optional field; full-literal tests updated).
Saves the root with the session (also immediately on root changes) and restores it on startup.

Tests. The workspace-session tests (24 passing) pass.

Manage Clients, Projects & Environments dialog rework

What changed. The entity-management dialog is restructured around a tab strip at the top: Clients, Projects, and Environments are now real tabs, and all of the dialog's content lives under them — each tab shows its item list on the left and the selected item's details on the right. The left list scrolls; the details pane scrolls next to an always-visible scrollbar that renders disabled (dim, inert) when the content fits, and supports click-to-jump when it doesn't. The + Add / − Remove buttons are back and visible (a missing flex minimum-height constraint let the list push the button row out of the fixed-height dialog — the empty state said "Click + Add below" while the button was clipped away). All buttons (+ Add, − Remove, Save, Close, color Pick…, icon Browse…) are now standard toolkit buttons with proper enabled/disabled states (− Remove disables with no selection, Save with a blank name); the tab strip is the toolkit tab control; the form was already toolkit input/checkbox/color-picker widgets.

Under the hood.

Tabs at the top of the dialog, toolkit buttons throughout, minimum-height flex fixes so the footer bars always render, scroll-tracking and an always-visible right-pane scrollbar.

Tests. Render-path change; build and clippy clean, existing entity/store tests unaffected.

v0.6.7
Jul 9, 2026 · 1 section
Jul 9, 2026
1 section

Changes

Add a spill-to-disk paging system for large result sets, with a memory byte budget, batch splitting when the row threshold is crossed, throttled progress updates, and windowed file manifests. Partially written spill files can be read mid-write, so results appear sooner.
Stream results into the grid as they arrive instead of rebuilding it for each batch; progress and row updates are coalesced and stale updates from previous runs are ignored.
Add configurable performance preferences (spill row threshold, memory budget, page chunk size, page margin, and window size), persisted to disk.
Add data-model preferences: column alignment, schema display mode, system-database placement, and emoji toggles, persisted to disk.
Add a preferences UI with performance and data-model tabs.
Improve the schema tree: column alignment, schema-name prefixes in flat mode, object-type folders, and subtree reset/expand.
Fix a bug where stale spill bookkeeping could delete spill files that were still in use.
Add column-value autocomplete: when your cursor is in a WHERE-clause value position (e.g. col = or col LIKE ), the completion popup now shows distinct values from that column instead of inserting a comma-joined snippet. It fetches up to 100 distinct non-null values (SELECT DISTINCT TOP 100), caches them in a bounded in-memory cache (cleared on disconnect or catalog refresh), and guards against stale fetches. A separate action still provides the old comma-joined in-list insertion. Default keybinding: shift-ctrl-alt-space on macOS, ctrl-shift-space on Linux/Windows.
Fix false "unknown table or view" diagnostics for tables that exist (e.g. HR.Department). The app now keeps its per-connection validation catalog in sync with live metadata on every catalog refresh; a schema-qualified reference that isn't found now falls back to a bare-name match, and an empty catalog no longer flags every table as unknown. New tests cover both behaviors.
Add system-schema and general-schema grouping preferences for the server explorer (schema-folders mode): system schemas (sys, INFORMATION_SCHEMA, guest, db_*) can be grouped into a "System Schemas" folder, hidden, or shown only when non-empty; general schemas can always show, show only when non-empty, or group empty schemas into an "Empty Schemas" folder, backed by a new metadata query that detects empty schemas.
Add alignment and spacing preferences for explorer object metadata: alignment (left/right) and column width in characters (0 = auto, sized to the widest sibling), set independently for the data-type and nullability segments, via a new Metadata Columns section in the Data Model pane.
Reorganize the Settings sidebar: Performance and Gestures moved from the top level into the Results folder.
Make the Settings dialog 20% taller and give the right-hand pane an always-visible scrollbar that dims (no thumb) when the content fits.
Persist Settings dialog UI state so it reopens exactly as it was closed: selected pane, sidebar folder expansion, and both scroll positions are saved and restored, and the restored pane keeps its highlight.
Fix the Confirm Data Changes dialog's Execute button doing nothing: confirming a data change no longer re-triggers the same confirmation in an endless loop; the query now runs, and the review clears once it starts.
Query editor: add always-visible vertical and horizontal scrollbar tracks (thumbs appear only when content overflows), and measure the true content width (the widest tab-expanded line) so long lines scroll horizontally instead of being clipped.
Size the on-device AI query summary to the available width: the word budget (roughly 10–60 words) now scales with the editor column's width.
Remove query execution metrics (First/Query/Server/Paint/Wall/Latency/Rows) from the bottom app status bar; they now live solely in the result grid's own status bar, which already showed them with tooltips.
Support non-contiguous grid selections: right-click exports are now selection-scoped — multi-row/column selections export those rows/columns, a cell range exports the rectangle, sparse cell selections export unique rows × unique columns with unselected positions as NULL, and no selection (or a lone cursor cell) still exports the whole set. Selection statistics handle the new selection shapes too.
Resume Connection dialog: add a details list (Server, Database, Environment, Credentials — environment and username resolved from the saved connection profile) and switch to the standard checkbox/button components. The "Don't ask again" checkbox, which previously never showed its checked state and toggled the wrong tab, is fixed.
Temporarily disable Windows and Linux arm64 builds.
Add extensive test coverage for the spill paging lifecycle and live connections.
Internal versioning, dependency, release-pipeline, and changelog-tracking maintenance.
v0.6.6
Jul 8, 2026 · 1 section
Jul 8, 2026
1 section

Changes

Store large-result spill files in the app data directory.
The release pipeline now publishes the changelog.
Internal versioning, changelog-tracking, and lint cleanups.
v0.6.5
Jul 7, 2026 · 1 section
Jul 7, 2026
1 section

Changes

Add foreign-key-based JOIN suggestions to IntelliSense.
Prevent unsigned builds from being published, and fail loudly on release-publishing errors.
Toolbar fixes.
Stream results into the grid as they arrive; improved on-disk spill format for large result sets.
Internal versioning, dependency, and lint cleanups.
v0.6.0
Jul 4, 2026 – Jul 6, 2026 · 3 sections
Jul 6, 2026
1 section

Changes

macOS builds are now always distribution-signed and notarized, not just on tagged releases.
Continued Windows feature work.
Add an in-app menu bar for Windows and Linux, plus proper app icons on those platforms.
Route IntelliSense through a single shared data model as the source of truth.
Add a custom title bar on Windows and Linux; name the app binary SQLLY.
Internal release-pipeline and build plumbing.
Jul 5, 2026
1 section

Changes

Add query timing metrics: first response, server total, paint time, wall clock, and base latency.
Internal release-pipeline maintenance.
Jul 4, 2026
1 section

Changes

Refresh now reloads the full schema tree from the refresh button/action.
Internal release-pipeline, versioning, dependency, and test cleanups.
v0.5.0
Jun 3, 2026 – Jul 3, 2026 · 27 sections
Jul 3, 2026
1 section

Changes

Fix editor focus, dropdown widths, and toggle icon sizing; add clipboard actions and keybinding customization.
Prevent hard crashes by catching unexpected errors.
Auto-scroll the diagnostic log to the bottom by default; add a clear button and an auto-scroll checkbox.
Fix modal overlays and add text selection in the log console.
Make the query toggle bar slightly taller and fix dropdown label alignment.
Standardize on shared UI components (checkboxes, sliders, tabs, dropdowns) for consistency.
Internal build, dependency, and lint cleanups.
Jul 2, 2026
1 section

Changes

Unify the server explorer into a single server-rooted drill-down tree.
Add browser-based Azure sign-in to the app.
Wire up the 'Add My IP to Server Firewall' feature end-to-end.
Fix a render-time bug in the results-grid right-click export and add regression coverage.
Unify IntelliSense around a single shared completion pipeline.
Internal build, cross-compilation, dependency, and lint cleanups.
Jul 1, 2026
1 section

Changes

Internal development work.
Jun 30, 2026
1 section

Changes

Fix an app crash on startup; add F5 to run the current query.
Jun 28, 2026
1 section

Changes

Continued work on the new native app.
Wire up the app's menu actions and the command-line tool runner.
Plan macOS release code signing.
Build Linux and Windows for both amd64 and arm64; restrict arm64-only builds to macOS.
Restructure the app layout with 15% scaling and resizable panels.
macOS builds now package a DMG alongside the .app.
Build and release-pipeline plumbing, cross-compilation setup, and lint/formatting cleanups.
Jun 26, 2026
1 section

Changes

formatting works now
Add query resume and gesture preferences
Add result selection stats formatter
Add date time-zone, header alignment, and Keychain migration
Jun 25, 2026
1 section

Changes

Always attempt Keychain operations; record and surface denial
Keychain: silently fall back to legacy keychain on missing entitlement
Azure tokens degrade to in-memory cache when keychain denies
Keychain status icon + OAuth completion page
Fix the OAuth completion page rendering
Fix the Entra token cache to be shared process-wide; add the missing keychain status icon
work
Add Copy Pretty Formatted (cmd-shift-J) and Open in Editor for JSON/XML result cells
Diff mode: show 'not a diff' message when result structures differ
Added named results
Add result-grid formatting preferences with per-column overrides
Jun 24, 2026
1 section

Changes

Refactor the results-grid controller; make stacked layout the default
Add result-set diff mode; keychain no-op on unsigned builds
Jun 23, 2026
1 section

Changes

work
work
Fix stacked result layout for single result sets; add result grid cell range selection
Jun 22, 2026
1 section

Changes

work:
Add server/database dropdowns to the query toggle bar + binary transport improvements
Add a distributable-app build script: Developer ID signing, notarization, stapling, and DMG packaging
Fix instant launch crash on other Macs with more resilient resource-bundle lookup
Harden the binary protocol: detect writer failures, remove unsafe reads, route write errors, and clean up framing
Remove a dev-only hardcoded path from the local-AI manager
Add keychain access-group support and live query timer/param flow layout
Update query-engine request/response logging and regression coverage
Update catalog embedding, search, vector storage, and SQL generation
Update the local-AI client, config, and embedding integration
Update model-engine IntelliSense
Add Update WHERE context menu for results grid
Jun 21, 2026
1 section

Changes

Add a binary rows-batch protocol for faster result streaming, plus preferences and CLI improvements
Harden the binary transport with stricter reads, frame-size caps, typed decode errors, and byte-aware batch splitting
Jun 19, 2026
1 section

Changes

Add query-engine wire diagnostics to the IntelliSense debug panel
IntelliSense: schema-qualified FROM completion + completion/preferences UI
IntelliSense work
Jun 18, 2026
1 section

Changes

Add structured data viewer, external editor, icon handling, catalog storage improvements, and expanded test coverage
Add on-device AI support for Apple Silicon (MLX)
Reset the local AI install phase after schema indexing completes
Fix MLX/local-AI reliability, add log console, improve grid + AI UX
Jun 17, 2026
1 section

Changes

Add SQL generation/validation, Ollama provider, and enforce Apple Silicon only
Add explicit binary/test targets to the CLI build manifest, schema-intelligence client, and SELECT * FROM keyword IntelliSense
IntelliSense: FROM keyword after SELECT *, FROM-only filtering, procedure name conventions
Add schema QA/SQL generation/validation panels, gating, and preference controls
Consolidate schema QA and SQL generation into unified AI chat panel
Internal code-quality and lint cleanups.
Refactor schema search into an AI assistant section, add AI chat panel tests
IntelliSense work
Add SQL generation retry loop, EXEC parameter completion, value-completion metadata, and sproc IntelliSense filtering
Harden the query engine's retry handling and add regression coverage for the max-attempts limit
Harden the SQL-generation retry loop with an all-or-nothing retry gate
Add name-inferred relationship detection and join-path inference filtering
Jun 16, 2026
1 section

Changes

Add schema intelligence catalog and AI-assisted schema Q&A
Add typed object cards, purpose-aware context assembly, and full-text schema search
Add vector/hybrid schema search with deferred embedding build
Harvest table sources from ALTER VIEW/FUNCTION bodies; IntelliSense cookbook
Enforce arm64-only macOS builds
Add grounded prompt rendering with universal grounding rules and citations
Jun 15, 2026
1 section

Changes

Add custom query tab bar, WHERE-column IntelliSense, project-files sidebar, and Entra save-to-stored
Expand databases to deep schema subtree in Entra explorer; harden keychain errors
Add server-explorer grouping (client/project/environment/server) + Register rename
Capture table aliases in CREATE VIEW bodies; cross-construct IntelliSense tests
Jun 14, 2026
1 section

Changes

Add an engine request/response correlation log and an acknowledgments dialog
Fix an idle validation storm and a duplicate-alias IntelliSense crash
Harden IntelliSense and offer scope-aware JOIN ON completions
Fetch distinct values on Ctrl+Space in value positions
Jun 13, 2026
1 section

Changes

Add subword (Ctrl+Arrow) editor navigation and rename the module to SQLLY
Jun 11, 2026
1 section

Changes

Add unified SQL model engine IntelliSense
Add column predicate validation and enhance IntelliSense completion matching
Rebind the foreign-key value-lookup shortcut and improve tokenizer indexing
Additional feature work
Add per-tab database switching and a unified server explorer
Add dotted-path and alias-aware IntelliSense to the model engine
Add a trigger-scoped value cache and foreign-key display comments to the model engine
Route additional AI models through the model provider
Jun 10, 2026
1 section

Changes

Add an AI review gate for data-changing queries
Add a SQL model engine and stored-procedure IntelliSense
Jun 9, 2026
1 section

Changes

Fix the Object Explorer sidebar collapsing to zero width on startup
Internal code-quality and lint cleanups.
Add on-device AI query summaries with throttling, persistence, and preferences
Persist per-query connection metadata and restore tabs as disconnected
Shorten the AI summary prompt for more concise summaries
Add a project file picker and automagic feature documentation
Add inline schema validation and clickable server-error feedback
Bound automagic project scans and repairs
Add query tab titles and USE database hints
Jun 8, 2026
1 section

Changes

Lock the startup sidebar width and add query/result UX instrumentation
Keep a triggered value lookup from being overwritten; add engine round-trip regression coverage
Add per-node Client/Project/Environment/friendly-name overrides to the Azure tree
Jun 7, 2026
1 section

Changes

Add SQL editor keybindings and layout guidance
Refine the IntelliSense debug panel width and lock the startup layout
Pool database connections in the engine to eliminate per-query connect latency
Give each query editor tab its own warm pooled connection
Internal cleanup of validation test fixtures.
Jun 6, 2026
1 section

Changes

Add Azure SQL firewall rule provisioning when a connection is blocked by the server firewall
Pass the Entra access token through to metadata tree connections
Loosen IntelliSense latency-budget test thresholds
Fix a UI hang when opening the firewall rule dialog
Jun 5, 2026
1 section

Changes

Add an IntelliSense customization pipeline and result pivot/export
Add result pivot views with hint parsing, IntelliSense, and pivot UI
Add a help browser, query result layout canvas, and spill row pager improvements
Fix a flaky concurrent-drain regression test with consumer-driven backpressure
Add connection gateway routing, host validation, editor completion, and help browser improvements
Fix result-layout statement splitting and add the SQLLY logo
Use the new SQLLY mascot logo everywhere; enable full release optimizations
Jun 4, 2026
1 section

Changes

Restructure the project into separate client and server areas
Add a spill-based result grid, design system, metadata catalog, Azure authentication, and sync server updates
Internal code-quality, formatting, and lint cleanups.
Add system-database grouping regression coverage
Ensure app icons ship correctly in packaged builds
Update the completion and query workspace controllers
Add auto-trigger gating for completions and completion-matcher regression coverage
Add scope-aware SQL completions
Update macOS UI defaults and app packaging
Update the Entra application identity
Register the SQLLY authentication callback
Update engine I/O and macOS explorer defaults
Update engine I/O, discovery support, and the workspace controller
Rename the app and its internal crates to SQLLY
Add live Entra server discovery to the server explorer
Jun 3, 2026
1 section

Changes

Initial project import
Plan the streaming result-set redesign
Internal code-quality and lint cleanups.