Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

The version lives in `rowset-studio/package.json`. `scripts/build-local-binary.sh`
and `scripts/package-macos.sh` stamp it into Studio (sidebar), the `rowset`
executable and the macOS app. Every change set bumps the patch version and adds
an entry here; after x.y.99 the next version is x.(y+1).0.

## 0.1.11 — 2026-10-11

- Fixed: a statement without a WHERE clause ran unguarded when another
  statement in the same script had one. A script is read as one statement
  carrying the riskiest kind, and update, insert and delete rank the same, so
  `UPDATE a SET x=1 WHERE id=1; DELETE FROM b` was judged an update with a
  WHERE and the delete emptied a table the guardrail would have refused on its
  own. Every statement's own missing WHERE is now recorded and judged, so the
  rules against a DELETE, an UPDATE or a SELECT without one apply to each
  statement of a script.
- Fixed: result column lineage on MySQL and MariaDB dialed the database
  directly instead of going through the connection's SSH tunnel, so over a
  tunnel no result was editable and every statement paid a failed connection
  attempt first. It now reaches the database the same way the pool does, and
  a resolved lineage is cached per connection and statement and dropped with
  the schema it describes - one prepare, catalog read or describe for a
  statement that is run again rather than one per run, on every engine.
- Fixed: a query whose columns could not be read left its pinned database
  connection behind. One connection of the pool was lost for the life of the
  process each time.
- Fixed: a MongoDB find or aggregate, a Redis scan and an Elasticsearch
  search were evaluated as if an administrator had asked for them, so a
  role-scoped rule and a read-only connection were not applied to document
  reads the way they are to SQL. All of them now go through the caller's own
  role, like every write already did.
- Fixed: a row backup taken inside a manual transaction on CockroachDB was
  not protected by a savepoint, so a backup read that failed left the
  caller's transaction aborted.
- Fixed: a write that returns rows - `UPDATE … RETURNING`, `DELETE … OUTPUT` -
  kept its row backup when the statement failed, leaving a saved copy of rows
  nothing had changed.
- Fixed: a request could use a manual transaction that the idle reaper rolled
  back in the same moment, because the transaction was only marked in use
  after its request body had been read. The claim is now taken under the lock
  the idle check uses, for SQL and MongoDB transactions alike.
- Fixed: a CSV import wrote binary values into SQLite and DuckDB as the text
  `'\x…'` instead of bytes. Both now use the engine's own binary literal, the
  one a row-backup restore already used.
- Fixed: shutdown could return while a notification or a background loop was
  still reading the store, which left a half-written database file behind.
  Close now waits for them.
- Changed: a command-line assistant (`claude`, `codex`) is offered and
  accepted only on a personal workspace. On a shared server the command would
  run on the server, as the server, for whoever asked.
- Changed: a full queue no longer holds shutdown up for the whole activity
  enqueue wait; a request waiting for room stops waiting and writes its own
  record.
- Performance: listing connections read every connection's nodes and resolved
  its grant one connection at a time, twice over - a workspace with twenty
  connections spent dozens of round trips on one listing. Nodes are read once
  and each grant resolved once.
- Performance: a result read allocates one slice per row instead of two, and
  dragging the editor's panel borders no longer re-renders the result grid or
  writes to local storage on every pointer move. The grid itself only renders
  when its result changes.
- Performance: a run reloads the schema when the statement really creates,
  alters, drops or truncates something; the word inside a string, a quoted
  name or a comment used to be enough.
- Fixed: the result grid measured its column widths from the first rows of a
  streaming result and never again, so columns stayed as wide as that first
  chunk made them.
- Internal: one implementation each for decrypting a row backup (five
  copies), quoting an identifier (two in Go, two in TypeScript), the NoSQL
  read guard (four), the policy scope picker (two) and the panel drag handle
  (two). Removed `writeDatabaseError`, `nullable`, `placeholders`,
  `mongoUpdateQuery` and `mongoDeleteQuery`, none of which had a caller.

## 0.1.10 — 2026-10-10

- Fixed: a statement held for review and then approved ran without a row
  backup. `ExecuteDeferred` took the live query path's access checks,
  policies, hooks, row limits and activity recording, but not its backup, so
  the one write that had been looked at by a second person was the one write
  that could not be put back. It now saves the rows an eligible UPDATE or
  DELETE is about to change, and a write whose rows cannot be saved does not
  run: nobody is waiting at that point to be asked whether to go on without
  one. A failed statement discards its backup, as on the live path.
- Changed: a DELETE with no WHERE clause is backed up as the whole table,
  where previously no backup was taken. The default policy refuses that
  statement, so the only way it runs is that someone deliberately allowed it,
  which makes it the write most worth being able to undo. `DELETE … LIMIT`
  and `DELETE … ORDER BY … LIMIT` keep no backup: the engine removes an
  arbitrary few rows, so the rows read beforehand are not the rows deleted
  and putting them back would collide with the ones that stayed.
- Changed: an administrator reaches every row backup in the organisation -
  the list, Restore and the script - where before each backup was readable
  only by the person whose statement made it. An approved write runs for its
  author, and on a production connection that author's own Restore is itself
  a production write and is held, so the person who cleared the write was the
  one person who could not undo it. The list says who each backup belongs to;
  the values stay encrypted until a restore or the admin-only script.
- Added: `Kit.EvaluateStatement` reports what the policies say about a
  statement the caller has not run. An escalation handler uses it to record
  why a statement needs review, so whoever reviews it sees the rule that
  stopped it instead of only the requester's own words.

## 0.1.9 — 2026-10-06

- Fixed: a result on screen kept a processor core busy for as long as the
  window stayed open. The grid reported how many rows a filter leaves from an
  effect, and the editor answered with a fresh object through a callback it
  rebuilt on every render: the effect re-ran because the callback was new, the
  answer re-rendered the editor because the object was new, and the two drove
  each other at full speed. Nothing looked wrong, which is why it went
  unnoticed. The count is now reported through a callback that keeps its
  identity, and it leaves the state alone when the numbers have not moved.
  `npm run test:e2e:idle` measures what the editor spends with a result shown
  and nobody typing, and fails if a loop comes back.

## 0.1.8 — 2026-10-06

- Fixed: a script's token positions pointed into the statements it was made
  of rather than into the script, so anything that reads a statement back out
  of its own text by position would have sliced the wrong text, silently. The
  whole script is lexed instead, and a test holds every token to the text it
  claims. Nothing in Studio was affected - the row backup leaves a script
  alone - but `sqlguard.Info` is a public contract, and a plugin that rewrites
  statements reads exactly those positions.

## 0.1.7 — 2026-10-05

- Fixed: a batch that returns several results showed only the first. SQL
  Server runs everything up to `GO` as one batch, so a script of several
  SELECTs is one statement to the server and returns a result per SELECT;
  0.1.6 read the first and stopped, which lost the rest of what had already
  run. Every result now arrives, each with its own columns, and the editor
  lists them as Result 1, Result 2 and so on, the way a run of separate
  statements already did. A statement inside the batch that changed rows
  rather than returning them is passed over rather than shown as an empty
  result.

## 0.1.6 — 2026-10-05

- Fixed: a maintenance script no longer has to be rewritten to run. SQL that
  leaves state on its connection - `SET`, `DECLARE`, a cursor, `PRINT`,
  `RAISERROR`, `WAITFOR`, a temporary table - was refused outright with
  "Session-control SQL is not supported in the web editor". It now runs, on a
  connection of its own that is discarded afterwards, so no setting or
  variable can reach the next request through the pool. A manual transaction's
  connection is discarded the same way. `BEGIN`, `COMMIT`, `ROLLBACK`,
  `SAVEPOINT` and a bare `USE` are still refused, and now say which control
  does the job instead of naming the whole category.
- Changed: several statements sent together are read as one rather than
  refused whatever they contain. The script takes the riskiest statement's
  kind and the union of its flags, so a `DROP` or a `DELETE` without a `WHERE`
  buried in a script is refused by the rules that already existed - which a
  blanket refusal never had to get right - while the batched delete and the
  reindex people write by hand now run. `deny_multi_statement` in My policies
  brings the old behaviour back; an installation several people share keeps
  it on. A script is not backed up row by row and has no execution plan, and
  both say so.
- Changed: on SQL Server the editor splits a script on `GO`, the batch
  separator sqlcmd and SSMS use, instead of on every semicolon. A `DECLARE`
  and the statements that use its variable now belong to one batch. Every
  other engine still splits on the semicolon.
