Skip to content

Commit 23b2d24

Browse files
committed
feat: add database capabilities
1 parent 8277d72 commit 23b2d24

19 files changed

Lines changed: 204 additions & 3 deletions

docs/1.guide/2.capabilities.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
icon: ph:check-circle-duotone
3+
---
4+
5+
# Database Capabilities
6+
7+
> Query database feature support at runtime with `db.capabilities`.
8+
9+
Different databases support different features. The `capabilities` property lets you check what the current database supports, enabling you to write portable code that adapts to the underlying database.
10+
11+
## Usage
12+
13+
Access capabilities directly on the database instance:
14+
15+
```ts
16+
import { createDatabase } from "db0";
17+
import sqlite from "db0/connectors/better-sqlite3";
18+
19+
const db = createDatabase(sqlite({}));
20+
21+
console.log(db.capabilities);
22+
// { supportsJSON: true, supportsBooleans: false, supportsArrays: false, ... }
23+
24+
if (db.capabilities.supportsArrays) {
25+
// Use PostgreSQL array syntax
26+
} else {
27+
// Use JSON or comma-separated values
28+
}
29+
```
30+
31+
## Available Flags
32+
33+
| Flag | Description |
34+
| ----------------------- | ------------------------------------------------ |
35+
| `supportsJSON` | Native JSON column type and functions |
36+
| `supportsBooleans` | Native boolean type (not 0/1 integers) |
37+
| `supportsArrays` | Native array column type |
38+
| `supportsDates` | Native date/timestamp types with timezone |
39+
| `supportsUUIDs` | Native UUID column type |
40+
| `supportsTransactions` | Transaction support with BEGIN/COMMIT/ROLLBACK |
41+
| `supportsBatch` | Batch execution of multiple statements |
42+
43+
## Capabilities by Connector
44+
45+
| Connector | JSON | Bool | Array | Date | UUID | Tx | Batch |
46+
| ----------------------- |:----:|:----:|:-----:|:----:|:----:|:--:|:-----:|
47+
| better-sqlite3 ||||||||
48+
| sqlite3 ||||||||
49+
| bun-sqlite ||||||||
50+
| node-sqlite ||||||||
51+
| libsql ||||||||
52+
| cloudflare-d1 ||||||||
53+
| postgresql ||||||||
54+
| pglite ||||||||
55+
| mysql2 ||||||||
56+
| planetscale ||||||||
57+
58+
> [!NOTE]
59+
> Cloudflare Hyperdrive connectors inherit the capabilities of their underlying database (PostgreSQL or MySQL).
60+
61+
## Use Cases
62+
63+
### Conditional Feature Usage
64+
65+
Adapt your queries based on database support:
66+
67+
```ts
68+
function storeList(db: Database, items: string[]) {
69+
if (db.capabilities.supportsArrays) {
70+
return db.sql`INSERT INTO data (items) VALUES (${items})`;
71+
} else {
72+
return db.sql`INSERT INTO data (items) VALUES (${JSON.stringify(items)})`;
73+
}
74+
}
75+
```
76+
77+
### Runtime Validation
78+
79+
Ensure the database meets your application requirements:
80+
81+
```ts
82+
function initDatabase(db: Database) {
83+
if (!db.capabilities.supportsTransactions) {
84+
throw new Error("This application requires transaction support");
85+
}
86+
}
87+
```
88+
89+
### Feature Detection in Libraries
90+
91+
Build database-agnostic libraries that adapt automatically:
92+
93+
```ts
94+
export function createRepository(db: Database) {
95+
return {
96+
saveTags(id: string, tags: string[]) {
97+
const value = db.capabilities.supportsArrays
98+
? tags
99+
: tags.join(",");
100+
return db.sql`UPDATE items SET tags = ${value} WHERE id = ${id}`;
101+
},
102+
};
103+
}
104+
```
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { DatabaseCapabilities } from "../../types.ts";
2+
3+
export const sqliteCapabilities: DatabaseCapabilities = {
4+
supportsJSON: true,
5+
supportsBooleans: false,
6+
supportsArrays: false,
7+
supportsDates: false,
8+
supportsUUIDs: false,
9+
supportsTransactions: true,
10+
supportsBatch: true,
11+
};
12+
13+
export const postgresqlCapabilities: DatabaseCapabilities = {
14+
supportsJSON: true,
15+
supportsBooleans: true,
16+
supportsArrays: true,
17+
supportsDates: true,
18+
supportsUUIDs: true,
19+
supportsTransactions: true,
20+
supportsBatch: true,
21+
};
22+
23+
export const mysqlCapabilities: DatabaseCapabilities = {
24+
supportsJSON: true,
25+
supportsBooleans: true,
26+
supportsArrays: false,
27+
supportsDates: true,
28+
supportsUUIDs: false,
29+
supportsTransactions: true,
30+
supportsBatch: true,
31+
};

src/connectors/better-sqlite3.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Database from "better-sqlite3";
44
import type { Connector, Primitive } from "db0";
55
import type { Statement as RawStatement } from "better-sqlite3";
66
import { BoundableStatement } from "./_internal/statement.ts";
7+
import { sqliteCapabilities } from "./_internal/capabilities.ts";
78

