KDB knowledge

Databases over HTTP (/api/databases)

List the databases of a server, create a new one, and drop one (its folder moves to _trash).

A server started for a data folder (kdb serve, or kdb serve --data-dir <folder>) serves every database in that folder. A database is one folder with its own tables. These routes list, create and drop them.

GET /api/databases

Syntax:

GET /api/databases

Lists the databases of the server, sorted by name.

ParameterWhereMeaningDefault
(none)-This route does not use db=-
Answer fieldMeaning
databases[]One object for each database you may read
databases[].nameThe database name
databases[].dirIts folder
databases[].defaulttrue for the default database (used when a request has no db=)
databases[].opentrue when the server has it open now
databases[].openingtrue while a request is opening it (a big database can take minutes)
databases[].opening_msOnly while it opens: for how long, in milliseconds
databases[].opening_phaseOnly while it opens: what it does now, such as replaying log (details in /api/stats)
databases[].busyOnly when true: a write or checkpoint held the database, so tables, bytes and wal_archive_bytes are from the last listing that got in. The list never waits for a database
databases[].tablesNumber of tables, or null when it is not open
databases[].bytesSize of its data files, or 0 when it is not open
databases[].errorOnly when an open database cannot be read: why
databases[].wal_archive_max_bytesOld log the database keeps for repairs and restores (0 = none)
databases[].wal_archive_sourcedatabase when it has its own ALTER DATABASE ... SET WAL_ARCHIVE, else server (the server default)
databases[].wal_archive_bytesOld log on disk now, or null when it is not open
defaultThe name of the default database
data_dirThe data folder (empty for kdb serve <folder>)
can_createtrue when you may create and drop databases here: the server has a data folder, and you have full rights
wal_archive_default_bytesThe server default for old log kept per database (wal_archive_mb in kdb.conf, or KDB_WAL_ARCHIVE_MB)

HTTP: Every database of the server

GET /api/databases

Response:

HTTP 200 (application/json)

{
  "databases": [
    {
      "name": "h119",
      "dir": "kdb-data/h119",
      "default": false,
      "encrypted": false,
      "open": true,
      "tables": 1,
      "bytes": 266,
      "opening": false,
      "wal_archive_max_bytes": 1073741824,
      "wal_archive_source": "server",
      "wal_archive_bytes": 0,
      "result_cache_bytes": 268435456,
      "result_cache_source": "server"
    },
    {
      "name": "demo",
      "dir": "kdb-data/demo",
      "default": false,
      "encrypted": false,
      "open": true,
      "tables": 0,
      "bytes": 0,
      "opening": false,
      "wal_archive_max_bytes": 1073741824,
      "wal_archive_source": "server",
      "wal_archive_bytes": 0,
      "result_cache_bytes": 268435456,
      "result_cache_source": "server"
    },
    {
      "name": "h1385",
      "dir": "kdb-data/h1385",
      "default": false,
      "encrypted": false,
      "open": true,
      "tables": 0,
      "bytes": 0,
      "opening": false,
      "wal_archive_max_bytes": 1073741824,
      "wal_archive_source": "server",
      "wal_archive_bytes": 0,
      "result_cache_bytes": 268435456,
      "result_cache_source": "server"
    },
    {
      "name": "h234",
      "dir": "kdb-data/h234",
      "default": false,
      "encrypted": false,
      "open": true,
      "tables": 0,
      "bytes": 0,
      "opening": false,
      "wal_archive_max_bytes": 1073741824,
      "wal_archive_source": "server",
      "wal_archive_bytes": 0,
      "result_cache_bytes": 268435456,
      "result_cache_source": "server"
    },
    {
      "name": "h315",
      "dir": "kdb-data/h315",
      "default": false,
      "encrypted": false,
      "open": true,
      "tables": 6,
      "bytes": 73878,
      "opening": false,
      "wal_archive_max_bytes": 1073741824,
      "wal_archive_source": "server",
      "wal_archive_bytes": 0,
      "result_cache_bytes": 268435456,
      "result_cache_source": "server"
    },
    {
      "name": "h316",
      "dir": "kdb-data/h316",
      "default": false,
      "encrypted": false,
      "open": true,
      "tables": 6,
      "bytes": 73878,
      "opening": false,
      "wal_archive_max_bytes": 1073741824,
      "wal_archive_source": "server",
      "wal_archive_bytes": 0,
      "result_cache_bytes": 268435456,
      "result_cache_source": "server"
    },
    {
      "name": "h60",
      "dir": "kdb-data/h60",
      "default": false,
      "encrypted": false,
      "open": true,
      "tables": 0,
      "bytes": 0,
      "opening": false,
      "wal_archive_max_bytes": 1073741824,
      "wal_archive_source": "server",
      "wal_archive_bytes": 0,
      "result_cache_bytes": 268435456,
      "result_cache_source": "server"
    }
  ],
  "default": "default",
  "data_dir": "kdb-data",
  "can_create": true,
  "wal_archive_default_bytes": 1073741824
}
$d = Invoke-RestMethod http://127.0.0.1:9900/api/databases
"default: $($d.default)   can_create: $($d.can_create)"
$d.databases | Select-Object name, tables, bytes, default | Format-Table -AutoSize
import requests