- Changed: a result grid shows about twice as many columns on the same screen.
  Each column is now measured from what it holds instead of being given the
  same width, rows are tighter, and the column type moves into the header's
  tooltip. The Compact/Roomy control in the result toolbar brings the roomier
  rows and the visible types back, and remembers which was chosen.
- Added: an XML value is shown as a link, as it is in SQL Server's own client,
  and opens laid out one element per line instead of as a single long line.

- Added: Console, a shell for every engine Rowset connects to, next to Query
  in the sidebar. It is modelled on the client each engine's users already
  know - psql, mysql, sqlcmd, sqlite3, duckdb, clickhouse-client, mongosh,
  redis-cli, cqlsh and the Kibana console - so `\dt`, `.tables`,
  `show collections`, `DESCRIBE TABLES`, `use`, `\x`, `\timing` and `\G`
  behave as they do there, with aligned columns, NULL shown explicitly and a
  colour per value kind. It is a client over the same API the Query editor
  uses, not a terminal: no process is started and nothing connects to a
  database directly, so a statement typed in the console gets the same policy
  decision, row backup and audit entry as one run from the editor. Commands
  that would need a shell or the local filesystem (`\!`, `\i`, `\copy`) are
  not offered rather than silently ignored, and an engine Rowset has no shell
  for says so instead of presenting an empty prompt.

## 0.1.5 — 2026-09-29

- Fixed: a connection could not be saved with the middle environment. Studio
  offered dev, preprod and prod while the server accepted only dev or prod,
  so picking the middle one failed with "environment must be dev or prod".
  There are now three environments - dev, test (staging, pre-production, UAT
  or QA) and prod - and the server accepts the usual spellings of each.
- Changed: when the encryption key of an installation no longer matches its
  database - a restored or regenerated `ROWSET_ENC_KEY` - the server says so
  once at startup, and Studio explains it where it used to show only
  "Workspace could not be decrypted or validated". Saved drafts are left
  untouched; putting the original key back (or naming it in
  `ROWSET_ENC_KEY_PREVIOUS`) opens them again, and the card offers to start
  an empty workspace instead.
- Fixed: the Test filter on Connections could never match anything, because
  a connection saved as test was stored as dev; the yellow badge and rail for
  that environment were unreachable for the same reason.

## 0.1.4 — 2026-09-25

- Changed: the backup taken before an UPDATE now keeps the row's key and the
  columns that statement writes, instead of the whole row. Restoring it can
  no longer undo a change someone else made afterwards to a column the
  statement never touched, and values it never read stay out of the backup.
  DELETE still keeps the whole row, since putting it back needs all of it.
  A SET list Rowset cannot read column by column, and an UPDATE that writes
  a key column, keep the whole row as before. MySQL columns with ON UPDATE
  CURRENT_TIMESTAMP are kept too, because the engine rewrites them without
  the statement naming them. The same applies to Cassandra.

## 0.1.3 — 2026-09-25

- Changed: a workspace no longer blocks statements the parser has not
  learned yet. Existing personal workspaces are relaxed once on upgrade;
  installations several people share keep failing closed. The guardrails that matter on your own database - DROP,
  TRUNCATE, and UPDATE or DELETE without a WHERE - are unchanged; "Block SQL
  statements the parser cannot classify" now starts off and can be turned on
  in My policies. A shared installation still starts with it on.
- Added: a statement Rowset does not recognise is marked in the result
  ("Not recognised") with a link that opens a prefilled issue, so the parser
  can learn it instead of the statement being worked around. Nothing is sent
  from Studio; the link only opens the form.

- Fixed: T-SQL scripts were blocked by "Block SQL statements the parser
  cannot classify". `DECLARE` (a variable, a table variable or a cursor),
  `PRINT`, `OPEN`, `CLOSE` and `DEALLOCATE` are session statements, and
  `FETCH` is a read, so row limits and masking apply to its rows.
- Fixed: `EXPLAIN ANALYZE` runs the statement it explains on PostgreSQL, so
  `EXPLAIN ANALYZE DELETE FROM orders` now counts as a DELETE instead of a
  read. Plain `EXPLAIN` still only plans.
- Fixed: `SET GLOBAL` and `SET PERSIST` change the server for everyone - one
  of them switches the audit log off - and now count as administration
  instead of session state. `SET SESSION` and plain `SET` are unchanged.
- Fixed: MySQL executable comments hid whatever they carried. MySQL runs the
  SQL inside `/*! … */` and `/*!50100 … */`, so `SELECT 1 /*! ; DROP TABLE
  users */` reached the database as a second statement while Rowset saw a
  comment. That text is now lexed as SQL on MySQL and MariaDB and still
  ignored on the other engines.
- Fixed: `SELECT … INTO` is a write. It creates a table on SQL Server and
  PostgreSQL and writes a file on MySQL (`INTO OUTFILE`, `INTO DUMPFILE`), so
  it no longer passes the rules as a read. `SELECT … INTO @variable` stays a
  read, and an output file is no longer reported as a table.
- Fixed: identifiers outside ASCII were split at the first non-ASCII byte, so
  `müşteri` was read as a table called `m` and masking, row filters and
  table-scoped policies never matched it.
- Fixed: MySQL string and comment rules. A backslash escapes the next
  character inside a string, and block comments do not nest, so `'\''` and
  `/* /* */ DELETE …` are read the way MySQL reads them.
- Fixed: PostgreSQL's `TABLE users` now names its table, and a keyword after
  a table name (`SELECT * INTO archive FROM users`) is no longer taken as its
  alias.
- Fixed: a T-SQL block could hide a write. Statement splitting cuts on
  semicolons, so `BEGIN TRY DELETE FROM orders` or `IF EXISTS (…) DROP TABLE
  tmp` arrived as one statement and was read as session control; a block, IF
  or WHILE now takes the kind of the riskiest statement inside it, and a DROP
  or TRUNCATE inside one is recognised as such.
- Changed: server and storage administration (`BACKUP`, `RESTORE`,
  `CHECKPOINT`, `SHUTDOWN`, `RECONFIGURE`, `FLUSH`, `RESET`, `LOCK`,
  `UNLOCK`, `REPAIR`) counts as a write: it changes the installation even
  when it changes no row. `PREPARE` and `WAITFOR` stay unclassified on
  purpose - the first hides its statement in a string, the second holds a
  session open.
- Changed: a write whose only target is a table variable and which reads no
  table (`INSERT INTO @Tables VALUES …`) counts as session state, since it
  changes nothing in the database; read-only roles may run it. A write that
  reads a table keeps its kind, so masking and row filters still apply.

## 0.1.2 — 2026-09-22

- Fixed: the editor's auto-refresh and "more" menus did nothing when an item
  was clicked. Both menus render through a portal, so the click-outside check
  matched their own items: the menu closed on mousedown and the item never
  received the click.

## 0.1.1 — 2026-09-19

- Changed: a shared installation now has everything a personal workspace
  has — scheduled queries, row backups, the assistant and Slack settings,
  MongoDB/Redis/Valkey/Elasticsearch reads and writes, and the SQLite,
  DuckDB, ClickHouse and CockroachDB connections. On a shared server,
  scheduled results are kept in a server folder per user
  (`ROWSET_SCHEDULE_OUTPUT_DIR`, default `scheduled-results` next to the
  database) and downloaded from the run list, and only administrators can
  open a row backup's script, since it holds stored values; everyone can
  restore their own backups.
- Changed: extensions add navigation groups next to Studio's own pages
  instead of replacing them.
- Fixed: row backups on SQLite and DuckDB connections were always skipped
  ("the changed rows could not be read"): the primary-key and column
  lookups used PostgreSQL's catalog. Both now back up and restore.

## 0.1.0 — 2026-09-19

- Changed: Rowset can now be extended from another Go module and another
  front-end project instead of by editing this repository.
  - Go: the module path is `github.com/rowsetdev/rowset-studio/rowset-core`
    (the SQL parser moved into it as `rowset-core/sqlguard`). The public
    package `rowset-core/rowset` builds a Rowset executable with plugins
    (`rowset.Main`): commands, server set-up, access rules
    (`Server.SetAccess`), sign-in routes (`Kit.IssueSession`), policy kinds,
    tables in the control database (`Setup.AddStorage`), activity backends,
    a served Studio build (`Server.SetWebUI`) and `Server.Run` /
    `OpenSession` for clients other than the HTTP API. Everything else stays
    internal.
  - Studio: the repository-root `package.json` publishes Studio as
    `@rowsetdev/studio`; `mountStudio(root, { extensions })` renders it with
    extensions, which can add pages, a sign-in form, notices, Account
    sections and policy roles.
