Quickstart: Query in the browser

v3.0.0

Open the sandbox, run your first statements against a live KDBS server, and watch an assertion refuse a write.

In this article

The sandbox runs SQL against a real KDBS server in your browser, with nothing to install. It connects to a dedicated sandbox server with four sample tables already loaded. This page explains what is on the screen, what the sandbox allows, and walks through a few statements with their real output.

What you get

The sandbox uses one dedicated kds_server that listens on an internal port and is not reachable from outside. Every visitor connects to the same server, so everyone sees the same tables: a table you create is visible to others, and changes to the sample tables are seen by all. Tables are kept until the sandbox server restarts, which starts it again from the sample tables. Do not store anything you want to keep.

The instance starts with four tables on one theme, flights. airlines and passengers are the reference tables; flights and tickets point at them through foreign keys.

CREATE TABLE airlines (id int64, code char(2), name varchar, country varchar) BTREE;
CREATE TABLE passengers (id int64, name varchar, member bool, joined_on date) BTREE;

CREATE TABLE flights (id int64, airline_id int64 REFERENCES airlines, code char(6), origin char(3), dest char(3), departs timestamp, aircraft varchar, base_fare decimal(10,2)) BTREE;

CREATE TABLE tickets (id int64, flight_id int64 REFERENCES flights, passenger_id int64 REFERENCES passengers, seat char(3), baggage_kg int64, checked_in bool, note varchar NULL) BTREE;
CREATE INDEX ix_tickets_passenger ON tickets(passenger_id) COVERING (flight_id, baggage_kg);
CREATE ASSERTION seat_limit ON tickets GROUP BY (flight_id) CHECK COUNT(*) <= 4;
CREATE ASSERTION baggage_cap ON tickets GROUP BY (passenger_id) CHECK SUM(baggage_kg) <= 46;
TableRowsWhat it shows
airlines3: KE Korean Air, OZ Asiana Airlines, LJ Jin AirThe simplest table. Fixed-width text (char(2) code) beside variable text. The INSERT leaves out id, so the engine issues 1, 2, 3.
passengers8, from Kim Minjun to Han Taeyang; four are membersA flag (bool, shown as 1 or 0) and a date.
flights4 flights, such as KE0101 from ICN to NRTA foreign key to airlines, char codes, a timestamp departure and an exact fare (decimal(10,2)), such as 212.50.
tickets9 tickets: 4 on flight 1, 2 on flight 2, 3 on flight 3, none on flight 4Two foreign keys (to flights and passengers), a nullable note, and the secondary index ix_tickets_passenger. Eight tickets have no note, so their note is NULL; one says wheelchair. Two assertions: seat_limit allows at most four tickets per flight, and flight 1, KE0101, is full. baggage_cap allows at most 46 kg of checked baggage per passenger, and passenger 1, Kim Minjun, is at 46 (23 kg on flight 1 and 23 kg on flight 3).

id is the primary key of each table. Every sample INSERT leaves it out, so each table’s keys start at 1.

The screen

The sandbox has a terminal on the left and a side panel on the right. The side panel has three tabs: Tables, History and Examples. Tables is the first tab.

PartWhat it does
Terminal (left)Type a statement at the kdbs> prompt. Enter runs it; Shift+Enter adds a line. Up and Down walk through earlier input. A status badge shows whether the connection is starting, ready or running a statement. Clear empties the screen; Restore samples drops and recreates the four sample tables with their index and assertions, for everyone. Tables you created stay.
Tables tabLists the tables on the server and the system catalog (sys.*). Expand a table to see its columns and types; the primary key is marked PK. Click a column name to insert it at the cursor. The SELECT button runs SELECT * FROM <table> LIMIT 100. Refresh reloads the list; it also reloads after a successful CREATE, DROP or ALTER.
History tabLists the statements you ran, newest first, with their result and time. Click one to load it into the prompt. History is kept in your browser.
Examples tabReady-made statements in five groups: Basics, Invariants, Flights, Transactions and Introspection.

When a statement is refused at a known position, the terminal draws a ^ under the offending token.

Sandbox commands

These words are handled by the page and are not sent to the server.

