Docs / Features

← README

Features

Everything PlatypusDB does, area by area. The README has the short version. To do something step by step, see the user guide.

Databases

DatabaseHow it connectsNotes
PostgreSQL 12+native protocolSeveral databases per connection, partitions, PostGIS, arrays and composite types
MySQL 5.7+, MariaDB 10.6+native protocolOn MySQL before 8.0.16 plans are read from EXPLAIN FORMAT=JSON; EXPLAIN ANALYZE needs 8.0.18
SQL Server 2017+, Azure SQLTDSTOP (n), [names], several result sets
ClickHouseHTTP interface (8123, or 8443 with TLS), Arrow resultsBrowsed, queried and designed; no data editing in the grid
SQLiteembedded
DuckDBembedded, Arrow results; DuckDB's library downloaded on first useAlso opens CSV, JSON, Parquet and Excel files as databases
libSQL / TursoHrana over HTTPToken in the password or in authToken
MongoDB 5+official drivermongosh syntax; transactions on replica sets and sharded clusters; documents of a find edited in the grid by _id
Redis 6+redis crate (RESP3, rustls)Commands in the editor; key types as tables, edited in the grid; JSON with RedisJSON
Elasticsearch 8 and OpenSearch 2HTTP API (reqwest, rustls)Requests as Kibana's console writes them, and SQL; indices as tables, edited in the grid
Apache Cassandra 4+ and ScyllaDB 5.4+scylla crate (rustls)CQL; keyspaces as schemas, tables, materialized views, types and functions in the navigator; edited in the grid by full primary key; no row count, sort or filter in the data viewer
CockroachDB, YugabyteDB, TimescaleDB, Supabase, Neon, AlloyDBPostgreSQL driverProfiles with their default port, user, database and SSL
Snowflake, BigQuery, Databricks, TrinoADBC driver, downloaded on first useQueries, results, navigator and autocompletion; no data editing, explain or manual commit yet
Any database with an ADBC driverADBC driver library you chooseIts URI and options as the driver documents them; an optional entrypoint symbol for libraries that do not export AdbcDriverInit (DuckDB: duckdb_adbc_init)
Oraclenative driver with Oracle Instant Client, downloaded on first usePL/SQL blocks (end them with a / line in scripts), real cancel, navigator, DDL; Windows needs the Visual C++ Redistributable, Linux libaio
IBM Db2IBM's ODBC driver, installed on first use (Windows)Registering the driver asks for administrator rights (UAC)
Any ODBC driver or DSN (Windows)the system's ODBC driver managerA DSN, or a driver and its connection string

The versions in the table are the oldest the app is checked against. The conformance suite runs on that version for PostgreSQL, MySQL, MariaDB, SQL Server and MongoDB; for Redis 6 (Redis Stack 7.4), Cassandra 4 (Cassandra 5) and ScyllaDB 5.4 (ScyllaDB 2025.1) it runs on a newer one, and Elasticsearch and OpenSearch are tested on 8.15 and 2.17. An older server still connects, with a message that some features may not work; Cassandra older than 4.0, ScyllaDB older than 5.4, Elasticsearch older than 7.10 and OpenSearch older than 1.0 are refused. Errors name the server they come from, e.g. Unknown column 'x' in 'field list' (MySQL 5.7.44), since the reason often depends on the version. The database pages say more for each one.

The ADBC drivers come from the dbc driver registry (Columnar): the first connection that needs one asks to download it, checks its SHA-256 and keeps it with the app's data. Oracle Instant Client and IBM's ODBC driver come from Oracle's and IBM's sites in the same way, with their license shown first. The download is offered as soon as the database is chosen for a new connection, over the form (the app reads only what is on this machine, so it works offline); Cancel leaves the form usable, and the offer returns at Test connection or at the first opening. Settings › Drivers lists and removes them, and shows the ODBC drivers and data sources of the system. A downloaded ADBC driver can be updated from there (the app looks for new versions once a day, or on Check for updates; Driver updates in Settings can install them by itself), and Versions… installs or goes back to another version, keeping the others. After a change the saved connections that use the driver are tried (connect, version, schemas, SELECT 1), with Go back if one fails. The built-in drivers update with the app.

Portable copy

Put the executable in a folder with an empty data folder beside it, as with VS Code. Connections, settings, history, scripts, logs and the web view's storage then live in data, and the folder can move to another machine or a USB stick. Saved passwords stay in the system's keychain unless a master password keeps them in the folder too. A portable copy updates by replacing its executable; the welcome screen says portable beside the version.

Package managers