- Changed: password sign-in, password changes, user and role administration
  and sign-in lockout are no longer part of the core; a desktop workspace
  never used them. Its sign-in, sessions and data are unchanged, and
  existing databases open as before.
- Fixed: the daily audit-chain check did not include an entry's reference in
  the hash it re-computed, so an installation that stores references would
  have reported intact entries as tampered.

## 0.0.101 — 2026-09-19

- Fixed: the SSH tunnel of a MongoDB, Redis/Valkey or Elasticsearch
  connection was dropped on save — the form showed the SSH section for these
  engines but still cleared its host before sending, so the connection was
  saved without a tunnel.
- Fixed: the WHERE guardrails (`deny_select_without_where`,
  `deny_update_without_where`, `deny_delete_without_where`) could be
  bypassed with a quoted identifier named like a keyword, e.g.
  `DELETE FROM t AS "where"` or `SELECT * FROM secrets AS "where"`. Quoted
  identifiers are no longer read as keywords anywhere in WHERE detection.
  A column compared with a literal (`NOT id = 5`) is also no longer taken as
  a constant, which had made such filtered writes look unfiltered.
- Fixed: in a MongoDB manual transaction begun on a database other than the
  connection's default, policies were evaluated — and row backups recorded
  — against the default database instead of the one being written, so a
  per-database policy did not apply and Restore targeted the wrong database.
- Fixed: restoring an UPDATE row backup reported success while restoring
  nothing when a backed-up row no longer existed under its key (it was
  deleted, or the UPDATE changed the key). The restore now stops, rolls
  back and says so; restoring values a row already holds still succeeds,
  including on MySQL, which counts only changed rows.
- Fixed: Redis/Valkey bulk writes reported total failure with 0 writes
  when one command failed, although the others had been written. A hash
  write onto an existing non-hash key is now refused before anything runs,
  and any other server-side failure reports how many writes went through.
- Fixed: the MongoDB, Redis and Elasticsearch query bars kept their fields
  from the previous query when the editor text changed in the same tab
  (history pick, typing in the editor); editing a field then rebuilt the old
  query over the new one. The bars now re-read any change they did not make.
- Fixed: SQL-format exports (and scheduled SQL exports) rounded
  floating-point values to 6 decimal places; they now keep the exact value.
- Fixed: a deferred statement run for its author ignored the row limit of
  a `limit_rows` policy while reading its result.
- Changed: the macOS release downloads (archives and app) now include
  DuckDB; Linux and Windows downloads still omit it.

## 0.0.100 — 2026-09-18

- Fixed: exporting a binary/blob/bit column to SQL format (or a scheduled SQL
  export) on MySQL or MSSQL produced a literal that re-imported as the wrong
  bytes — `sqlValueLiteral` quoted the hex-encoded value as a plain string
  (`'\xdeadbeef'` on MySQL, `N'\xdeadbeef'` on MSSQL) instead of that engine's
  binary-literal syntax (`X'deadbeef'`, `0xdeadbeef`), unlike the row-backup
  restore and CSV import code paths which already handled this correctly.
  Verified live against real MySQL and MSSQL containers: a `VARBINARY`
  column now round-trips byte-for-byte through export → reimport.
- Fixed: Cassandra's "Show DDL" for a table dropped `DESC` clustering order
  entirely — it read the clustering column names but never their
  `ClusteringOrder`, so re-running the generated DDL silently recreated the
  table with ascending order. Now emits `WITH CLUSTERING ORDER BY (...)`.
  Verified live against a real Cassandra container.
- Fixed: the audit log's SHA-256 hash chain (tamper-evidence) was written
  correctly but never verified — `VerifyPreparedAudit` existed but nothing
  ever called it. Added `Store.VerifyAuditChain`, wired into a daily
  background check (`auditIntegrityCheck`) that walks every organization's
  chain and logs an error if an entry no longer matches what it was written
  with. Verified with a direct-database tampering test.

## 0.0.99 — 2026-09-18

- Added: bulk write endpoints for MongoDB (`insertMany`), Redis/Valkey
  (`bulkWrite`, pipelined) and Elasticsearch (`bulkIndex`, via `_bulk`) —
  up to 10,000 items per call, the same cap row backups and CSV import use.
  Redis validates every write before any of them run, so a bad entry never
  leaves some keys written and others not; Elasticsearch reports
  per-document failures instead of failing the whole batch. Wired into the
  write dialog: MongoDB/Elasticsearch get a "Bulk insert" checkbox that
  swaps the document textarea for a JSON array, Redis/Valkey gets its own
  "Bulk" tab. Not available inside an open MongoDB transaction, and Redis
  bulk writes skip row backup (a DUMP per key would mean thousands of
  snapshots for one call).
- Also fixed: the auto-refresh and "more" toolbar menus in the query editor
  were `absolute`-positioned inside the editor pane, so on a narrow pane
  they got clipped by the pane's own edge instead of the window's. Both now
  render into a body-level portal at a viewport-clamped fixed position.

## 0.0.98 — 2026-09-18

- Added: SSH tunnel support for MongoDB, Redis/Valkey and Elasticsearch,
  matching what the SQL engines and Cassandra already had. All three route
  every connection they open (Mongo's replica-set discovery included, ES's
  HTTP transport, Redis's single connection) through one SSH tunnel via a
  `DialContext`/`Dialer` adapter — the same pattern Cassandra already used.
  Removed the explicit "not supported yet" rejections and connection-form
  gates. Verified live: an in-process SSH test server forwarding real
  connections to Mongo/Redis/Elasticsearch containers, covering
  untrusted-host-key rejection, host-key learning, wrong-host-key
  rejection, and a working query through the tunnel — the same regression
  test the SQL engines already had, now table-driven to cover both.

## 0.0.97 — 2026-09-18

- Removed: `reports/database-client-audit-2026-09-17.md` and its
  `reports/audit-evidence/` — the report predated this session's
  Snowflake removal (still listed it as a live finding) and its
  MongoDB/Redis/Valkey/Elasticsearch write rows were marked unimplemented,
  which this session's row-backup work made false. `scripts/audit/run.py`
  still works to produce a fresh one.

## 0.0.96 — 2026-09-18

- Added: row backups for Redis/Valkey and Elasticsearch, completing row
  backups across every supported engine. Redis/Valkey capture a key's
  exact state with `DUMP`/`PTTL` before a write or delete and restore it
  byte-for-byte (with its original TTL) via `RESTORE`, or remove it if it
  didn't exist before — Redis has no distinct update mode, so a `Write`
  "Set" is backed up the same as a delete, unlike Mongo/Elasticsearch
  insert. Elasticsearch fetches the document's `_source` before an update
  or delete and restores it with a full re-index. Verified live: Redis Set
  on a new key, Set overwriting an existing key (with TTL), and Delete,
  each captured and restored correctly; Elasticsearch Insert (not backed
  up, matching Mongo), Update and Delete, each captured and restored
  correctly.

## 0.0.95 — 2026-09-18

- Added: row backups for Cassandra. A governed UPDATE/DELETE with a WHERE
  clause (USING TTL/TIMESTAMP and IF are tolerated) saves the matched rows
  first; Activity → Row backups restores them in one click, in CQL batches
  of up to 50, or opens a real, runnable BEGIN BATCH/APPLY BATCH script to
  review first. Supports the common scalar CQL types (text, int family,
  boolean, uuid/timeuuid, timestamp, blob, float/double, decimal, varint,
  inet); collections, tuples, UDTs, counters, duration, date and
  time-of-day columns are deliberately skipped rather than guessed at, with
  a reason shown in the run result, and counter tables are skipped
  entirely. Verified live: UPDATE and DELETE capture, both restore paths,
  and both skip cases (unsupported column type, counter table), round-trip
  a uuid/text/boolean/decimal/blob/timestamp row exactly.
- Docs: repo hygiene files added — CODE_OF_CONDUCT.md, CONTRIBUTING.md,
  issue templates and a pull request template.

## 0.0.94 — 2026-09-17

- Added: the MongoDB editor's Run button now executes `updateOne()`/
  `deleteOne()` typed directly into it, in shell syntax
  (`db.col.updateOne(filter, update)` / `db.col.deleteOne(filter)`) or the
  raw request JSON (`{"collection","filter","update"}`, or `{"collection",
  "filter","delete":true}`) — the same two forms `find()`/`aggregate()`
  already accepted. Previously only the Write dialog could run these;
  typing one into the editor and hitting Run failed with "Unsupported
  query field". Row backups, the WHERE guardrail and manual-commit
  transactions all apply exactly as they do from the Write dialog.
- Docs: updated README — CockroachDB gets actual (not just estimated) plans
  since it has `ExplainAnalyze`, noted MongoDB's row backups (the only NoSQL
  engine with them so far), and fixed the client-audit link's stale
  "14-engine" count after Snowflake's removal.