CommandWhat it does
helpLists the statements the sandbox accepts and these commands.
tablesRuns SHOW TABLES.
clearClears the screen. Ctrl+L does the same.
resetRestores the sample tables, the same as Restore samples. It is refused, and nothing is dropped, while a table of yours has a foreign key into a sample table. Drop that table first.

Limits

The sandbox is a demonstration, so it limits what one visitor can do.

  • The page connects over a WebSocket and falls back to plain HTTP. If your network lets the page load but blocks the WebSocket, the page switches to plain HTTP after a few seconds and the badge reads Connected (HTTP). Everything works the same way. Add ?transport=http to the address to force it.
  • One statement at a time. A pasted script is split on ; and the statements are sent one after another.
  • About 5 statements per second, with a burst of up to 20. Faster input is refused with “Too many statements; slow down.”
  • A statement that runs longer than 10 seconds resets your connection. The connection cannot be reused safely after a timeout, so the sandbox opens a new one. An open transaction is rolled back.
  • The session closes after 10 minutes without input, and after 60 minutes in total. Up to 3 sessions per visitor address are allowed.
  • A statement may be up to 4,096 bytes. At most 500 rows of a result are shown; the row count line says how many there were.
  • An explicit transaction is rolled back after 30 seconds. The sandbox sets txn_lifetime_ceiling_ms = 30000.
  • Some statements are disabled: STOP, SYNC, any SET other than SET ISOLATION LEVEL and SET DURABILITY, and CREATE NAMESPACE / DROP NAMESPACE. They are refused with a message that starts ERR sandbox:.
  • The server is small: one core, a buffer pool of 4,096 pages (32 MiB), at most 256 rows per INSERT, and a data file of at most 256 MiB. A server that reaches the file limit stops and is started again from the sample tables.
  • Visitors share the server. A long-running transaction of one visitor can delay others, which is why an explicit transaction is rolled back after 30 seconds.

Walk through a few statements

Each step shows what you type and what the terminal prints. Every result line is followed by a timing in the real terminal; the timings are left out here because they vary.

Note. The server is shared, so ids and rows in your output can differ if another visitor changed the sample tables. Run reset first to start from the original rows.

1. List the tables

Type tables. The terminal runs SHOW TABLES for you:

kdbs> SHOW TABLES
types objects columns tables indexes patterns access_stats cabins fkeys assertions airlines passengers flights tickets

The first ten names are system tables. The last four are the sample tables.

2. Read every flight

SELECT * FROM flights;
kdbs> SELECT * FROM flights
┌────┬────────────┬────────┬────────┬──────┬─────────────────────┬──────────┬───────────┐
│ id │ airline_id │ code   │ origin │ dest │ departs             │ aircraft │ base_fare │
├────┼────────────┼────────┼────────┼──────┼─────────────────────┼──────────┼───────────┤
│  1 │          1 │ KE0101 │ ICN    │ NRT  │ 2026-11-02 08:30:00 │ A321     │    212.50 │
│  2 │          2 │ OZ0202 │ GMP    │ CJU  │ 2026-11-02 10:05:00 │ A330     │     64.00 │
│  3 │          1 │ KE0303 │ ICN    │ SFO  │ 2026-11-03 19:40:00 │ B777     │   1180.00 │
│  4 │          3 │ LJ0404 │ PUS    │ KIX  │ 2026-11-04 13:15:00 │ B737     │     98.90 │
└────┴────────────┴────────┴────────┴──────┴─────────────────────┴──────────┴───────────┘
(4 rows)

3. Insert a row and let the engine issue the key

The table has four columns. Three values leave the key out, and the reply returns the issued key as id=.

INSERT INTO passengers VALUES ('Oh Jisoo', 0, '2026-10-13');
kdbs> INSERT INTO passengers VALUES ('Oh Jisoo', 0, '2026-10-13')
INSERTED oid=4005 id=9 page=133 slot=8

oid is the table’s internal id; page and slot are where the row was placed. See INSERT.

4. Join flights to their airlines