Besides the installers of each release, the app can come from winget (winget install PlatypusLabs.PlatypusDB), Homebrew (brew install --cask platypusdb), the AUR (platypusdb-bin) and Flathub (dev.platypusdb.desktop), once each package is published. Installed from Flatpak or the AUR, the app says when a new version is out but leaves the update to the package manager.

Connections

Choosing the database for a new connection

Notes, comments and schema documentation

The comments, notes and tags of a table and its columns

The exported HTML documentation with its ER diagram

SQL editor

JOIN completion from the foreign keys

Results and the data viewer

Data analysis

A result as a bar chart

Data editor

Pending changes in the data editor

Table editor

Designing a table

Right-click a table, Edit table… (F4), or a schema, Create table…; Edit table… is also in a table's tab, next to Open data, and in the data viewer's bar, next to Structure, which opens the table's tab. Either brings forward the editor already open on the table. Columns (type, NOT NULL, default, primary key, comment), indexes, foreign keys with their ON DELETE/ON UPDATE actions, and UNIQUE and CHECK constraints. Saving shows the CREATE TABLE or ALTER TABLE script first and runs it in one transaction where the database can roll DDL back (MySQL commits each statement). SQLite and DuckDB, which alter little in place, get the table created again with its rows copied.

ER diagrams, schema check, comparison and history

Visual query builder

Two tables joined on their foreign key

Drag tables onto a canvas, or add them from the list with Enter or their +: they join on their foreign keys. The join button between two tables shows its condition and switches between JOIN and LEFT JOIN. Tick the columns, add a WHERE, then run the SELECT or open it in the editor. The tables list beside the canvas and each table box resize by their edge or corner; the list keeps its width.

Query plans and performance

Explain analyze with the heatmap and an index suggestion

The query manager with the trace of a statement

Server activity

Import, export and transfer

MongoDB

Statements in mongosh syntax: db.orders.find({ status: 'paid' }).sort({ placed_at: -1 }).limit(10), aggregate, countDocuments, distinct, insertOne/insertMany, updateOne/updateMany, replaceOne, deleteOne/deleteMany, bulkWrite, findOneAndUpdate and the like, index and statistics methods, .explain(), db.runCommand and the server's helpers (users, profiler, rs.status), show dbs, show collections and use, with every option passed on, with the shell's values (ObjectId(...), ISODate(...), NumberLong(...), /regex/i) and Extended JSON. Results are a grid of the top-level fields with the focused document as a tree beside it (F7). The navigator lists databases, collections, fields sampled from the documents and indexes; the editor completes collections, methods and fields. Generate data fills a collection with fake documents: new ObjectIds or numbers after the largest _id, lists of words for arrays, small documents for nested fields. mongodb+srv:// and connection string options go in the Database field (shop?authSource=admin&replicaSet=rs0).

Editing: the result of a plain find on one collection (the data viewer, or a statement you type; not a view, an aggregation or a projection without _id) is edited in the grid by _id. Each change is one updateOne, insertOne or deleteOne that Preview SQL shows, written with the BSON type of the column (int, long, double, decimal, bool, string, objectId, date, document, array); a changed number keeps the numeric type the document stores when that type holds it exactly. Changing _id, columns of mixed types or of binary data, and fields with a . in their name or starting with $ are refused with the reason. Each command must change one document. On a replica set a save is all or nothing, in a transaction of its own or in the one you have open; on a standalone server the commands run one after the other. Details in the MongoDB guide.

Manual commit works on a replica set or a sharded cluster, not on a standalone server. The reads and writes of documents, createCollection and createIndex run in a transaction of the connection's session, opened at the first change (reads before it open none and see what is committed) and ended by Commit or Rollback; metadata, the server's commands and the navigator run outside it. What MongoDB does not run in a transaction (drop, renameCollection, dropIndex, createView, $out, $merge) is refused in manual commit, leaving the transaction as it was. When the server ends a transaction (after 60 seconds by default, or on a duplicate key or a write conflict), the error says so, the changes are discarded and the transaction shows as closed. Details in the MongoDB guide.

Redis

The editor runs Redis commands, one per line, quoted as redis-cli quotes them (SET k "a b", "\xff" for bytes); # and -- lines are comments. Replies are rows: a value, a map's fields, a list's items, nested replies as JSON in the document tree beside the grid (F7). SELECT n moves the session to another database; MULTI queues commands until EXEC, which sends them as one transaction and shows each reply. Subscriptions and MONITOR are refused: they would take over the connection. Cancel stops a blocking command (BLPOP, XREAD BLOCK).

