Skip to content

Commit

Permalink
feat(mysql, postgres): add support for connection URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
marcj committed Mar 17, 2024
1 parent 6f33804 commit 2518670
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 18 deletions.
4 changes: 4 additions & 0 deletions packages/mysql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
* You should have received a copy of the MIT License along with this program.
*/

export * from './src/config.js';
export * from './src/mysql-adapter.js';
export * from './src/mysql-platform.js';
export * from './src/mysql-schema-parser.js';
export * from './src/mysql-serializer.js';
68 changes: 68 additions & 0 deletions packages/mysql/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { cast } from '@deepkit/type';
import { PoolConfig } from 'mariadb';

interface AdapterClientConfig {
acquireTimeout?: number;
connectionLimit?: number;
idleTimeout?: number;
initializationTimeout?: number;
minDelayValidation?: number;
minimumIdle?: number;
resetAfterUse?: boolean;
noControlAfterUse?: boolean;
leakDetectionTimeout?: number;

database?: string;
connectAttributes?: any;
charset?: string;
collation?: string;
user?: string;
password?: string;

host?: string;
port?: number;
socketPath?: string;
connectTimeout?: number;
socketTimeout?: number;
debug?: boolean;
debugCompress?: boolean;
debugLen?: number;
logParam?: boolean;
trace?: boolean;
multipleStatements?: boolean;
ssl?: boolean;
compress?: boolean;
logPackets?: boolean;
forceVersionCheck?: boolean;
foundRows?: boolean;
initSql?: string | string[];
sessionVariables?: any;
maxAllowedPacket?: number;
keepAliveDelay?: number;
rsaPublicKey?: string;
cachingRsaPublicKey?: string;
allowPublicKeyRetrieval?: boolean;
insertIdAsNumber?: boolean;
prepareCacheLength?: number;
metaEnumerable?: boolean;
metaAsArray?: boolean;
rowsAsArray?: boolean;
}

export function parseConnectionString(url: string): PoolConfig {
const parsed = new URL(url);

const options: {[name: string]: any} = {};
for (const [key, value] of parsed.searchParams.entries()) {
options[key] = value;
}

return cast<AdapterClientConfig>({
host: parsed.hostname,
port: parsed.port,
database: parsed.pathname.slice(1),
user: parsed.username,
password: parsed.password,
...options,
});
}
37 changes: 23 additions & 14 deletions packages/mysql/src/mysql-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
} from '@deepkit/type';
import { AbstractClassType, asyncOperation, ClassType, empty, isArray } from '@deepkit/core';
import { FrameCategory, Stopwatch } from '@deepkit/stopwatch';
import { parseConnectionString } from './config.js';

/**
* Converts a specific database error to a more specific error, if possible.
Expand Down Expand Up @@ -564,24 +565,32 @@ export class MySQLDatabaseQueryFactory extends SQLDatabaseQueryFactory {
}

export class MySQLDatabaseAdapter extends SQLDatabaseAdapter {
protected pool = createPool({
multipleStatements: true,
maxAllowedPacket: 16_000_000,

// https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/callback-api.md#migrating-from-2x-or-mysqlmysql2-to-3x
insertIdAsNumber: true,
decimalAsNumber: true,
bigIntAsNumber: true,

...this.options
});
public connectionPool = new MySQLConnectionPool(this.pool);
public platform = new MySQLPlatform(this.pool);
protected options: PoolConfig;
protected pool: Pool;
public connectionPool: MySQLConnectionPool;
public platform: MySQLPlatform;

constructor(
protected options: PoolConfig = {},
options: PoolConfig | string = {},
additional: Partial<PoolConfig> = {},
) {
super();

const defaults: PoolConfig = {
multipleStatements: true,
maxAllowedPacket: 16_000_000,
// https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/callback-api.md#migrating-from-2x-or-mysqlmysql2-to-3x
insertIdAsNumber: true,
decimalAsNumber: true,
bigIntAsNumber: true,
}

options = typeof options === 'string' ? parseConnectionString(options) : options;
this.options = Object.assign(defaults, options, additional);

this.pool = createPool(this.options);
this.connectionPool = new MySQLConnectionPool(this.pool);
this.platform = new MySQLPlatform(this.pool);
}

getName(): string {
Expand Down
26 changes: 26 additions & 0 deletions packages/mysql/tests/config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { expect, test } from '@jest/globals';
import { parseConnectionString } from '../src/config.js';

test('basics', async () => {
const config = parseConnectionString('mysql://user1:password2@localhost:5432/mydb');
expect(config).toEqual({
host: 'localhost',
port: 5432,
password: 'password2',
database: 'mydb',
user: 'user1'
});
});

test('with-params', async () => {
const config = parseConnectionString('mysql://user1:password2@localhost:5432/mydb?acquireTimeout=1000&keepAliveDelay=5');
expect(config).toEqual({
host: 'localhost',
port: 5432,
password: 'password2',
database: 'mydb',
user: 'user1',
acquireTimeout: 1000,
keepAliveDelay: 5
});
});
1 change: 1 addition & 0 deletions packages/mysql/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"types": []
},
"reflection": [
"src/config.ts",
"tests/**/*.ts"
],
"include": [
Expand Down
4 changes: 4 additions & 0 deletions packages/postgres/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
* You should have received a copy of the MIT License along with this program.
*/

export * from './src/config.js';
export * from './src/postgres-adapter.js';
export * from './src/postgres-platform.js';
export * from './src/postgres-schema-parser.js';
export * from './src/postgres-serializer.js';
43 changes: 43 additions & 0 deletions packages/postgres/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ConnectionOptions } from 'tls';
import { PoolConfig } from 'pg';
import { cast } from '@deepkit/type';

