@rip-lang/db 0.10.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,320 +2,159 @@
2
2
 
3
3
  # Rip DB - @rip-lang/db
4
4
 
5
- > **Simple RestFUL HTTP server for concurrent DuckDB read and write queries**
5
+ > **A lightweight DuckDB HTTP server with the official DuckDB UI built in**
6
6
 
7
- Uses custom Zig-based DuckDB bindings (`lib/`) for fast, type-safe database access.
7
+ Rip DB turns any DuckDB database into a full-featured HTTP server — complete
8
+ with the official DuckDB UI for interactive queries, notebooks, and data
9
+ exploration. It connects to DuckDB via pure Bun FFI (no npm packages, no
10
+ native build step) and implements DuckDB's binary serialization protocol
11
+ to power the UI with native-speed data transfer.
8
12
 
9
- ## Overview
13
+ ## Quick Start
10
14
 
11
15
  ```bash
12
- # Install globally
13
- bun add -g @rip-lang/db
16
+ # Install DuckDB and Rip DB
17
+ brew install duckdb # macOS (or see duckdb.org for Linux)
18
+ bun add -g @rip-lang/db # Installs rip-db command
14
19
 
15
- # Run with in-memory database
16
- rip-db
17
-
18
- # Run with file-based database
19
- rip-db mydb.duckdb
20
-
21
- # Specify port
22
- rip-db mydb.duckdb --port=8080
23
- ```
24
-
25
- Or run directly with Rip:
26
-
27
- ```bash
28
- rip db.rip :memory: --port=4000
20
+ # Start the server
21
+ rip-db # In-memory database
22
+ rip-db mydata.duckdb # File-based database
23
+ rip-db mydata.duckdb --port 8080
29
24
  ```
30
25
 
31
- ## API Endpoints
26
+ Open **http://localhost:4213** for the official DuckDB UI.
32
27
 
33
- ### POST /sql
28
+ ## What It Does
34
29
 
35
- Execute any SQL statement (queries or mutations) with JSON body.
30
+ Rip DB sits between your clients and DuckDB, providing two interfaces:
36
31
 
37
- **Request:**
38
- ```json
39
- {
40
- "sql": "SELECT * FROM users WHERE id = ?",
41
- "params": [1]
42
- }
43
32
  ```
44
-
45
- **Response (JSONCompact format):**
46
- ```json
47
- {
48
- "meta": [
49
- {"name": "id", "type": "INTEGER"},
50
- {"name": "name", "type": "VARCHAR"},
51
- {"name": "email", "type": "VARCHAR"}
52
- ],
53
- "data": [
54
- [1, "Alice", "alice@example.com"]
55
- ],
56
- "rows": 1,
57
- "time": 0.001
58
- }
33
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
34
+ │ DuckDB UI │ binary │ rip-db │ FFI │ DuckDB │
35
+ │ (Browser) │◀────────▶│ (Bun) │◀────────▶│ (native) │
36
+ └─────────────┘ └─────────────┘ └─────────────┘
37
+
38
+ HTTP/S
39
+ (JSON)
40
+
41
+ ┌─────────────────┐
42
+ HTTP Clients │
43
+ │ (curl, apps) │
44
+ └─────────────────┘
59
45
  ```
60
46
 
61
- ### POST /
47
+ **DuckDB UI** — The official DuckDB notebook interface loads instantly in your
48
+ browser. Rip DB proxies the UI assets from ui.duckdb.org and implements the
49
+ full binary serialization protocol that the UI uses to communicate with DuckDB.
50
+ This includes query execution, SQL tokenization for syntax highlighting, and
51
+ Server-Sent Events for real-time catalog updates.
62
52
 