## 0.0.93 — 2026-09-17

- Fixed: reopening a MongoDB `find()` from Activity/History failed with
  "Max time must be between 1 and 600000 ms." The saved request always
  includes `maxTimeMs`, and 0 (the backend's own "no limit" default) was
  being rejected as out of range instead of accepted.
- Fixed: reopening an `aggregate()` from Activity/History failed outright
  ("Unsupported query field: pipeline"), because only its shell-syntax form
  was recognized — the raw `{collection,pipeline,...}` object History
  actually saves fell through to `find()`'s field validation. The query bar
  now also switches to Aggregate mode when reopening one.

## 0.0.92 — 2026-09-17

- Added MongoDB row backups: an update/delete from the Write dialog (or
  inside a manual transaction) can save the documents it is about to
  change first, the same "back up before UPDATE/DELETE" preference SQL
  writes already use. Activity → Row backups lists them with a Mongo shell
  script for review and a one-click Restore, which re-inserts (for a
  delete) or fully replaces by _id (for an update) — not a partial `$set`
  replay, since that can't be inverted field by field. Restore runs
  document by document rather than in one transaction, since MongoDB
  transactions need a replica set. Verified against a live server end to
  end, including clicking through Restore in the browser.

## 0.0.91 — 2026-09-17

- Added pagination to the Activity statements table: 20 rows per page with
  Previous/Next, resetting to page 1 whenever a filter changes.

## 0.0.90 — 2026-09-17

- Added MongoDB manual-commit transactions: the toolbar's manual-commit
  toggle begins a session/transaction, the Write dialog's insert/update/
  delete join it, and Commit/Rollback end it — same UI as SQL engines.
  Requires a replica set or mongos; a standalone server's rejection surfaces
  as the begin call's error.
- Fixed: MongoDB document insert returned an empty, unusable `_id` in its
  response (`bson.MarshalExtJSON` errors on a bare scalar value like an
  ObjectID; the error was silently swallowed). Found via manual browser
  testing of the transaction feature above, not a directed check — none of
  the existing tests asserted on the returned id's content, only that the
  call didn't error. Fixed and covered by a new regression check.
- ClickHouse's Go driver silently no-ops `Rollback()` — a transaction
  "succeeds" without reverting anything. Confirmed live and left disabled;
  MongoDB got the same check and passed for genuine (replica-set) support.

## 0.0.89 — 2026-09-17

- Added an Aggregate mode to the MongoDB query bar: a Find/Aggregate toggle
  and a pipeline editor, kept in sync with `db.collection.aggregate([...])`
  in the editor below it (previously only reachable by hand-typing shell
  syntax).
- Enabled CockroachDB's "Explain with actual rows" (the backend already
  supported EXPLAIN ANALYZE; only the capability flag hid it).
- Added a live CockroachDB transaction test (Begin/Execute/Rollback and
  Begin/Execute/Commit) and a live DuckDB one — both engines advertised
  Transactions but neither was exercised live before, unlike PostgreSQL,
  MySQL, MariaDB, SQL Server and SQLite.

## 0.0.88 — 2026-09-17

- Removed Snowflake support (connection type, driver, DSN/schema/DDL
  handling, engine picker and logo) to focus engine coverage elsewhere.
- Added Elasticsearch `sort`/`search_after` pagination past the 10,000-hit
  window and `aggs` in the search bar's raw JSON, with aggregation results
  and the next page's `search_after` values shown in the grid.
- Added MongoDB read-only aggregation: `db.<collection>.aggregate([...])` in
  the editor runs a pipeline, with `$out`, `$merge`, `$lookup` and other
  writing/cross-collection/JavaScript stages rejected before it runs.
- Added a "Test all" action on the Connections page to test every filtered
  connection at once.

## 0.0.87 — 2026-09-17

- Added MongoDB document insert/update/delete and Elasticsearch document
  index/update/delete, behind the same policy/read-only/audit guardrails as
  SQL writes; update requires operator documents ($set/$unset/…), never a
  full-document replace.
- Added Redis/Valkey string and hash-field writes and key deletion, with
  optional TTL.
- Added a "Write" action in the query toolbar for MongoDB, Redis/Valkey and
  Elasticsearch connections to drive these from Studio.
- Added CockroachDB and ClickHouse EXPLAIN (text plan, no ANALYZE yet).
- Added SQLite and DuckDB CSV import.
- Schema Compare's connection pickers now hide MongoDB, Redis, Valkey and
  Elasticsearch, which have no table/column metadata to diff.

## 0.0.86 — 2026-09-16

- Expanded Cassandra from personal read-only queries to policy-governed CQL
  reads, writes, DDL and batches in personal and shared workspaces, with audit
  logging, read-only enforcement and automatic schema cache invalidation.
- Added Cassandra contact points, consistency and page-size settings, plus TLS
  and SSH tunnel support.
- Added Cassandra table DDL viewing, CSV/JSON export and typed CSV import in
  bounded logged batches, with explicit partial-failure behavior.
- Added CQL-aware classification and automated/live integration coverage for
  comments, strings, multi-statements, batches, writes, DDL and imports.

## 0.0.85 — 2026-09-16

- Fixed sidebar navigation occasionally leaving the SQL editor visible after
  a query completed even though browser history had moved to the selected
  page. Rowset now detects that router/history desynchronization and recovers
  immediately without interfering with running-query or transaction prompts.
- Updated the Studio runtime and build chain to React 19.3, React Router 8.4,
  Vite 8.3 and the matching React Vite plugin and type definitions.

## 0.0.83 — 2026-09-15

- Server (Host/Port, or HA nodes) moved up next to username/password
  in the connection form, instead of near the bottom.
- Dropped the separate Alias field - it's derived from Name
  automatically now, same as it always was when left blank.
- Valkey has its real logo instead of a colored-initial badge.

## 0.0.82 — 2026-09-15

- HA nodes are opt-in now: a new connection shows a plain Host + Port
  row instead of always showing the multi-node "name / host / port /
  Remove" editor. A "This connection has more than one node" checkbox
  reveals the full editor when it's actually needed.

## 0.0.81 — 2026-09-15

- **Fixed Windows opening Internet Explorer instead of the default
  browser** for Rowset Studio, found on a real Windows Server: the
  old `rundll32 url.dll,FileProtocolHandler` trick goes through IE's
  own URL handler on some Windows builds. Switched to `cmd /c start`,
  which respects the actual default-browser association.
- Auto-refresh now allows `EXEC`/`EXECUTE`/`CALL` (a stored procedure
  call isn't a plain write), so watching a diagnostic proc like
  `sp_whoisactive` on an interval - the original motivating use case -
  actually works; plain DML/DDL keywords are still excluded.
- Query results show which physical node they ran against (as a small
  badge next to Completed), for connections with more than one node.
- **Added Valkey support**, wire-compatible with Redis so it reuses
  the same query editor and schema browsing.
- The New/Edit connection form: TLS now defaults to Off instead of
  full certificate verification; added "preprod" to Environment;
  username and password are next to each other instead of opposite
  ends of the form, and the form is visibly more compact overall.

## 0.0.80 — 2026-09-15

- **`rowset desktop` now survives closing the terminal it was started
  from**, on every platform. It used to run attached to whatever
  console launched it (typing `rowset` in cmd.exe/PowerShell and
  closing that window killed it, since Windows terminates a console
  process tree on close, and the same applies on Linux/macOS without
  something detaching it). It now re-execs itself once, detached
  (`Setsid` on macOS/Linux, `DETACHED_PROCESS` on Windows), and the
  original invocation waits for the detached copy to report itself
  ready before returning - so scripts calling `rowset desktop`
  synchronously still see a real failure if startup fails, but
  otherwise get their prompt back immediately.
- The macOS menu-bar app opts out of this (`ROWSET_DETACHED=1`) since
  it already manages the server process directly and needs to notice
  it crashing later, not just a failed launch - its behavior is
  unchanged.

## 0.0.79 — 2026-09-15

- **License changed from Apache 2.0 to the PolyForm Noncommercial
  License 1.0.0.** The source stays open to read, fork and modify,
  and free for any noncommercial use, but commercial use now needs a
  separate license.

## 0.0.78 — 2026-09-15

- Auto-refresh's active state is amber now (a better fit for "live"
  than the info-blue used elsewhere), and its pulsing dot no longer
  clips against the button's corner as it fades — it's a proper
  ping-ring indicator inline with the interval label instead.