SELECT a.name, f.code, f.origin, f.dest FROM flights AS f JOIN airlines AS a ON f.airline_id = a.id;
kdbs> SELECT a.name, f.code, f.origin, f.dest FROM flights AS f JOIN airlines AS a ON f.airline_id = a.id
┌─────────────────┬────────┬──────────┬────────┐
│ a.name          │ f.code │ f.origin │ f.dest │
├─────────────────┼────────┼──────────┼────────┤
│ Korean Air      │ KE0101 │ ICN      │ NRT    │
│ Asiana Airlines │ OZ0202 │ GMP      │ CJU    │
│ Korean Air      │ KE0303 │ ICN      │ SFO    │
│ Jin Air         │ LJ0404 │ PUS      │ KIX    │
└─────────────────┴────────┴──────────┴────────┘
(4 rows)

The join runs in the order written: for each flight, the engine looks up the airline by primary key.

5. Aggregate per flight

SELECT flight_id, COUNT(*), SUM(baggage_kg) FROM tickets GROUP BY flight_id;
kdbs> SELECT flight_id, COUNT(*), SUM(baggage_kg) FROM tickets GROUP BY flight_id
┌───────────┬──────────┬─────────────────┐
│ flight_id │ count(*) │ sum(baggage_kg) │
├───────────┼──────────┼─────────────────┤
│         1 │        4 │              58 │
│         2 │        2 │              10 │
│         3 │        3 │              76 │
└───────────┴──────────┴─────────────────┘
(3 rows)

Flight 1 has four tickets, the most seat_limit allows. Flight 2 has two, so it has room. Flight 4 has no tickets, so it does not appear.

6. Let an assertion refuse a write

A fifth ticket on flight 1 is refused before it is written. A ticket on flight 2, which has room, is admitted:

kdbs> INSERT INTO tickets VALUES (1, 6, '16B', 0, 0, NULL)
ERR ASSERTION_VIOLATION retryable=0 assertion "seat_limit" group (flight_id=1): COUNT(*) would exceed bound 4
kdbs> INSERT INTO tickets VALUES (2, 7, '04A', 0, 0, NULL)
INSERTED oid=4019 id=10 page=139 slot=9

Passenger 1 has checked 46 kg in total, which is the baggage_cap limit. Five more kilograms on any flight would bring the total to 51:

kdbs> INSERT INTO tickets VALUES (4, 1, '10A', 5, 0, NULL)
ERR ASSERTION_VIOLATION retryable=0 assertion "baggage_cap" group (passenger_id=1): SUM(baggage_kg) would exceed bound 46

retryable=0 means a retry would fail the same way. SHOW ASSERTIONS shows each assertion’s counters:

kdbs> SHOW ASSERTIONS
assertions=2
assertion_id=1 name=seat_limit rel=tickets enforcing=1 checks=12 violations=1 reserved=10 aborted=0 def=CREATE ASSERTION seat_limit ON tickets GROUP BY (flight_id) CHECK COUNT(*) <= 4
assertion_id=2 name=baggage_cap rel=tickets enforcing=1 checks=11 violations=1 reserved=10 aborted=0 def=CREATE ASSERTION baggage_cap ON tickets GROUP BY (passenger_id) CHECK SUM(baggage_kg) <= 46

Each assertion has refused one write.

See CREATE ASSERTION.

7. Join three tables

Each ticket points at a flight and a passenger. This join reads the tickets of flight KE0101 with the passenger’s name:

SELECT f.code, p.name, t.seat, t.checked_in FROM tickets AS t JOIN flights AS f ON t.flight_id = f.id JOIN passengers AS p ON t.passenger_id = p.id WHERE f.code = 'KE0101';
kdbs> SELECT f.code, p.name, t.seat, t.checked_in FROM tickets AS t JOIN flights AS f ON t.flight_id = f.id JOIN passengers AS p ON t.passenger_id = p.id WHERE f.code = 'KE0101'
┌────────┬─────────────┬────────┬──────────────┐
│ f.code │ p.name      │ t.seat │ t.checked_in │
├────────┼─────────────┼────────┼──────────────┤
│ KE0101 │ Kim Minjun  │ 12A    │            1 │
│ KE0101 │ Lee Seoyeon │ 12B    │            1 │
│ KE0101 │ Park Jiho   │ 14C    │            0 │
│ KE0101 │ Choi Yuna   │ 15A    │            0 │
└────────┴─────────────┴────────┴──────────────┘
(4 rows)

The four tickets are the four seats that make seat_limit refuse a fifth.

8. Reach a baggage limit and a reference

