Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MSSQL Support #128

Merged
merged 3 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ npm install kysely pg
npm install kysely mysql2
npm install kysely better-sqlite3
npm install @libsql/kysely-libsql
npm install kysely tedious tarn @tediousjs/connection-string
```

## Generating type definitions
Expand All @@ -36,6 +37,9 @@ DATABASE_URL=C:/Program Files/sqlite3/db

# LibSQL
DATABASE_URL=libsql://token@host:port/database

# MSSQL
DATABASE_URL=Server=mssql;Database=database;User Id=user;Password=password
```

> If your URL contains a password with special characters, those characters may need to be [percent-encoded](https://en.wikipedia.org/wiki/Percent-encoding#Reserved_characters).
Expand Down
17 changes: 15 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@types/minimist": "^1.2.5",
"@types/node": "^20.11.16",
"@types/pg": "^8.11.0",
"@types/tedious": "4.0.14",
"@typescript-eslint/parser": "^6.20.0",
"better-sqlite3": "^9.3.0",
"eslint": "^8.56.0",
Expand All @@ -73,16 +74,22 @@
},
"peerDependencies": {
"@libsql/kysely-libsql": "^0.3.0",
"@tediousjs/connection-string": "^0.5.0",
"better-sqlite3": ">=7.6.2",
"kysely-bun-worker": "^0.5.3",
"kysely": ">=0.19.12",
"kysely": "^0.27.0",
"mysql2": "^2.3.3 || ^3.0.0",
"pg": "^8.8.0"
"pg": "^8.8.0",
"tarn": "^3.0.0",
"tedious": "^16.6.0"
},
"peerDependenciesMeta": {
"@libsql/kysely-libsql": {
"optional": true
},
"@tediousjs/connection-string": {
"optional": true
},
"better-sqlite3": {
"optional": true
},
Expand All @@ -97,6 +104,12 @@
},
"pg": {
"optional": true
},
"tarn": {
"optional": true
},
"tedious": {
"optional": true
}
},
"eslintConfig": {
Expand Down
1 change: 1 addition & 0 deletions src/cli/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export const VALID_DIALECTS = [
'sqlite',
'libsql',
'bun-sqlite',
'mssql',
];
4 changes: 4 additions & 0 deletions src/core/connection-string-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export class ConnectionStringParser {
return 'postgres';
}

if (connectionString.toLowerCase().includes('user id=')) {
return 'mssql';
}

return 'sqlite';
}

Expand Down
6 changes: 5 additions & 1 deletion src/core/dialect-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
BunSqliteDialect,
LibsqlDialect,
MssqlDialect,
MysqlDialect,
PostgresDialect,
SqliteDialect,
Expand All @@ -12,7 +13,8 @@ export type DialectName =
| 'libsql'
| 'mysql'
| 'postgres'
| 'sqlite';
| 'sqlite'
| 'mssql';

/**
* Returns a dialect instance for a pre-defined dialect name.
Expand All @@ -28,6 +30,8 @@ export class DialectManager {
return new MysqlDialect();
case 'postgres':
return new PostgresDialect();
case 'mssql':
return new MssqlDialect();
default:
return new SqliteDialect();
}
Expand Down
1 change: 1 addition & 0 deletions src/dialects/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './bun-sqlite';
export * from './libsql';
export * from './mssql';
export * from './mysql';
export * from './postgres';
export * from './sqlite';
2 changes: 2 additions & 0 deletions src/dialects/mssql/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './mssql-adapter';
export * from './mssql-dialect';
38 changes: 38 additions & 0 deletions src/dialects/mssql/mssql-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { IdentifierNode } from '../../ast';
import { Adapter } from '../../core';

export class MssqlAdapter extends Adapter {
// https://github.com/tediousjs/tedious/tree/master/src/data-types
override readonly scalars = {
bigint: new IdentifierNode('number'),
binary: new IdentifierNode('Buffer'),
bit: new IdentifierNode('Buffer'),
char: new IdentifierNode('string'),
date: new IdentifierNode('Date'),
datetime: new IdentifierNode('Date'),
datetime2: new IdentifierNode('Date'),
datetimeoffset: new IdentifierNode('Date'),
decimal: new IdentifierNode('number'),
number: new IdentifierNode('number'),
double: new IdentifierNode('number'),
float: new IdentifierNode('number'),
int: new IdentifierNode('number'),
image: new IdentifierNode('Buffer'),
money: new IdentifierNode('number'),
nchar: new IdentifierNode('string'),
ntext: new IdentifierNode('string'),
numeric: new IdentifierNode('number'),
nvarchar: new IdentifierNode('string'),
real: new IdentifierNode('number'),
smalldatetime: new IdentifierNode('Date'),
smallint: new IdentifierNode('number'),
smallmoney: new IdentifierNode('number'),
text: new IdentifierNode('string'),
time: new IdentifierNode('Date'),
tinyint: new IdentifierNode('number'),
tvp: new IdentifierNode('unknown'),
uniqueidentifier: new IdentifierNode('string'),
varbinary: new IdentifierNode('Buffer'),
varchar: new IdentifierNode('string'),
};
}
61 changes: 61 additions & 0 deletions src/dialects/mssql/mssql-dialect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { MssqlDialect as KyselyMssqlDialect } from 'kysely';
import { CreateKyselyDialectOptions, Dialect } from '../../core';
import { MssqlAdapter } from './mssql-adapter';
import { MssqlIntrospector } from './mssql-introspector';

export class MssqlDialect extends Dialect {
readonly adapter = new MssqlAdapter();
readonly introspector = new MssqlIntrospector();

async createKyselyDialect(options: CreateKyselyDialectOptions) {
const tedious = await import('tedious');
const tarn = await import('tarn');
const { parseConnectionString } = await import(
'@tediousjs/connection-string'
);

let server = '';
let port = 1433;

const connectionParams: any = parseConnectionString(
options.connectionString,
);

server = connectionParams.server;
// https://www.connectionstrings.com/microsoft-data-sqlclient/using-a-non-standard-port/
if (server.includes(',')) {
const [serverName, portString] = server.split(',') as [string, string];
server = serverName;
port = parseInt(portString, 10);
}

return new KyselyMssqlDialect({
tarn: {
...tarn,
options: {
min: 0,
max: 1,
},
},
tedious: {
...tedious,
connectionFactory: () =>
new tedious.Connection({
authentication: {
type: 'default',
options: {
password: connectionParams.password,
userName: connectionParams['user id'],
},
},
options: {
database: connectionParams.database,
port,
trustServerCertificate: true,
},
server,
}),
},
});
}
}
10 changes: 10 additions & 0 deletions src/dialects/mssql/mssql-introspector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { DatabaseMetadata, EnumCollection } from '../../core';
import { IntrospectOptions, Introspector } from '../../introspector';

export class MssqlIntrospector extends Introspector<any> {
async introspect(options: IntrospectOptions<any>) {
const tables = await this.getTables(options);
const enums = new EnumCollection();
return new DatabaseMetadata(tables, enums);
}
}