## 0.0.77 — 2026-09-15

- Auto-refresh is now a single clock-icon button next to Run instead of
  a full-width "Auto-refresh: …" dropdown.
- Explain's icon is now a small node diagram instead of a generic
  circled-i.

## 0.0.76 — 2026-09-15

- **Auto-refresh**: a dropdown next to Run (Off/3s/5s/10s/30s) re-runs
  the current statement on an interval and updates the result in
  place — for watching a running process, a queue, or anything else
  worth checking every few seconds without hitting Run by hand. Only
  enabled for statements that look read-only; editing the statement
  into a write turns it back off.
- Fixed a real "index/PK/FK metadata not yet loaded" warning that was
  shown for every DuckDB and ClickHouse schema load regardless of
  whether that metadata was actually missing — it wasn't, for either
  of them. Both now load real primary keys, and DuckDB also loads
  real foreign keys and indexes; ClickHouse has no foreign keys at
  all, so there's nothing left to warn about there. Cassandra now
  also loads its secondary indexes and materialized views, which it
  previously flagged as simply not implemented.
- The sidebar and the connections list's "Open" link no longer trigger
  the browser's own link-target preview on hover — they navigate with
  the app's router instead of a real `<a href>`, since there's no
  actual new-tab use case for a one-use local session.

## 0.0.75 — 2026-09-15

- **Results filter redesign**: Filter and Edit rows are real bordered
  buttons now; Filter toggles a panel instead of adding a new
  condition on every click, with a dedicated "+ Add condition" button
  for additional ones; "Clear filters" reads as a (subdued) destructive
  action; and the row count moved out of the toolbar into the status
  bar, where it only lights up as "X of Y rows filtered" once a filter
  actually narrows something.
- The Primary/Secondary node picker no longer shows (disabled) for
  every single-node connection — only when the connection actually has
  more than one node to route between.

## 0.0.74 — 2026-09-15

- **Results Filter button is now visible**: it was plain text with no
  icon or hover state next to the properly-styled Edit rows button;
  now it matches, and highlights when a filter is active.
