Skip to content

Commit 0c1c044

Browse files
committed
Updating drivers for DBs
1 parent 731dbe8 commit 0c1c044

File tree

9 files changed

+37
-88
lines changed

9 files changed

+37
-88
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ jobs:
2929

3030
# Check that code is correctly formatted
3131
- run: deno fmt --check
32+
33+
# Publish to JSR
34+
- run: deno publish

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@acr/dbx",
3+
"version": "0.9.0",
34
"license": "MIT",
45
"exports": "./mod.ts",
56
"fmt": {
@@ -23,7 +24,6 @@
2324
"release": "release",
2425
"test": "deno test -A",
2526
"test-mysql": "./test/test-mysql.sh",
26-
"test-mysql2": "./test/test-mysql2.sh",
2727
"test-postgres": "./test/test-postgres.sh"
2828
}
2929
}

src/db.ts

Lines changed: 23 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { DDL } from "./ddl.ts";
44
import type { Class, Identifiable, Parameter, Row, Schema } from "./types.ts";
55
import { Repository } from "./repository.ts";
66

7+
// Import Driver Types
8+
import type { Database as SQLite } from "jsr:@db/sqlite";
9+
import type { Client as Postgres } from "jsr:@dewars/postgres";
10+
711
const TTY = Deno.stderr.isTerminal();
812