Passenger 2, Lee Seoyeon, has 15 kg on flight 1, so baggage_cap leaves 31 kg. A ticket for 31 kg on flight 4 is admitted, which puts the passenger at the limit, and one more kilogram is refused:

kdbs> INSERT INTO tickets VALUES (4, 2, '07C', 31, 0, 'extra bag')
INSERTED oid=4019 id=11 page=139 slot=10
kdbs> INSERT INTO tickets VALUES (4, 2, '07D', 1, 0, NULL)
ERR ASSERTION_VIOLATION retryable=0 assertion "baggage_cap" group (passenger_id=2): SUM(baggage_kg) would exceed bound 46

A foreign key refuses a ticket for a flight that does not exist. The terminal marks the position of the bad value:

kdbs> INSERT INTO tickets VALUES (9, 1, '01A', 0, 0, NULL)
                                  ^
ERR FK_VIOLATION retryable=0 'flight_id' references row id=9 of 'flights', which does not exist (byte 28)

9. Filter on NULL and on a timestamp

IS NOT NULL finds the tickets that carry a note, including the one from step 8:

kdbs> SELECT id, seat, note FROM tickets WHERE note IS NOT NULL
┌────┬──────┬────────────┐
│ id │ seat │ note       │
├────┼──────┼────────────┤
│  6 │ 03E  │ wheelchair │
│ 11 │ 07C  │ extra bag  │
└────┴──────┴────────────┘
(2 rows)

A row whose note is NULL never matches a comparison, so this query returns the same two rows:

kdbs> SELECT id, seat, note FROM tickets WHERE note >= 'a'
┌────┬──────┬────────────┐
│ id │ seat │ note       │
├────┼──────┼────────────┤
│  6 │ 03E  │ wheelchair │
│ 11 │ 07C  │ extra bag  │
└────┴──────┴────────────┘
(2 rows)

A timestamp is compared with a quoted 'YYYY-MM-DD HH:MM:SS' literal:

kdbs> SELECT code, origin, dest, departs FROM flights WHERE departs >= '2026-11-03 00:00:00'
┌────────┬────────┬──────┬─────────────────────┐
│ code   │ origin │ dest │ departs             │
├────────┼────────┼──────┼─────────────────────┤
│ KE0303 │ ICN    │ SFO  │ 2026-11-03 19:40:00 │
│ LJ0404 │ PUS    │ KIX  │ 2026-11-04 13:15:00 │
└────────┴────────┴──────┴─────────────────────┘
(2 rows)

10. Try to change a primary key

The primary key is the row’s identity and cannot be updated. The refusal names the byte position, and the terminal marks it:

kdbs> UPDATE flights SET id = 9 WHERE id = 1
                         ^
ERR primary-key column 'id' cannot be updated at byte 19; it is the tuple's identity, not a field of it

11. Roll back a change

Inside a transaction, the change is visible to you. After ROLLBACK it is gone.

kdbs> BEGIN
BEGIN
kdbs> UPDATE tickets SET seat = '99Z' WHERE id = 1
UPDATED 1
kdbs> SELECT * FROM tickets WHERE id = 1
┌────┬───────────┬──────────────┬──────┬────────────┬────────────┬──────┐
│ id │ flight_id │ passenger_id │ seat │ baggage_kg │ checked_in │ note │
├────┼───────────┼──────────────┼──────┼────────────┼────────────┼──────┤
│  1 │         1 │            1 │ 99Z  │         23 │          1 │ NULL │
└────┴───────────┴──────────────┴──────┴────────────┴────────────┴──────┘
(1 row)
kdbs> ROLLBACK
ROLLBACK
kdbs> SELECT * FROM tickets WHERE id = 1
┌────┬───────────┬──────────────┬──────┬────────────┬────────────┬──────┐
│ id │ flight_id │ passenger_id │ seat │ baggage_kg │ checked_in │ note │
├────┼───────────┼──────────────┼──────┼────────────┼────────────┼──────┤
│  1 │         1 │            1 │ 12A  │         23 │          1 │ NULL │
└────┴───────────┴──────────────┴──────┴────────────┴────────────┴──────┘
(1 row)

Try it. Pick Examples → Introspection → Recorded query patterns to see the statement shapes the server has recorded so far (SHOW PATTERNS). Type reset to restore the sample tables.

Next steps