89
export interface ConnectorOptions {
910
cwd?: string;
@@ -35,6 +36,7 @@ export default function sqliteConnector(
3536
return {
3637
name: "sqlite",
3738
dialect: "sqlite",
39+
capabilities: sqliteCapabilities,
3840
getInstance: () => getDB(),
3941
exec: (sql) => getDB().exec(sql),
4042
prepare: (sql) => new StatementWrapper(() => getDB().prepare(sql)),

src/connectors/bun-sqlite.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mkdirSync } from "node:fs";
33
import { Database, Statement as RawStatement } from "bun:sqlite";
44
import type { Connector, Primitive } from "db0";
55
import { BoundableStatement } from "./_internal/statement.ts";
6+
import { sqliteCapabilities } from "./_internal/capabilities.ts";
67

78
export interface ConnectorOptions {
89
cwd?: string;
@@ -34,6 +35,7 @@ export default function bunSqliteConnector(
3435
return {
3536
name: "sqlite",
3637
dialect: "sqlite",
38+
capabilities: sqliteCapabilities,
3739
getInstance: () => getDB(),
3840
exec: (sql) => getDB().exec(sql),
3941
prepare: (sql) => new StatementWrapper(getDB().prepare(sql)),

src/connectors/cloudflare-d1.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
} from "@cloudflare/workers-types";
55
import type { Connector, Primitive } from "db0";
66
import { BoundableStatement } from "./_internal/statement.ts";
7+
import { sqliteCapabilities } from "./_internal/capabilities.ts";
78

89
export interface ConnectorOptions {
910
bindingName?: string;
@@ -28,6 +29,7 @@ export default function cloudflareD1Connector(
2829
return {
2930
name: "cloudflare-d1",
3031
dialect: "sqlite",
32+
capabilities: sqliteCapabilities,
3133
getInstance: () => getDB(),
3234
exec: (sql) => getDB().exec(sql),
3335
prepare: (sql) => new StatementWrapper(getDB().prepare(sql)),

src/connectors/cloudflare-hyperdrive-mysql.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import mysql from "mysql2/promise";
22
import type { Connector, Primitive } from "db0";
33
import { BoundableStatement } from "./_internal/statement.ts";
4+
import { mysqlCapabilities } from "./_internal/capabilities.ts";
45
import { getHyperdrive } from "./_internal/cloudflare.ts";
56

67
type OmitMysqlConfig = Omit<
@@ -65,6 +66,7 @@ export default function cloudflareHyperdriveMysqlConnector(
6566
return {
6667
name: "cloudflare-hyperdrive-mysql",
6768
dialect: "mysql",
69+
capabilities: mysqlCapabilities,
6870
getInstance: () => getConnection(),
6971
exec: (sql) => query(sql),
7072
prepare: (sql) => new StatementWrapper(sql, query),

src/connectors/cloudflare-hyperdrive-postgresql.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import pg from "pg";
33
import type { Connector, Primitive } from "db0";
44

55
import { BoundableStatement } from "./_internal/statement.ts";
6+
import { postgresqlCapabilities } from "./_internal/capabilities.ts";
67
import { getHyperdrive } from "./_internal/cloudflare.ts";
78

89
type OmitPgConfig = Omit<
@@ -46,6 +47,7 @@ export default function cloudflareHyperdrivePostgresqlConnector(
4647
return {
4748
name: "cloudflare-hyperdrive-postgresql",
4849
dialect: "postgresql",
50+
capabilities: postgresqlCapabilities,
4951
getInstance: () => getClient(),
5052
exec: (sql) => query(sql),
5153
prepare: (sql) => new StatementWrapper(sql, query),

src/connectors/libsql/core.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Client, InStatement } from "@libsql/client";
22
import type { Connector, Primitive } from "db0";
33
import { BoundableStatement } from "../_internal/statement.ts";
4+
import { sqliteCapabilities } from "../_internal/capabilities.ts";
45

56
export type ConnectorOptions = {
67
getClient: () => Client;
@@ -17,6 +18,7 @@ export default function libSqlCoreConnector(
1718
return {
1819
name: opts.name || "libsql-core",
1920
dialect: "libsql",
21+
capabilities: sqliteCapabilities,
2022
getInstance: async () => opts.getClient(),
2123
exec: (sql) => query(sql),
2224
prepare: (sql) => new StatementWrapper(sql, query),

src/connectors/mysql2.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import mysql from "mysql2/promise";
22
import type { Connector, Primitive } from "db0";
33
import { BoundableStatement } from "./_internal/statement.ts";
4+
import { mysqlCapabilities } from "./_internal/capabilities.ts";
45

56
export type ConnectorOptions = mysql.ConnectionOptions;
67

@@ -33,6 +34,7 @@ export default function mysqlConnector(
3334
return {
3435
name: "mysql",
3536
dialect: "mysql",
37+
capabilities: mysqlCapabilities,
3638
getInstance: () => getConnection(),
3739
exec: (sql) => query(sql),
3840
prepare: (sql) => new StatementWrapper(sql, query),

src/connectors/node-sqlite.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mkdirSync } from "node:fs";
33
import type { Connector, Primitive } from "db0";
44
import type { DatabaseSync, StatementSync } from "node:sqlite";
55
import { BoundableStatement } from "./_internal/statement.ts";
6+
import { sqliteCapabilities } from "./_internal/capabilities.ts";
67

78
export interface ConnectorOptions {
89
cwd?: string;
@@ -41,6 +42,7 @@ export default function nodeSqlite3Connector(
4142
return {
4243
name: "node-sqlite",
4344
dialect: "sqlite",
45+
capabilities: sqliteCapabilities,
4446
getInstance: () => getDB(),
4547
exec(sql: string) {
4648
getDB().exec(sql);

0 commit comments

Comments
 (0)