From 9a58f1cb569d0839475bfa7f0763d3ce151ba14e Mon Sep 17 00:00:00 2001 From: Gerben Date: Tue, 5 Mar 2024 14:27:48 +0100 Subject: [PATCH 1/3] feat: add planetscale support --- .env.example | 6 ++++ package.json | 1 + pnpm-lock.yaml | 15 ++++++-- src/connectors/planetscale.ts | 56 +++++++++++++++++++++++++++++ src/index.ts | 1 + test/connectors/_tests.ts | 32 +++++++++++++++++ test/connectors/planetscale.test.ts | 16 +++++++++ 7 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 src/connectors/planetscale.ts create mode 100644 test/connectors/planetscale.test.ts diff --git a/.env.example b/.env.example index d4da5fe..5009770 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,7 @@ POSTGRESQL_URL=postgresql://:@localhost:5432/db0 + +# PlanetScale +PLANETSCALE_HOST=aws.connect.psdb.cloud +PLANETSCALE_USERNAME=username +PLANETSCALE_PASSWORD=password + \ No newline at end of file diff --git a/package.json b/package.json index e48b0f6..30d85ad 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ }, "devDependencies": { "@libsql/client": "^0.5.2", + "@planetscale/database": "^1.16.0", "@types/better-sqlite3": "^7.6.9", "@types/bun": "^1.0.8", "@types/pg": "^8.11.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b233ade..ff203cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@libsql/client': specifier: ^0.5.2 version: 0.5.2 + '@planetscale/database': + specifier: ^1.16.0 + version: 1.16.0 '@types/better-sqlite3': specifier: ^7.6.9 version: 7.6.9 @@ -40,7 +43,7 @@ importers: version: 16.4.5 drizzle-orm: specifier: ^0.29.4 - version: 0.29.4(@libsql/client@0.5.2)(@types/better-sqlite3@7.6.9)(@types/pg@8.11.2)(better-sqlite3@9.4.3)(pg@8.11.3) + version: 0.29.4(@libsql/client@0.5.2)(@planetscale/database@1.16.0)(@types/better-sqlite3@7.6.9)(@types/pg@8.11.2)(better-sqlite3@9.4.3)(pg@8.11.3) eslint: specifier: ^8.57.0 version: 8.57.0 @@ -76,7 +79,7 @@ importers: version: 0.20.14 drizzle-orm: specifier: ^0.29.4 - version: 0.29.4(@libsql/client@0.5.2)(@types/better-sqlite3@7.6.9)(@types/pg@8.11.2)(better-sqlite3@9.4.3)(pg@8.11.3) + version: 0.29.4(@libsql/client@0.5.2)(@planetscale/database@1.16.0)(@types/better-sqlite3@7.6.9)(@types/pg@8.11.2)(better-sqlite3@9.4.3)(pg@8.11.3) jiti: specifier: ^1.21.0 version: 1.21.0 @@ -1092,6 +1095,11 @@ packages: '@parcel/watcher-win32-x64': 2.4.1 dev: true + /@planetscale/database@1.16.0: + resolution: {integrity: sha512-HNUrTqrd8aTRZYMDcsoZ62s36sIWkMMmKZBOehoCWR2WrfNPKq+Q1yQef5okl3pSVlldFnu2h/dbHjOsDTHXug==} + engines: {node: '>=16'} + dev: true + /@rollup/plugin-alias@5.1.0(rollup@3.29.4): resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} engines: {node: '>=14.0.0'} @@ -2490,7 +2498,7 @@ packages: - supports-color dev: true - /drizzle-orm@0.29.4(@libsql/client@0.5.2)(@types/better-sqlite3@7.6.9)(@types/pg@8.11.2)(better-sqlite3@9.4.3)(pg@8.11.3): + /drizzle-orm@0.29.4(@libsql/client@0.5.2)(@planetscale/database@1.16.0)(@types/better-sqlite3@7.6.9)(@types/pg@8.11.2)(better-sqlite3@9.4.3)(pg@8.11.3): resolution: {integrity: sha512-ZnSM8TAxFhzH7p1s3+w3pRE/eKaOeNkH9SKitm717pubDVVcV2I0BCDBPGKV+pe02+wMfw37ntlTcCyo2rA3IA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' @@ -2562,6 +2570,7 @@ packages: optional: true dependencies: '@libsql/client': 0.5.2 + '@planetscale/database': 1.16.0 '@types/better-sqlite3': 7.6.9 '@types/pg': 8.11.2 better-sqlite3: 9.4.3 diff --git a/src/connectors/planetscale.ts b/src/connectors/planetscale.ts new file mode 100644 index 0000000..64486c7 --- /dev/null +++ b/src/connectors/planetscale.ts @@ -0,0 +1,56 @@ +import { Client, type Config } from "@planetscale/database"; + +import type { Connector, Statement } from "../types"; + +export default function planetscaleConnector(opts: Config) { + let _client: undefined | Client; + function getClient() { + if (_client) { + return _client; + } + const client = new Client(opts); + _client = client; + return client; + } + + // Discussion on how @planetscale/database client works: + // https://github.com/drizzle-team/drizzle-orm/issues/1743#issuecomment-1879479647 + function query(sql: string, params?: unknown[]) { + const client = getClient(); + return client.execute(sql, params); + } + + return { + name: "planetscale", + exec(sql: string) { + return query(sql); + }, + prepare(sql: string) { + const stmt = { + _sql: sql, + _params: [], + bind(...params) { + if (params.length > 0) { + this._params = params; + } + return stmt; + }, + all(...params) { + return query(this._sql, params || this._params).then((r) => r.rows); + }, + run(...params) { + return query(this._sql, params || this._params).then((r) => ({ + result: r, + rows: r.rows, + })); + }, + get(...params) { + return query(this._sql, params || this._params).then( + (r) => r.rows[0], + ); + }, + }; + return stmt; + }, + }; +} diff --git a/src/index.ts b/src/index.ts index 3d91c13..63d1645 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ export const connectors = { "libsql-web": "db0/connectors/libsql/web", bun: "db0/connectors/bun-sqlite", "bun-sqlite": "db0/connectors/bun-sqlite", + planetscale: "db0/connectors/planetscale", } as const; export type ConnectorName = keyof typeof connectors; diff --git a/test/connectors/_tests.ts b/test/connectors/_tests.ts index 0cc3476..d92f419 100644 --- a/test/connectors/_tests.ts +++ b/test/connectors/_tests.ts @@ -32,3 +32,35 @@ export function testConnector(opts: { connector: Connector }) { `); }); } + +export function testMySQLConnector(opts: { connector: Connector }) { + let db: Database; + beforeAll(() => { + db = createDatabase(opts.connector); + }); + + const userId = "1001"; + + it("drop and create table", async () => { + await db.sql`DROP TABLE IF EXISTS users`; + await db.sql`CREATE TABLE users (\`id\` VARCHAR(4) PRIMARY KEY, \`firstName\` TEXT, \`lastName\` TEXT, \`email\` TEXT)`; + }); + + it("insert", async () => { + await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; + }); + + it("select", async () => { + const { rows } = await db.sql`SELECT * FROM users WHERE id = ${userId}`; + expect(rows).toMatchInlineSnapshot(` + [ + { + "email": "", + "firstName": "John", + "id": "1001", + "lastName": "Doe", + }, + ] + `); + }); +} diff --git a/test/connectors/planetscale.test.ts b/test/connectors/planetscale.test.ts new file mode 100644 index 0000000..2246177 --- /dev/null +++ b/test/connectors/planetscale.test.ts @@ -0,0 +1,16 @@ +import { describe } from "vitest"; +import connector from "../../src/connectors/planetscale"; +import { testMySQLConnector } from "./_tests"; + +describe.runIf(process.env.PLANETSCALE_HOST && process.env.PLANETSCALE_USERNAME && process.env.PLANETSCALE_PASSWORD)( + "connectors: planetscale.test", + () => { + testMySQLConnector({ + connector: connector({ + host: process.env.PLANETSCALE_HOST!, + username: process.env.PLANETSCALE_USERNAME!, + password: process.env.PLANETSCALE_PASSWORD!, + }), + }); + }, +); From f0b9247e67ddaddd1e4523c3e54960b13bb7eafc Mon Sep 17 00:00:00 2001 From: Gerben Date: Tue, 5 Mar 2024 14:53:28 +0100 Subject: [PATCH 2/3] refactor: merge dialects into single test function --- test/connectors/_tests.ts | 53 +++++++++----------------- test/connectors/better-sqlite3.test.ts | 1 + test/connectors/libsql.test.ts | 1 + test/connectors/planetscale.test.ts | 5 ++- test/connectors/postgresql.test.ts | 1 + 5 files changed, 25 insertions(+), 36 deletions(-) diff --git a/test/connectors/_tests.ts b/test/connectors/_tests.ts index d92f419..44dfc08 100644 --- a/test/connectors/_tests.ts +++ b/test/connectors/_tests.ts @@ -1,7 +1,15 @@ import { beforeAll, expect, it } from "vitest"; import { Connector, Database, createDatabase } from "../../src"; -export function testConnector(opts: { connector: Connector }) { +const dialects = [ + "mysql", + "postgresql", + "sqlite", + "libsql", +] as const; +type SQLDialect = typeof dialects[number]; + +export function testConnector(opts: { connector: Connector, dialect?: SQLDialect }) { let db: Database; beforeAll(() => { db = createDatabase(opts.connector); @@ -11,39 +19,16 @@ export function testConnector(opts: { connector: Connector }) { it("drop and create table", async () => { await db.sql`DROP TABLE IF EXISTS users`; - await db.sql`CREATE TABLE users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; - }); - - it("insert", async () => { - await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; - }); - - it("select", async () => { - const { rows } = await db.sql`SELECT * FROM users WHERE id = ${userId}`; - expect(rows).toMatchInlineSnapshot(` - [ - { - "email": "", - "firstName": "John", - "id": "1001", - "lastName": "Doe", - }, - ] - `); - }); -} - -export function testMySQLConnector(opts: { connector: Connector }) { - let db: Database; - beforeAll(() => { - db = createDatabase(opts.connector); - }); - - const userId = "1001"; - - it("drop and create table", async () => { - await db.sql`DROP TABLE IF EXISTS users`; - await db.sql`CREATE TABLE users (\`id\` VARCHAR(4) PRIMARY KEY, \`firstName\` TEXT, \`lastName\` TEXT, \`email\` TEXT)`; + switch (opts.dialect) { + case "mysql": { + await db.sql`CREATE TABLE users (\`id\` VARCHAR(4) PRIMARY KEY, \`firstName\` TEXT, \`lastName\` TEXT, \`email\` TEXT)`; + break; + } + default: { + await db.sql`CREATE TABLE users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; + break; + } + } }); it("insert", async () => { diff --git a/test/connectors/better-sqlite3.test.ts b/test/connectors/better-sqlite3.test.ts index eed5285..3454de4 100644 --- a/test/connectors/better-sqlite3.test.ts +++ b/test/connectors/better-sqlite3.test.ts @@ -8,6 +8,7 @@ describe("connectors: better-sqlite3", () => { const tmpDir = fileURLToPath(new URL(".tmp/better-sqlite3", import.meta.url)); rmSync(tmpDir, { recursive: true, force: true }); testConnector({ + dialect: "sqlite", connector: connector({ cwd: tmpDir, }), diff --git a/test/connectors/libsql.test.ts b/test/connectors/libsql.test.ts index 6497d58..97663fc 100644 --- a/test/connectors/libsql.test.ts +++ b/test/connectors/libsql.test.ts @@ -15,6 +15,7 @@ describe("connectors: libsql", () => { } mkdirSync(dirname(dbPath), { recursive: true }); testConnector({ + dialect: "libsql", connector: libSql({ url: `file:${dbPath}`, }), diff --git a/test/connectors/planetscale.test.ts b/test/connectors/planetscale.test.ts index 2246177..9a3fcad 100644 --- a/test/connectors/planetscale.test.ts +++ b/test/connectors/planetscale.test.ts @@ -1,11 +1,12 @@ import { describe } from "vitest"; import connector from "../../src/connectors/planetscale"; -import { testMySQLConnector } from "./_tests"; +import { testConnector } from "./_tests"; describe.runIf(process.env.PLANETSCALE_HOST && process.env.PLANETSCALE_USERNAME && process.env.PLANETSCALE_PASSWORD)( "connectors: planetscale.test", () => { - testMySQLConnector({ + testConnector({ + dialect: "mysql", connector: connector({ host: process.env.PLANETSCALE_HOST!, username: process.env.PLANETSCALE_USERNAME!, diff --git a/test/connectors/postgresql.test.ts b/test/connectors/postgresql.test.ts index eea2ab2..3831c72 100644 --- a/test/connectors/postgresql.test.ts +++ b/test/connectors/postgresql.test.ts @@ -6,6 +6,7 @@ describe.runIf(process.env.POSTGRESQL_URL)( "connectors: postgresql.test", () => { testConnector({ + dialect: "postgresql", connector: connector({ url: process.env.POSTGRESQL_URL!, }), From 842b364435c0c5dd68aebed8a4f9b2f435cd440c Mon Sep 17 00:00:00 2001 From: Gerben Date: Tue, 5 Mar 2024 15:21:08 +0100 Subject: [PATCH 3/3] docs: add planetscale connector docs --- docs/2.connectors/1.index.md | 1 + docs/2.connectors/planetscale.md | 46 +++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/2.connectors/1.index.md b/docs/2.connectors/1.index.md index 85fadec..e977a24 100644 --- a/docs/2.connectors/1.index.md +++ b/docs/2.connectors/1.index.md @@ -11,6 +11,7 @@ Currently supported connectors: - [Bun](/connectors/bun) - [Cloudflare D1](/connectors/cloudflare) - [LibSQL](/connectors/libsql) +- [PlanetScale](/connectors/planetscale) - [PostgreSQL](/connectors/postgresql) - [SQLite](/connectors/sqlite) diff --git a/docs/2.connectors/planetscale.md b/docs/2.connectors/planetscale.md index 57946ea..77076dd 100644 --- a/docs/2.connectors/planetscale.md +++ b/docs/2.connectors/planetscale.md @@ -8,6 +8,46 @@ icon: simple-icons:planetscale :read-more{to="https://planetscale.com"} -::read-more{to="https://github.com/unjs/db0/issues/32"} -This connector is planned to be supported. Follow up via [unjs/db0#32](https://github.com/unjs/db0/issues/32). -:: +## Usage + +For this connector, you need to install [`@planetscale/database`](https://www.npmjs.com/package/@planetscale/database) dependency: + +:pm-install{name="@planetscale/database"} + +Use `planetscale` connector: + +```js +import { createDatabase, sql } from "db0"; +import planetscale from "db0/connectors/planetscale"; + +const db = createDatabase( + planetscale({ + host: "aws.connect.psdb.cloud", + username: "username", + password: "password", + }), +); +``` + +## Options + +### `host` + +Planetscale host. + +### `username` + +Planetscale username. + +### `password` + +Planetscale password. + +### 'url' + +Connection URL string. +The `host`, `username` and `password` are extracted from the URL. + +:read-more{title="Create a database password" to="https://planetscale.com/docs/tutorials/planetscale-serverless-driver"} + +:read-more{title="@planetscale/database client options" to="https://github.com/planetscale/database-js"}