An IBM Db2 plugin for Tabularis, the open-source database client.
This plugin enables Tabularis to connect to Db2 through ODBC, browse schemas and objects, run SQL queries, perform inline CRUD operations, and generate DDL through the Tabularis JSON-RPC plugin interface.
Requires Tabularis v0.30.0 or later. That is the first release that serves a plugin's Visual EXPLAIN parser to Tabularis Web as well as the desktop app; the plugin will not load on earlier versions of Tabularis.
Discord - Join our discord server and chat with the maintainers.
- Verification Checklist
- Features
- Supported Db2 Data Types
- Requirements
- Installation
- How It Works
- Supported Operations
- Building from Source
- Development
- Known Limitations
- Changelog
- Maintainers
- License
Every feature below was verified against Db2 12.1 Community Edition and Tabularis 0.32.0, through the live integration suite and in the Tabularis UI; the linked issues carry the results.
| Feature | Status | Issue |
|---|---|---|
| Read table data | Verified | — |
| Schema & object browsing | Verified | #1 |
| Query execution | Verified | #2 |
| Insert record | Verified | #3 |
| Update record | Verified | #4 |
| Delete record | Verified | #5 |
| DDL generation | Verified | #6 |
| View support | Verified | #7 |
| Index management | Verified | #8 |
| Foreign key management | Verified | #9 |
| Routine support | Verified | #10 |
| Explain query | Verified | #11 |
- Connection via ODBC — Connect to Db2 using host, port, database, credentials, and optional plugin settings.
- Schema & Object Browsing — List schemas, tables, columns, views, indexes, foreign keys, and routines.
- Query Execution — Run any SQL statement, with pagination that leaves your
ORDER BY,FETCH FIRSTand isolation clauses alone, typed values (integers, floats, booleans, exact decimals, BLOB previews) and Db2's own error messages. Multi-statement scripts run on one connection, andBEGIN ... ENDblocks stay one statement. - Inline Editing — Insert, update, and delete rows directly from the Tabularis grid, including tables without a primary key: the row is matched on all its columns and exactly one row is changed.
- DDL Generation — Generate
CREATE TABLE,ALTER TABLE,CREATE INDEX, andADD FOREIGN KEYSQL that Db2 runs as is: quoted identifiers, identity columns, column comments, and theREORGa column change needs. Existing tables can be exported and dumped with their real definition. - Modern Plugin Manifest — Uses current Tabularis plugin manifest fields including
settings, modern capabilities, color/icon metadata, and connection string support. - Modular Rust Codebase — RPC, client creation, metadata handlers, CRUD, DDL, and utilities are split into dedicated modules with unit tests.
| Category | Types |
|---|---|
| Numeric | SMALLINT, INTEGER, BIGINT, DECIMAL, DECFLOAT, REAL, DOUBLE |
| String | CHAR, VARCHAR, CLOB, GRAPHIC, VARGRAPHIC, DBCLOB |
| Date/Time | DATE, TIME, TIMESTAMP |
| Binary | BLOB, BINARY, VARBINARY |
| Other | BOOLEAN, XML |
This plugin uses the odbc-api crate and requires a working Db2 ODBC driver on the host system.
Typical requirement:
- IBM Db2 ODBC driver installed
- ODBC driver manager available on the OS
- network connectivity to the Db2 server
You can configure the driver name from Tabularis through the plugin settings.
On Linux and macOS the driver manager is unixODBC
(apt install unixodbc, brew install unixodbc); Windows ships its own. The
IBM driver itself needs libxml2.so.2: distributions that only ship the newer
libxml2.so.16 (for example Ubuntu 25.10 and later) need a compatibility
package providing it, otherwise the driver manager reports
Can't open lib '.../libdb2.so' : file not found.
This plugin is published on the Tabularium registry. Install it from Tabularis's in-app plugin browser: Settings → Plugins, search for Db2, and install.
-
Download the latest release for your platform from the Releases page.
-
Extract the archive.
-
Copy everything the archive contains (
tabularis-db2-pluginortabularis-db2-plugin.exe,.tabulariumand theexplain/folder with the Visual EXPLAIN parser) into the Tabularis plugins directory:OS Plugins Directory Linux ~/.local/share/tabularis/plugins/drivers/db2/macOS ~/Library/Application Support/tabularis/plugins/drivers/db2/Windows %APPDATA%\tabularis\plugins\drivers\db2\ -
Restart Tabularis.
The plugin is a standalone Rust binary that communicates with Tabularis through JSON-RPC 2.0 over stdio:
- Tabularis spawns the plugin process.
- Requests are sent as newline-delimited JSON-RPC messages to the plugin
stdin. - The plugin opens Db2 ODBC connections on demand and writes JSON-RPC responses to
stdout.
The code is intentionally split by responsibility:
src/main.rs— request loop and dispatchsrc/client.rs— ODBC connection string construction, one connection per request, text fetchingsrc/handlers/metadata.rs— schemas, tables, columns, views, indexes, FKs, routinessrc/handlers/query.rs— connection test, query executionsrc/handlers/explain.rs—EXPLAIN PLAN, read back from the explain tablessrc/handlers/crud.rs— insert, update, deletesrc/handlers/ddl.rs— SQL generation helperssrc/utils/— pure helpers with unit testsexplain/— the Visual EXPLAIN parser (TypeScript): turns the explain-table rowsexplain_queryreturns into the plan tree Tabularis draws. It is built intoexplain/dist/index.iife.js, declared underexplain_parsersin.tabulariumand shipped in every release archive, the same way as the SQL Server plugin's SHOWPLAN parser.
| Method | Description |
|---|---|
initialize |
Loads plugin settings sent by Tabularis |
test_connection / ping |
Verifies Db2 connectivity |
get_databases |
Returns the current server/database identity |
get_schemas |
Lists the user schemas, empty ones included (Db2's own SYS*, NULLID and SQLJ are left out) |
get_tables |
Lists the tables of the selected schema: base, typed, materialized query and created temporary tables, and nicknames |
get_columns |
Returns column metadata for a table or view: type with length/precision/scale, nullability, key, identity, generated flag, default and comment |
get_foreign_keys |
Lists the foreign keys of a table, one entry per column: referenced table and column, ON DELETE / ON UPDATE rule, and position within a composite key |
get_indexes |
Lists the indexes of a table, one entry per key column: uniqueness, primary key, position, and the expression of an expression-based index |
get_views |
Lists views in the selected schema |
get_view_definition |
Returns the view's query: the stored CREATE VIEW text without its header |
get_view_columns |
Returns column metadata for a view |
create_view / alter_view / drop_view |
Create a view from a query, replace its query (CREATE OR REPLACE VIEW, keeping an explicit column list) and drop it |
get_routines |
Lists functions and procedures, with the argument types that tell overloads apart |
get_routine_parameters |
Returns a routine's parameters with their direction, its return value or returned columns |
get_routine_definition |
Returns the CREATE statement of an SQL routine |
execute_query |
Runs one statement. A result set (SELECT, WITH, VALUES, CALL) is paged on the cursor and returned with typed values; anything else returns the number of rows it changed |
execute_query_batch |
Runs the statements of a script in order on one connection, reporting each one's result or error |
explain_query |
Returns Db2's access plan in Tabularis's raw form (engine: db2, format: db2-explain-tables-json): the rows of the explain tables, which the plugin's parser draws with tables and indexes, row estimates, costs, predicates and join types. With analyze, the statement also runs and its real row count and time are reported |
insert_record |
Inserts a row, rendering each value for its column type (numbers validated, text and binary data bound as parameters, NULL, column defaults, BLOB uploads) |
fetch_blob_as_data_url / save_blob_to_file |
Read the whole value of a binary column for the preview and for export |
update_record |
Sets one column of one row, found by its primary key (single or composite) or, for a table without one, by the values of all its columns |
delete_record |
Deletes one row, found the same way as for update_record |
get_schema_snapshot |
Returns all tables with columns and foreign keys |
get_all_columns_batch |
Returns all column metadata for a schema |
get_all_foreign_keys_batch |
Returns all FK metadata for a schema |
get_create_table_sql |
Generates CREATE TABLE (identity columns, defaults, primary key) and COMMENT ON COLUMN statements |
get_add_column_sql |
Generates ALTER TABLE ... ADD COLUMN; a NOT NULL column without a default gets WITH DEFAULT, which Db2 requires on a table with rows |
get_alter_column_sql |
Generates one statement per changed attribute (rename, type, nullability, default, identity, comment), followed by REORG TABLE when the change requires it |
get_create_index_sql |
Generates CREATE [UNIQUE] INDEX in the table's schema |
get_create_foreign_key_sql |
Generates ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY; referential actions Db2 does not have (ON UPDATE CASCADE) are refused |
get_table_ddl |
Rebuilds the CREATE TABLE of an existing table from the catalog, with its indexes and comments, for table export and database dumps |
drop_index |
Drops an index of the table, in whichever schema the index lives |
drop_foreign_key |
Drops a foreign key of the table (ALTER TABLE ... DROP FOREIGN KEY) |
- Rust (edition 2021)
just(optional, wraps the common cargo invocations)- Node.js 22.18 or later and pnpm
(the version pinned in
explain/package.json), for the Visual EXPLAIN parser - The ODBC driver manager development files (
unixodbc-devon Debian/Ubuntu,brew install unixodbcon macOS; nothing to install on Windows) - A Db2 ODBC driver and a running Db2 instance (for integration tests)
just build # debug build, plus the Visual EXPLAIN parser bundle
just release # release build (what the GitHub Actions workflow ships)
just build-explain # the parser bundle alone (explain/dist/index.iife.js)Or directly with cargo:
cargo build --releaseThe binary will be located at target/release/tabularis-db2-plugin.
just dev-install # build + copy binary, manifest and parser bundle into the Tabularis plugins dir
just uninstall # remove the installed pluginA docker-compose.yml is provided to spin up a local IBM Db2 Community Edition instance for development and integration testing.
- Docker (or Podman with the
dockerCLI alias) - IBM Data Server Driver for ODBC and CLI (
clidriver) installed on the host and registered in/etc/odbcinst.ini
Installing the IBM clidriver on Linux:
curl -fSL https://public.dhe.ibm.com/ibmdl/export/pub/software/data/db2/drivers/odbc_cli/linuxx64_odbc_cli.tar.gz \
-o /tmp/clidriver.tar.gz
sudo mkdir -p /opt/ibm
sudo tar xf /tmp/clidriver.tar.gz -C /opt/ibm
echo -e "[Db2]\nDescription = IBM Db2 ODBC Driver\nDriver = /opt/ibm/clidriver/lib/libdb2.so" \
| sudo tee /etc/odbcinst.iniA helper script handles the full lifecycle (the just recipes wrap it):
# Start the container and seed TEST_SCHEMA with fixtures
just demo-db # ./scripts/setup-test-db.sh
# Drop the fixture schemas and load the fixtures again (keeps the container)
just demo-db-reseed # ./scripts/setup-test-db.sh reseed
# Check container health and seed status
just demo-db-status # ./scripts/setup-test-db.sh status
# Stop and remove the container (including volumes)
just demo-db-stop # ./scripts/setup-test-db.sh teardownThe first start can take several minutes while Db2 creates the database. The script waits up to 600 seconds (override with DB2_MAX_WAIT) and streams progress.
| Parameter | Value |
|---|---|
| Host | localhost |
| Port | 50000 |
| Database | TESTDB |
| User | db2inst1 |
| Password | db2test123 |
| Schema | TEST_SCHEMA |
Once the container is healthy:
just integration # DB2_TEST=1 cargo test --test integration -- --test-threads=1The tests look for an ODBC driver registered as Db2; set DB2_ODBC_DRIVER
when your odbcinst.ini entry has another name. DB2_HOST, DB2_PORT,
DB2_DATABASE, DB2_USER and DB2_PASSWORD override the container defaults.
The same suite runs in CI as the "Live Db2 integration" job.
Integration tests are gated behind the
DB2_TESTenvironment variable so thatcargo testalone only runs unit tests (no Db2 connection required).
just test # cargo test --bins — unit tests for SQL builders, quoting, pagination
just lint # clippy -D warnings
just fmt # cargo fmt --all
just validate-manifest # .tabularium against the live registry schema
just test-explain # typecheck and test the Visual EXPLAIN parserThe parser's tests (explain/tests/) run on explain payloads captured from
Db2 12.1 and compare the plans with committed goldens. Regenerate the goldens
with UPDATE_GOLDENS=1 pnpm --dir explain test after an intended change, and
review the diff.
just repl # cargo run --bin test_pluginecho '{"jsonrpc":"2.0","method":"initialize","params":{"settings":{}},"id":1}' \
| ./target/debug/tabularis-db2-pluginPR titles must follow Conventional Commits
(type: subject, type(scope): subject, or type!: subject for a breaking
change) — enforced by CI on every PR. Add a BREAKING CHANGE: footer to the
PR description for breaking changes that don't fit cleanly into the title.
Every PR also needs exactly one prerelease:alpha / prerelease:beta /
prerelease:rc / prerelease:stable label, so CI knows which release
channel to target when suggesting the next version. There's no default —
CI fails with a clear error if the label is missing, rather than guessing.
| PR title type | Version impact |
|---|---|
feat |
minor |
fix, refactor, perf |
patch |
docs, style, chore, test, ci, build |
none — no release suggested |
any type with ! or a BREAKING CHANGE: footer |
major |
CI posts a comment on the PR suggesting the next tag/version based on the
title's type and the prerelease:* label — informational only, nothing is
tagged or released automatically. The suggestion updates (and marks the
previous suggestion as outdated) only when the underlying classification
actually changes, not on every edit to the title text.
To release: bump version in .tabularium and Cargo.toml (CI checks they
agree), add a ## [x.y.z] - YYYY-MM-DD section to CHANGELOG.md,
merge, then push the vx.y.z tag. The release workflow refuses a tag that
does not match the manifest, builds every platform, smoke-tests each binary
and publishes the release with that changelog section as its notes.
- Language: Rust (edition 2021)
- Database driver:
odbc-api - Serialization: serde + serde_json
- Visual EXPLAIN parser: TypeScript, bundled with tsup, tested with vitest
- Protocol: JSON-RPC 2.0 over stdio
- The plugin requires a working Db2 ODBC driver on the host system.
explain_querywrites the plan into the Db2 explain tables (the session user's own, elseSYSTOOLS) and deletes it once read. When neither schema has them, they are created inSYSTOOLSwithSYSPROC.SYSINSTALLOBJECTS, which needs the authority to create tables there. Costs and row estimates are the optimizer's, and depend on up-to-date statistics (RUNSTATS).- Analyze runs the statement (a data change is committed) and reports its real row count and elapsed time for the statement as a whole. Per-operator actuals would need an activity event monitor, which a client cannot assume it may create.
- Catalog queries were designed using Db2 catalog conventions and DBeaver’s Db2 extension as a reference, but they should still be validated against the specific Db2 edition/version you target.
- Every statement is committed on its own: connections run in autocommit mode and are not kept between requests, so an explicit transaction cannot span several statements run from the editor. Wrap the statements in a
BEGIN ... ENDcompound statement to run them atomically. - A
BLOBin a query result carries its first 10,240 bytes and its full size;BINARY,VARBINARYandFOR BIT DATAvalues are shown as hexadecimal text. - Paging skips rows on the cursor, so a very deep page of a very large result takes longer than the first ones. Filter or order the result to get to the rows you need.
- Changing a column's type or nullability, or dropping a column, leaves a Db2 table in "reorg pending" state. The statements generated by the column editor end with the
REORG TABLEthat clears it; after anALTER TABLEof your own (or the "Delete Column" action), run theCALL SYSPROC.ADMIN_CMD('REORG TABLE ...')the error message suggests. - Db2 only supports
NO ACTIONandRESTRICTforON UPDATE, and has noSET DEFAULTreferential action; the foreign key editor reports that instead of generating SQL that cannot run. - A dump or export keeps identity columns as they are defined. Restoring rows into a
GENERATED ALWAYSidentity column fails (SQL0798N), as Db2 does not accept explicit values there; foreign keys are not part of the generatedCREATE TABLE. - Db2 has no
JSONcolumn type (JSON is stored inCLOB,VARCHARorBLOBcolumns), so it is not offered when creating a column. - Routines are read-only: they are listed, described and opened, but creating, running or dropping one goes through the SQL editor. External and built-in routines have no stored source, so only SQL routines can be opened.
get_databasesis intentionally conservative and currently returns the active server/database identity instead of enumerating every possible database on an instance.
- @debba
Apache-2.0.