d = requests.get("http://127.0.0.1:9900/api/databases").json()
for db in d["databases"]:
    print(db["name"], db["tables"], db["bytes"])

POST /api/databases

Syntax:

POST /api/databases?name=<name>
POST /api/databases        body: {"name": "<name>"}
POST /api/databases        body: name=<name>  (Content-Type: application/x-www-form-urlencoded)

Creates an empty database: a new folder in the data folder.

ParameterWhereMeaningDefault
nameURL, form field, or JSON bodyThe new database's name: letters, digits, _ and -, at most 64 characters. Case is ignored.required
Answer fieldMeaning
nameThe name of the new database
createdtrue

HTTP: Create with ?name=, then with a JSON body

POST /api/databases?name=help_shop
###
POST /api/databases
Content-Type: application/json

{"name": "help_forum"}
###
POST /exec?db=help_shop&compat=0

CREATE TABLE items (id INT PRIMARY KEY, name STRING);
SHOW TABLES

Response:

POST /api/databases?name=help_shop

HTTP 200 (application/json)

{"name": "help_shop", "created": true}

POST /api/databases

HTTP 200 (application/json)

{"name": "help_forum", "created": true}

POST /exec?db=help_shop&compat=0

HTTP 200 (application/json)

{
  "query": "CREATE TABLE items (id INT PRIMARY KEY, name STRING);\nSHOW TABLES",
  "results": [
    {
      "statement": "CREATE TABLE items (\n  id INT PRIMARY KEY,\n  name STRING\n)",
      "columns": [],
      "dataset": [],
      "count": 0,
      "truncated": false,
      "rows_affected": 0,
      "message": "table 'items' created",
      "timings": {"parse_us": 15, "execute_us": 19598}
    },
    {
      "statement": "SHOW TABLES",
      "columns": [
        {"name": "name", "type": "STRING"},
        {"name": "kind", "type": "STRING"},
        {"name": "partition", "type": "STRING"},
        {"name": "columns", "type": "INT"},
        {"name": "rows", "type": "LONG"}
      ],
      "dataset": [
        ["items", "relational", "NONE", 2, 0]
      ],
      "count": 1,
      "truncated": false,
      "rows_affected": 0,
      "message": "",
      "timings": {"parse_us": 15, "execute_us": 13}
    }
  ],
  "timings": {"parse_us": 30, "execute_us": 19611}
}

HTTP: The same name again

POST /api/databases?name=help_twice
###
POST /api/databases?name=HELP_TWICE

Response:

POST /api/databases?name=help_twice

HTTP 200 (application/json)

{"name": "help_twice", "created": true}

POST /api/databases?name=HELP_TWICE

HTTP 409 (application/json)

{"error": "database 'help_twice' already exists"}

HTTP: A name with a space

POST /api/databases
Content-Type: application/x-www-form-urlencoded

name=my shop

Response:

HTTP 400 (application/json)

{"error": "bad database name 'my shop': use only letters, digits, _ and -"}
curl -s -X POST "http://127.0.0.1:9900/api/databases?name=shop"
Invoke-RestMethod -Method Post "http://127.0.0.1:9900/api/databases" -ContentType "application/json" -Body '{"name":"shop"}'
const res = await fetch("http://127.0.0.1:9900/api/databases", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "shop" }),
});
console.log(res.status, await res.json());

DELETE /api/databases/<name>

Syntax:

DELETE /api/databases/<name>

Drops a database. Its folder is not deleted: it moves to <data folder>/_trash/<time in ms>-<name>.

ParameterWhereMeaningDefault
<name>pathThe database to droprequired
Answer fieldMeaning
nameThe database
droppedtrue
moved_toThe folder in _trash where it went

HTTP: Create, drop, and the database is gone

POST /api/databases?name=help_scratch
###
DELETE /api/databases/help_scratch
###
GET /exec?db=help_scratch&query=SELECT 1

Response:

POST /api/databases?name=help_scratch

HTTP 200 (application/json)

{"name": "help_scratch", "created": true}

DELETE /api/databases/help_scratch

HTTP 200 (application/json)

{
  "name": "help_scratch",
  "dropped": true,
  "moved_to": "kdb-data/_trash/1792647850424-help_scratch"
}

GET /exec?db=help_scratch&query=SELECT 1

HTTP 404 (application/json)

{"error": "database 'help_scratch' does not exist; create it with CREATE DATABASE help_scratch"}

HTTP: A database that does not exist

DELETE /api/databases/help_never_made

Response:

HTTP 404 (application/json)

{"error": "database 'help_never_made' does not exist"}

HTTP: GET cannot drop

GET /api/databases/help_scratch

Response:

HTTP 405 (application/json)

{"error": "method GET is not allowed here"}
curl -s -X DELETE "http://127.0.0.1:9900/api/databases/shop"
(Invoke-RestMethod -Method Delete "http://127.0.0.1:9900/api/databases/shop").moved_to
using var http = new HttpClient();
var res = await http.DeleteAsync("http://127.0.0.1:9900/api/databases/shop");
Console.WriteLine($"{(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");