v0.28.0: A Filter Panel You Can Click Through, Tables That Refresh Themselves, and a Read-Only MCP the Database Enforces TabularisDB released v0.28.0, a database client update with more than 80 commits and close to 70 merged pull requests from nineteen external contributors, arriving five days after v0.27.0. The release rebuilds the structured filter panel around clicking rather than typing, adds interval-based table tab refresh, and fixes two security reports against the MCP server by moving read-only mode from a keyword check into the database session. Connection secrets now go to the keychain by default, and metadata fixes improve Generate SQL, routines and imports on MySQL 8, PostgreSQL and SQLite. v0.28.0 comes five days after v0.27.0 https://tabularis.dev/blog/v0270-cell-value-filter-stable-columns-tsql-batches and is the largest release in a while: more than 80 commits and close to 70 merged pull requests from nineteen external contributors. Most of it arrived during the community giveaway https://tabularis.dev/contribute/leaderboard , which closes today. There is no single theme, so this post is grouped by where you will notice it. The structured filter panel is rebuilt around clicking instead of typing. Table tabs can refresh on an interval. Tabs can be reopened, duplicated and switched by recency, and a long query that finishes while you are elsewhere sends a notification. Two security reports against the MCP server are fixed by moving read-only mode from a keyword check into the database session. Connection secrets go to the keychain by default, and a long list of metadata fixes makes Generate SQL, routines and imports more faithful on MySQL 8, PostgreSQL and SQLite. Around the app, three new themes reached the registry, Tabularium shipped seven minor versions in five days, and the plugins got a round of fixes. A Structured Filter You Can Click Through The structured filter panel had grown features over several releases but still asked you to type values and % wildcards by hand, and it sat on top of the first rows of the grid it was filtering. Four pull requests change that. The panel is part of the layout. @gcapellib https://github.com/gcapellib moves it out of the overlay in PR 922 https://github.com/TabularisDB/tabularis/pull/922 : it now sits above the grid and pushes it down, with a maximum height and its own scroll for long filter lists, so the rows an applied filter returns are never hidden under it. Since it no longer covers anything, a click outside does not close it; Esc , the close button and the Filters button still do. The panel resets when you switch to another table tab, so filters do not leak from one table to the next. Match all or Match any. Also from @gcapellib https://github.com/gcapellib , PR 920 https://github.com/TabularisDB/tabularis/pull/920 adds a two-button toggle to the panel header. Match all joins the rows with AND , as before; Match any joins them with OR and wraps the result in parentheses so it combines safely with anything else in the WHERE . Operators for text. @Detoy https://github.com/Detoy adds contains , starts with , ends with , is empty and is not empty for string columns in PR 927 https://github.com/TabularisDB/tabularis/pull/927 , closing 852 https://github.com/TabularisDB/tabularis/issues/852 . The first three generate LIKE patterns with % , and the escape character escaped, so a value with an underscore matches literally. The escape character is on every driver, because ESCAPE '\' is a syntax error on MySQL and MariaDB under the default sql mode . is empty matches both NULL and '' . Pick values instead of typing them. In PR 956 https://github.com/TabularisDB/tabularis/pull/956 , closing 869 https://github.com/TabularisDB/tabularis/issues/869 , @Detoy https://github.com/Detoy puts a value picker next to the value field. It loads the column's 100 most frequent non- NULL values with their counts, narrowed by the panel's other complete rows when matching all, with a search box. One pick fills the field with = , several switch the operator to IN , and = / NOT IN stay negated. Nothing is applied until you press Apply . The query runs on the tab's connection, schema and session, goes through the same request guard as the table, and is discarded if the filter changes before it returns. It is not offered on BLOB, binary or JSON columns. Video unavailable Three fixes make the panel safer to leave half-filled. An empty row, which opening the panel adds, used to produce id = '' on close, which PostgreSQL rejects on numeric columns; incomplete rows are now skipped, both by Apply All and by the per-row Apply @gcapellib https://github.com/gcapellib , PR 941 https://github.com/TabularisDB/tabularis/pull/941 . IN and NOT IN values containing commas, escaped quotes or empty strings now survive the split @vivekyarra https://github.com/vivekyarra , PR 969 https://github.com/TabularisDB/tabularis/pull/969 . On MySQL and MariaDB, backslashes in typed and picked values are escaped when each clause is built. Tables That Refresh Themselves Watching a queue, a job table or a log table meant pressing refresh over and over. @Detoy https://github.com/Detoy closes 868 https://github.com/TabularisDB/tabularis/issues/868 with PR 915 https://github.com/TabularisDB/tabularis/pull/915 : table tabs get a refresh button with an interval select beside it, Off , 5s , 10s , 30s or 60s . New tabs start at Off, and each tab keeps its own interval. When a timer is running, the icon, the Auto-refresh label and the select switch to the accent color, so you can tell at a glance which tabs are live. Video unavailable Most of the work is in what refreshing must not do. A tick keeps the grid, the scroll position and the query settings, and a failure is shown inline while the last good rows stay on screen. The timer pauses while you edit a cell, have pending changes, or have rows selected, since selected rows are indexes and new data would shift them onto other records; a status line says Paused while editing or Paused while rows are selected . Inactive tabs stop polling, and coming back either waits the remaining time or refreshes once if the deadline has passed, without replaying missed ticks. Every table request goes through one queue per tab, so a manual run, a page change and a tick never overlap, and a late tick result is dropped if you edited, paged, changed the query or switched connection in the meantime. Tabs: Reopen, Duplicate, Switch by Recency Reopen the last closed tab. @M4Y4NK-V3RM4 https://github.com/M4Y4NK-V3RM4 adds Ctrl+Shift+T Cmd+Shift+T on macOS in PR 914 https://github.com/TabularisDB/tabularis/pull/914 , closing 866 https://github.com/TabularisDB/tabularis/issues/866 . Closed tabs go onto a stack of 20 per connection, kept in memory, and come back at their previous position with their SQL, title, filter, sort and parameters; results are not kept, so you run the query again. Every close path feeds the stack, including Close others and Close to the right . The tab context menu has a matching Reopen closed tab item, and the shortcut can be rebound. Duplicate a tab. @dyk1454683243-sudo https://github.com/dyk1454683243-sudo adds Duplicate tab to the tab context menu in PR 875 https://github.com/TabularisDB/tabularis/pull/875 . The copy opens right after the original with the same SQL and parameters; a duplicated materialized-view or read-only definition tab stays read-only. Switch by recency. With PR 1011 https://github.com/TabularisDB/tabularis/pull/1011 , closing 999 https://github.com/TabularisDB/tabularis/issues/999 , @Detoy https://github.com/Detoy makes Ctrl+Tab go back to the tab you used before instead of the next one in the bar: after visiting A, C and A again, Ctrl+Tab selects C. The order holds while Ctrl is held, repeated Tab presses walk through it, and Ctrl+Shift+Tab walks back. Each connection has its own history. Video unavailable Long Queries, Window Placement and Unsaved Edits A notification when a long query finishes. @M4Y4NK-V3RM4 https://github.com/M4Y4NK-V3RM4 closes 867 https://github.com/TabularisDB/tabularis/issues/867 with PR 928 https://github.com/TabularisDB/tabularis/pull/928 . If a query runs longer than a threshold and Tabularis is not the focused window when it ends, you get a desktop notification with the tab title, the outcome and the duration; Run All sends one notification for the whole batch with the count of succeeded and failed statements. Two settings under Settings → General → Query execution control it: Notify when long queries finish on and Notification threshold 20 seconds . Auto-refresh ticks never notify. @vivekyarra https://github.com/vivekyarra also fixes a quiet data loss in PR 970 https://github.com/TabularisDB/tabularis/pull/970 : an edit typed just before switching console tabs could be dropped by the debounce; it is now kept. And in PR 953 https://github.com/TabularisDB/tabularis/pull/953 , closing 534 https://github.com/TabularisDB/tabularis/issues/534 , the main window remembers its size and position across restarts, stays on a connected monitor, and still respects Start maximized . MCP Read-Only Mode, Enforced by the Database Two security advisories were reported against the built-in MCP server this cycle, both rated high, and both fixed in this release. The threat model is the one in our security policy: an AI client steered by untrusted content, or an untrusted MCP client, controls the text it sends. GHSA-vqmg-c6j7-6c3j https://github.com/TabularisDB/tabularis/security/advisories/GHSA-vqmg-c6j7-6c3j : side-effecting SELECT and PRAGMA statements. Read-only mode and the write-approval gate classified a query by its first keyword. Statements that start like a read can write: SELECT … INTO creates a table on PostgreSQL, SELECT … INTO OUTFILE writes a file on the MySQL server, SELECT setval ... resets a sequence, FOR UPDATE takes locks, and PRAGMA user version = 42 changes a SQLite database. All of them passed as reads, ran with read-only mode on, and skipped approval. It is the same class of bug as the EXPLAIN ANALYZE case fixed earlier. The fix has two layers. The classifier now treats SELECT … INTO , INTO OUTFILE / INTO DUMPFILE , FOR UPDATE / FOR SHARE / LOCK IN SHARE MODE , known side-effecting PostgreSQL functions, PRAGMA assignments and action pragmas as writes, including inside CTEs and EXPLAIN ANALYZE . More importantly, the database now enforces it: every query on a read-only connection, and every query that skips approval because it was classified as a read, runs through a new read-only path. PostgreSQL wraps it in BEGIN READ ONLY , MySQL in START TRANSACTION READ ONLY , SQLite turns on query only inside a transaction, and all three always roll back. If the reset fails or the call is dropped, the connection is closed instead of going back to the pool, so the setting cannot leak into your own editor session. Plugin drivers keep the existing path. A follow-up, PR 1054 https://github.com/TabularisDB/tabularis/pull/1054 , closes the cases a read-only transaction cannot: on PostgreSQL, lo export writes a file on the server even inside BEGIN READ ONLY , and the backend-signal and dblink functions act outside the transaction. For those the classifier is the only check, and two tricks hid them from it. Function names written as quoted identifiers, "lo export" ... , pg catalog."lo export" ... or U&"lo export" ... , were blanked before the deny list ran; and a quote inside a $$…$$ , $tag$…$tag$ or E'…' literal threw off the string scanner. Both literal forms are now skipped as whole strings, and any call through a quoted name is classified as unknown, which read-only mode blocks and the approval gate asks about. Quoted columns and aliases still classify as reads. GHSA-xrx3-j98v-67x9 https://github.com/TabularisDB/tabularis/security/advisories/GHSA-xrx3-j98v-67x9 : SQL injection through describe table on SQLite. The SQLite driver built its introspection queries as PRAGMA table xinfo '