913
// See https://stackoverflow.com/questions/49285864/is-there-a-valueof-similar-to-keyof-in-typescript
@@ -25,12 +29,17 @@ const Hook = {
2529

2630
const Provider = {
2731
MYSQL: "mysql",
28-
MYSQL2: "mysql2",
2932
POSTGRES: "postgres",
3033
SQLITE: "sqlite",
3134
} as const;
3235
type Provider = Values<typeof Provider>;
3336

37+
const Drivers = {
38+
MYSQL: "npm:mysql2@^3/promise",
39+
POSTGRES: "jsr:@dewars/postgres@0",
40+
SQLITE: "jsr:@db/sqlite@0",
41+
} as const;
42+
3443
export interface Client {
3544
type: string;
3645
close(): Promise<void>;
@@ -59,29 +68,8 @@ async function connect(config: ClientConfig): Promise<Client> {
5968

6069
// MySQL
6170
if (config.type === Provider.MYSQL) {
62-
const mysql = await import("https://deno.land/x/mysql@v2.12.1/mod.ts");
63-
if (!config.debug) await mysql.configLogger({ enable: false });
64-
config = Object.assign(config, { db: config.database });
65-
if (!config.charset) config.charset = "utf8mb4";
66-
const nativeClient = await new mysql.Client().connect(config);
67-
return new class implements Client {
68-
type = config.type;
69-
close() {
70-
return nativeClient.close();
71-
}
72-
execute(sql: string, parameters?: Parameter[]) {
73-
return nativeClient.execute(sql, parameters);
74-
}
75-
query(sql: string, parameters?: Parameter[]) {
76-
return nativeClient.query(sql, parameters);
77-
}
78-
}();
79-
}
80-
81-
// MySQL2
82-
if (config.type === Provider.MYSQL2) {
83-
const mysql2 = await import("npm:mysql2@^3.11/promise");
84-
const nativeClient = await mysql2.createConnection({
71+
const mysql = await import(Drivers.MYSQL);
72+
const nativeClient = await mysql.createConnection({
8573
host: config.hostname ?? "127.0.0.1",
8674
database: config.database,
8775
user: config.username,
@@ -91,32 +79,29 @@ async function connect(config: ClientConfig): Promise<Client> {
9179
return new class implements Client {
9280
type = config.type;
9381
close() {
94-
// TODO
95-
return Promise.resolve();
82+
return nativeClient.close();
9683
}
9784
async execute(sql: string, parameters?: Parameter[]) {
98-
// deno-lint-ignore no-explicit-any
99-
const [rsh] = await (nativeClient as any).execute(sql, parameters);
85+
const [rsh] = await nativeClient.execute(sql, parameters);
10086
// deno-lint-ignore no-explicit-any
10187
return { affectedRows: (rsh as any).affectedRows, lastInsertId: (rsh as any).insertId };
10288
}
10389
async query(sql: string, parameters?: Parameter[]) {
104-
// deno-lint-ignore no-explicit-any
105-
const [rows] = await (nativeClient as any).query(sql, parameters);
90+
const [rows] = await nativeClient.query(sql, parameters);
10691
return rows as Row[];
10792
}
10893
}();
10994
}
11095

11196
// Postgres
11297
if (config.type === Provider.POSTGRES) {
113-
const postgres = await import("https://deno.land/x/postgres@v0.19.3/mod.ts");
98+
const postgres = await import(Drivers.POSTGRES);
11499
config = Object.assign(config, { user: config.username });
115-
const nativeClient = await new postgres.Pool(config, config.poolSize ?? 1).connect();
100+
const nativeClient = await new postgres.Pool(config, config.poolSize ?? 1).connect() as Postgres;
116101
return new class implements Client {
117102
type = config.type;
118103
close() {
119-
return Promise.resolve();
104+
return nativeClient.end();
120105
}
121106
async execute(sql: string, parameters?: Parameter[]) {
122107
const qar = await nativeClient.queryArray(sql, parameters);
@@ -131,22 +116,19 @@ async function connect(config: ClientConfig): Promise<Client> {
131116

132117
// Sqlite
133118
if (config.type === Provider.SQLITE) {
134-
const sqlite = await import("https://deno.land/x/sqlite@v3.9.1/mod.ts");
135-
const nativeClient = new sqlite.DB(config.database ?? Deno.env.get("DB_FILE") ?? ":memory:");
119+
const sqlite = await import(Drivers.SQLITE);
120+
const nativeClient = new sqlite.Database(config.database ?? Deno.env.get("DB_FILE") ?? ":memory:") as SQLite;
136121
return new class implements Client {
137122
type = config.type;
138123
close() {
139-
nativeClient.close();
140-
return Promise.resolve();
124+
return Promise.resolve(nativeClient.close());
141125
}
142126
execute(sql: string, parameters?: Parameter[]) {
143-
// deno-lint-ignore no-explicit-any
144-
nativeClient.query(sql, parameters as any);
127+
nativeClient.exec(sql, parameters);
145128
return Promise.resolve({ affectedRows: nativeClient.changes, lastInsertId: nativeClient.lastInsertRowId });
146129
}
147130
query(sql: string, parameters?: Parameter[]) {
148-
// deno-lint-ignore no-explicit-any
149-
return Promise.resolve(nativeClient.queryEntries(sql, parameters as any));
131+
return Promise.resolve(nativeClient.prepare(sql).all(parameters));
150132
}
151133
}();
152134
}

src/ddl.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const dataTypes = {
1313
const serialType = {
1414
sqlite: " AUTOINCREMENT",
1515
mysql: " AUTO_INCREMENT",
16-
mysql2: " AUTO_INCREMENT",
1716
postgres: "",
1817
};
1918

@@ -55,7 +54,7 @@ export class DDL {
5554
static createColumn(dbType: string, name: string, column: Column, namePad: number, padWidth = DDL.padWidth, defaultWidth = DDL.defaultWidth): string {
5655
if (typeof (column.default) === "object") column.default = "('" + JSON.stringify(column.default) + "')";
5756
if (column.dateOn === "insert") column.default = "CURRENT_TIMESTAMP";
58-
if (column.dateOn === "update") column.default = "CURRENT_TIMESTAMP" + ((dbType !== DB.Provider.MYSQL && dbType !== DB.Provider.MYSQL2) ? "" : " ON UPDATE CURRENT_TIMESTAMP");
57+
if (column.dateOn === "update") column.default = "CURRENT_TIMESTAMP" + ((dbType !== DB.Provider.MYSQL) ? "" : " ON UPDATE CURRENT_TIMESTAMP");
5958
const pad = "".padEnd(padWidth);
6059
let type = dataTypes[column.type as keyof typeof dataTypes];
6160
const autoIncrement = column.primaryKey && column.type === "integer";
@@ -66,7 +65,7 @@ export class DDL {
6665
const as = asExpression ? " GENERATED ALWAYS AS (" + asExpression + ") " + (column.generatedType?.toUpperCase() || "VIRTUAL") : "";
6766
const def = Object.hasOwn(column, "default") ? " DEFAULT " + column.default : "";
6867
const key = column.primaryKey ? " PRIMARY KEY" : (column.unique ? " UNIQUE" : "");
69-
const comment = (dbType === DB.Provider.MYSQL || dbType === DB.Provider.MYSQL2) && column.comment ? " COMMENT '" + column.comment.replace(/'/g, "''") + "'" : "";
68+
const comment = (dbType === DB.Provider.MYSQL) && column.comment ? " COMMENT '" + column.comment.replace(/'/g, "''") + "'" : "";
7069

7170
// Correct Postgres JSON type
7271
if (dbType === DB.Provider.POSTGRES && type === "JSON") type = "JSONB";
@@ -84,7 +83,7 @@ export class DDL {
8483
// TODO: multivalued indexes only supported on MYSQL for now, Postgres and SQLite will use the entire
8584
const subType = indice.subType ?? "CHAR(32)";
8685
if (indice.array !== undefined) {
87-
columns[indice.array] = "(CAST(" + columns[indice.array] + " AS " + subType + (dbType === DB.Provider.MYSQL || dbType === DB.Provider.MYSQL2 ? " ARRAY" : "") + "))";
86+
columns[indice.array] = "(CAST(" + columns[indice.array] + " AS " + subType + (dbType === DB.Provider.MYSQL ? " ARRAY" : "") + "))";
8887
}
8988

9089
const name = indice.name ?? "";
@@ -97,7 +96,6 @@ export class DDL {
9796

9897
const wrapper = (columns: string[], s = ",", w = false) => columns.map((c) => w ? "COALESCE(" + c + ",'')" : c).join(s);
9998
if (dbType === DB.Provider.MYSQL) return `${pad}CREATE FULLTEXT INDEX ${table}_${name} ON ${table} (${wrapper(columns, ",")});\n`;
100-
if (dbType === DB.Provider.MYSQL2) return `${pad}CREATE FULLTEXT INDEX ${table}_${name} ON ${table} (${wrapper(columns, ",")});\n`;
10199
if (dbType === DB.Provider.POSTGRES) return `${pad}CREATE INDEX ${table}_${name} ON ${table} USING GIN (TO_TSVECTOR('english', ${wrapper(columns, "||' '||", true)}));`;
102100

103101
return "";

src/repository.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ export class Repository<T extends Identifiable> extends EventTarget {
288288
const wrapper = (columns: string[], s = ",", w = false) => columns.map((c) => w ? "COALESCE(" + c + ",'')" : c).join(s);
289289
if (fullTextColumns?.length) {
290290
if (DB.type === DB.Provider.MYSQL) expressions.push("MATCH (" + wrapper(fullTextColumns) + ") AGAINST (? IN BOOLEAN MODE)");
291-
if (DB.type === DB.Provider.MYSQL2) expressions.push("MATCH (" + wrapper(fullTextColumns) + ") AGAINST (? IN BOOLEAN MODE)");
292291
if (DB.type === DB.Provider.POSTGRES) expressions.push("TO_TSVECTOR('english', " + wrapper(fullTextColumns) + ") @@ TO_TSQUERY(?)");
293292
} else expressions.push(column + " LIKE ?");
294293
} else if (column === "$sql") {
@@ -309,7 +308,6 @@ export class Repository<T extends Identifiable> extends EventTarget {
309308
// See this SQ for Postgres operators: https://stackoverflow.com/a/38374876/2772798
310309
if (key === "contains") {
311310
if (DB.type === DB.Provider.MYSQL) expressions.push("? " + op + " (" + column + ")");
312-
if (DB.type === DB.Provider.MYSQL2) expressions.push("? " + op + " (" + column + ")");
313311
if (DB.type === DB.Provider.POSTGRES) expressions.push("JSONB_EXISTS(CAST(" + column + " AS JSONB), ?)");
314312
if (DB.type === DB.Provider.SQLITE) expressions.push(column + " LIKE ?");
315313
} else expressions.push(column + " " + op + (explode ? " (" + join("?", value[key].length) + ")" : " ?"));

test/ddl.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ CREATE INDEX accounts_valueList ON accounts (id,(CAST(valueList AS CHAR(32) ARRA
6767
CREATE FULLTEXT INDEX accounts_fulltext ON accounts (comments,country,phone,name);
6868
`.trim();
6969

70-
Deno.test("Table Creation MySQL/MySQL2", function () {
70+
Deno.test("Table Creation MySQL", function () {
7171
const ddl = DDL.createTable(AccountSchema as Schema, "mysql", "accounts");
7272
if (DEBUG) console.log(`\nMYSQL\n${HR}\n${ddl}\n\n`);
7373
assertEquals(ddl.trim(), MYSQL);
@@ -123,6 +123,9 @@ Deno.test("Actual Table", async function () {
123123
// Select table from Information Schema
124124
const noTable = await DB.query(sql);
125125
assertEquals(noTable.length, 0);
126+
});
126127

128+
// Execute the table creation on the provided platform
129+
Deno.test("Disconnect", { sanitizeResources: false, sanitizeOps: false }, async function () {
127130
await DB.disconnect();
128131
});

test/helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DDL } from "../src/ddl.ts";
44
import type { Schema } from "../src/types.ts";
55

66
const PROVIDER = Deno.env.get("TEST_PROVIDER") ?? Deno.args[0];
7-
if (!PROVIDER) console.warn("\n⚠️ Assuming SQLITE provider. You can use 'TEST_PROVIDER=<provider>' or '-- <provider>' (mysql, mysql2, postgres, sqlite)\n");
7+
if (!PROVIDER) console.warn("\n⚠️ Assuming SQLITE provider. You can use 'TEST_PROVIDER=<provider>' or '-- <provider>' (mysql, postgres, sqlite)\n");
88

99
setup({
1010
handlers: { console: new ConsoleHandler("DEBUG") },
@@ -23,7 +23,7 @@ export async function dbInit(type: string, schemas?: Schema[]) {
2323
if (schemas) await createTables(schemas);
2424
} catch (ex) {
2525
console.error("\n❌ Could not connect to DB '" + type + "' using 'dbx@" + hostname + "' or could not execute SQL!\n");
26-
console.error("MESSAGE: " + (ex as Error).message + "");
26+
console.error("MESSAGE: " + (ex as Error).message + "", ex);
2727
console.error("\nMake sure the user is created in the database (with NO password) and the DB is up\n");
2828
Deno.exit(1);
2929
}

test/test-mysql.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ NAME="mysql"
88

99
# Start a MySQL container on port 13306 with empty password
1010
echo -n "🛢️ Starting Database Container ... "
11-
docker run -p "$PORT":3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_AUTHENTICATION_PLUGIN="mysql_native_password" --rm --name="$NAME" -d mysql:latest
11+
docker run -p "$PORT":3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=1 --rm --name="$NAME" -d mysql:latest
1212

1313
# Wait for MySQL to start (\use IP as host to prevent socket file)
1414
echo -n "😴 Waiting for container to be ready ... "

test/test-mysql2.sh

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)