The navigator lists the databases with keys (db0, db1…, and the session's), each with a table per key type: string, hash, list, set, zset, json (with RedisJSON) and stream, with how many keys each has when the database has up to 10,000. Opening one runs BROWSE db0.hash, which finds the keys with SCAN … TYPE (never KEYS) and reads big keys a chunk at a time as the grid scrolls: a row per value, key, value, ttl for strings and JSON, key, field, value for hashes, key, index, value for lists, key, member for sets, key, member, score for sorted sets, key, id, fields for streams. MATCH user:* filters the keys, LIMIT n caps the rows. Values that are not UTF-8 show quoted with escapes.

The grid edits the rows: a value (SET … KEEPTTL, HSET, LSET, JSON.SET), a TTL (EXPIRE, PERSIST for NULL), a string key's name (RENAMENX), a hash field or a set or sorted set member (moved), a score; new rows add keys and items (SET … NX, HSETNX, RPUSH, SADD, ZADD NX, XADD), deleted rows remove them (list items by their index). A new or renamed key, field or member that already exists stops the save before anything runs, unless the same save deletes it. The preview shows the commands, which run as one MULTI/EXEC (a command the server refuses does not undo the others), and Undo script puts the values back. FLUSHDB and FLUSHALL ask first as an UPDATE without WHERE does, and on production connections commands that change the server (CONFIG SET, SHUTDOWN) ask like DDL. Read-only connections run only the commands that read. rediss:// URLs and SSL modes from require up use TLS; the user and password are the ACL's. Terminal mode runs redis-cli with the password in its environment.

Elasticsearch and OpenSearch

One driver for both; the server says which it is, and the connection shows it (Connected: OpenSearch 2.17.1). Tested on Elasticsearch 8.15 and OpenSearch 2.17, not on Elasticsearch 7.x. Log in with a user and password (Basic), or leave the user empty and put an API key (the encoded value) in the password field. elasticsearch://user:pass@host:9200/logs-* and opensearch:// URLs work; elasticsearchs:// and SSL modes from require up use TLS. The optional index pattern of the connection (logs-*, or several separated by commas) narrows what the navigator lists.

The editor takes requests as Kibana's Dev Tools console writes them: a line GET /orders/_search (the slash is optional; methods GET, POST, PUT, DELETE, HEAD), then its JSON body, which ends where its braces close. The NDJSON bodies of _bulk and _msearch end at a blank line or the next request. Anything else is SQL (_sql on Elasticsearch, _plugins/_sql on OpenSearch), ended by a ; or a blank line, with typed columns (numbers, booleans, dates) and the cursor of the server for long results. #, // and -- lines are comments, Ctrl+Enter runs the statement under the cursor and Alt+X the script. Highlighting follows the kind of statement; completion offers the methods, the indices, the APIs after an index, the fields of the index where a key of a body goes, and the same names in SQL. A search gives documents, laid out as MongoDB's: a column for each top-level field, objects and arrays as JSON in the document tree beside the grid (F7). Aggregations come as results of their own (the metrics in one row, a table per bucket aggregation), a _msearch gives a result per search, and a _bulk with failing items lists the items with the server's reason. Errors keep the server's reason (no such index [x]), and an SQL error points at its place in the statement. Cancel stops a search on the server.

The navigator lists the indices as tables, with their health, shards and replicas in the tooltip (system and closed ones say so), and the aliases and data streams as views. Hidden indices stay hidden. The columns of an index are the fields of its mapping (customer.address.zip for objects, title.raw for multi-fields). Opening an index runs a search of all its documents and reads them a page at a time (1,000 rows, then Fetch all) with a point in time and search_after (a scroll on OpenSearch), so a read does not stop at the first 10,000 hits. The columns are the same on every page: a field that no mapped column has goes into a last _other column as JSON, and a value that does not fit its mapped type shows as text.

The grid edits the documents: a changed cell is an _update by _id, an object replaces the old one, a new row is a _create when it has an _id (an _id that exists is refused by the server) and a generated document without one, a deleted row is a DELETE. The preview shows the requests, which run one after the other and wait for the refresh; a request the server refuses stops the rest, and the ones before it stay. Undo script puts the old values back, or the whole _source of a deleted document (a document with a generated _id is left to delete by hand). _delete_by_query and _update_by_query without a query (or with match_all), and DELETE /_all, ask first like a DELETE without WHERE, and say how many documents the index has. Read-only connections run only the requests and the SQL that read. Agents get the same checks through the MCP server.

Transactions and safety

Workspace

Command palettePlatypus Light

Sharing and sync

Settings and themes

Settings, one section per tab