From d94a3f0657884afcdff0b0a1d5edfc949baaa451 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 11:57:44 +0700 Subject: [PATCH 1/9] docs: PluginKit 40 for the shared SQLite foreign key query --- development/plugin-development.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development/plugin-development.mdx b/development/plugin-development.mdx index 8115978..2ddb5b4 100644 --- a/development/plugin-development.mdx +++ b/development/plugin-development.mdx @@ -13,7 +13,7 @@ A plugin is a macOS loadable bundle target with `WRAPPER_EXTENSION = tableplugin | Key | Type | Required | Purpose | |-----|------|----------|---------| -| `TableProPluginKitVersion` | integer | Yes | The PluginKit ABI the plugin was built against. Current value: 39 | +| `TableProPluginKitVersion` | integer | Yes | The PluginKit ABI the plugin was built against. Current value: 40 | | `TableProProvidesDatabaseTypeIds` | array of strings | Recommended | Database type IDs the plugin serves, which is what makes lazy loading possible | | `CFBundleShortVersionString` | string | Yes | Plugin version shown in **Settings > Plugins** | | `CFBundleVersion` | string | Yes | Build number. Of two copies of a plugin the higher build loads, and a registry update needs a higher build | From 24a8a1a84c68e4c36e1151650731e4c827710188 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 13:31:40 +0700 Subject: [PATCH 2/9] docs: show an open transaction on the Mac, and the iOS transaction follow-ups --- developers/mcp-tools.mdx | 2 +- features/sql-editor.mdx | 8 ++++++++ features/workspace-rail.mdx | 2 +- ios/index.mdx | 6 +++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/developers/mcp-tools.mdx b/developers/mcp-tools.mdx index 1b67b5d..df806fb 100644 --- a/developers/mcp-tools.mdx +++ b/developers/mcp-tools.mdx @@ -183,7 +183,7 @@ Formats are `csv`, `json` and `sql`; pass exactly one of `query` or `tables[]`. `confirm_destructive_operation` runs one `DROP`, `TRUNCATE` or `ALTER … DROP`. Anything that is not destructive is rejected; use `execute_query` for those. The user approves it first, through your elicitation prompt if your client supports elicitation, otherwise through TablePro's own dialog on their Mac. -`transaction_control` takes `action` as `begin`, `commit` or `rollback`. The transaction stays open across calls until it is committed or rolled back, and it runs on the same session as the user's own tabs, so their editor runs and their grid saves land inside it while it is open. Leave nothing open. +`transaction_control` takes `action` as `begin`, `commit` or `rollback`. The transaction stays open across calls until it is committed or rolled back, and it runs on the same session as the user's own tabs, so their editor runs and their grid saves land inside it while it is open. TablePro shows it above the connection's tabs and on its connections strip entry, with **Roll Back** and **Commit**, until it ends. Leave nothing open. On Redis the three actions are `MULTI`, `EXEC` and `DISCARD`. Every command between `begin` and `commit` answers `QUEUED` and does nothing until `commit` runs them, and `commit` fails naming each command the server then refused. `rollback` drops the block whole. diff --git a/features/sql-editor.mdx b/features/sql-editor.mdx index ddd6683..bcbc736 100644 --- a/features/sql-editor.mdx +++ b/features/sql-editor.mdx @@ -55,6 +55,14 @@ PostgreSQL, Redshift, CockroachDB, MySQL, MariaDB, TiDB, SQLite, DuckDB, SQL Ser Two things survive a rollback in any of those four cases. MySQL, MariaDB and Oracle commit a `CREATE`, `ALTER` or `DROP` the moment it runs, along with everything before it, and CockroachDB commits the open transaction before it processes any DDL at all. +### The open transaction bar + +While a session holds a transaction, a bar above every tab of the connection says **Transaction Open**, names the database, and offers **Roll Back** and **Commit**. It shows whoever opened the transaction, a `BEGIN` you ran, a grid save inside one, or an [MCP client's](/developers/mcp-tools) `transaction_control`, and goes once it ends, by its buttons, by a typed `COMMIT` or `ROLLBACK`, or with the session. On PostgreSQL, Redshift and CockroachDB each database keeps its own connection, so two databases with open transactions show a row each. The [connections strip](/features/workspace-rail) marks the entry with an hourglass, so you see it from another connection. + +**Transaction Failed** means a statement inside it failed and it can no longer commit, so only **Roll Back** is offered. **Roll Back** asks first at every [Safe Mode](/features/safe-mode) level. **Commit** asks what a typed `COMMIT` asks: nothing at **Silent**, a confirmation at the Alert levels, a confirmation and Touch ID at the Safe Mode levels. At **Read-Only** it is hidden. + +Both act on the connection the bar names. When other work ran there after you clicked, they do nothing and say so: check the transaction and try again. + ### Statements a transaction cannot hold One of these anywhere in the batch leaves the whole of it unwrapped. The engine either refuses the statement inside a block, as PostgreSQL refuses `VACUUM`, or applies it and quietly throws it away, as SQLite does with `PRAGMA foreign_keys`. diff --git a/features/workspace-rail.mdx b/features/workspace-rail.mdx index 3e2a462..273aa9e 100644 --- a/features/workspace-rail.mdx +++ b/features/workspace-rail.mdx @@ -48,7 +48,7 @@ Each entry keeps its own tabs. Clicking one shows its tabs and returns you to th Moving between schemas inside one database, such as PostgreSQL's, keeps the entry and its tabs. Tables of the same name in two schemas are told apart by their schema-qualified tab titles. -On PostgreSQL, Redshift, and CockroachDB every database entry keeps its own server connection, so moving between two entries reconnects nothing and leaves each database's open transaction and temp tables where they were. Closing an entry closes its connection and rolls back a transaction still open there, so it asks first when there is one. +On PostgreSQL, Redshift, and CockroachDB every database entry keeps its own server connection, so moving between two entries reconnects nothing and leaves each database's open transaction and temp tables where they were. Closing an entry closes its connection and rolls back a transaction still open there, so it asks first when there is one. An hourglass on the entry's icon means a transaction is open there; on other engines it marks every entry of the connection, since one connection holds the transaction for all of them. The strip takes the keyboard too. Click into it, then use the arrow keys to move the highlight. Type the start of a connection, database, or schema name to jump to it, and press `Return` to open the entry. diff --git a/ios/index.mdx b/ios/index.mdx index aa9274e..fc0c323 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -177,7 +177,7 @@ A running query appears in a Live Activity on the lock screen and Dynamic Island ### Open transactions -After `BEGIN` in **Query**, a bar sits at the bottom of every section until the transaction ends, by its buttons or by a typed `COMMIT` or `ROLLBACK`. On a MySQL or MariaDB server with autocommit off, opening a table is enough to start one. The bar names the database, or the connection on an engine with one database, and the connection list marks the row with an hourglass. Where their titles do not fit, **Roll Back** and **Commit** show as an arrow and a checkmark. +After `BEGIN` in **Query**, a bar sits at the bottom of every section until the transaction ends, by its buttons or by a typed `COMMIT` or `ROLLBACK`. A typed `ROLLBACK` reloads open tables and closes row screens the way **Roll Back** does. On a MySQL or MariaDB server with autocommit off, opening a table is enough to start one. The bar names the database, or the connection on an engine with one database, and the connection list marks the row with an hourglass. Where their titles do not fit, **Roll Back** and **Commit** show as an arrow and a checkmark. Orders table with a Transaction Open bar above the tab bar, beside the connection list with an hourglass on the Shop connection @@ -194,9 +194,9 @@ After `BEGIN` in **Query**, a bar sits at the bottom of every section until the **Transaction Failed** means a PostgreSQL statement inside it failed, and the server keeps none of it. Only **Roll Back** is offered, and a typed `COMMIT` rolls it back as well. -The bar shows on PostgreSQL, Redshift, MySQL, MariaDB, TiDB, SQLite and Oracle. DuckDB, SQL Server and OceanBase do not report their session state, so an open transaction there shows nothing. +The bar shows on PostgreSQL, Redshift, MySQL, MariaDB, TiDB, SQLite, Oracle, DuckDB and SQL Server. OceanBase does not report its session state, so an open transaction there shows nothing. -A connection that drops with a transaction open reconnects and says the server rolled the transaction back. One that drops during **Commit** never got the answer: check which changes were saved before running them again. Switching database on PostgreSQL or Redshift with a transaction open asks first, since the switch rolls it back. +A connection that drops with a transaction open reconnects and says the server rolled the transaction back. One that drops during **Commit** never got the answer: check which changes were saved before running them again. Switching database on PostgreSQL or Redshift, saving a connection edit that reconnects, or deleting the connection asks first while a transaction is open, since each rolls it back. On MySQL, MariaDB, TiDB, OceanBase and Oracle, **Truncate** and **Drop** commit an open transaction, and their confirmation says so. Two iPad windows on one connection follow each other's database switch. ## What is missing From 5b69e9184cb8cf0fc2fbffcd471440ad3d3b8935 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 14:24:01 +0700 Subject: [PATCH 3/9] docs(ios): socket rows in imports, widget skips Mac-only connections, SSH off keeps settings --- ios/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/index.mdx b/ios/index.mdx index cf2602d..e020ef2 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -195,7 +195,7 @@ A connection that drops with a transaction open reconnects and says the server r - **Schema changes.** Truncating and dropping a table is the whole list; creating or altering tables, columns, indexes, triggers, and views is Mac only. - **Changing Redis data.** Keys open read only; change them in **Query**. -- **Socket connections.** A MySQL or MariaDB connection that uses a socket on the Mac opens there only. The connection list shows **Mac socket** and the path, marked **Opens on Mac only**, and a tap says why. Its socket path syncs here and back intact. +- **Socket connections.** A MySQL or MariaDB connection that uses a socket on the Mac opens there only. The connection list shows **Mac socket** and the path, marked **Opens on Mac only**, and a tap says why. Importing a `.tablepro` file marks such a row the same way. Its socket path syncs here and back intact. - **AI.** No chat, no inline suggestions, no MCP server. ## Security @@ -204,7 +204,7 @@ Turn on Face ID, Touch ID, or Optic ID under **Settings > Security**. A cold lau Each connection carries its own [safe mode](/features/safe-mode) level, synced from the Mac. Set it with **Safe Mode** under **Organization** in the connection's form. iOS has three: **Off**, **Confirm Writes**, and **Read-Only**, which refuses writes outright. **Default Safe Mode** under **Settings > New Connections** sets the level a new connection starts at. -SSH tunnels authenticate with a password or a private key. A tunnel set to **SSH Agent** on the Mac signs in here with a private key on this device and stays **SSH Agent** on the Mac. An unknown host key, the SSH server's or a jump host's, prompts with its fingerprint first, on the connection's own screen. The question waits there until you answer it, and leaving the screen ends that attempt. A key pasted under **Paste Key**, or picked as a text file, is kept in the Keychain beside the connection's passwords, never in the saved connection list. A key file that is not text is copied into the app and read from there. Setting **Auth Method** to **Password**, choosing **Import File**, or turning **SSH Tunnel** off deletes the saved key when you tap **Save**. +SSH tunnels authenticate with a password or a private key. A tunnel set to **SSH Agent** on the Mac signs in here with a private key on this device and stays **SSH Agent** on the Mac. An unknown host key, the SSH server's or a jump host's, prompts with its fingerprint first, on the connection's own screen. The question waits there until you answer it, and leaving the screen ends that attempt. A key pasted under **Paste Key**, or picked as a text file, is kept in the Keychain beside the connection's passwords, never in the saved connection list. A key file that is not text is copied into the app and read from there. Setting **Auth Method** to **Password**, choosing **Import File**, or turning **SSH Tunnel** off deletes the saved key when you tap **Save**. Turning the tunnel off keeps its other settings, here and on the Mac, for when you turn it back on. ### Jump hosts @@ -227,4 +227,4 @@ Hops synced from the Mac arrive with key paths that name files on the Mac, so ea Three App Intents put the app in Shortcuts, the Share Sheet, and Siri: **Open Connection**, **Add Row to Table**, **Add Rows to Table**. See [iOS Shortcuts](/integrations/ios-shortcuts) for pickers, data formats, and limits. -A **Quick Connect** home screen widget opens a saved connection directly, and Handoff carries an open connection between devices. A `tablepro://connect/` link opens a connection and `tablepro://connect//table/` opens a table; the Mac's other links do less or nothing here, as [URL scheme](/developers/url-scheme#on-iphone-and-ipad) lists. A `.tablepro` file from Files or AirDrop imports its connections, leaving out any saved queries it carries. +A **Quick Connect** home screen widget opens a saved connection directly and leaves out the ones that open on the Mac only. Handoff carries an open connection between devices. A `tablepro://connect/` link opens a connection and `tablepro://connect//table/` opens a table; the Mac's other links do less or nothing here, as [URL scheme](/developers/url-scheme#on-iphone-and-ipad) lists. A `.tablepro` file from Files or AirDrop imports its connections, leaving out any saved queries it carries. From d1d97254a153d892987829b97583ced95d0f8c02 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 14:39:02 +0700 Subject: [PATCH 4/9] docs(databases): read Redis commands by line and redis-cli quoting on both apps --- connections/connection-form.mdx | 2 +- databases/redis.mdx | 2 +- ios/index.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/connections/connection-form.mdx b/connections/connection-form.mdx index f36c71f..d528a38 100644 --- a/connections/connection-form.mdx +++ b/connections/connection-form.mdx @@ -100,7 +100,7 @@ A connect you did not start never runs the script. A restored window at launch, ## Startup commands -Statements split on semicolons and newlines and run in order on the connection that just opened, after every connect including an automatic reconnect. They all run on one connection, so write one dialect. +Statements split on semicolons and newlines outside quotes and comments, and run in order on the connection that just opened, after every connect including an automatic reconnect. They all run on one connection, so write one dialect. ```sql MySQL diff --git a/databases/redis.mdx b/databases/redis.mdx index 130120d..73ed2d1 100644 --- a/databases/redis.mdx +++ b/databases/redis.mdx @@ -122,7 +122,7 @@ Toggle the filter bar to search keys by pattern. Patterns are Redis glob (`*` an ## Redis CLI -Each statement is one command; separate several with `;`. Commands the driver does not recognize go through too, and only the result formatting is type-aware. A recognized command with arguments its result view does not model, such as `FLUSHDB ASYNC` or `CONFIG SET a 1 b 2`, is also sent exactly as typed, so the server answers it as `redis-cli` would. Arguments are quoted the way `redis-cli` quotes them, so `"` and `'` both work and `\xHH` writes a raw byte; unbalanced quotes are rejected rather than guessed at. Redis has no comment syntax and none is stripped, so a `--` or `#` line is sent to the server and fails. +A command ends at a `;` or a line break outside quotes, so each line is its own command and a quoted value may run across lines. Commands the driver does not recognize go through too, and only the result formatting is type-aware. A recognized command with arguments its result view does not model, such as `FLUSHDB ASYNC` or `CONFIG SET a 1 b 2`, is also sent exactly as typed, so the server answers it as `redis-cli` would. Arguments are read the way `redis-cli` reads them: `"` and `'` both quote, `\n` and `\xHH` inside double quotes write a byte, only `\'` is an escape inside single quotes, and a backslash outside quotes is part of the argument, so `DEL victim\` deletes `victim\`. Unbalanced quotes are rejected rather than guessed at. Redis has no comment syntax and none is stripped, so a `--` or `#` line is a command of its own and fails at the server. ```redis SET mykey "hello" EX 60 diff --git a/ios/index.mdx b/ios/index.mdx index aa9274e..389df71 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -169,7 +169,7 @@ Safe Mode reads only the text that runs, so a `SELECT` before an `UPDATE` runs w On SQL Server, lines with no semicolon between them are one statement up to the next line holding only `GO`, so **Run** sends that batch. In a selection or **Run All Statements**, each `GO` line ends a batch, each batch goes to the server whole, and `GO 5` runs its batch five times. -On Redis a command ends at a semicolon or at a line break outside quotes, so each line is its own command and a quoted value may run across lines. A command with an unclosed quote fails without running. +On Redis a command ends at a semicolon or at a line break outside quotes, so each line is its own command and a quoted value may run across lines. A command with an unclosed quote fails without running. Quotes and escapes read as they do on the Mac: see [Redis CLI](/databases/redis#redis-cli). **Stop** cancels the running statement and starts no other. On MySQL and Redis the statement keeps running on the server until it finishes. Results copy or export as JSON, CSV, or SQL `INSERT`. A value over 4 KB, or a binary value, stops the copy or export with **Cannot Export**: leave that column out of the `SELECT`. Copying rows from a table reads values over 4 KB in full first. From 2d4da336b007648bb00f29ff2fef484ef5ab53a4 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 15:39:57 +0700 Subject: [PATCH 5/9] docs: ClickHouse on iPhone and iPad --- databases/clickhouse.mdx | 6 ++++++ ios/index.mdx | 11 +++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/databases/clickhouse.mdx b/databases/clickhouse.mdx index 1e45a02..1557804 100644 --- a/databases/clickhouse.mdx +++ b/databases/clickhouse.mdx @@ -80,6 +80,12 @@ The default is **Disabled**, which sends plain HTTP. Any other mode switches the The CA file may be PEM or DER. **Verify CA** needs one, since it checks no hostname, and a connection without it is refused. If **Verify CA** or **Verify Identity** cannot read the file, the connection fails rather than falling back to the public root store. An imported URL with `ssl=true` opens as Verify Identity, and `sslmode=none` as Required. See [SSL/TLS](/connections/ssl). +## On iPhone and iPad + +The ClickHouse driver is compiled into TablePro Mobile and talks to the same HTTP interface. Browsing, queries, and row editing work, with edits matched on every value of the row; creating or altering tables stays on the Mac. Unlike the Mac grid, a save or delete there waits for its mutation to finish, and one is refused when another row holds the same values. See [iPhone and iPad](/ios). + +Behind an SSH tunnel, **Verify Identity** checks the certificate against the server's own host name, not the local end of the tunnel. + ## Limitations - No foreign keys and no multi-statement transactions. diff --git a/ios/index.mdx b/ios/index.mdx index 81ead5f..027eb26 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -1,6 +1,6 @@ --- title: iPhone and iPad -description: Browse tables, run queries, and edit rows on ten database engines from a synced connection list +description: Browse tables, run queries, and edit rows on eleven database engines from a synced connection list --- You need iOS 18 or later. Builds ship on their own schedule, so a feature in a Mac release note may not be on your phone yet. Your version and build are under **Settings > About**. Connections saved on a Mac come over iCloud; the rest of your Mac setup stays on the Mac. @@ -28,7 +28,7 @@ Both answers can change later, under **Settings > iCloud** and **Settings > Priv ## Supported databases -Every driver is compiled in, so there is no plugin system and nothing to install. The type picker offers these ten: +Every driver is compiled in, so there is no plugin system and nothing to install. The type picker offers these eleven: | Type | Notes | | --- | --- | @@ -38,6 +38,7 @@ Every driver is compiled in, so there is no plugin system and nothing to install | **PostgreSQL** | Port 5432 by default | | **SQL Server** | TDS, including Microsoft Entra ID sign-in on a connection synced from the Mac | | **Oracle** | Service name or SID | +| **ClickHouse** | Port 8123 by default, the HTTP interface | | **Redis** | Port 6379 by default | | **SQLite** | Copies the file you pick into the app | | **DuckDB** | A file on the device, or in-memory | @@ -46,7 +47,7 @@ Every driver is compiled in, so there is no plugin system and nothing to install Redshift is not in the picker, but the driver is present: a Redshift connection created on the Mac connects once it syncs over. -MongoDB, ClickHouse, Cassandra, BigQuery, Spanner, Snowflake and the rest have no iOS driver. Their connections still sync and sit in the list dimmed, marked **Opens on Mac only**. A tap says why and connects nothing. Rename, move, edit or delete one as usual; the fields you leave alone sync back to the Mac intact. Importing a `.tablepro` file marks these rows the same way. +MongoDB, Cassandra, BigQuery, Spanner, Snowflake and the rest have no iOS driver. Their connections still sync and sit in the list dimmed, marked **Opens on Mac only**. A tap says why and connects nothing. Rename, move, edit or delete one as usual; the fields you leave alone sync back to the Mac intact. Importing a `.tablepro` file marks these rows the same way. Connection list with the MongoDB and ClickHouse rows dimmed and marked Opens on Mac only, beside the Opens on Mac Only alert for the MongoDB connection @@ -130,9 +131,11 @@ On Redis the list holds the current database's keys. A key opens by type: a stri Tap a row to open it full screen, page between rows, edit values, toggle one to `NULL`, and save. An emptied date, number, boolean or JSON field saves `NULL`, and in a NOT NULL column it keeps its value; an emptied text field saves an empty string. While you edit, **Cancel** takes the place of the back button and asks before it throws away a changed value, and so does **Connections** in another tab. Inserting and deleting rows, and truncating or dropping a table, are here too; a PostgreSQL foreign table offers neither Truncate nor Drop. Editing needs a primary key, and a row whose key holds binary data opens read only. +A ClickHouse key is not unique, so a ClickHouse row is matched on every value it holds: a save is an `ALTER TABLE … UPDATE` and a delete an `ALTER TABLE … DELETE`, and a row holding a value over 4 KB or binary data opens read only, with no **Load full value**. Each one waits until the server has applied it. When another row holds the same value in every column, the save or delete is refused with **Identical Rows**, since it would change both. + A new row starts with every column on **DEFAULT**, which leaves that column out of the `INSERT` so the database fills it in. The badge beside a field switches it between **DEFAULT**, **NULL** and a typed value, and **NULL** is offered on nullable columns only. Generated columns are never written, and an auto-increment key stays on **DEFAULT** until you type one. -A save, insert, delete, truncate or drop runs inside a read-write transaction. MySQL, MariaDB, TiDB, PostgreSQL, Redshift, Oracle and SQLite report what their session is already holding, and a write there runs inside that rather than under a transaction of its own, so the rows wait for whoever opened it. A server that starts its sessions read-only is a different problem: see [Server read-only is not Safe Mode](/features/safe-mode#server-read-only-is-not-safe-mode). SQL typed into the Query section is yours and goes out as written. +A save, insert, delete, truncate or drop runs inside a read-write transaction, except on ClickHouse, which has none. MySQL, MariaDB, TiDB, PostgreSQL, Redshift, Oracle and SQLite report what their session is already holding, and a write there runs inside that rather than under a transaction of its own, so the rows wait for whoever opened it. A server that starts its sessions read-only is a different problem: see [Server read-only is not Safe Mode](/features/safe-mode#server-read-only-is-not-safe-mode). SQL typed into the Query section is yours and goes out as written. ### JSON values From 1ca251c904aca9ea5ad816e1a38e3cc511d403e7 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 16:06:40 +0700 Subject: [PATCH 6/9] docs(ios): query suggestions for functions, schemas and enums, the Query menu, and adding History entries --- features/autocomplete.mdx | 2 +- ios/index.mdx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/features/autocomplete.mdx b/features/autocomplete.mdx index f21536e..b64b4f1 100644 --- a/features/autocomplete.mdx +++ b/features/autocomplete.mdx @@ -188,7 +188,7 @@ Comments suppress completion, and a brace or bracket inside a string literal doe ## On iPhone and iPad -The iOS query editor completes keywords, tables, views, columns and aliases by the same rules, in the bar above the keyboard: see [Querying](/ios#querying). Functions, operators, schema names, enum values and favorite keywords are Mac only. +The iOS query editor completes keywords, tables, views, columns, aliases, the engine's functions and operators, schema names and enum values by the same rules, in the bar above the keyboard: see [Querying](/ios#querying). Favorite keywords are Mac only. ## My new table does not complete diff --git a/ios/index.mdx b/ios/index.mdx index aa9274e..7a929ab 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -149,7 +149,7 @@ A value over 4 KB arrives cut. Tap **Load full value** first; until then it is p ### Querying -Suggestions fill the bar above the keyboard as you type, in front of the fixed keys. After `FROM`, `JOIN`, `INTO` or `UPDATE` they are tables and views; after `SELECT`, `WHERE`, `ON` or `SET`, the columns of the tables the statement names; elsewhere, keywords. An alias and a dot, as in `i.`, give that table's columns. Tap one to replace the word you are typing, quoted when the engine needs it. A table's columns load the first time it needs them and again after each run. On iPad with a hardware keyboard, `Tab` inserts the first suggestion and `Escape` hides the bar until you type or move the cursor. +Suggestions fill the bar above the keyboard as you type, in front of the fixed keys. After `FROM`, `JOIN`, `INTO` or `UPDATE` they are tables and views; after `SELECT`, `WHERE`, `ON` or `SET`, the columns of the tables the statement names, and the engine's own functions; elsewhere, keywords. An alias and a dot, as in `i.`, give that table's columns, and a schema and a dot, as in `sales.`, give that schema's tables (a database on MySQL and MariaDB). Comparing an enum column, as in `status = `, offers its values. Tap one to replace the word you are typing, quoted when the engine needs it. A table's columns load the first time it needs them and again after each run, over a second connection where the engine allows one, so typing leaves what `SELECT ROW_COUNT()` or `SHOW WARNINGS` reports for your last statement alone. On iPad with a hardware keyboard, `Tab` inserts the first suggestion and `Escape` hides the bar until you type or move the cursor. Query editor on iPhone suggesting Invoice and InvoiceLine for SELECT * FROM Inv, beside Total and InvoiceId suggested for WHERE i. @@ -158,7 +158,9 @@ Suggestions fill the bar above the keyboard as you type, in front of the fixed k **Run** sends the statement the cursor is in, and a faint band behind the text marks it before you tap. Select text first to send the selection instead; a selection hides the band. With the cursor past the last statement or inside a comment, **Run** sends nothing, keeps the last result, and shows "The cursor is not in a statement." Past 10,000 characters the editor drops its colors and the band, and **Run** still finds the statement. -Touch and hold **Run** for **Run All Statements**, which sends every statement in the editor in order. It is in the **More** menu too. On a hardware keyboard, `Cmd+Enter` runs the statement or the selection, `Cmd+Shift+Enter` runs every statement, and `Cmd+.` stops. +Touch and hold **Run** for **Run All Statements**, which sends every statement in the editor in order. It is in the **More** menu too. On a hardware keyboard, `Cmd+Enter` runs the statement or the selection, `Cmd+Shift+Enter` runs every statement, and `Cmd+.` stops. On iPad the menu bar's **Query** menu lists **Run Statement**, **Run All Statements**, **Stop** and **Clear Query**, and its **View** menu switches sections with `Cmd+1` through `Cmd+4`. + +Tap a query in **History**, or a table under **SELECT * FROM ...** in the **More** menu, to add it to the editor. It fills an empty editor; otherwise it goes after your text as a statement of its own, with the cursor in it, and **Undo** takes it back. A statement that creates, alters or drops a table refreshes the table list in every window open on the connection. Query section with the second of three SELECT statements banded and its result b 2, beside the Run menu holding Run Statement and Run All Statements From 2972d6e519b39c1e2cfe453ab549501b015735c1 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 16:47:57 +0700 Subject: [PATCH 7/9] docs(ssh): SSH profiles on iPhone and iPad, socket forwards, and the ssh_config rules the Mac now follows --- connections/ssh-profiles.mdx | 2 ++ connections/ssh-tunneling.mdx | 6 +++++- ios/index.mdx | 6 +++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/connections/ssh-profiles.mdx b/connections/ssh-profiles.mdx index 647785a..27e644a 100644 --- a/connections/ssh-profiles.mdx +++ b/connections/ssh-profiles.mdx @@ -39,3 +39,5 @@ Deleting a profile hands its configuration and its secrets to each connection th ## iCloud Sync Profiles sync across Macs with iCloud Sync on and the **SSH Profiles** toggle set in **Settings > Sync**. Passwords, key passphrases, and TOTP secrets stay in the local Keychain unless the **Passwords** toggle, nested under **Connections**, is on too, and that covers newly saved credentials only: re-save an existing password to include it. + +On [iPhone and iPad](/ios#ssh-profiles), a connection that uses a profile keeps the profile's server and jump hosts read only and signs in with the profile's password once it reaches iCloud Keychain. Turning **SSH Tunnel** off there unlinks the profile from that connection on every device. diff --git a/connections/ssh-tunneling.mdx b/connections/ssh-tunneling.mdx index fda6a1d..b69d3fa 100644 --- a/connections/ssh-tunneling.mdx +++ b/connections/ssh-tunneling.mdx @@ -67,6 +67,8 @@ If a trusted server's key later changes, an **SSH Host Key Changed** alert lists Pick an alias from the **Config Host** picker and `HostName`, `User`, `Port`, `IdentityFile`, `IdentitiesOnly`, `IdentityAgent`, and `ProxyJump` are resolved from the config at connect time. Anything typed into the form overrides the file, which is re-read whenever it or an `Include`d file changes. +`Match` blocks take the criteria ssh does, `localnetwork` and `tagged` included. A tunnel counts as `sessiontype none` with an empty `command`, and a criterion TablePro does not know keeps its block from applying. + `IdentityFile` picks which agent key is offered, not only which key file is read, and a `.pub` is enough. That is how a 1Password or Secretive key gets pinned to a host. `IdentitiesOnly yes` then drops every agent key the config did not name, which is what keeps a thirty-key agent inside a server's `MaxAuthTries`. @@ -101,7 +103,7 @@ For a database behind more than one bastion, expand **Jump Hosts** and add each | **Auth** | **Private Key** or **SSH Agent**. Password auth is not offered for a hop | | **Key File** | Private key path, under Private Key auth | -With the list left empty and an SSH host that matches a config entry carrying `ProxyJump`, that directive is followed instead. +With the list left empty and an SSH host that matches a config entry carrying `ProxyJump`, that directive is followed instead. A hop's own `ProxyJump` is followed too, but in a list only the first hop's, as `ssh -J a,b` does. `ProxyJump none` turns it off, a hop written as `ssh://user@host:port` reads the same as `user@host:port`, and a hop with no `User` is named in the error before anything is dialed. A connection with hops syncs to [iPhone and iPad](/ios#jump-hosts) with every hop, its port and its key path intact. A key path names a file on the Mac, so give each hop a key on the device before the connection opens there. The tunnel's own port and auth method make the same round trip: a port you left empty stays empty, so `~/.ssh/config` still decides it. @@ -153,6 +155,8 @@ Nothing is listening where that socket points, and the message names which of th A jump host has no **Agent Socket** field of its own, so its agent comes from `IdentityAgent` or `SSH_AUTH_SOCK`. +`IdentityAgent SSH_AUTH_SOCK` means that variable, as it does for ssh. `IdentityAgent none` turns the agent off: a jump host from `~/.ssh/config` then signs in with its `IdentityFile` keys, and a tunnel set to **SSH Agent** stops with an error naming that line. + ### "The SSH agent from … holds no keys." The agent answered and offered nothing. 1Password serves keys only while it is running and unlocked, and Secretive only while its agent is loaded; unlock it and add the key there. `ssh-add` loads keys into the `SSH_AUTH_SOCK` agent alone, so it is the fix only when that is the socket in the message. diff --git a/ios/index.mdx b/ios/index.mdx index aa9274e..701fe2e 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -211,7 +211,7 @@ Turn on Face ID, Touch ID, or Optic ID under **Settings > Security**. A cold lau Each connection carries its own [safe mode](/features/safe-mode) level, synced from the Mac. Set it with **Safe Mode** under **Organization** in the connection's form. iOS has three: **Off**, **Confirm Writes**, and **Read-Only**, which refuses writes outright. **Default Safe Mode** under **Settings > New Connections** sets the level a new connection starts at. -SSH tunnels authenticate with a password or a private key. A tunnel set to **SSH Agent** on the Mac signs in here with a private key on this device and stays **SSH Agent** on the Mac. An unknown host key, the SSH server's or a jump host's, prompts with its fingerprint first, on the connection's own screen. The question waits there until you answer it, and leaving the screen ends that attempt. A key pasted under **Paste Key**, or picked as a text file, is kept in the Keychain beside the connection's passwords, never in the saved connection list. A key file that is not text is copied into the app and read from there. Setting **Auth Method** to **Password**, choosing **Import File**, or turning **SSH Tunnel** off deletes the saved key when you tap **Save**. +SSH tunnels authenticate with a password or a private key. A tunnel set to **SSH Agent** on the Mac signs in here with a private key on this device and stays **SSH Agent** on the Mac. An unknown host key, the SSH server's or a jump host's, prompts with its fingerprint first, on the connection's own screen. The question waits there until you answer it, and leaving the screen ends that attempt. A key pasted under **Paste Key**, or picked as a text file, is kept in the Keychain beside the connection's passwords, never in the saved connection list. A key file that is not text is copied into the app and read from there. Setting **Auth Method** to **Password**, choosing **Import File**, or turning **SSH Tunnel** off deletes the saved key when you tap **Save**. A tunnel the Mac forwards to a unix socket on the server forwards to the same socket here. ### Jump hosts @@ -224,6 +224,10 @@ Hops synced from the Mac arrive with key paths that name files on the Mac, so ea Edit Connection form with two jump hosts marked No key on this device, beside the editor for bastion1.example.com with Same Key as SSH Server switched off +### SSH profiles + +A connection that uses an [SSH profile](/connections/ssh-profiles) on the Mac arrives with the profile's server, sign-in method and jump hosts, read only in the form: change them in the profile on the Mac. It signs in with the profile's password once the Mac syncs passwords to iCloud Keychain; until then the password field works as it does for any tunnel. A key passphrase saved here wins over the profile's, since it goes with the key on this device. Turning **SSH Tunnel** off unlinks the profile from that connection on every device. + ## Privacy **Share Usage Data** under **Settings > Privacy** sends one small report a day: a hashed device identifier, the app and iOS versions, your language, the database types you use, and when you first connected and first ran a query. It is off unless you chose **Share Usage Data** on first launch, and it never carries a hostname, username, password, query, or row. From b0146b190fba08cb34cbe3aa79a9a819707e4c1c Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 18:30:17 +0700 Subject: [PATCH 8/9] docs(ios): saved queries add to the editor as History entries do --- ios/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/index.mdx b/ios/index.mdx index 94a6aaa..db382ec 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -163,7 +163,7 @@ Suggestions fill the bar above the keyboard as you type, in front of the fixed k Touch and hold **Run** for **Run All Statements**, which sends every statement in the editor in order. It is in the **More** menu too. On a hardware keyboard, `Cmd+Enter` runs the statement or the selection, `Cmd+Shift+Enter` runs every statement, and `Cmd+.` stops. On iPad the menu bar's **Query** menu lists **Run Statement**, **Run All Statements**, **Stop** and **Clear Query**, and its **View** menu switches sections with `Cmd+1` through `Cmd+4`. -Tap a query in **History**, or a table under **SELECT * FROM ...** in the **More** menu, to add it to the editor. It fills an empty editor; otherwise it goes after your text as a statement of its own, with the cursor in it, and **Undo** takes it back. A statement that creates, alters or drops a table refreshes the table list in every window open on the connection. +Tap a query in **History** or **Saved Queries**, or a table under **SELECT * FROM ...** in the **More** menu, to add it to the editor. It fills an empty editor; otherwise it goes after your text as a statement of its own, with the cursor in it, and **Undo** takes it back. A statement that creates, alters or drops a table refreshes the table list in every window open on the connection. Query section with the second of three SELECT statements banded and its result b 2, beside the Run menu holding Run Statement and Run All Statements @@ -182,7 +182,7 @@ A running query appears in a Live Activity on the lock screen and Dynamic Island ### Saved queries -Open the **More** menu (•••) beside **Run** and choose **Saved Queries**. The list holds this connection's queries and the global ones, in their folders, and search matches names, keywords and SQL. Tap a query to put its SQL in the editor in place of what was there. Nothing runs until you tap **Run**. +Open the **More** menu (•••) beside **Run** and choose **Saved Queries**. The list holds this connection's queries and the global ones, in their folders, and search matches names, keywords and SQL. Tap a query to add its SQL to the editor the way a **History** entry goes in: it fills an empty editor, otherwise it follows your text and **Undo** takes it back. Nothing runs until you tap **Run**. **Save Query…** in the same menu keeps the editor's text, with the first line of the SQL filled in as its name. Turn on **Global** to list the query on every connection. Keywords and the size limit follow the [Mac's rules](/features/favorites#keywords), and a keyword expands in the Mac's editor only. From 61ecb5caf8a62040ff507c96b72dafbaa99d9aed Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 19:07:01 +0700 Subject: [PATCH 9/9] docs(connections): the Preferred SSL mode on iPhone and iPad, Required that never falls back, and IAM Identity Center renewal --- connections/aws-iam.mdx | 2 +- connections/ssl.mdx | 12 +++++++++--- ios/index.mdx | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/connections/aws-iam.mdx b/connections/aws-iam.mdx index feb0651..6e94edf 100644 --- a/connections/aws-iam.mdx +++ b/connections/aws-iam.mdx @@ -89,7 +89,7 @@ IAM also requires encryption in transit, so an [SSL mode](/connections/ssl) of * `web_identity_token_file` is not supported and says so rather than reporting the profile incomplete. -A connect that fails on an expired IAM Identity Center session offers the browser sign-in; `aws sso login --profile ` does the same from the shell. +A token from IAM Identity Center renews itself with its refresh token in its last 15 minutes, as the AWS CLI does, and the renewed token goes back into `~/.aws/sso/cache`. A connect that fails because the session itself ended offers the browser sign-in; `aws sso login --profile ` does the same from the shell. ## Tunnels and port forwards diff --git a/connections/ssl.mdx b/connections/ssl.mdx index 151534d..93094e5 100644 --- a/connections/ssl.mdx +++ b/connections/ssl.mdx @@ -40,7 +40,7 @@ A new connection starts on the mode that matches the driver's own default, and s | Driver | Default | What Preferred does | |---|---|---| | PostgreSQL, Redshift, CockroachDB | Preferred | libpq's own `sslmode=prefer` fallback to plain TCP. Same as `psql` and DataGrip | -| MySQL, MariaDB | Preferred | Tries TLS, then retries plain on an SSL handshake error. Auth and network errors are not retried | +| MySQL, MariaDB | Preferred | TLS when the server offers it, plain when it does not, and a plain retry after an SSL handshake error. Auth and network errors are not retried | | SQL Server | Preferred | FreeTDS `encryption=request`: the login over TLS, the rest in plain TCP unless the server forces encryption | | Teradata | Disabled | Opens a TLS transport, retries on a plain socket if it fails to come up | | MongoDB, Redis, Cassandra, ClickHouse, Elasticsearch, Typesense, SurrealDB, Weaviate | Disabled | Nothing. No fallback exists, so Preferred forces TLS exactly like Required | @@ -66,6 +66,8 @@ A saved connection set to **Disabled** on one of these ports shows a warning und Verify CA and Verify Identity do not survive a tunnel. An SSH tunnel, Cloudflare tunnel, Cloud SQL Auth Proxy, or SOCKS proxy has the driver connect to `127.0.0.1`, and no server certificate is issued for that, so the mode drops to Required for the tunneled connection and the certificate paths are cleared. TLS itself still runs the whole way to the database; only the certificate check goes. If you need the check, reach the server without a tunnel. +On iPhone and iPad the check does survive an SSH tunnel: MySQL, MariaDB, PostgreSQL, Redshift, Redis and Oracle check the certificate against the host saved in the connection. An Oracle server saved as an IP address is still checked against `127.0.0.1`, and Verify Identity refuses it. + Forwarding to a unix socket drops TLS altogether, because a socket cannot negotiate it. The SSH tunnel encrypts that path instead. ## Troubleshooting @@ -84,6 +86,10 @@ Server response: FATAL: no pg_hba.conf entry for host "…", user "…", databas The PostgreSQL server requires SSL and the connection is set to Disabled. Switch to **Preferred** or **Required (skip verify)**. +### "SSL is required, but the server does not support it" + +The MySQL or MariaDB server offers no TLS and the connection is set to Required or stricter, which never falls back to plain text. Turn on TLS on the server, or switch to **Preferred** to allow plain text. + ### "Connections using insecure transport are prohibited" The MySQL server has `require_secure_transport=ON`. Switch to **Preferred** or **Required (skip verify)**. @@ -96,7 +102,7 @@ Driver and server share no TLS version or cipher. Update the server, or on a dev The server's certificate chains to nothing in the system trust store. Set **Verify CA** and fill **Certificate** with the CA's PEM, or drop to **Required (skip verify)** to skip validation. -### "hostname does not match certificate" +### "hostname does not match certificate" / "Hostname verification failed" The certificate's CN or SAN does not cover the host you typed. Change **Host** to a name the certificate covers. @@ -110,7 +116,7 @@ Enter the key's passphrase in **Key Passphrase**, which appears once **Client Ke ## On iPhone and iPad -Four modes, Preferred excluded. MySQL, MariaDB, PostgreSQL, Redshift, and Redis get **CA Certificate**, **Client Certificate**, and **Client Key** rows alongside them. Oracle gets Disabled, Required, and Verify Identity with no certificate rows, so Verify Identity checks the server against the system trust store; an Oracle connection saved with Verify CA does not connect until its mode changes. SQL Server gets only Disabled and Required. +The Mac's five modes, and Preferred does what the table above says for each driver. MySQL, MariaDB, PostgreSQL, Redshift, and Redis get **CA Certificate**, **Client Certificate**, and **Client Key** rows alongside them. Oracle gets Disabled, Preferred, Required, and Verify Identity with no certificate rows, so Verify Identity checks the server against the system trust store; an Oracle connection saved with Verify CA does not connect until its mode changes. SQL Server gets Disabled, Preferred, and Required. Each row takes a file or pasted text. **Client Certificate** also accepts a PKCS#12 (`.p12` or `.pfx`) and fills in both the certificate and its key from it. iOS cannot read a `.p12` exported without a password, and rejects a private key carrying its own passphrase: strip the passphrase or export a `.p12`. diff --git a/ios/index.mdx b/ios/index.mdx index 1980169..d188377 100644 --- a/ios/index.mdx +++ b/ios/index.mdx @@ -125,9 +125,9 @@ A key for this device and the IAM Identity Center settings stay on this device. AWS Profile Is on the Mac error with Edit Connection and Retry, beside the connection form's Authentication section set to AWS IAM (Profile) with an empty Access Key for This Device section -**Sign In with IAM Identity Center** shows a code and opens the browser; confirm the code there. When that sign-in expires, opening the connection offers to sign in again. +**Sign In with IAM Identity Center** shows a code and opens the browser; confirm the code there. The sign-in renews itself with its refresh token until the IAM Identity Center session ends; after that, opening the connection offers to sign in again. -A token needs an encrypted connection. A MySQL, MariaDB or PostgreSQL connection with **SSL Mode** on **Disabled** connects with **Required**, and a MySQL server that offers no TLS is refused before the token is sent. A Redis connection with SSL off fails until you turn SSL on. +A token needs an encrypted connection. A MySQL, MariaDB or PostgreSQL connection with **SSL Mode** on **Disabled** or **Preferred** connects with **Required**, and a server that offers no TLS is refused before the token is sent. A Redis connection with SSL off fails until you turn SSL on. ## What works