- The "X / Y loaded rows" counter only stands out (a highlighted "X of
  Y rows match" badge) once a filter actually narrows the result;
  otherwise it's a quiet "N rows loaded", so a filter matching every
  row (e.g. "any column contains 2" when a date column has a year like
  2026) doesn't read as broken.

## 0.0.73 — 2026-09-15

- **JSON view for query results**: a third view next to Grid and Text,
  syntax-colored, showing one JSON object per row. MongoDB and
  Elasticsearch results open in it by default, since their Grid view is
  a single "document" column of stringified JSON; every other engine
  still opens in Grid and can switch to JSON from the same toolbar.
- Fixed two lint errors from the previous change set (a useless
  assignment in the Elasticsearch query bar, and `eslint-disable`
  comments referencing a rule this project's config never registers).

## 0.0.72 — 2026-09-15

- **Query editors for Redis, Cassandra and Elasticsearch**: all three engines
  had a schema browser and a "not available yet" placeholder where the query
  editor should be; now they have real editors (a scan bar for Redis, CQL for
  Cassandra, a search bar for Elasticsearch), wired into `Run`, `Run all`,
  Monaco syntax highlighting and the "Open" row action in the schema tree,
  the same as every other engine.
- Fixed the Elasticsearch query bar sending an empty `{}` query clause (which
  Elasticsearch rejects as malformed) when the bar's fields hadn't been
  edited yet — it now defaults to `{"match_all":{}}`.

## 0.0.71 — 2026-09-15

- **Real logos** for CockroachDB, Snowflake, Redis, Cassandra and
  Elasticsearch (Simple Icons marks, recolored to each brand's color),
  replacing the colored-initial badges from the previous release.
- **ClickHouse no longer lists `INFORMATION_SCHEMA` and `information_schema`
  as two separate databases** — they're the same schema under a case-variant
  alias ClickHouse keeps for MySQL compatibility; only the canonical
  lowercase one is shown now.
- **Saved query snippets**: a "Snippets" menu in the SQL toolbar saves the
  current statement by name, per connection, and inserts a saved one back
  into the editor. The backend and API client already existed from an
  earlier change but had no UI until now.
- **Read-only connections**: a "Safe mode" checkbox on a connection blocks
  every write statement (INSERT/UPDATE/DELETE/DDL) on it, the same way a
  read-only role already does, but without needing to set one up. Shown as
  a "Safe mode" badge in the SQL toolbar and a lock badge in the connections
  list.

## 0.0.70 — 2026-09-15

- Redis, Cassandra and Elasticsearch pseudo-tables no longer show "Open
  SELECT", "Show DDL" or export actions in the schema tree — none of those
  paths work yet for these engines, and showing them just led to a confusing
  error. The auto-commit/transaction toggle is likewise hidden for them.
- CockroachDB, Snowflake, Redis, Cassandra and Elasticsearch get a colored
  initial badge instead of all sharing the same generic gray icon, so they
  are tellable apart in the connection list, sidebar and command palette.

## 0.0.69 — 2026-09-15

- **MongoDB query bar** now also populates itself from a query opened from
  Activity or History, not just typed shell syntax; those entries are saved
  as the raw find-request JSON, which the bar previously failed to parse and
  silently left at its defaults.

## 0.0.68 — 2026-09-15

- **Five new engines**: CockroachDB and Snowflake connect through the normal
  SQL editor (CockroachDB reuses PostgreSQL's wire protocol and catalogs
  directly; Snowflake uses its own `database/sql` driver — enter your account
  identifier as the server). Redis, Cassandra and Elasticsearch have working
  backends (key scan with type/TTL preview for Redis, read-only CQL SELECT
  for Cassandra, `_search` for Elasticsearch) reachable through the API, but
  their dedicated query screens are not built yet — for now their connections
  can be created, tested and schema-browsed from the sidebar; the query editor
  shows a notice instead of the SQL/Mongo editor. HA nodes and SSH tunnelling
  are not available for Redis, Cassandra, Elasticsearch or Snowflake yet.
- **Command palette** (⇧⌘K / Ctrl+Shift+K): jump to any page or saved
  connection from anywhere in Studio. Uses Shift so it doesn't collide with
  the schema explorer's existing ⌘K search-box shortcut.

## 0.0.67 — 2026-09-15

- **MongoDB query bar**: added Max Time MS to Options, mapped to the shell's
  `.maxTimeMS()` and enforced server-side as a per-query timeout.

## 0.0.66 — 2026-09-14

- **MongoDB query bar**, Compass-style, above the `db.collection.find()`
  editor: a Filter field plus an Options panel for Project, Sort, Skip and
  Limit. Editing the bar rewrites the editor's shell syntax; switching tabs
  re-reads the bar from the editor. `find()` now also accepts a projection as
  its second argument and `.skip()` in the chain. Collation and max time are
  left out of this pass; add them later if needed.

## 0.0.65 — 2026-09-14

- Fixed the MongoDB and ClickHouse logos rendering larger than the other
  engine logos in the schema tree; a global `height: auto` rule was
  overriding their `height` attribute for non-square source SVGs.

## 0.0.64 — 2026-09-14

- **MongoDB query editor** accepts shell syntax (`db.customers.find({}).sort({...}).limit(n)`)
  in addition to the JSON find form; removed the redundant explanation strip
  and SQL-only tools (Explain, "Explain with actual rows") from the Mongo
  editor toolbar.
- **Connection names must be unique** per org, checked case- and
  whitespace-insensitively on both create and update, with a clear error in
  the form. Existing duplicates are left as-is; connection pickers now show
  host/port/database next to a name that collides with another connection.
- **New Connection form**: Engine selector moved to the top of the form,
  above the engine-specific fields.

## 0.0.63 — 2026-09-13

- **Schema comparison** in the sidebar and a database's menu compares two
  selected schemas, showing added, changed and removed tables, columns and
  index summaries. Inspect the table DDL side by side. Migration drafts open
  in the existing SQL editor on the target connection without running them.
  Only simple nullable column additions are generated automatically; other
  changes are marked MANUAL. Incomplete metadata disables draft generation.
  CHECK constraints, full index/FK definitions, views and routines are outside
  this initial metadata comparison.
- **SQLite** connections open an existing local file, with SQL queries,
  transactions, table/view/trigger discovery, keys, indexes and original DDL.
- **DuckDB** connections open an existing local file, with SQL queries,
  transactions, table/view discovery and DDL. Native builds include the Go
  driver using CGO; portable non-CGO builds hide DuckDB in the new-connection
  form and reject it on the API. Index/key discovery is not yet available.
- **ClickHouse** connections use the native TCP protocol (9440 with TLS,
  9000 without), with database/table/view discovery, DDL and SQL queries.
  Interactive transactions and index/key discovery are not yet available.
- **Local result filters** combine column conditions without rerunning SQL;
  filtered CSV/JSON export and original row identities are preserved.
- **SQL value parameters** use `{{name}}` with text, number, boolean or NULL
  values; scripts validate all parameters before starting.
- **MongoDB in the Query editor** lists databases and collections, finds and sorts
  documents with Extended JSON filters, cancels reads and exports results as
  JSON. BSON types are preserved as canonical Extended JSON. SELECT, table,
  schema, timeout and row-limit policies apply to finds. This initial version
  is read-only, uses authSource=admin, and does not support aggregation,
  SRV URLs or SSH tunnels.
- Additional engines currently run in personal workspaces with one configured
  endpoint. SQL INSERT export, CSV import and inline row editing are not yet
  available for the new engines; SQL engines can export CSV/JSON. Application
  settings and query-result storage are unchanged.

## 0.0.62 — 2026-09-13

- `#` starts a comment only on MySQL and MariaDB. On PostgreSQL and SQL Server
  it is read as written, so the `#>` operator works and a `;` after it is seen
  as a second statement — which the multiple-statements guard then refuses,
  rather than the parser hiding it behind a comment. The statement classifier
  and the editor both split by the connection's engine.
- Results are no longer cut off at a hidden size. The 16 MB per-row and 32 MB
  per-result display limits, which stopped a large result mid-stream, are gone;
  the row-count policy is what bounds a result.
- Updated the build dependencies flagged by npm audit (browserslist, nanoid);
  they are development-only and not part of the app.
- Export a table as SQL: **Export as SQL (INSERT)** writes multi-row INSERT
  statements that quote the table and columns for the engine, write NULL for
  null and keep every value's type; a database can replay them. CSV exports now
  begin with a UTF-8 byte-order mark so Excel shows non-ASCII text (Turkish,
  say) correctly.
- The dialect-aware "#" handling now reaches every place statements are split:
  the editor's run and quick-fix paths, inline problem checks, autocomplete and
  Run on several connections. So a SQL Server "#temp" table and PostgreSQL "#>"
  are never mistaken for a comment, on any screen.

## 0.0.61 — 2026-09-13

- Fixed: Run on several connections and the SQL assistant were given the
  60-second limit ordinary API requests have. A script that ran longer was cut
  off, its statements cancelled, and progress reached the page only when the
  whole run had finished. Both now stream and run for as long as they need,
  like a single query.

## 0.0.60 — 2026-09-13

- A history read waits at most two seconds for statements still being
  recorded, then shows what is stored. Against the local database that wait is
  a few milliseconds; it keeps the history page from hanging when a remote
  activity store is slow or unreachable.

## 0.0.59 — 2026-09-13

- The database explorer keeps room at its right edge for the scrollbar macOS
  draws over the content while scrolling, so it no longer covers the column
  types aligned to that edge.

## 0.0.58 — 2026-09-13

- **Run on several connections** now runs on the server, up to ten
  connections at once (chosen in the dialog, four by default). A browser opens
  at most six connections to one address and shares them with the rest of
  Studio, so running from the page capped real concurrency at six and stalled
  everything else meanwhile. One request now streams each connection's
  progress; every statement still goes through the query handler as the same
  person, so policies, row backups, timeouts and history apply per connection,
  and results reach the page unchanged, large integers included. Stop all
  ends the statements on the databases themselves.
- A trigger on a table or view is listed under that table, after its columns
  and indexes, and the table shows that it has triggers. The Triggers group
  keeps only triggers without such a table.
- Definitions a database returns on one line — MySQL and MariaDB triggers and
  views, PostgreSQL triggers, SQL Server views and functions — are laid out on
  lines in Show DDL. Only whitespace between tokens changes; a definition the
  author wrote on lines is shown exactly as written. Copy and Open in editor
  take the text as shown.

## 0.0.57 — 2026-09-12

- The editor marks problems as you type, from the text and the schema already
  loaded, with no round trip to the database:
  - a string, quoted name or comment that is never closed, and a bracket that
    closes nothing (errors);
  - a table or view that is not in the loaded schema, and `alias.column` where
    the table has no such column, each with close names to change to;
  - a column two tables of the same query both have, used without saying
    which, with a fix to qualify it for each;
  - UPDATE or DELETE without WHERE.
- Checks stay quiet when a name cannot be verified: CTEs, tables the script
  creates, table functions, system catalogues, other databases, subquery
  aliases, and a table name that several schemas share where the search path
  decides — a column is missing only if no candidate has it. A subquery's
  columns are checked against its own tables, not the outer query's.
- `#` starts a comment only on MySQL and MariaDB when inspecting, so SQL Server
  temporary tables and PostgreSQL's `#>` no longer look like broken brackets.
- Fixed: the GROUP BY quick fix inserted at the wrong place in a statement that
  had blank lines before it.
- The quick-fix menu is wide enough to show the whole fix.

## 0.0.56 — 2026-09-12

- **Run on several connections** (editor ⋯ menu) runs the selection, or the
  whole editor, on the connections you pick, each in its own database. Up to
  four run at a time; on each one the statements run in order in auto-commit
  and stop at the first error without stopping the others. Every statement
  goes through the normal query endpoint, so policies, row backups and history
  apply to each connection as usual.
- A script that changes data or schema asks for confirmation first and names
  the production connections among the targets; changing the choice asks
  again. Anything not recognisably a read counts as a change — procedure
  calls, SET, EXPLAIN ANALYZE, SELECT … INTO, a CTE ending in DELETE.
- The Connections tab lists every connection with its status, statements,
  rows and time next to the result of the one picked, showing a failure first.
  Results whose columns match can be combined into one grid with the
  connection in the first column. Stop all cancels every running statement.

## 0.0.55 — 2026-09-12

- Statements are recorded by one background writer in batches instead of two
  writes on every request. With 48 concurrent writers the local store went from
  613 to about 46,000 records a second. A record is never dropped: when the
  queue is full a request waits for room rather than competing with the writer
  for SQLite's lock, and history still shows a statement as soon as it has run.
- The audit chain reads its newest entry through an index. At 200,000 audit
  entries that read took 40 ms on every statement, behind a lock; it now takes
  0.01 ms.
- History is listed through its index instead of sorting every statement a
  person ever ran (20 ms at 200,000 rows).
- Retention periods are applied once a day on a shared server. A personal
  workspace keeps its history and audit log unless a period is set explicitly.
  Old entries are removed in small batches, so statements keep being recorded
  meanwhile, and the cutoff is compared in the same format the entries use.
- Snapshots are taken at most once a day, after Rowset is already answering.
  Taking one on every start meant a few restarts in a day rotated out last
  week's copy, and a large database held up opening the app.
- Before a new version applies migrations to an existing database, Rowset
  copies it to `snapshots/before-<migration>-<time>.sqlite3` and does not
  upgrade when that copy cannot be written. The daily snapshot is taken after
  migrations, so it could never undo one that went wrong.
- A loaded schema is reused for 30 seconds and loaded once when the explorer,
  autocomplete, the diagram and the assistant ask at the same time. DDL, a
  script or a procedure run through Rowset, the end of a transaction, editing
  the connection and Refresh all read the catalog again straight away.

## 0.0.54 — 2026-09-12

- Rowset writes a snapshot of its own database into `snapshots/` beside it on
  every start and keeps the newest seven, so a bad migration or a mistake is
  recoverable. SQLite's VACUUM INTO takes the copy in one transaction, which
  keeps it consistent with the write-ahead log.
- Starting on a data directory with no database says so on the console, and
  names ROWSET_DESKTOP_DIR, instead of quietly coming up as a new installation
  with no connections in it.
- `scripts/restart-try.sh` rebuilds and restarts the local instance against its
  own data directory.

## 0.0.53 — 2026-09-12

- Columns of the tables in the statement are offered through their alias, and
  a column that several of those tables share is offered only that way, since
  the bare name would not resolve.
- A SELECT that aggregates without a GROUP BY offers one: the quick fix on the
  statement fills in the columns it has to group by.

## 0.0.52 — 2026-09-12

- Autocomplete follows the clause you are in: columns come first while
  selecting or filtering, tables after FROM and JOIN.
- After FROM, the joins your foreign keys allow are offered with the ON
  clause already written, using the alias already in the statement.

## 0.0.51 — 2026-09-12

- **Slack notifications**: paste an incoming webhook address in Account and
  Rowset posts when a scheduled query runs — every run or only failures —
  with how long it took, how many rows it wrote and the file it produced.
  A statement in the editor that takes longer than a threshold you set can
  report itself too. The rows are never sent, only what ran.
- Every group in the schema browser, tables included, starts collapsed;
  searching opens them so matches are never hidden.

## 0.0.50 — 2026-09-12

- The assistant moved from the bottom tabs to a panel beside the editor,
  opened with the Assistant button in the toolbar, so it can stay open while
  a statement runs and its results come in. Its state is remembered.
- Format has an icon of its own; the wand now means the assistant.

## 0.0.49 — 2026-09-12

- **AI assistant**, set up in Account: either your own Anthropic or OpenAI
  key, kept encrypted like a connection password, or the `claude` / `codex`
  command already signed in on this computer, in which case no key reaches
  Rowset at all.
- It can write a statement from the schema, explain the one in the editor,
  rewrite it to run faster using the indexes that exist, say which index
  would help, and explain a failed statement and correct it.
- Table and column names, their types and the indexes of the open database
  are sent as context, never table contents, and sharing the schema can be
  turned off.

## 0.0.48 — 2026-09-12

- SQL shown outside the editor is coloured by the editor's own tokenizer
  instead of a short keyword list, so every keyword, function, string and
  number reads exactly as it does while typing.

## 0.0.47 — 2026-09-12

- The Plan tab appears only once Explain has produced a plan, and goes away
  with the next run.

## 0.0.46 — 2026-09-12

- SQL shown outside the editor (an object's DDL, the statements to review
  before applying grid changes) uses the editor's own colours.

## 0.0.45 — 2026-09-12

- **Diagram**: a map of a database's tables and the foreign keys between
  them, with primary and foreign key columns marked. Open it from a
  database's ⋯ menu in the explorer; drag to move, scroll to zoom, filter by
  name, and click a table to pick out what it is related to.

## 0.0.44 — 2026-09-12

- **Add and delete rows in the result grid**, next to editing cells: Add row
  types a new row at the end, and clicking a row's number marks it for
  deletion. Review shows the INSERT, UPDATE and DELETE statements, coloured,
  before anything runs; they run through the editor, so policies, manual
  commit and row backups apply as to any statement.

## 0.0.43 — 2026-09-12

- The DDL of an object is shown with SQL colouring.

## 0.0.42 — 2026-09-12

- SQL Server table DDL writes PRIMARY KEY and UNIQUE instead of the
  catalog's PRIMARY_KEY_CONSTRAINT spelling.

## 0.0.41 — 2026-09-12

- **Show DDL** in the schema browser: the statement that creates a table,
  view, procedure, function, trigger or sequence. MySQL and MariaDB answer
  with their own SHOW CREATE text, SQL Server and PostgreSQL with their
  stored definitions, and CREATE TABLE is built from the catalog where the
  engine has no function for it (columns, defaults, identity, keys, checks,
  foreign keys and indexes).

## 0.0.40 — 2026-09-12

- Opening the SQL editor always collapses the navigation to icons, however
  it was left before. Expanding it there lasts for that visit; every other
  page keeps the width last chosen on such a page.

## 0.0.39 — 2026-09-12

- Fixed: the navigation kept its old width in the SQL editor. Every
  workspace already had an explicit expanded/collapsed setting saved, which
  overrode the new automatic mode; the setting now lives under its own name
  and starts as automatic.

## 0.0.38 — 2026-09-12

- The navigation shows only its icons in the SQL editor, where the explorer
  needs the width, and stays open on the other pages. Using the collapse
  button fixes your choice everywhere until you use it again.
- Shutdown Rowset is tinted red.

## 0.0.37 — 2026-09-12

- The row limit policy no longer describes itself as rewriting statements,
  because it does not: it reads the rows up to the limit and stops.
- Live tests run against a desktop instance put a policy's value back too,
  not only whether it was on, so a test run cannot leave a workspace with
  its own row limit.
- "Shutdown Rowset" stays on one line in the sidebar again.

## 0.0.36 — 2026-09-12

- The schema browser lists **sequences** (PostgreSQL, SQL Server, MariaDB)
  next to views, procedures, functions and triggers. All of these groups
  stay collapsed until you open them.
- Expanding a database reloads its schema, so objects created since the
  last look appear without pressing refresh.

## 0.0.35 — 2026-09-12

- The row limit no longer rewrites statements. Rowset runs the SQL exactly
  as written and stops reading once the cap is reached, then cancels the
  rest, so no statement can be made invalid by a LIMIT or TOP that the user
  did not write. Exports and scheduled files follow the same rule, and an
  export that stops at the cap says so.

## 0.0.34 — 2026-09-12

- **Results are capped at 10,000 rows by default**, by the "Limit result
  rows" policy, which existing workspaces also get. It rewrites the
  statement (LIMIT, or TOP on SQL Server), says so under the result and
  offers Export all rows. Change the number or turn it off in My policies.
- That rewriting now handles more statements: SELECT DISTINCT and CTEs on
  SQL Server, MySQL's `LIMIT offset, count`, `FETCH FIRST n ROWS ONLY`, and
  statements ending in FOR UPDATE or LOCK IN SHARE MODE, which used to
  produce invalid SQL or an error.

## 0.0.33 — 2026-09-12

Performance of large results, now that nothing caps them:

- Streaming a result no longer copies every row already received on each
  progress update, which made a long result slower the longer it ran.
  Progress is reported less often as a result grows.
- Row batches are also flushed by size, so a table with large values cannot
  produce one enormous line for the browser to parse.
- Saving the editor's tabs waits longer between saves for large workspaces
  instead of encrypting everything on each keystroke.

## 0.0.32 — 2026-09-12

- The editor no longer caps results at 1000 rows. A result is capped only
  by the "Limit result rows" policy, which then says so and offers
  **Export all rows (CSV)**. Rows stream in as they arrive and the grid
  only renders what is on screen; Stop still ends a run.

## 0.0.31 — 2026-09-12

- **Procedures, functions, triggers and events with BEGIN … END bodies**
  (MySQL, MariaDB, SQL Server) were rejected as "multiple SQL statements",
  and the editor split them at their semicolons. Both now keep the body in
  one statement; END IF / END LOOP and BEGIN TRANSACTION are understood.
- A live test now creates and uses tables with keys, indexes, views,
  functions, procedures (CALL / EXEC), triggers and sequences on every
  engine, and checks the schema browser lists them.
- No hidden limits without a policy: exports, imports, plans, schema reads
  and restores are no longer cut off after 60 seconds; the editor's row cap
  says "Showing the first 1000 rows" with **Show up to 10,000** and
  **Export all rows (CSV)** instead of claiming a policy; a query that hits
  the connection's query timeout says so and where to change it (default
  10 minutes, up to 24 hours, per connection); personal workspaces accept
  32 MB requests, 16 MB of saved tabs and 20 manual-commit transactions.
- Errors reading policies, scheduled runs or a table's column types are
  reported instead of silently showing defaults.
- A divider separates the theme switch from Shutdown Rowset.

## 0.0.30 — 2026-09-12

- Long queries: a personal workspace no longer stops statements after 8–10
  minutes (a connection's own query timeout and policy timeouts still
  apply), a manual transaction is never rolled back as idle while one of
  its statements is running, idle transactions are kept for an hour
  instead of 5 minutes, and the desktop app keeps the computer from idle
  sleep while statements, exports, imports or restores run.
- The personal owner is no longer rate limited, which could make a busy
  editor fail with "too many requests".
- NaN and Infinity values (PostgreSQL float and numeric) no longer break
  the result; they show and restore as `NaN`, `Infinity` and `-Infinity`.
- Restoring SQL Server `sql_variant` values keeps each value's own type.
- The all-types test now covers far more types (PostgreSQL ranges,
  geometric types, tsvector, arrays, special numbers; MySQL/MariaDB text
  and blob sizes, bit(64), decimal(65,30), spatial types, inet4/inet6/uuid;
  SQL Server text/ntext/image, max types, time(7), sql_variant,
  hierarchyid, geography, geometry) and runs over the HTTP API. With
  ROWSET_E2E_DESKTOP_DIR it runs against a running desktop instance, so
  its work shows in that instance's Activity and Row backups.

## 0.0.29 — 2026-09-11

Every data feature was run against a table of all common column types on
PostgreSQL, MySQL, MariaDB and SQL Server (full, edge-value and NULL rows),
plus a 20,000-row table. Fixed what that found:

- Row backups: restoring skips generated and computed columns (and SQL
  Server rowversion), leaves identity columns out of UPDATEs, and inserts
  deleted rows with `OVERRIDING SYSTEM VALUE` on PostgreSQL identity tables.
- **Restore** in Activity → Row backups now puts rows back in one
  transaction, with policies applied; on SQL Server identity tables it turns
  IDENTITY_INSERT on for it. The script stays available as **Script**.
- Results show dates as `2024-02-29`, times as `13:45:10.123` and
  timestamps without the ISO `T…Z` form, so exports import back into MySQL
  and generated SQL works on every engine.
- SQL Server uniqueidentifier values show as GUIDs instead of hex, and
  binary columns always show as hex.
- CSV import writes hex (`\x…`) into binary columns as bytes, and editing
  a binary cell in the grid generates a hex literal.

## 0.0.28 — 2026-09-11

- The browser's Back button asks before leaving the SQL editor, even when
  nothing is running; links in the app ask only when a query runs or a
  transaction is open.

## 0.0.27 — 2026-09-11

- Autocomplete offers database names on MySQL, MariaDB and SQL Server, and
  on SQL Server suggests schemas after `database.`.
- Leaving the SQL editor while a query runs or a transaction is open asks
  first; a trackpad side swipe no longer navigates back out of the app.
- The row backup setting explains how the 10,000-row check works.

## 0.0.26 — 2026-09-11

- More compact Database Explorer; counts in square boxes.
- Tables show Open SELECT and Copy name on hover again; their ⋯ menu has
  **Export as CSV / JSON** and Import CSV.
- Table export downloads the whole table. It runs as a SELECT with the same
  policies, row limits and result hooks as the editor, so the default
  "no SELECT without WHERE" policy blocks it until turned off.
- Scheduled queries now honour policy row limits too.
- The result grid's column resize handle is invisible until hovered.

## 0.0.25 — 2026-09-11

- Redesigned Database Explorer: a search box for databases, tables and
  columns (⌘K / Ctrl K), + to add a connection, engine groups separated by
  lines, rounded count pills, and a ⋯ menu on connections (refresh, edit)
  and tables (open SELECT, copy name, import CSV).
- A line separates Shutdown Rowset from the version in the sidebar.

## 0.0.24 — 2026-09-11

- Redesigned the unsaved-tabs screen: each copy lists its tabs with
  Restore tabs, Download and Discard. Restored or discarded copies no longer
  come back on the next start, copies whose tabs were saved after all are
  cleared silently, and tabs already in the workspace are not added twice.

## 0.0.23 — 2026-09-11

- The row backup setting moved to Account (on by default).
- When a statement's rows cannot be backed up (more than 10,000 rows, or an
  UPDATE on a table without a primary key), it does not run; Rowset asks
  whether to run it without a backup.
- Fixed: tabs opened from a row backup made the tab autosave fail with
  "invalid JSON request".

## 0.0.22 — 2026-09-11

- Running a restore script no longer makes a new row backup of its own.
- The row backup option moved from the toolbar to the ⋯ menu
  (“Back up rows before UPDATE/DELETE”).
- Row backups list the statement without its leading comments, and MySQL
  tables no longer show the database name twice.

## 0.0.21 — 2026-09-11

- Row backups are optional: the **Row backup** switch in the editor toolbar
  turns them on or off (on by default, remembered in the browser).

## 0.0.20 — 2026-09-11

- **Row backups**: before an UPDATE or DELETE on one table with a WHERE
  clause, Rowset saves the rows it is about to change (up to 10,000,
  encrypted, newest 100 kept). Activity → Row backups opens a restore script
  in a new editor tab: INSERTs for deleted rows, UPDATEs by primary key for
  changed ones. UPDATEs on tables without a primary key are not backed up,
  and the editor says why. A statement that fails keeps no backup.
- Fixed: `WHERE id <= 2` and `WHERE status != 'x'` were treated as always
  true, so the UPDATE/DELETE-without-WHERE policies blocked them.
- Redesigned CSV import dialog (drop zone, mapping table, progress) and a
  lighter schema tree: plain counts instead of boxed badges, compact rows,
  search with the refresh button inside.
- The light/dark switch sits next to Shutdown Rowset in the sidebar.

## 0.0.19 — 2026-09-11

- **Import CSV** from a table in the schema browser: preview, delimiter
  detection, header and empty-as-NULL options, column mapping. The file
  uploads in chunks and is inserted in one transaction, so either every row
  is imported or none is; policies apply as to any INSERT.
- Fixed: after switching tabs, Run and Explain could use the selection of
  the previous tab.
- A new run in a tab clears its old execution plan.
- The desktop sidebar no longer shows the account email and role, and
  Shutdown Rowset uses the normal text colour.

## 0.0.18 — 2026-09-11

- **Edit rows** in the result grid: when a result comes from one table and
  includes its primary key, double-click a cell to change it (or set NULL).
  Review shows the generated UPDATE statements; applying runs them through
  the editor (policies and manual commit apply) and reloads the result.
- Query results report which table column each result column comes from,
  also inside manual-commit transactions.

## 0.0.17 — 2026-09-11

- **Schedules** run a SELECT every day, on chosen weekdays or every few
  minutes, in a chosen time zone, and save each result as a new CSV or JSON
  file in a folder. They run while Rowset Studio is running, even with the
  browser closed; a run missed while it was closed either runs once when it
  opens or is skipped. Runs go through the same policies as the editor, are
  listed per schedule and appear in Activity. "Schedule this query" in the
  editor starts a schedule from the current statement. The SQL of a schedule
  is stored encrypted.

## 0.0.16 — 2026-09-11

- **Explain** draws the execution plan of the statement under the cursor in
  a new Plan tab: operators, row flow, cost heat, warnings and properties.
  PostgreSQL, MySQL, MariaDB and SQL Server show the estimated plan without
  running the statement; **Explain with actual rows** runs a SELECT to show
  actual rows and timings (PostgreSQL, MySQL, MariaDB). Policies apply to
  actual-row runs. The diagram comes from executionflow.

## 0.0.15 — 2026-09-11

- The desktop app has no password: it signs in through its launcher every
  time it opens. Sign out and the first-use password step are gone; an
  expired session shows how to open Rowset Studio again.
- Licensed under the Apache License 2.0; release archives include LICENSE.

## 0.0.14 — 2026-09-11

- First use asks the desktop owner to choose a password; signing in later
  needs only that password (no email).
- **Shutdown Rowset** sits at the bottom of the sidebar and confirms in the
  page instead of a browser dialog.
- Account page redesigned: profile, installation details and password change.

## 0.0.13 — 2026-09-11

- Releases: `scripts/release-build.sh` builds archives for macOS (arm64,
  amd64), Linux and Windows (amd64, arm64), a universal macOS app and
  `SHA256SUMS`; pushing a version tag publishes them through GitHub Actions.
- One-line installers for the current user: `install.sh` (macOS, Linux) and
  `install.ps1` (Windows), with checksum verification, update and uninstall.
- **Quit Rowset** in the sidebar stops the local server, rolling back open
  transactions after confirmation.
- The macOS app is named Rowset Studio.
- CI runs the Go and Studio tests on every push and pull request.

## 0.0.12 — 2026-09-11

First public snapshot of Rowset Studio.

- Local server with a one-use loopback sign-in; single executable for macOS,
  Windows and Linux, plus a macOS menu-bar app.
- Connections for PostgreSQL, MySQL, MariaDB and SQL Server with TLS modes
  (`disable`, `require`, `verify-ca`, `verify-full`), CA and client
  certificates, and multi-node topology with primary/secondary routing.
- SQL editor: run statement, selection or all statements with a result per
  statement, auto-commit or manual commit mode, cancellation, `.sql` open and
  download, workspace export and import.
- Transactions report whether they are active, aborted or lost; a lost
  transaction is cleaned up and reported instead of failing silently.
- Schema browser with table actions and compact search.
- Notebooks with Markdown notes and SQL cells, encrypted autosave, Markdown and
  SQL export; saved queries from earlier builds are imported once.
- Activity page with your statements across all connections.
- Default and custom policies enforced before a statement reaches the database.
- Workspace autosave with revision checks against concurrent windows.