@powersync/service-module-mssql 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/LICENSE +67 -0
  2. package/README.md +3 -0
  3. package/ci/init-mssql.sql +50 -0
  4. package/dist/api/MSSQLRouteAPIAdapter.d.ts +21 -0
  5. package/dist/api/MSSQLRouteAPIAdapter.js +248 -0
  6. package/dist/api/MSSQLRouteAPIAdapter.js.map +1 -0
  7. package/dist/common/LSN.d.ts +37 -0
  8. package/dist/common/LSN.js +64 -0
  9. package/dist/common/LSN.js.map +1 -0
  10. package/dist/common/MSSQLSourceTable.d.ts +27 -0
  11. package/dist/common/MSSQLSourceTable.js +35 -0
  12. package/dist/common/MSSQLSourceTable.js.map +1 -0
  13. package/dist/common/MSSQLSourceTableCache.d.ts +14 -0
  14. package/dist/common/MSSQLSourceTableCache.js +28 -0
  15. package/dist/common/MSSQLSourceTableCache.js.map +1 -0
  16. package/dist/common/mssqls-to-sqlite.d.ts +18 -0
  17. package/dist/common/mssqls-to-sqlite.js +143 -0
  18. package/dist/common/mssqls-to-sqlite.js.map +1 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/module/MSSQLModule.d.ts +15 -0
  23. package/dist/module/MSSQLModule.js +68 -0
  24. package/dist/module/MSSQLModule.js.map +1 -0
  25. package/dist/replication/CDCPoller.d.ts +67 -0
  26. package/dist/replication/CDCPoller.js +183 -0
  27. package/dist/replication/CDCPoller.js.map +1 -0
  28. package/dist/replication/CDCReplicationJob.d.ts +17 -0
  29. package/dist/replication/CDCReplicationJob.js +76 -0
  30. package/dist/replication/CDCReplicationJob.js.map +1 -0
  31. package/dist/replication/CDCReplicator.d.ts +18 -0
  32. package/dist/replication/CDCReplicator.js +55 -0
  33. package/dist/replication/CDCReplicator.js.map +1 -0
  34. package/dist/replication/CDCStream.d.ts +106 -0
  35. package/dist/replication/CDCStream.js +536 -0
  36. package/dist/replication/CDCStream.js.map +1 -0
  37. package/dist/replication/MSSQLConnectionManager.d.ts +23 -0
  38. package/dist/replication/MSSQLConnectionManager.js +97 -0
  39. package/dist/replication/MSSQLConnectionManager.js.map +1 -0
  40. package/dist/replication/MSSQLConnectionManagerFactory.d.ts +10 -0
  41. package/dist/replication/MSSQLConnectionManagerFactory.js +28 -0
  42. package/dist/replication/MSSQLConnectionManagerFactory.js.map +1 -0
  43. package/dist/replication/MSSQLErrorRateLimiter.d.ts +10 -0
  44. package/dist/replication/MSSQLErrorRateLimiter.js +34 -0
  45. package/dist/replication/MSSQLErrorRateLimiter.js.map +1 -0
  46. package/dist/replication/MSSQLSnapshotQuery.d.ts +71 -0
  47. package/dist/replication/MSSQLSnapshotQuery.js +190 -0
  48. package/dist/replication/MSSQLSnapshotQuery.js.map +1 -0
  49. package/dist/types/mssql-data-types.d.ts +66 -0
  50. package/dist/types/mssql-data-types.js +62 -0
  51. package/dist/types/mssql-data-types.js.map +1 -0
  52. package/dist/types/types.d.ts +177 -0
  53. package/dist/types/types.js +141 -0
  54. package/dist/types/types.js.map +1 -0
  55. package/dist/utils/mssql.d.ts +80 -0
  56. package/dist/utils/mssql.js +329 -0
  57. package/dist/utils/mssql.js.map +1 -0
  58. package/dist/utils/schema.d.ts +21 -0
  59. package/dist/utils/schema.js +131 -0
  60. package/dist/utils/schema.js.map +1 -0
  61. package/package.json +51 -0
  62. package/src/api/MSSQLRouteAPIAdapter.ts +283 -0
  63. package/src/common/LSN.ts +77 -0
  64. package/src/common/MSSQLSourceTable.ts +54 -0
  65. package/src/common/MSSQLSourceTableCache.ts +36 -0
  66. package/src/common/mssqls-to-sqlite.ts +151 -0
  67. package/src/index.ts +1 -0
  68. package/src/module/MSSQLModule.ts +82 -0
  69. package/src/replication/CDCPoller.ts +241 -0
  70. package/src/replication/CDCReplicationJob.ts +87 -0
  71. package/src/replication/CDCReplicator.ts +70 -0
  72. package/src/replication/CDCStream.ts +688 -0
  73. package/src/replication/MSSQLConnectionManager.ts +113 -0
  74. package/src/replication/MSSQLConnectionManagerFactory.ts +33 -0
  75. package/src/replication/MSSQLErrorRateLimiter.ts +36 -0
  76. package/src/replication/MSSQLSnapshotQuery.ts +230 -0
  77. package/src/types/mssql-data-types.ts +79 -0
  78. package/src/types/types.ts +224 -0
  79. package/src/utils/mssql.ts +420 -0
  80. package/src/utils/schema.ts +172 -0
  81. package/test/src/CDCStream.test.ts +206 -0
  82. package/test/src/CDCStreamTestContext.ts +212 -0
  83. package/test/src/CDCStream_resumable_snapshot.test.ts +152 -0
  84. package/test/src/env.ts +11 -0
  85. package/test/src/mssql-to-sqlite.test.ts +474 -0
  86. package/test/src/setup.ts +12 -0
  87. package/test/src/util.ts +189 -0
  88. package/test/tsconfig.json +28 -0
  89. package/test/tsconfig.tsbuildinfo +1 -0
  90. package/tsconfig.json +26 -0
  91. package/tsconfig.tsbuildinfo +1 -0
  92. package/vitest.config.ts +15 -0
