Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions connections/connection-form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ Metadata connections are the extra ones opened to read a database's object list
| [Beancount](/databases/beancount) | File | No | No | No | No | No | No |
| [DynamoDB](/databases/dynamodb) | AWS API | No | No | No | No | No | No |
| [BigQuery](/databases/bigquery) | Cloud API | No | No | No | No | No | No |
| [Bigtable](/databases/bigtable) | Cloud API | No | No | No | No | No | No |
| [Spanner](/databases/spanner) | Cloud API | No | No | No | No | No | No |
| [Firestore and Firebase Realtime Database](/databases/firebase) | Cloud API | No | No | No | No | No | No |
| [Cloudflare D1](/databases/cloudflare-d1) | Cloud API | No | No | No | No | No | No |
Expand Down
2 changes: 1 addition & 1 deletion connections/ssh-tunneling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ To share one SSH config across connections, save it with **Save Current as Profi
<img className="hidden dark:block" src="/images/ssh-tunnel-config-dark.png" alt="Network section with SSH Tunnel selected and a saved profile in the Profile picker" />
</Frame>

**SSH Tunnel** is not offered on SQLite, DuckDB, PGlite, libSQL, Turso, Beancount, BigQuery, Spanner, Cloudflare D1, Cloudflare R2 SQL, DynamoDB, or Snowflake: each is reached over a local file, a loopback socket, or a vendor HTTP API. For SAP HANA, forward the port yourself, as its [Limitations](/databases/sap-hana#limitations) show.
**SSH Tunnel** is not offered on SQLite, DuckDB, PGlite, libSQL, Turso, Beancount, BigQuery, Bigtable, Spanner, Cloudflare D1, Cloudflare R2 SQL, DynamoDB, or Snowflake: each is reached over a local file, a loopback socket, or a vendor HTTP API. For SAP HANA, forward the port yourself, as its [Limitations](/databases/sap-hana#limitations) show.

## Authentication methods

Expand Down
2 changes: 1 addition & 1 deletion connections/ssl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ A new connection starts on the mode that matches the driver's own default, and s
| Trino | Disabled | Sends every request over HTTPS with no fallback, again like Required |
| SAP HANA | Verify Identity | Nothing. No fallback exists, so Preferred forces TLS exactly like Required |
| Oracle | Disabled | Connects in plain TCP, so it behaves like Disabled. A red warning appears under the picker; use Required to enforce TCPS |
| Snowflake, BigQuery, Spanner, Firestore, Firebase Realtime Database, DynamoDB, Cloudflare D1, Cloudflare R2 SQL, libSQL / Turso | Always encrypted | These drivers are HTTPS and manage TLS themselves. No **SSL Mode** |
| Snowflake, BigQuery, Bigtable, Spanner, Firestore, Firebase Realtime Database, DynamoDB, Cloudflare D1, Cloudflare R2 SQL, libSQL / Turso | Always encrypted | These drivers are HTTPS and manage TLS themselves. No **SSL Mode** |
| SQLite, DuckDB, Beancount, PGlite | None | Local files or an in-process engine. No **SSL Mode** |

## Ports that mean TLS
Expand Down
2 changes: 1 addition & 1 deletion customization/general-settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ How many seconds a query runs before it is cancelled. The default is 60; the pic

A connection can override this value under **Options > Query timeout**. Leave that field empty to inherit this setting, or enter `0` for no limit. The same section has a separate connect timeout for tunnels, proxies and database sign-in; it does not change how long a query can run.

Where the engine can enforce it, the timeout becomes a server-side setting: `statement_timeout` on PostgreSQL, `max_execution_time` on MySQL and ClickHouse, `max_statement_time` on MariaDB, `ob_query_timeout` on OceanBase. On SQLite it bounds how long a statement waits for a locked database instead. Drivers that talk HTTP (BigQuery, Spanner, Cloudflare D1, LibSQL, Etcd, DynamoDB, Elasticsearch, Typesense, and ClickHouse) bound the request at the timeout plus 30 seconds, and Oracle enforces it in the client: the connection is closed to unblock the call, and the next query reconnects and restores your current schema. SAP HANA ends the statement with the same `ALTER SYSTEM CANCEL SESSION` that **Stop** sends.
Where the engine can enforce it, the timeout becomes a server-side setting: `statement_timeout` on PostgreSQL, `max_execution_time` on MySQL and ClickHouse, `max_statement_time` on MariaDB, `ob_query_timeout` on OceanBase. On SQLite it bounds how long a statement waits for a locked database instead. Drivers that talk HTTP (BigQuery, Bigtable, Spanner, Cloudflare D1, LibSQL, Etcd, DynamoDB, Elasticsearch, Typesense, and ClickHouse) bound the request at the timeout plus 30 seconds, and Oracle enforces it in the client: the connection is closed to unblock the call, and the next query reconnects and restores your current schema. SAP HANA ends the statement with the same `ALTER SYSTEM CANCEL SESSION` that **Stop** sends.

Older servers have neither: MySQL before 5.7.8, MariaDB before 10.1.1. A statement that passes the timeout there is stopped with `KILL QUERY` from a second connection, which reaches a `SELECT` on MySQL and any statement at all on MariaDB. A server behind a proxy that reports a version it does not have still gets the right one of the two.

Expand Down
141 changes: 141 additions & 0 deletions databases/bigtable.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
title: Google Cloud Bigtable
description: Connect to Google Cloud Bigtable, browse rows by key, filter on keys and cells, and edit cells
---

import RegistryPlugin from "/snippets/registry-plugin.mdx";

Every row comes back in ascending key order, with its key in `_key` and one column per `family:qualifier`.

<RegistryPlugin name="Google Cloud Bigtable" plugin="Bigtable Driver" />

## Quick setup

Click **New Connection…**, select **Google Cloud Bigtable**, pick an **Auth Method**, then click **Save & Connect**.

The toolbar's **Instance** switcher moves between the project's instances without reconnecting, and an open tab stays on the instance it opened on. See [Tabs](/features/tabs#where-a-tab-points).

## Connection settings

| Field | Description |
|-------|-------------|
| **Auth Method** | **Service Account Key**, the default, or **Application Default Credentials** |
| **Service Account Key** | A path to the JSON key file, or the JSON pasted in. Stored in the Keychain |
| **Project ID** | The Google Cloud project. Empty takes the project the key names. Under Application Default Credentials, empty takes the credentials file's project, then `GOOGLE_CLOUD_PROJECT` |
| **Instance ID** | The Bigtable instance. Empty opens the instance whose ID sorts first |
| **App Profile ID** | Options tab. Routes reads and writes through that app profile. The instance's default profile when empty |

**Project ID** and **Instance ID** stand in for a host, a port and the Database field. Any instance connects, no connection URL opens one, and [SSH Tunnel](/connections/ssh-tunneling) is not offered.

## Authentication

| Auth Method | What is sent |
|-------------|--------------|
| **Service Account Key** | An OAuth token signed with the key |
| **Application Default Credentials** | The token from `gcloud auth application-default login`, or from the file `GOOGLE_APPLICATION_CREDENTIALS` names |

Reading needs the Bigtable Reader role and editing the Bigtable User role, granted on the project or on the instance.

## Browsing tables

The sidebar lists the instance's tables. A grid page has a column for each `family:qualifier` its rows hold, so two pages can show different columns; the **Structure** tab lists the columns of the first 50 rows. In a column where the row has no cell, the grid reads **No Field** ([Editing a cell](/features/change-tracking#editing-a-cell)). Each cell shows its newest version.

Keys, qualifiers and values are bytes. UTF-8 text with no control characters other than tab and newline shows as itself. A key or qualifier that is anything else, or text that starts with `b64:`, shows as `b64:` and its base64, and that form reads back to the same bytes: type it that way in a filter, a request line or a new row's `_key`. A value of that kind shows as hex. To filter on it, type it as `b64:` and its base64.

A table's grid keeps key order: clicking a column header does not sort it. A request's results in the editor sort on screen when you click a header. A later page starts after the last key of the page before, and a page jumped to directly skips the rows before it by reading their keys alone.

The status bar's total stays blank until **Count Exactly** reads the key of every row the filters admit. See [Pages and row counts](/features/data-grid#pages-and-row-counts).

## Filtering

Most filters on `_key` read only the keys they name:

| Operators on `_key` | What Bigtable reads |
|----------|--------------------|
| equals, in list | Exact keys. A list splits on commas, or takes a JSON array such as `["a,b", "c"]` |
| starts with | The key range of that prefix |
| greater than, greater or equal, less than, less or equal, between | A key range |
| not equals, not in list, contains, not contains, ends with, matches regex | A row key regex, over every row |

With **Match Case** off, equals, in list and starts with on `_key` run as a row key regex over every row too.

A filter on a `family:qualifier` column keeps or drops whole rows by that cell's newest value. A row without the cell matches only is NULL, so not equals, not contains and not in list leave it out too:

| Operators on a column | What Bigtable runs |
|----------|--------------------|
| equals, not equals, contains, not contains, starts with, ends with, in list, not in list | A value regex |
| matches regex | The pattern as written, in RE2 syntax. It has to match the whole value: `.*ann.*` finds `ann` anywhere, `ann` only the value `ann` |
| greater than, greater or equal, less than, less or equal, between | A value range, compared byte by byte |
| is empty, is not empty | An empty value regex |
| is NULL, is not NULL | Whether the row has the cell |

Values are compared as bytes, so `9` sorts after `10`. Under **Match any**, filters that are all equals, in list, starts with or a range on `_key` read only the keys each one names. Mix in any other filter and a range on `_key` is refused, while equals, in list and starts with on `_key` run as a row key regex over every row.

In a **Raw Filter** row, write a `RowFilter` as the Bigtable REST reference shows it, bytes in base64; it runs unchanged. A cell filter such as `valueRegexFilter` trims each row to the matching cells and drops rows left with none. To keep or drop whole rows, wrap it in a `condition` whose `trueFilter` is `{"passAllFilter": true}`.

## Editing

Each changed row is one request, and its cells change together. A save has no transaction: when one row fails, the rows written before it stay written.

| Change | Request |
|--------|---------|
| New, duplicated or pasted row | `INSERT` with the key you typed in `_key` and every cell you filled. Refused when a row already has that key |
| Edited cells | `PUT` with the changed cells |
| Cell set to NULL, or **Remove Field** | The cell set to `null` in the `PUT`, which deletes every version of it |
| Deleted row | `DELETE` on the key |

A new row needs a `_key` and at least one cell. **Duplicate** and pasted rows copy every cell but `_key`, which shows DEFAULT until you type a key. The grid offers only the columns the rows on the page hold, so an empty table shows `_key` alone: write a table's first row, or a cell under a column the page does not show, with `INSERT` or `PUT` in the request editor. The column family must already exist, and the **DDL** tab of the Structure tab lists them under `columnFamilies`.

An existing row's key cannot change. The save is refused, so duplicate the row, give the copy the new key, and delete the old one. **Set DEFAULT** in the row inspector is refused on an existing row too; set the cell to NULL to delete it, or enter a value. Text typed into a cell or into `_key` that starts with `b64:` is written as the bytes its base64 decodes to. Cells are written with the server's clock.

## Request editor

| Request | What it does |
|---------|--------------|
| `GET events/user#42` | One row |
| `SCAN events/user#?limit=20` | Rows whose key starts with the prefix: 100, or the `?limit=` given. `SCAN events` reads from the table's first row |
| `READ events {"rowsLimit": "10"}` | A ReadRows body from the REST reference |
| `INSERT events/user#42 {"profile:name": "Ann"}` | Creates the row. Refused when the key already has a row, and refuses `null` |
| `PUT events/user#42 {"profile:name": "Ann"}` | Sets cells, and creates the row when the key has none. `null` deletes a cell, and `b64:` text writes bytes |
| `DELETE events/user#42` | Deletes the row |
| `DELETE events/user#42 profile:name` | Deletes that cell, every version of it |

The table goes before the first slash, and the key is everything after it up to a space or a `?`. A key holding either goes in JSON quotes, `GET events/"a b?c"`, and a binary key as `b64:<base64>`. A column after `DELETE` runs to the end of the line, so it may hold spaces.

A request does not get the row cap in [Settings > Data](/customization/data-settings): a `READ` without `rowsLimit` returns up to 5,000,000 rows, so give it one.

For [Safe Mode](/features/safe-mode), `GET`, `SCAN` and `READ` are reads and every other request is a write. A `DELETE`, or a `PUT` that sets a cell to `null`, also raises the dangerous query warning on **Silent**.

## SSL/TLS

Rows go to `bigtable.googleapis.com` and instances and tables to `bigtableadmin.googleapis.com`, both over HTTPS only. [Per-driver defaults](/connections/ssl#per-driver-defaults) lists Bigtable among the drivers with no **SSL Mode**.

## Limitations

- No GoogleSQL. Use the request editor.
- No reverse scans. Rows come in ascending key order only; to start late in a table, filter `_key` with greater than.
- Only the newest version of each cell shows, in a `READ` result too. An edit adds a new version; removing a cell deletes every version of it.
- Tables and column families cannot be created, changed, truncated or dropped. Use the Google Cloud console or `cbt`.
- Export fixes its columns from the first 500 rows. Columns that appear only later go into an `__extra__` column as JSON, with bytes as `b64:` and base64.
- Import is not available. Write rows with `INSERT` or `PUT` in the request editor.
- A Bigtable connection synced to iPhone or iPad appears in the list but does not open there; open it on the Mac. See [Supported databases](/ios#supported-databases).

## Troubleshooting

### Permission denied: ….

The message names what was refused, such as "read events" or "list instances". Grant Bigtable Reader to read or Bigtable User to edit. A refused "list instances" means the role sits on one instance, not the project: fill in **Instance ID** so the connection skips the listing.

### Project … has no Bigtable instance.

The project in the message lists no Bigtable instance. With **Project ID** empty, that project came from the key or the Application Default Credentials, which need not be the one holding Bigtable. Set **Project ID** to the project with the instance.

### Row … already exists. Edit that row, or give the new one another key.

A new, duplicated or pasted row, or an `INSERT` line, names a key another row already has. Change `_key` on the new row, or edit the existing row instead.

## Related

- [Filtering](/features/filtering)
- [Safe Mode](/features/safe-mode)
- [Import & Export](/features/import-export)
5 changes: 3 additions & 2 deletions databases/index.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
title: Supported Databases
description: All 39 engines TablePro connects to, their default ports, and which ones need a plugin
description: All 40 engines TablePro connects to, their default ports, and which ones need a plugin
---

import DriverCounts from "/snippets/driver-counts.mdx";

Thirty-nine engines, and every one of them is free to use. What differs between them is where the
Forty engines, and every one of them is free to use. What differs between them is where the
driver comes from, not what the license covers.

<DriverCounts />
Expand All @@ -17,6 +17,7 @@ driver comes from, not what the license covers.
| [Amazon Redshift](/databases/redshift) | 5439 | Built-in |
| [Beancount](/databases/beancount) | File-based | Plugin |
| [BigQuery](/databases/bigquery) | API-based | Plugin |
| [Bigtable](/databases/bigtable) | API-based | Plugin |
| [Cassandra](/databases/cassandra) | 9042 | Plugin |
| [ClickHouse](/databases/clickhouse) | 8123 | Built-in |
| [Cloudflare D1](/databases/cloudflare-d1) | API-based | Plugin |
Expand Down
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@
"databases/etcd",
"databases/firebase",
"databases/bigquery",
"databases/bigtable",
"databases/kafka",
"databases/libsql",
"databases/mariadb",
Expand Down
2 changes: 1 addition & 1 deletion features/change-tracking.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Double-click a cell, or press `Enter` on it. `Enter` commits the edit to the que

Right-click an editable cell and open **Set Value** for the common ones without typing: **Empty**, **NULL** on a nullable column, **Default** on a column the server fills in, and date functions such as `NOW()` or `CURRENT_DATE` on date columns.

On MongoDB, Firestore and the Firebase Realtime Database a document can lack a field altogether, which the grid shows as **No Field**. **Remove Field** on the same right-click menu deletes the field rather than storing NULL. See [missing fields and null](/databases/mongodb#missing-fields-and-null).
On MongoDB, Firestore and the Firebase Realtime Database a document can lack a field altogether, and on Bigtable a row can lack a cell; the grid shows either as **No Field**. **Remove Field** on the same right-click menu deletes the field rather than storing NULL. Bigtable stores no NULL, so **NULL** and **Remove Field** both delete the cell. See [missing fields and null](/databases/mongodb#missing-fields-and-null).

## Adding and deleting rows

Expand Down
2 changes: 1 addition & 1 deletion features/copy-objects.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ have none, so the command does not appear on them.

## Limitations

Copying is SQL only. MongoDB, Redis, DynamoDB, Firestore, the Firebase Realtime Database, Elasticsearch,
Copying is SQL only. MongoDB, Redis, DynamoDB, Firestore, the Firebase Realtime Database, Bigtable, Elasticsearch,
Typesense, Kafka, etcd and SurrealDB have no
`CREATE TABLE` and no row writer to copy through, so neither command appears on them, and neither
end of a copy may be one of them.
Expand Down
4 changes: 2 additions & 2 deletions features/data-grid.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ A narrow window trims that bar rather than cutting it off. The Columns and Filte

Click a header to cycle through the first direction, its opposite, and off. `Shift`-click a second header to sort by both. Sorting re-runs the query with a new `ORDER BY`, so it orders the whole table rather than the page in front of you. In a query tab that query is the one statement behind the result, never the rest of the script.

A Cassandra or ScyllaDB table has no header sort, and a query result there sorts the rows it holds. See [Cassandra](/databases/cassandra#browsing-and-editing).
A Cassandra, ScyllaDB or Bigtable table has no header sort, and a query result there sorts the rows it holds. See [Cassandra](/databases/cassandra#browsing-and-editing) and [Bigtable](/databases/bigtable#browsing-tables).

**Sort direction** in [Settings > Data](/customization/data-settings) decides which way that first click goes. It is Ascending out of the box; set it to Descending to open on the newest rows and reach them in one click.

Expand Down Expand Up @@ -138,7 +138,7 @@ A table tab pages. The status bar offers 5, 10, 20, 100, 500 or 1,000 rows per p

A large table shows an estimated total prefixed with `~` instead of running a slow `COUNT(*)`. **Count Exactly** beside it runs the real count in the background, and `Cmd+.` cancels that. While a total is an estimate, **Last** and **All rows…** stay unavailable and **Next** works as long as the last page came back full.

Cassandra, ScyllaDB and the Firebase Realtime Database have no estimate, and their count reads every partition or key, so a table there shows no total until you click **Count Exactly**.
Cassandra, ScyllaDB, Bigtable and the Firebase Realtime Database have no estimate, and their count reads every partition or key, so a table there shows no total until you click **Count Exactly**.

A query tab does not page. It stops at the [row cap](/customization/data-settings) instead and offers **Fetch All** to load the rest, and a query carrying its own `LIMIT`, `FETCH FIRST`, or `TOP` is never capped. `Cmd+.` cancels a running query or a Fetch All.

Expand Down
Loading
Loading