63
- Execute SQL with raw body (duck-ui compatible). Used by [duck-ui](https://demo.duckui.com).
53
+ **JSON API** Any HTTP client can execute SQL queries and receive JSON
54
+ responses. Use it from curl, your application code, or any language that
55
+ speaks HTTP.
64
56
 
65
- **Request:**
66
- ```
67
- POST / HTTP/1.1
68
- Content-Type: application/x-www-form-urlencoded
57
+ ## Features
69
58
 
70
- SELECT * FROM users WHERE id = 1
71
- ```
72
-
73
- **Response:** Same JSONCompact format as `/sql`.
74
-
75
- ### GET /health
59
+ - **Official DuckDB UI** Interactive notebooks, syntax highlighting, data exploration
60
+ - **Full binary protocol** — Native DuckDB UI serialization implemented in Rip
61
+ - **Pure Bun FFI** — Direct calls to DuckDB's C API using the modern chunk-based interface
62
+ - **Zero npm dependencies for DuckDB** — Uses the system-installed DuckDB library
63
+ - **Parameterized queries** — Prepared statements with type-safe parameter binding
64
+ - **Complete type support** — All DuckDB types handled natively, including UUID, DECIMAL, TIMESTAMP, LIST, STRUCT, MAP
65
+ - **DECIMAL precision preserved** — Exact string representation, never converted to floating point
66
+ - **Timestamps as UTC** — All timestamps returned as JavaScript Date objects (UTC)
67
+ - **Powered by @rip-lang/api** — Fast, lightweight HTTP server framework
68
+ - **Single binary** — One `rip-db` command, one process, one database
76
69
 
77
- Simple health check (no database query).
78
-
79
- ```json
80
- { "ok": true }
81
- ```
82
-
83
- ### GET /status
84
-
85
- Database info including table list.
86
-
87
- ```json
88
- {
89
- "ok": true,
90
- "database": "mydb.duckdb",
91
- "tables": ["users", "orders", "products"],
92
- "time": "2026-02-02T14:30:00.000Z"
93
- }
94
- ```
95
-
96
- ### GET /tables
97
-
98
- List all tables in the database.
99
-
100
- ```json
101
- {
102
- "ok": true,
103
- "tables": ["users", "orders", "products"]
104
- }
105
- ```
106
-
107
- ### GET /schema/:table
108
-
109
- Get schema for a specific table.
110
-
111
- ```json
112
- {
113
- "ok": true,
114
- "table": "users",
115
- "columns": [
116
- { "column_name": "id", "data_type": "INTEGER", "is_nullable": "NO" },
117
- { "column_name": "name", "data_type": "VARCHAR", "is_nullable": "YES" },
118
- { "column_name": "email", "data_type": "VARCHAR", "is_nullable": "YES" }
119
- ]
120
- }
121
- ```
70
+ ## JSON API
122
71
 
123
- ### GET /ui
72
+ For programmatic access from any HTTP client.
124
73
 
125
- Built-in SQL console. Open in browser:
126
-
127
- ```
128
- http://localhost:4000/ui
129
- ```
130
-
131
- ## DuckDB UI Compatibility
132
-
133
- rip-db implements the official DuckDB UI binary protocol, making it compatible with
134
- DuckDB's built-in UI. This means you can use the beautiful DuckDB UI with rip-db!
135
-
136
- ### Binary Protocol Endpoints
137
-
138
- | Endpoint | Method | Purpose |
139
- |-------------------|--------|----------------------------|
140
- | `/ddb/run` | POST | Execute SQL (binary result)|
141
- | `/ddb/interrupt` | POST | Cancel running query |
142
- | `/ddb/tokenize` | POST | Syntax highlighting |
143
- | `/info` | GET | Version info |
144
-
145
- ### How It Works
146
-
147
- ```
148
- ┌─────────────┐ Binary Protocol ┌─────────────┐
149
- │ DuckDB UI │ ◀─────────────────────▶│ rip-db │
150
- │ (Browser) │ │ Server │
151
- └─────────────┘ └─────────────┘
152
- ```
153
-
154
- The UI sends SQL to `/ddb/run`, rip-db executes it and returns results in DuckDB's
155
- binary format. The UI has no idea it's talking to rip-db instead of native DuckDB!
156
-
157
- For protocol details, see [PROTOCOL.md](./PROTOCOL.md).
158
-
159
- ## Examples
160
-
161
- ### Create a table
162
-
163
- ```bash
164
- curl -X POST http://localhost:4000/sql \
165
- -H "Content-Type: application/json" \
166
- -d '{"sql": "CREATE TABLE users (id INTEGER PRIMARY KEY, name VARCHAR, email VARCHAR)"}'
167
- ```
74
+ ### POST /sql
168
75
 
169
- ### Insert data
76
+ Execute SQL with optional parameters:
170
77
 
171
78
  ```bash
172
- curl -X POST http://localhost:4000/sql \
79
+ curl -X POST http://localhost:4213/sql \
173
80
  -H "Content-Type: application/json" \
174
- -d '{"sql": "INSERT INTO users VALUES (?, ?, ?)", "params": [1, "Alice", "alice@example.com"]}'
81
+ -d '{"sql": "SELECT * FROM users WHERE id = $1", "params": [1]}'
175
82
  ```
176
83
 
177
- ### Query with parameters
178
-
179
- ```bash
180
- curl -X POST http://localhost:4000/sql \
181
- -H "Content-Type: application/json" \
182
- -d '{"sql": "SELECT * FROM users WHERE name LIKE ?", "params": ["%Ali%"]}'
183
- ```
84
+ ### POST /
184
85
 
185
- ### Aggregations
86
+ Execute raw SQL (body is the query):
186
87
 
187
88
  ```bash
188
- curl -X POST http://localhost:4000/sql \
189
- -H "Content-Type: application/json" \
190
- -d '{"sql": "SELECT COUNT(*) as total, AVG(age) as avg_age FROM users"}'
89
+ curl -X POST http://localhost:4213/ -d "SELECT 42 as answer"
191
90
  ```
192
91
 
193
- ## Response Format
194
-
195
- All responses use JSONCompact format (compatible with DuckDB HTTP Server and duck-ui):
196
-
197
- | Field | Type | Description |
198
- |-------|------|-------------|
199
- | `meta` | object[] | Column metadata: `[{name, type}, ...]` |
200
- | `data` | any[][] | Row data as arrays |
201
- | `rows` | number | Number of rows returned |
202
- | `time` | number | Execution time in seconds |
203
- | `error` | string | Error message (on failure) |
92
+ Response format:
204
93
 
205
- **Success response:**
206
94
  ```json
207
95
  {
208
- "meta": [{"name": "id", "type": "INTEGER"}, {"name": "name", "type": "VARCHAR"}],
209
- "data": [[1, "Alice"], [2, "Bob"]],
210
- "rows": 2,
96
+ "meta": [{"name": "answer", "type": "INTEGER"}],
97
+ "data": [[42]],
98
+ "rows": 1,
211
99
  "time": 0.001
212
100
  }
213
101
  ```
214
102
 
215
- **Error response:**
216
- ```json
217
- {"error": "Table 'foo' does not exist"}
218
- ```
219
-
220
- This format is compatible with [duck-ui](https://demo.duckui.com) and other DuckDB HTTP clients.
221
-
222
- ## Performance
103
+ ### Other Endpoints
223
104
 
224
- Our custom Zig-based DuckDB bindings deliver exceptional performance:
105
+ | Endpoint | Method | Description |
106
+ |----------|--------|-------------|
107
+ | `/health` | GET | Health check |
108
+ | `/tables` | GET | List all tables |
109
+ | `/schema/:table` | GET | Table schema |
225
110
 
226
- | Operation | Latency | Throughput |
227
- |-----------|---------|------------|
228
- | Point lookup (WHERE id=?) | 0.09ms | 11,000 qps |
229
- | Range scan (LIMIT 100) | 0.20ms | 5,000 qps |
230
- | Aggregation (COUNT/AVG/MAX) | 0.12ms | 8,400 qps |
231
- | JOIN + GROUP BY | 0.25ms | 4,000 qps |
232
- | INSERT (single row) | 0.13ms | 7,700 qps |
111
+ ## DuckDB UI
233
112
 
234
- ### Comparison to MySQL/PostgreSQL
113
+ The official DuckDB UI is available at the root URL. It provides:
235
114
 
236
- | Operation | Our DuckDB | MySQL/PG (localhost) | Speedup |
237
- |-----------|------------|----------------------|---------|
238
- | Point lookup | **0.09ms** | 0.3-1ms | 3-10x faster |
239
- | Range scan | **0.20ms** | 1-5ms | 5-25x faster |
240
- | Aggregation | **0.12ms** | 2-20ms | 17-170x faster |
241
- | JOIN + GROUP BY | **0.25ms** | 5-50ms | 20-200x faster |
242
- | INSERT | **0.13ms** | 0.5-2ms | 4-15x faster |
115
+ - **SQL Notebooks** Write and execute queries in a notebook interface
116
+ - **Syntax Highlighting** — Real-time SQL tokenization as you type
117
+ - **Data Exploration** Browse tables, schemas, and query results
118
+ - **Multiple Databases** Attach and query across databases
243
119
 
244
- **Why so fast?**
245
- - **Zero network latency** DuckDB runs in-process
246
- - **No connection overhead**No auth, handshake, or protocol parsing
247
- - **Columnar engine** — DuckDB is optimized for analytical queries
248
- - **Direct FFI** — Zig bindings call DuckDB's C API directly
120
+ The UI communicates with Rip DB using DuckDB's binary serialization protocol,
121
+ which Rip DB implements in full. This means the UI works exactly as it does
122
+ with the official `duckdb -ui` command same features, same performance.
249
123
 
250
- ## Architecture & Concurrency
124
+ ## How It Works
251
125
 
252
- ### Single Process, High Concurrency
126
+ Rip DB is built from three files:
253
127
 
254
- DuckDB only allows **one process** to access a database file at a time (for write access). This means rip-db runs as a **single process** — it does NOT use rip-server's multi-worker model.
128
+ | File | Lines | Role |
129
+ |------|-------|------|
130
+ | `db.rip` | ~390 | HTTP server — routes, middleware, UI proxy |
131
+ | `lib/duckdb.mjs` | ~800 | FFI driver — modern chunk-based DuckDB C API |
132
+ | `lib/duckdb-binary.rip` | ~550 | Binary serializer — DuckDB UI protocol |
255
133
 
256
- However, rip-db handles **high concurrency** through two mechanisms:
134
+ The FFI driver uses DuckDB's modern chunk-based API (`duckdb_fetch_chunk`,
135
+ `duckdb_vector_get_data`) to read query results directly from columnar memory.
136
+ No deprecated per-value functions, no intermediate copies. For complex types
137
+ like DECIMAL, ENUM, LIST, and STRUCT, it uses DuckDB's logical type
138
+ introspection to read values with full fidelity.
257
139
 
258
- ```
259
- ┌─────────────────────────────────────────────────────────┐
260
- │ rip-db (single process) │
261
- ├─────────────────────────────────────────────────────────┤
262
- │ Bun HTTP Server (async I/O) │
263
- │ ├─ Request 1 ──┐ │
264
- │ ├─ Request 2 ──┼── thousands of concurrent │
265
- │ ├─ Request 3 ──┤ connections via event loop │
266
- │ └─ Request N ──┘ │
267
- ├─────────────────────────────────────────────────────────┤
268
- │ rip-api (routes, middleware) │
269
- ├─────────────────────────────────────────────────────────┤
270
- │ DuckDB (multi-threaded query engine) │
271
- │ └─ Queries parallelized across CPU cores │
272
- └─────────────────────────────────────────────────────────┘
273
- ```
274
-
275
- **Layer 1: Bun's Async I/O**
276
- - Single JavaScript thread with event loop
277
- - Handles thousands of concurrent HTTP connections
278
- - Non-blocking I/O — while one request waits on DuckDB, others are processed
279
- - No thread-per-request overhead
280
-
281
- **Layer 2: DuckDB's Multi-Threading**
282
- - Queries are parallelized across all CPU cores internally
283
- - MVCC for concurrent reads
284
- - Writes serialized automatically by DuckDB
285
-
286
- ### Why Not Multi-Process?
287
-
288
- | Approach | Works with DuckDB? | Why |
289
- |----------|-------------------|-----|
290
- | Multi-process (rip-server) | ❌ No | DuckDB only allows one process with write access |
291
- | Multi-threaded | ✅ Yes | DuckDB handles this internally |
292
- | Async I/O | ✅ Yes | Bun's event loop handles concurrent connections |
293
-
294
- ### The Result
295
-
296
- Even as a single process, rip-db can handle:
297
- - **Thousands of concurrent connections** (Bun async I/O)
298
- - **Parallel query execution** (DuckDB multi-threading)
299
- - **High throughput** (11,000+ queries/sec on typical hardware)
300
-
301
- This is the correct architecture for DuckDB — single process, high concurrency.
140
+ The binary serializer implements the same wire protocol that DuckDB's official
141
+ UI extension uses. It handles all DuckDB types including native 16-byte UUID
142
+ serialization, uint64-aligned validity bitmaps, and proper timestamp encoding.
302
143
 
303
144
  ## Requirements
304
145
 
305
- - Bun 1.0+
306
- - rip-lang 2.0+
307
- - @rip-lang/api 0.5+
146
+ - **Bun** 1.0+
147
+ - **DuckDB** library installed on the system
148
+ - macOS: `brew install duckdb`
149
+ - Linux: Install from [duckdb.org](https://duckdb.org/docs/installation)
150
+ - **rip-lang** 2.8+ (installed automatically as a dependency)
308
151
 
309
- ## Building from Source
310
-
311
- To rebuild the native DuckDB bindings (requires Zig 0.15+ and DuckDB):
152
+ Set `DUCKDB_LIB_PATH` if DuckDB is not in a standard location:
312
153
 
313
154
  ```bash
314
- ./src/build.sh
155
+ DUCKDB_LIB_PATH=/path/to/libduckdb.dylib rip-db
315
156
  ```
316
157
 
317
- This creates `lib/{platform}-{arch}/duckdb.node` for your platform.
318
-
319
158
  ## License
320
159
 
321
160
  MIT
package/bin/rip-db CHANGED
@@ -1,17 +1,12 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { execSync } from 'child_process';
4
- import { fileURLToPath } from 'url';
3
+ import { execFileSync } from 'child_process';
5
4
  import { dirname, join } from 'path';
6
5
 
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = dirname(__filename);
9
-
10
- const dbRip = join(__dirname, '..', 'db.rip');
11
- const args = process.argv.slice(2).join(' ');
6
+ const dbRip = join(dirname(new URL(import.meta.url).pathname), '..', 'db.rip');
12
7
 
13
8
  try {
14
- execSync(`rip ${dbRip} ${args}`, { stdio: 'inherit' });
15
- } catch (error) {
16
- process.exit(error.status || 1);
9
+ execFileSync('rip', [dbRip, ...process.argv.slice(2)], { stdio: 'inherit' });
10
+ } catch (e) {
11
+ process.exit(e.status || 1);
17
12
  }