@@ -0,0 +1,113 @@
1
+ import { BaseObserver, logger } from '@powersync/lib-services-framework';
2
+ import sql from 'mssql';
3
+ import { NormalizedMSSQLConnectionConfig } from '../types/types.js';
4
+ import { POWERSYNC_VERSION } from '@powersync/service-core';
5
+ import { MSSQLParameter } from '../types/mssql-data-types.js';
6
+ import { addParameters } from '../utils/mssql.js';
7
+
8
+ export const DEFAULT_SCHEMA = 'dbo';
9
+
10
+ export interface MSSQLConnectionManagerListener {
11
+ onEnded(): void;
12
+ }
13
+
14
+ export class MSSQLConnectionManager extends BaseObserver<MSSQLConnectionManagerListener> {
15
+ private readonly pool: sql.ConnectionPool;
16
+
17
+ constructor(
18
+ public options: NormalizedMSSQLConnectionConfig,
19
+ poolOptions: sql.PoolOpts<sql.Connection>
20
+ ) {
21
+ super();
22
+ // The pool is lazy - no connections are opened until a query is performed.
23
+ this.pool = new sql.ConnectionPool({
24
+ authentication: options.authentication,
25
+ user: options.username,
26
+ password: options.password,
27
+ server: options.hostname,
28
+ port: options.port,
29
+ database: options.database,
30
+ pool: poolOptions,
31
+ options: {
32
+ appName: `powersync/${POWERSYNC_VERSION}`,
33
+ encrypt: true, // Required for Azure
34
+ trustServerCertificate: options.trustServerCertificate
35
+ }
36
+ });
37
+ }
38
+
39
+ public get connectionTag() {
40
+ return this.options.tag;
41
+ }
42
+
43
+ public get connectionId() {
44
+ return this.options.id;
45
+ }
46
+
47
+ public get databaseName() {
48
+ return this.options.database;
49
+ }
50
+
51
+ public get schema() {
52
+ return this.options.schema ?? DEFAULT_SCHEMA;
53
+ }
54
+
55
+ private async ensureConnected(): Promise<void> {
56
+ await this.pool.connect();
57
+ }
58
+
59
+ async createTransaction(): Promise<sql.Transaction> {
60
+ await this.ensureConnected();
61
+ return this.pool.transaction();
62
+ }
63
+
64
+ async createRequest(): Promise<sql.Request> {
65
+ await this.ensureConnected();
66
+ return this.pool.request();
67
+ }
68
+
69
+ async query(query: string, parameters?: MSSQLParameter[]): Promise<sql.IResult<any>> {
70
+ await this.ensureConnected();
71
+ for (let tries = 2; ; tries--) {
72
+ try {
73
+ logger.debug(`Executing query: ${query}`);
74
+ let request = this.pool.request();
75
+ if (parameters) {
76
+ request = addParameters(request, parameters);
77
+ }
78
+ return await request.query(query);
79
+ } catch (e) {
80
+ if (tries == 1) {
81
+ throw e;
82
+ }
83
+ logger.warn('Query error, retrying..', e);
84
+ }
85
+ }
86
+ }
87
+
88
+ async execute(procedure: string, parameters?: MSSQLParameter[]): Promise<sql.IProcedureResult<any>> {
89
+ await this.ensureConnected();
90
+ let request = this.pool.request();
91
+ if (parameters) {
92
+ if (parameters) {
93
+ request = addParameters(request, parameters);
94
+ }
95
+ }
96
+ return request.execute(procedure);
97
+ }
98
+
99
+ async end(): Promise<void> {
100
+ if (this.pool.connected) {
101
+ try {
102
+ await this.pool.close();
103
+ } catch (error) {
104
+ // We don't particularly care if any errors are thrown when shutting down the pool
105
+ logger.warn('Error shutting down MSSQL connection pool', error);
106
+ } finally {
107
+ this.iterateListeners((listener) => {
108
+ listener.onEnded?.();
109
+ });
110
+ }
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,33 @@
1
+ import { logger } from '@powersync/lib-services-framework';
2
+ import { ResolvedMSSQLConnectionConfig } from '../types/types.js';
3
+ import { MSSQLConnectionManager } from './MSSQLConnectionManager.js';
4
+ import sql from 'mssql';
5
+
6
+ export class MSSQLConnectionManagerFactory {
7
+ private readonly connectionManagers: Set<MSSQLConnectionManager>;
8
+ public readonly connectionConfig: ResolvedMSSQLConnectionConfig;
9
+
10
+ constructor(connectionConfig: ResolvedMSSQLConnectionConfig) {
11
+ this.connectionConfig = connectionConfig;
12
+ this.connectionManagers = new Set<MSSQLConnectionManager>();
13
+ }
14
+
15
+ create(poolOptions: sql.PoolOpts<sql.Connection>) {
16
+ const manager = new MSSQLConnectionManager(this.connectionConfig, poolOptions);
17
+ manager.registerListener({
18
+ onEnded: () => {
19
+ this.connectionManagers.delete(manager);
20
+ }
21
+ });
22
+ this.connectionManagers.add(manager);
23
+ return manager;
24
+ }
25
+
26
+ async shutdown() {
27
+ logger.info('Shutting down MSSQL connection Managers...');
28
+ for (const manager of this.connectionManagers.values()) {
29
+ await manager.end();
30
+ }
31
+ logger.info('MSSQL connection Managers shutdown completed.');
32
+ }
33
+ }
@@ -0,0 +1,36 @@
1
+ import { ErrorRateLimiter } from '@powersync/service-core';
2
+ import { setTimeout } from 'timers/promises';
3
+
4
+ export class MSSQLErrorRateLimiter implements ErrorRateLimiter {
5
+ nextAllowed: number = Date.now();
6
+
7
+ async waitUntilAllowed(options?: { signal?: AbortSignal | undefined } | undefined): Promise<void> {
8
+ const delay = Math.max(0, this.nextAllowed - Date.now());
9
+ // Minimum delay between connections, even without errors
10
+ this.setDelay(500);
11
+ await setTimeout(delay, undefined, { signal: options?.signal });
12
+ }
13
+
14
+ mayPing(): boolean {
15
+ return Date.now() >= this.nextAllowed;
16
+ }
17
+
18
+ reportError(e: any): void {
19
+ const message = (e.message as string) ?? '';
20
+ if (message.includes('password authentication failed')) {
21
+ this.setDelay(900_000);
22
+ } else if (message.includes('ENOTFOUND')) {
23
+ // DNS lookup issue - incorrect URI or deleted instance
24
+ this.setDelay(120_000);
25
+ } else if (message.includes('ECONNREFUSED')) {
26
+ // Could be fail2ban or similar
27
+ this.setDelay(120_000);
28
+ } else {
29
+ this.setDelay(30_000);
30
+ }
31
+ }
32
+
33
+ private setDelay(delay: number) {
34
+ this.nextAllowed = Math.max(this.nextAllowed, Date.now() + delay);
35
+ }
36
+ }
@@ -0,0 +1,230 @@
1
+ import { bson, ColumnDescriptor, SourceTable } from '@powersync/service-core';
2
+ import { SqliteValue } from '@powersync/service-sync-rules';
3
+ import { ServiceAssertionError } from '@powersync/lib-services-framework';
4
+ import { MSSQLBaseType } from '../types/mssql-data-types.js';
5
+ import sql from 'mssql';
6
+ import { escapeIdentifier } from '../utils/mssql.js';
7
+ import { MSSQLSourceTable } from '../common/MSSQLSourceTable.js';
8
+
9
+ export interface MSSQLSnapshotQuery {
10
+ initialize(): Promise<void>;
11
+
12
+ /**
13
+ * Returns an async iterable iterator that yields the column metadata for the query followed by rows of data.
14
+ */
15
+ next(): AsyncIterableIterator<sql.IColumnMetadata | sql.IRecordSet<any>>;
16
+ }
17
+
18
+ export type PrimaryKeyValue = Record<string, SqliteValue>;
19
+
20
+ /**
21
+ * Snapshot query using a plain SELECT * FROM table
22
+ *
23
+ * This supports all tables but does not efficiently resume the snapshot
24
+ * if the process is restarted.
25
+ */
26
+ export class SimpleSnapshotQuery implements MSSQLSnapshotQuery {
27
+ public constructor(
28
+ private readonly transaction: sql.Transaction,
29
+ private readonly table: MSSQLSourceTable
30
+ ) {}
31
+
32
+ public async initialize(): Promise<void> {}
33
+
34
+ public async *next(): AsyncIterableIterator<sql.IColumnMetadata | sql.IRecordSet<any>> {
35
+ const metadataRequest = this.transaction.request();
36
+ metadataRequest.stream = true;
37
+ const metadataPromise = new Promise<sql.IColumnMetadata>((resolve, reject) => {
38
+ metadataRequest.on('recordset', resolve);
39
+ metadataRequest.on('error', reject);
40
+ });
41
+
42
+ metadataRequest.query(`SELECT TOP(0) * FROM ${this.table.toQualifiedName()}`);
43
+
44
+ const columnMetadata: sql.IColumnMetadata = await metadataPromise;
45
+ yield columnMetadata;
46
+
47
+ const request = this.transaction.request();
48
+ const stream = request.toReadableStream();
49
+
50
+ request.query(`SELECT * FROM ${this.table.toQualifiedName()}`);
51
+
52
+ // MSSQL only streams one row at a time
53
+ for await (const row of stream) {
54
+ yield row;
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Performs a table snapshot query, batching by ranges of primary key data.
61
+ *
62
+ * This may miss some rows if they are modified during the snapshot query.
63
+ * In that case, replication will pick up those rows afterward,
64
+ * possibly resulting in an IdSnapshotQuery.
65
+ *
66
+ * Currently, this only supports a table with a single primary key column,
67
+ * of a select few types.
68
+ */
69
+ export class BatchedSnapshotQuery implements MSSQLSnapshotQuery {
70
+ /**
71
+ * Primary key types that we support for batched snapshots.
72
+ *
73
+ * Can expand this over time as we add more tests,
74
+ * and ensure there are no issues with type conversion.
75
+ */
76
+ static SUPPORTED_TYPES = [
77
+ MSSQLBaseType.TEXT,
78
+ MSSQLBaseType.NTEXT,
79
+ MSSQLBaseType.VARCHAR,
80
+ MSSQLBaseType.NVARCHAR,
81
+ MSSQLBaseType.CHAR,
82
+ MSSQLBaseType.NCHAR,
83
+ MSSQLBaseType.UNIQUEIDENTIFIER,
84
+ MSSQLBaseType.TINYINT,
85
+ MSSQLBaseType.SMALLINT,
86
+ MSSQLBaseType.INT,
87
+ MSSQLBaseType.BIGINT
88
+ ];
89
+
90
+ static supports(table: SourceTable | MSSQLSourceTable): boolean {
91
+ const sourceTable = table instanceof MSSQLSourceTable ? table.sourceTable : table;
92
+ if (sourceTable.replicaIdColumns.length != 1) {
93
+ return false;
94
+ }
95
+ const primaryKey = sourceTable.replicaIdColumns[0];
96
+
97
+ return primaryKey.typeId != null && BatchedSnapshotQuery.SUPPORTED_TYPES.includes(Number(primaryKey.typeId));
98
+ }
99
+
100
+ private readonly key: ColumnDescriptor;
101
+ lastKey: string | bigint | null = null;
102
+
103
+ public constructor(
104
+ private readonly transaction: sql.Transaction,
105
+ private readonly table: MSSQLSourceTable,
106
+ private readonly batchSize: number = 10_000,
107
+ lastKeySerialized: Uint8Array | null
108
+ ) {
109
+ this.key = table.sourceTable.replicaIdColumns[0];
110
+
111
+ if (lastKeySerialized != null) {
112
+ this.lastKey = this.deserializeKey(lastKeySerialized);
113
+ }
114
+ }
115
+
116
+ public async initialize(): Promise<void> {
117
+ // No-op
118
+ }
119
+
120
+ public getLastKeySerialized(): Uint8Array {
121
+ return bson.serialize({ [this.key.name]: this.lastKey });
122
+ }
123
+
124
+ public async *next(): AsyncIterableIterator<sql.IColumnMetadata | sql.IRecordSet<any>> {
125
+ const escapedKeyName = escapeIdentifier(this.key.name);
126
+ const metadataRequest = this.transaction.request();
127
+ metadataRequest.stream = true;
128
+ const metadataPromise = new Promise<sql.IColumnMetadata>((resolve, reject) => {
129
+ metadataRequest.on('recordset', resolve);
130
+ metadataRequest.on('error', reject);
131
+ });
132
+ metadataRequest.query(`SELECT TOP(0) * FROM ${this.table.toQualifiedName()}`);
133
+
134
+ const columnMetadata: sql.IColumnMetadata = await metadataPromise;
135
+
136
+ const foundPrimaryKey = columnMetadata[this.key.name];
137
+ if (!foundPrimaryKey) {
138
+ throw new Error(
139
+ `Cannot find primary key column ${this.key.name} in results. Keys: ${Object.keys(columnMetadata.columns).join(', ')}`
140
+ );
141
+ }
142
+
143
+ yield columnMetadata;
144
+
145
+ const request = this.transaction.request();
146
+ const stream = request.toReadableStream();
147
+ if (this.lastKey == null) {
148
+ request.query(`SELECT TOP(${this.batchSize}) * FROM ${this.table.toQualifiedName()} ORDER BY ${escapedKeyName}`);
149
+ } else {
150
+ if (this.key.typeId == null) {
151
+ throw new Error(`typeId required for primary key ${this.key.name}`);
152
+ }
153
+ request
154
+ .input('lastKey', this.lastKey)
155
+ .query(
156
+ `SELECT TOP(${this.batchSize}) * FROM ${this.table.toQualifiedName()} WHERE ${escapedKeyName} > @lastKey ORDER BY ${escapedKeyName}`
157
+ );
158
+ }
159
+
160
+ // MSSQL only streams one row at a time
161
+ for await (const row of stream) {
162
+ this.lastKey = row[this.key.name];
163
+ yield row;
164
+ }
165
+ }
166
+
167
+ private deserializeKey(key: Uint8Array) {
168
+ const decoded = bson.deserialize(key, { useBigInt64: true });
169
+ const keys = Object.keys(decoded);
170
+ if (keys.length != 1) {
171
+ throw new ServiceAssertionError(`Multiple keys found: ${keys.join(', ')}`);
172
+ }
173
+ if (keys[0] != this.key.name) {
174
+ throw new ServiceAssertionError(`Key name mismatch: expected ${this.key.name}, got ${keys[0]}`);
175
+ }
176
+
177
+ return decoded[this.key.name];
178
+ }
179
+ }
180
+
181
+ /**
182
+ * This performs a snapshot query using a list of primary keys.
183
+ *
184
+ * This is not used for general snapshots, but is used when we need to re-fetch specific rows
185
+ * during streaming replication.
186
+ */
187
+ export class IdSnapshotQuery implements MSSQLSnapshotQuery {
188
+ static supports(table: SourceTable | MSSQLSourceTable) {
189
+ // We have the same requirements as BatchedSnapshotQuery.
190
+ // This is typically only used as a fallback when ChunkedSnapshotQuery
191
+ // skipped some rows.
192
+ return BatchedSnapshotQuery.supports(table);
193
+ }
194
+
195
+ public constructor(
196
+ private readonly transaction: sql.Transaction,
197
+ private readonly table: MSSQLSourceTable,
198
+ private readonly keys: PrimaryKeyValue[]
199
+ ) {}
200
+
201
+ public async initialize(): Promise<void> {
202
+ // No-op
203
+ }
204
+
205
+ public async *next(): AsyncIterableIterator<sql.IColumnMetadata | sql.IRecordSet<any>> {
206
+ const metadataRequest = this.transaction.request();
207
+ metadataRequest.stream = true;
208
+ const metadataPromise = new Promise<sql.IColumnMetadata>((resolve, reject) => {
209
+ metadataRequest.on('recordset', resolve);
210
+ metadataRequest.on('error', reject);
211
+ });
212
+ metadataRequest.query(`SELECT TOP(0) * FROM ${this.table.toQualifiedName()}`);
213
+ const columnMetadata: sql.IColumnMetadata = await metadataPromise;
214
+ yield columnMetadata;
215
+
216
+ const keyDefinition = this.table.sourceTable.replicaIdColumns[0];
217
+ const ids = this.keys.map((record) => record[keyDefinition.name]);
218
+
219
+ const request = this.transaction.request();
220
+ const stream = request.toReadableStream();
221
+ request
222
+ .input('ids', ids)
223
+ .query(`SELECT * FROM ${this.table.toQualifiedName()} WHERE ${escapeIdentifier(keyDefinition.name)} = @ids`);
224
+
225
+ // MSSQL only streams one row at a time
226
+ for await (const row of stream) {
227
+ yield row;
228
+ }
229
+ }
230
+ }
@@ -0,0 +1,79 @@
1
+ import { ColumnDescriptor } from '@powersync/service-core';
2
+ import { ISqlType } from 'mssql';
3
+
4
+ export interface MSSQLColumnDescriptor extends ColumnDescriptor {
5
+ /** The underlying system type id. For base types system type id == user type id */
6
+ typeId: number;
7
+ /** The unique user type id that uniquely identifies the type */
8
+ userTypeId: number;
9
+ // /** The name of the user/alias type. For example SYSNAME, GEOMETRY */
10
+ // userTypeName: string;
11
+ }
12
+
13
+ /** The shared system type id for all CLR_UDT types in SQL Server */
14
+ export const CLR_UDT_TYPE_ID = 240;
15
+
16
+ /**
17
+ * Enum mapping the base MSSQL data types to their system type IDs.
18
+ */
19
+ export enum MSSQLBaseType {
20
+ IMAGE = 34,
21
+ TEXT = 35,
22
+ UNIQUEIDENTIFIER = 36,
23
+ DATE = 40,
24
+ TIME = 41,
25
+ DATETIME2 = 42,
26
+ DATETIMEOFFSET = 43,
27
+ TINYINT = 48,
28
+ SMALLINT = 52,
29
+ INT = 56,
30
+ SMALLDATETIME = 58,
31
+ REAL = 59,
32
+ MONEY = 60,
33
+ DATETIME = 61,
34
+ FLOAT = 62,
35
+ SQL_VARIANT = 98,
36
+ NTEXT = 99,
37
+ BIT = 104,
38
+ DECIMAL = 106,
39
+ NUMERIC = 108,
40
+ SMALLMONEY = 122,
41
+ BIGINT = 127,
42
+ VARBINARY = 165,
43
+ VARCHAR = 167,
44
+ BINARY = 173,
45
+ CHAR = 175,
46
+ TIMESTAMP = 189,
47
+ NVARCHAR = 231,
48
+ NCHAR = 239,
49
+ XML = 241,
50
+ JSON = 244
51
+ }
52
+
53
+ /**
54
+ * Enum mapping some of the extended user-defined MSSQL data types to their user type IDs.
55
+ */
56
+ export enum MSSQLExtendedUserType {
57
+ // VARBINARY system type [155]
58
+ VECTOR = 255,
59
+ // NVARCHAR system type [231]
60
+ SYSNAME = 256,
61
+ // CLR_UDT system type [240]
62
+ HIERARCHYID = 128,
63
+ // CLR_UDT system type [240]
64
+ GEOMETRY = 129,
65
+ // CLR_UDT system type [240]
66
+ GEOGRAPHY = 130
67
+ }
68
+
69
+ export enum MSSQLUserDefinedType {
70
+ VECTOR = 'vector',
71
+ SYSNAME = 'sysname',
72
+ HIERARCHYID = 'hierarchyid'
73
+ }
74
+
75
+ export interface MSSQLParameter {
76
+ name: string;
77
+ value: any;
78
+ type?: (() => ISqlType) | ISqlType;
79
+ }