interface AdapterClientConfig {
user?: string;
database?: string;
password?: string;
port?: number;
host?: string;
connectionString?: string;
keepAlive?: boolean;
statement_timeout?: false | number;
parseInputDatesAsUTC?: boolean;
ssl?: boolean | ConnectionOptions;
query_timeout?: number;
keepAliveInitialDelayMillis?: number;
idle_in_transaction_session_timeout?: number;
application_name?: string;
connectionTimeoutMillis?: number;

max?: number;
min?: number;
idleTimeoutMillis?: number;
}

export function parseConnectionString(url: string): PoolConfig {
const parsed = new URL(url);

const options: {[name: string]: any} = {};
for (const [key, value] of parsed.searchParams.entries()) {
options[key] = value;
}

return cast<AdapterClientConfig>({
host: parsed.hostname,
port: parsed.port,
database: parsed.pathname.slice(1),
user: parsed.username,
password: parsed.password,
...options,
});
}
13 changes: 10 additions & 3 deletions packages/postgres/src/postgres-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
ReflectionProperty,
resolvePath,
} from '@deepkit/type';
import { parseConnectionString } from './config.js';

/**
* Converts a specific database error to a more specific error, if possible.
Expand Down Expand Up @@ -571,13 +572,19 @@ export class PostgresSQLDatabaseQueryFactory extends SQLDatabaseQueryFactory {
}

export class PostgresDatabaseAdapter extends SQLDatabaseAdapter {
protected pool = new pg.Pool(this.options);
public connectionPool = new PostgresConnectionPool(this.pool);
protected options: PoolConfig;
protected pool: pg.Pool;
public connectionPool : PostgresConnectionPool;
public platform = new PostgresPlatform();
closed = false;

constructor(protected options: PoolConfig) {
constructor(options: PoolConfig | string, additional: Partial<PoolConfig> = {}) {
super();
const defaults: PoolConfig = {};
options = 'string' === typeof options ? parseConnectionString(options) : options;
this.options = Object.assign(defaults, options, additional);
this.pool = new pg.Pool(this.options);
this.connectionPool = new PostgresConnectionPool(this.pool);

pg.types.setTypeParser(1700, parseFloat);
pg.types.setTypeParser(20, parseInt);
Expand Down
26 changes: 26 additions & 0 deletions packages/postgres/tests/config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { expect, test } from '@jest/globals';
import { parseConnectionString } from '../src/config.js';

test('basics', async () => {
const config = parseConnectionString('postgres://user1:password2@localhost:5432/mydb');
expect(config).toEqual({
host: 'localhost',
port: 5432,
password: 'password2',
database: 'mydb',
user: 'user1'
});
});

test('with-params', async () => {
const config = parseConnectionString('postgres://user1:password2@localhost:5432/mydb?query_timeout=1000&keepAlive=true');
expect(config).toEqual({
host: 'localhost',
port: 5432,
password: 'password2',
database: 'mydb',
user: 'user1',
query_timeout: 1000,
keepAlive: true
});
});
1 change: 1 addition & 0 deletions packages/postgres/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
]
},
"reflection": [
"src/config.ts",
"tests/**/*.ts"
],
"include": [
Expand Down
1 change: 1 addition & 0 deletions packages/sqlite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@

export * from './src/sqlite-adapter.js';
export * from './src/sqlite-platform.js';
export * from './src/sqlite-schema-parser.js';
export * from './src/sqlite-serializer.js';
4 changes: 3 additions & 1 deletion packages/sqlite/src/sqlite-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,9 @@ export class SQLiteDatabaseAdapter extends SQLDatabaseAdapter {

constructor(protected sqlitePath: string | ':memory:' = ':memory:') {
super();

if (this.sqlitePath.startsWith('sqlite://')) {
this.sqlitePath = this.sqlitePath.substring('sqlite://'.length);
}
this.connectionPool = new SQLiteConnectionPool(this.sqlitePath);
}

Expand Down

0 comments on commit 2518670

Please sign in to comment.