@prisma/adapter-mssql 6.15.0-dev.3 → 6.15.0-dev.30

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.
package/dist/index.d.mts CHANGED
@@ -26,11 +26,10 @@ declare class MssqlQueryable implements SqlQueryable {
26
26
  }
27
27
 
28
28
  export declare class PrismaMssql implements SqlDriverAdapterFactory {
29
- private readonly config;
30
- private readonly options?;
29
+ #private;
31
30
  readonly provider = "sqlserver";
32
31
  readonly adapterName: string;
33
- constructor(config: sql.config, options?: PrismaMssqlOptions | undefined);
32
+ constructor(configOrString: sql.config | string, options?: PrismaMssqlOptions);
34
33
  connect(): Promise<PrismaMssqlAdapter>;
35
34
  }
36
35
 
@@ -47,6 +46,8 @@ declare class PrismaMssqlAdapter extends MssqlQueryable implements SqlDriverAdap
47
46
 
48
47
  declare type PrismaMssqlOptions = {
49
48
  schema?: string;
49
+ onPoolError?: (err: unknown) => void;
50
+ onConnectionError?: (err: unknown) => void;
50
51
  };
51
52
 
52
53
  export { }
package/dist/index.d.ts CHANGED
@@ -26,11 +26,10 @@ declare class MssqlQueryable implements SqlQueryable {
26
26
  }
27
27
 
28
28
  export declare class PrismaMssql implements SqlDriverAdapterFactory {
29
- private readonly config;
30
- private readonly options?;
29
+ #private;
31
30
  readonly provider = "sqlserver";
32
31
  readonly adapterName: string;
33
- constructor(config: sql.config, options?: PrismaMssqlOptions | undefined);
32
+ constructor(configOrString: sql.config | string, options?: PrismaMssqlOptions);
34
33
  connect(): Promise<PrismaMssqlAdapter>;
35
34
  }
36
35
 
@@ -47,6 +46,8 @@ declare class PrismaMssqlAdapter extends MssqlQueryable implements SqlDriverAdap
47
46
 
48
47
  declare type PrismaMssqlOptions = {
49
48
  schema?: string;
49
+ onPoolError?: (err: unknown) => void;
50
+ onConnectionError?: (err: unknown) => void;
50
51
  };
51
52
 
52
53
  export { }
package/dist/index.js CHANGED
@@ -35,60 +35,201 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/mssql.ts
38
- var import_driver_adapter_utils2 = require("@prisma/driver-adapter-utils");
38
+ var import_driver_adapter_utils3 = require("@prisma/driver-adapter-utils");
39
39
  var import_async_mutex = require("async-mutex");
40
- var import_mssql2 = __toESM(require("mssql"));
40
+ var import_mssql3 = __toESM(require("mssql"));
41
41
 
42
42
  // package.json
43
43
  var name = "@prisma/adapter-mssql";
44
44
 
45
- // src/conversion.ts
45
+ // src/connection-string.ts
46
46
  var import_driver_adapter_utils = require("@prisma/driver-adapter-utils");
47
47
  var import_mssql = __toESM(require("mssql"));
48
+ function mapIsolationLevelFromString(level) {
49
+ const normalizedLevel = level.toUpperCase().replace(/\s+/g, "");
50
+ switch (normalizedLevel) {
51
+ case "READCOMMITTED":
52
+ return import_mssql.default.ISOLATION_LEVEL.READ_COMMITTED;
53
+ case "READUNCOMMITTED":
54
+ return import_mssql.default.ISOLATION_LEVEL.READ_UNCOMMITTED;
55
+ case "REPEATABLEREAD":
56
+ return import_mssql.default.ISOLATION_LEVEL.REPEATABLE_READ;
57
+ case "SERIALIZABLE":
58
+ return import_mssql.default.ISOLATION_LEVEL.SERIALIZABLE;
59
+ case "SNAPSHOT":
60
+ return import_mssql.default.ISOLATION_LEVEL.SNAPSHOT;
61
+ default:
62
+ throw new Error(`Invalid isolation level: ${level}`);
63
+ }
64
+ }
65
+ var debug = (0, import_driver_adapter_utils.Debug)("prisma:driver-adapter:mssql:connection-string");
66
+ function extractSchemaFromConnectionString(connectionString) {
67
+ const withoutProtocol = connectionString.replace(/^sqlserver:\/\//, "");
68
+ const parts = withoutProtocol.split(";");
69
+ for (const part of parts) {
70
+ const [key, value] = part.split("=", 2);
71
+ if (key?.trim() === "schema") {
72
+ return value?.trim();
73
+ }
74
+ }
75
+ return void 0;
76
+ }
77
+ function parseConnectionString(connectionString) {
78
+ const withoutProtocol = connectionString.replace(/^sqlserver:\/\//, "");
79
+ const [hostPart, ...paramParts] = withoutProtocol.split(";");
80
+ const config = {
81
+ server: "",
82
+ options: {},
83
+ pool: {}
84
+ };
85
+ const [host, portStr] = hostPart.split(":");
86
+ config.server = host.trim();
87
+ if (portStr) {
88
+ const port = parseInt(portStr, 10);
89
+ if (isNaN(port)) {
90
+ throw new Error(`Invalid port number: ${portStr}`);
91
+ }
92
+ config.port = port;
93
+ }
94
+ for (const part of paramParts) {
95
+ const [key, value] = part.split("=", 2);
96
+ if (!key) continue;
97
+ const trimmedKey = key.trim();
98
+ const trimmedValue = value.trim();
99
+ switch (trimmedKey) {
100
+ case "database":
101
+ case "initial catalog":
102
+ config.database = trimmedValue;
103
+ break;
104
+ case "user":
105
+ case "username":
106
+ case "uid":
107
+ case "userid":
108
+ config.user = trimmedValue;
109
+ break;
110
+ case "password":
111
+ case "pwd":
112
+ config.password = trimmedValue;
113
+ break;
114
+ case "encrypt":
115
+ config.options = config.options || {};
116
+ config.options.encrypt = trimmedValue.toLowerCase() === "true";
117
+ break;
118
+ case "trustServerCertificate":
119
+ config.options = config.options || {};
120
+ config.options.trustServerCertificate = trimmedValue.toLowerCase() === "true";
121
+ break;
122
+ case "connectionLimit": {
123
+ config.pool = config.pool || {};
124
+ const limit = parseInt(trimmedValue, 10);
125
+ if (isNaN(limit)) {
126
+ throw new Error(`Invalid connection limit: ${trimmedValue}`);
127
+ }
128
+ config.pool.max = limit;
129
+ break;
130
+ }
131
+ case "connectTimeout":
132
+ case "connectionTimeout": {
133
+ const connectTimeout = parseInt(trimmedValue, 10);
134
+ if (isNaN(connectTimeout)) {
135
+ throw new Error(`Invalid connection timeout: ${trimmedValue}`);
136
+ }
137
+ config.connectionTimeout = connectTimeout;
138
+ break;
139
+ }
140
+ case "loginTimeout": {
141
+ const loginTimeout = parseInt(trimmedValue, 10);
142
+ if (isNaN(loginTimeout)) {
143
+ throw new Error(`Invalid login timeout: ${trimmedValue}`);
144
+ }
145
+ config.connectionTimeout = loginTimeout;
146
+ break;
147
+ }
148
+ case "socketTimeout": {
149
+ const socketTimeout = parseInt(trimmedValue, 10);
150
+ if (isNaN(socketTimeout)) {
151
+ throw new Error(`Invalid socket timeout: ${trimmedValue}`);
152
+ }
153
+ config.requestTimeout = socketTimeout;
154
+ break;
155
+ }
156
+ case "poolTimeout": {
157
+ const poolTimeout = parseInt(trimmedValue, 10);
158
+ if (isNaN(poolTimeout)) {
159
+ throw new Error(`Invalid pool timeout: ${trimmedValue}`);
160
+ }
161
+ config.pool = config.pool || {};
162
+ config.pool.acquireTimeoutMillis = poolTimeout * 1e3;
163
+ break;
164
+ }
165
+ case "applicationName":
166
+ case "application name":
167
+ config.options = config.options || {};
168
+ config.options.appName = trimmedValue;
169
+ break;
170
+ case "isolationLevel":
171
+ config.options = config.options || {};
172
+ config.options.isolationLevel = mapIsolationLevelFromString(trimmedValue);
173
+ break;
174
+ case "schema":
175
+ break;
176
+ default:
177
+ debug(`Unknown connection string parameter: ${trimmedKey}`);
178
+ }
179
+ }
180
+ if (!config.server || config.server.trim() === "") {
181
+ throw new Error("Server host is required in connection string");
182
+ }
183
+ return config;
184
+ }
185
+
186
+ // src/conversion.ts
187
+ var import_driver_adapter_utils2 = require("@prisma/driver-adapter-utils");
188
+ var import_mssql2 = __toESM(require("mssql"));
48
189
  function mapColumnType(col) {
49
190
  switch (col.type) {
50
- case import_mssql.default.VarChar:
51
- case import_mssql.default.Char:
52
- case import_mssql.default.NVarChar:
53
- case import_mssql.default.NChar:
54
- case import_mssql.default.Text:
55
- case import_mssql.default.NText:
56
- case import_mssql.default.Xml:
57
- return import_driver_adapter_utils.ColumnTypeEnum.Text;
58
- case import_mssql.default.Bit:
59
- return import_driver_adapter_utils.ColumnTypeEnum.Boolean;
60
- case import_mssql.default.TinyInt:
61
- case import_mssql.default.SmallInt:
62
- case import_mssql.default.Int:
63
- return import_driver_adapter_utils.ColumnTypeEnum.Int32;
64
- case import_mssql.default.BigInt:
65
- return import_driver_adapter_utils.ColumnTypeEnum.Int64;
66
- case import_mssql.default.DateTime2:
67
- case import_mssql.default.SmallDateTime:
68
- case import_mssql.default.DateTime:
69
- case import_mssql.default.DateTimeOffset:
70
- return import_driver_adapter_utils.ColumnTypeEnum.DateTime;
71
- case import_mssql.default.Real:
72
- return import_driver_adapter_utils.ColumnTypeEnum.Float;
73
- case import_mssql.default.Float:
74
- case import_mssql.default.Money:
75
- case import_mssql.default.SmallMoney:
76
- return import_driver_adapter_utils.ColumnTypeEnum.Double;
77
- case import_mssql.default.UniqueIdentifier:
78
- return import_driver_adapter_utils.ColumnTypeEnum.Uuid;
79
- case import_mssql.default.Decimal:
80
- case import_mssql.default.Numeric:
81
- return import_driver_adapter_utils.ColumnTypeEnum.Numeric;
82
- case import_mssql.default.Date:
83
- return import_driver_adapter_utils.ColumnTypeEnum.Date;
84
- case import_mssql.default.Time:
85
- return import_driver_adapter_utils.ColumnTypeEnum.Time;
86
- case import_mssql.default.VarBinary:
87
- case import_mssql.default.Binary:
88
- case import_mssql.default.Image:
89
- return import_driver_adapter_utils.ColumnTypeEnum.Bytes;
191
+ case import_mssql2.default.VarChar:
192
+ case import_mssql2.default.Char:
193
+ case import_mssql2.default.NVarChar:
194
+ case import_mssql2.default.NChar:
195
+ case import_mssql2.default.Text:
196
+ case import_mssql2.default.NText:
197
+ case import_mssql2.default.Xml:
198
+ return import_driver_adapter_utils2.ColumnTypeEnum.Text;
199
+ case import_mssql2.default.Bit:
200
+ return import_driver_adapter_utils2.ColumnTypeEnum.Boolean;
201
+ case import_mssql2.default.TinyInt:
202
+ case import_mssql2.default.SmallInt:
203
+ case import_mssql2.default.Int:
204
+ return import_driver_adapter_utils2.ColumnTypeEnum.Int32;
205
+ case import_mssql2.default.BigInt:
206
+ return import_driver_adapter_utils2.ColumnTypeEnum.Int64;
207
+ case import_mssql2.default.DateTime2:
208
+ case import_mssql2.default.SmallDateTime:
209
+ case import_mssql2.default.DateTime:
210
+ case import_mssql2.default.DateTimeOffset:
211
+ return import_driver_adapter_utils2.ColumnTypeEnum.DateTime;
212
+ case import_mssql2.default.Real:
213
+ return import_driver_adapter_utils2.ColumnTypeEnum.Float;
214
+ case import_mssql2.default.Float:
215
+ case import_mssql2.default.Money:
216
+ case import_mssql2.default.SmallMoney:
217
+ return import_driver_adapter_utils2.ColumnTypeEnum.Double;
218
+ case import_mssql2.default.UniqueIdentifier:
219
+ return import_driver_adapter_utils2.ColumnTypeEnum.Uuid;
220
+ case import_mssql2.default.Decimal:
221
+ case import_mssql2.default.Numeric:
222
+ return import_driver_adapter_utils2.ColumnTypeEnum.Numeric;
223
+ case import_mssql2.default.Date:
224
+ return import_driver_adapter_utils2.ColumnTypeEnum.Date;
225
+ case import_mssql2.default.Time:
226
+ return import_driver_adapter_utils2.ColumnTypeEnum.Time;
227
+ case import_mssql2.default.VarBinary:
228
+ case import_mssql2.default.Binary:
229
+ case import_mssql2.default.Image:
230
+ return import_driver_adapter_utils2.ColumnTypeEnum.Bytes;
90
231
  default:
91
- throw new import_driver_adapter_utils.DriverAdapterError({
232
+ throw new import_driver_adapter_utils2.DriverAdapterError({
92
233
  kind: "UnsupportedNativeDataType",
93
234
  type: col["udt"]?.name ?? "N/A"
94
235
  });
@@ -97,25 +238,28 @@ function mapColumnType(col) {
97
238
  function mapIsolationLevel(level) {
98
239
  switch (level) {
99
240
  case "READ COMMITTED":
100
- return import_mssql.default.ISOLATION_LEVEL.READ_COMMITTED;
241
+ return import_mssql2.default.ISOLATION_LEVEL.READ_COMMITTED;
101
242
  case "READ UNCOMMITTED":
102
- return import_mssql.default.ISOLATION_LEVEL.READ_UNCOMMITTED;
243
+ return import_mssql2.default.ISOLATION_LEVEL.READ_UNCOMMITTED;
103
244
  case "REPEATABLE READ":
104
- return import_mssql.default.ISOLATION_LEVEL.REPEATABLE_READ;
245
+ return import_mssql2.default.ISOLATION_LEVEL.REPEATABLE_READ;
105
246
  case "SERIALIZABLE":
106
- return import_mssql.default.ISOLATION_LEVEL.SERIALIZABLE;
247
+ return import_mssql2.default.ISOLATION_LEVEL.SERIALIZABLE;
107
248
  case "SNAPSHOT":
108
- return import_mssql.default.ISOLATION_LEVEL.SNAPSHOT;
249
+ return import_mssql2.default.ISOLATION_LEVEL.SNAPSHOT;
109
250
  default:
110
- throw new import_driver_adapter_utils.DriverAdapterError({
251
+ throw new import_driver_adapter_utils2.DriverAdapterError({
111
252
  kind: "InvalidIsolationLevel",
112
253
  level
113
254
  });
114
255
  }
115
256
  }
116
- function mapArg(arg) {
117
- if (arg instanceof Uint8Array) {
118
- return Buffer.from(arg);
257
+ function mapArg(arg, argType) {
258
+ if (arg === null) {
259
+ return null;
260
+ }
261
+ if (typeof arg === "string" && argType.scalarType === "bigint") {
262
+ arg = BigInt(arg);
119
263
  }
120
264
  if (typeof arg === "bigint") {
121
265
  if (arg >= BigInt(Number.MIN_SAFE_INTEGER) && arg <= BigInt(Number.MAX_SAFE_INTEGER)) {
@@ -123,28 +267,74 @@ function mapArg(arg) {
123
267
  }
124
268
  return arg.toString();
125
269
  }
270
+ if (typeof arg === "string" && argType.scalarType === "datetime") {
271
+ arg = new Date(arg);
272
+ }
273
+ if (arg instanceof Date) {
274
+ switch (argType.dbType) {
275
+ case "TIME":
276
+ return formatTime(arg);
277
+ case "DATE":
278
+ return formatDate(arg);
279
+ default:
280
+ return formatDateTime(arg);
281
+ }
282
+ }
283
+ if (typeof arg === "string" && argType.scalarType === "bytes") {
284
+ return Buffer.from(arg, "base64");
285
+ }
286
+ if (Array.isArray(arg) && argType.scalarType === "bytes") {
287
+ return Buffer.from(arg);
288
+ }
289
+ if (ArrayBuffer.isView(arg)) {
290
+ return Buffer.from(arg.buffer, arg.byteOffset, arg.byteLength);
291
+ }
126
292
  return arg;
127
293
  }
128
294
  function mapRow(row, columns) {
129
295
  return row.map((value, i) => {
296
+ const type = columns?.[i]?.type;
130
297
  if (value instanceof Date) {
131
- if (columns?.[i]?.type === import_mssql.default.Time) {
132
- return value.toISOString().split("T").at(1)?.replace("Z", "");
298
+ if (type === import_mssql2.default.Time) {
299
+ return value.toISOString().split("T")[1].replace("Z", "");
133
300
  }
134
301
  return value.toISOString();
135
302
  }
303
+ if (typeof value === "number" && type === import_mssql2.default.Real) {
304
+ for (let digits = 7; digits <= 9; digits++) {
305
+ const parsed = Number.parseFloat(value.toPrecision(digits));
306
+ if (value === new Float32Array([parsed])[0]) {
307
+ return parsed;
308
+ }
309
+ }
310
+ return value;
311
+ }
136
312
  if (Buffer.isBuffer(value)) {
137
313
  return Array.from(value);
138
314
  }
139
- if (typeof value === "string" && columns?.[i].type === import_mssql.default.UniqueIdentifier) {
315
+ if (typeof value === "string" && type === import_mssql2.default.UniqueIdentifier) {
140
316
  return value.toLowerCase();
141
317
  }
142
- if (typeof value === "boolean" && columns?.[i]?.type === import_mssql.default.Bit) {
318
+ if (typeof value === "boolean" && type === import_mssql2.default.Bit) {
143
319
  return value ? 1 : 0;
144
320
  }
145
321
  return value;
146
322
  });
147
323
  }
324
+ function formatDateTime(date) {
325
+ const pad = (n, z = 2) => String(n).padStart(z, "0");
326
+ const ms = date.getUTCMilliseconds();
327
+ return date.getUTCFullYear() + "-" + pad(date.getUTCMonth() + 1) + "-" + pad(date.getUTCDate()) + " " + pad(date.getUTCHours()) + ":" + pad(date.getUTCMinutes()) + ":" + pad(date.getUTCSeconds()) + (ms ? "." + String(ms).padStart(3, "0") : "");
328
+ }
329
+ function formatDate(date) {
330
+ const pad = (n, z = 2) => String(n).padStart(z, "0");
331
+ return date.getUTCFullYear() + "-" + pad(date.getUTCMonth() + 1) + "-" + pad(date.getUTCDate());
332
+ }
333
+ function formatTime(date) {
334
+ const pad = (n, z = 2) => String(n).padStart(z, "0");
335
+ const ms = date.getUTCMilliseconds();
336
+ return pad(date.getUTCHours()) + ":" + pad(date.getUTCMinutes()) + ":" + pad(date.getUTCSeconds()) + (ms ? "." + String(ms).padStart(3, "0") : "");
337
+ }
148
338
 
149
339
  // src/errors.ts
150
340
  function convertDriverError(error) {
@@ -331,7 +521,7 @@ function isDriverError(error) {
331
521
  }
332
522
 
333
523
  // src/mssql.ts
334
- var debug = (0, import_driver_adapter_utils2.Debug)("prisma:driver-adapter:mssql");
524
+ var debug2 = (0, import_driver_adapter_utils3.Debug)("prisma:driver-adapter:mssql");
335
525
  var MssqlQueryable = class {
336
526
  constructor(conn) {
337
527
  this.conn = conn;
@@ -340,7 +530,7 @@ var MssqlQueryable = class {
340
530
  adapterName = name;
341
531
  async queryRaw(query) {
342
532
  const tag = "[js::query_raw]";
343
- debug(`${tag} %O`, query);
533
+ debug2(`${tag} %O`, query);
344
534
  const { recordset, columns: columnsList } = await this.performIO(query);
345
535
  const columns = columnsList?.[0];
346
536
  return {
@@ -351,7 +541,7 @@ var MssqlQueryable = class {
351
541
  }
352
542
  async executeRaw(query) {
353
543
  const tag = "[js::execute_raw]";
354
- debug(`${tag} %O`, query);
544
+ debug2(`${tag} %O`, query);
355
545
  return (await this.performIO(query)).rowsAffected?.[0] ?? 0;
356
546
  }
357
547
  async performIO(query) {
@@ -359,7 +549,7 @@ var MssqlQueryable = class {
359
549
  const req = this.conn.request();
360
550
  req.arrayRowMode = true;
361
551
  for (let i = 0; i < query.args.length; i++) {
362
- req.input(`P${i + 1}`, mapArg(query.args[i]));
552
+ req.input(`P${i + 1}`, mapArg(query.args[i], query.argTypes[i]));
363
553
  }
364
554
  const res = await req.query(query.sql);
365
555
  return res;
@@ -368,8 +558,8 @@ var MssqlQueryable = class {
368
558
  }
369
559
  }
370
560
  onError(error) {
371
- debug("Error in performIO: %O", error);
372
- throw new import_driver_adapter_utils2.DriverAdapterError(convertDriverError(error));
561
+ debug2("Error in performIO: %O", error);
562
+ throw new import_driver_adapter_utils3.DriverAdapterError(convertDriverError(error));
373
563
  }
374
564
  };
375
565
  var MssqlTransaction = class extends MssqlQueryable {
@@ -390,14 +580,14 @@ var MssqlTransaction = class extends MssqlQueryable {
390
580
  }
391
581
  }
392
582
  async commit() {
393
- debug(`[js::commit]`);
583
+ debug2(`[js::commit]`);
394
584
  await this.transaction.commit();
395
585
  }
396
586
  async rollback() {
397
- debug(`[js::rollback]`);
587
+ debug2(`[js::rollback]`);
398
588
  await this.transaction.rollback().catch((e) => {
399
589
  if (e.code === "EABORT") {
400
- debug(`[js::rollback] Transaction already aborted`);
590
+ debug2(`[js::rollback] Transaction already aborted`);
401
591
  return;
402
592
  }
403
593
  throw e;
@@ -418,8 +608,12 @@ var PrismaMssqlAdapter = class extends MssqlQueryable {
418
608
  usePhantomQuery: true
419
609
  };
420
610
  const tag = "[js::startTransaction]";
421
- debug("%s options: %O", tag, options);
611
+ debug2("%s options: %O", tag, options);
422
612
  const tx = this.pool.transaction();
613
+ tx.on("error", (err) => {
614
+ debug2("Error from pool connection: %O", err);
615
+ this.options?.onConnectionError?.(err);
616
+ });
423
617
  try {
424
618
  await tx.begin(isolationLevel !== void 0 ? mapIsolationLevel(isolationLevel) : void 0);
425
619
  return new MssqlTransaction(tx, options);
@@ -442,16 +636,31 @@ var PrismaMssqlAdapter = class extends MssqlQueryable {
442
636
  }
443
637
  };
444
638
  var PrismaMssqlAdapterFactory = class {
445
- constructor(config, options) {
446
- this.config = config;
447
- this.options = options;
448
- }
449
639
  provider = "sqlserver";
450
640
  adapterName = name;
641
+ #config;
642
+ #options;
643
+ constructor(configOrString, options) {
644
+ if (typeof configOrString === "string") {
645
+ this.#config = parseConnectionString(configOrString);
646
+ const extractedSchema = extractSchemaFromConnectionString(configOrString);
647
+ this.#options = {
648
+ ...options,
649
+ schema: options?.schema ?? extractedSchema
650
+ };
651
+ } else {
652
+ this.#config = configOrString;
653
+ this.#options = options ?? {};
654
+ }
655
+ }
451
656
  async connect() {
452
- const pool = new import_mssql2.default.ConnectionPool(this.config);
657
+ const pool = new import_mssql3.default.ConnectionPool(this.#config);
658
+ pool.on("error", (err) => {
659
+ debug2("Error from pool client: %O", err);
660
+ this.#options?.onPoolError?.(err);
661
+ });
453
662
  await pool.connect();
454
- return new PrismaMssqlAdapter(pool, this.options);
663
+ return new PrismaMssqlAdapter(pool, this.#options);
455
664
  }
456
665
  };
457
666
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.mjs CHANGED
@@ -1,58 +1,202 @@
1
1
  // src/mssql.ts
2
2
  import {
3
- Debug,
3
+ Debug as Debug2,
4
4
  DriverAdapterError as DriverAdapterError2
5
5
  } from "@prisma/driver-adapter-utils";
6
6
  import { Mutex } from "async-mutex";
7
- import sql2 from "mssql";
7
+ import sql3 from "mssql";
8
8
 
9
9
  // package.json
10
10
  var name = "@prisma/adapter-mssql";
11
11
 
12
- // src/conversion.ts
13
- import { ColumnTypeEnum, DriverAdapterError } from "@prisma/driver-adapter-utils";
12
+ // src/connection-string.ts
13
+ import { Debug } from "@prisma/driver-adapter-utils";
14
14
  import sql from "mssql";
15
+ function mapIsolationLevelFromString(level) {
16
+ const normalizedLevel = level.toUpperCase().replace(/\s+/g, "");
17
+ switch (normalizedLevel) {
18
+ case "READCOMMITTED":
19
+ return sql.ISOLATION_LEVEL.READ_COMMITTED;
20
+ case "READUNCOMMITTED":
21
+ return sql.ISOLATION_LEVEL.READ_UNCOMMITTED;
22
+ case "REPEATABLEREAD":
23
+ return sql.ISOLATION_LEVEL.REPEATABLE_READ;
24
+ case "SERIALIZABLE":
25
+ return sql.ISOLATION_LEVEL.SERIALIZABLE;
26
+ case "SNAPSHOT":
27
+ return sql.ISOLATION_LEVEL.SNAPSHOT;
28
+ default:
29
+ throw new Error(`Invalid isolation level: ${level}`);
30
+ }
31
+ }
32
+ var debug = Debug("prisma:driver-adapter:mssql:connection-string");
33
+ function extractSchemaFromConnectionString(connectionString) {
34
+ const withoutProtocol = connectionString.replace(/^sqlserver:\/\//, "");
35
+ const parts = withoutProtocol.split(";");
36
+ for (const part of parts) {
37
+ const [key, value] = part.split("=", 2);
38
+ if (key?.trim() === "schema") {
39
+ return value?.trim();
40
+ }
41
+ }
42
+ return void 0;
43
+ }
44
+ function parseConnectionString(connectionString) {
45
+ const withoutProtocol = connectionString.replace(/^sqlserver:\/\//, "");
46
+ const [hostPart, ...paramParts] = withoutProtocol.split(";");
47
+ const config = {
48
+ server: "",
49
+ options: {},
50
+ pool: {}
51
+ };
52
+ const [host, portStr] = hostPart.split(":");
53
+ config.server = host.trim();
54
+ if (portStr) {
55
+ const port = parseInt(portStr, 10);
56
+ if (isNaN(port)) {
57
+ throw new Error(`Invalid port number: ${portStr}`);
58
+ }
59
+ config.port = port;
60
+ }
61
+ for (const part of paramParts) {
62
+ const [key, value] = part.split("=", 2);
63
+ if (!key) continue;
64
+ const trimmedKey = key.trim();
65
+ const trimmedValue = value.trim();
66
+ switch (trimmedKey) {
67
+ case "database":
68
+ case "initial catalog":
69
+ config.database = trimmedValue;
70
+ break;
71
+ case "user":
72
+ case "username":
73
+ case "uid":
74
+ case "userid":
75
+ config.user = trimmedValue;
76
+ break;
77
+ case "password":
78
+ case "pwd":
79
+ config.password = trimmedValue;
80
+ break;
81
+ case "encrypt":
82
+ config.options = config.options || {};
83
+ config.options.encrypt = trimmedValue.toLowerCase() === "true";
84
+ break;
85
+ case "trustServerCertificate":
86
+ config.options = config.options || {};
87
+ config.options.trustServerCertificate = trimmedValue.toLowerCase() === "true";
88
+ break;
89
+ case "connectionLimit": {
90
+ config.pool = config.pool || {};
91
+ const limit = parseInt(trimmedValue, 10);
92
+ if (isNaN(limit)) {
93
+ throw new Error(`Invalid connection limit: ${trimmedValue}`);
94
+ }
95
+ config.pool.max = limit;
96
+ break;
97
+ }
98
+ case "connectTimeout":
99
+ case "connectionTimeout": {
100
+ const connectTimeout = parseInt(trimmedValue, 10);
101
+ if (isNaN(connectTimeout)) {
102
+ throw new Error(`Invalid connection timeout: ${trimmedValue}`);
103
+ }
104
+ config.connectionTimeout = connectTimeout;
105
+ break;
106
+ }
107
+ case "loginTimeout": {
108
+ const loginTimeout = parseInt(trimmedValue, 10);
109
+ if (isNaN(loginTimeout)) {
110
+ throw new Error(`Invalid login timeout: ${trimmedValue}`);
111
+ }
112
+ config.connectionTimeout = loginTimeout;
113
+ break;
114
+ }
115
+ case "socketTimeout": {
116
+ const socketTimeout = parseInt(trimmedValue, 10);
117
+ if (isNaN(socketTimeout)) {
118
+ throw new Error(`Invalid socket timeout: ${trimmedValue}`);
119
+ }
120
+ config.requestTimeout = socketTimeout;
121
+ break;
122
+ }
123
+ case "poolTimeout": {
124
+ const poolTimeout = parseInt(trimmedValue, 10);
125
+ if (isNaN(poolTimeout)) {
126
+ throw new Error(`Invalid pool timeout: ${trimmedValue}`);
127
+ }
128
+ config.pool = config.pool || {};
129
+ config.pool.acquireTimeoutMillis = poolTimeout * 1e3;
130
+ break;
131
+ }
132
+ case "applicationName":
133
+ case "application name":
134
+ config.options = config.options || {};
135
+ config.options.appName = trimmedValue;
136
+ break;
137
+ case "isolationLevel":
138
+ config.options = config.options || {};
139
+ config.options.isolationLevel = mapIsolationLevelFromString(trimmedValue);
140
+ break;
141
+ case "schema":
142
+ break;
143
+ default:
144
+ debug(`Unknown connection string parameter: ${trimmedKey}`);
145
+ }
146
+ }
147
+ if (!config.server || config.server.trim() === "") {
148
+ throw new Error("Server host is required in connection string");
149
+ }
150
+ return config;
151
+ }
152
+
153
+ // src/conversion.ts
154
+ import {
155
+ ColumnTypeEnum,
156
+ DriverAdapterError
157
+ } from "@prisma/driver-adapter-utils";
158
+ import sql2 from "mssql";
15
159
  function mapColumnType(col) {
16
160
  switch (col.type) {
17
- case sql.VarChar:
18
- case sql.Char:
19
- case sql.NVarChar:
20
- case sql.NChar:
21
- case sql.Text:
22
- case sql.NText:
23
- case sql.Xml:
161
+ case sql2.VarChar:
162
+ case sql2.Char:
163
+ case sql2.NVarChar:
164
+ case sql2.NChar:
165
+ case sql2.Text:
166
+ case sql2.NText:
167
+ case sql2.Xml:
24
168
  return ColumnTypeEnum.Text;
25
- case sql.Bit:
169
+ case sql2.Bit:
26
170
  return ColumnTypeEnum.Boolean;
27
- case sql.TinyInt:
28
- case sql.SmallInt:
29
- case sql.Int:
171
+ case sql2.TinyInt:
172
+ case sql2.SmallInt:
173
+ case sql2.Int:
30
174
  return ColumnTypeEnum.Int32;
31
- case sql.BigInt:
175
+ case sql2.BigInt:
32
176
  return ColumnTypeEnum.Int64;
33
- case sql.DateTime2:
34
- case sql.SmallDateTime:
35
- case sql.DateTime:
36
- case sql.DateTimeOffset:
177
+ case sql2.DateTime2:
178
+ case sql2.SmallDateTime:
179
+ case sql2.DateTime:
180
+ case sql2.DateTimeOffset:
37
181
  return ColumnTypeEnum.DateTime;
38
- case sql.Real:
182
+ case sql2.Real:
39
183
  return ColumnTypeEnum.Float;
40
- case sql.Float:
41
- case sql.Money:
42
- case sql.SmallMoney:
184
+ case sql2.Float:
185
+ case sql2.Money:
186
+ case sql2.SmallMoney:
43
187
  return ColumnTypeEnum.Double;
44
- case sql.UniqueIdentifier:
188
+ case sql2.UniqueIdentifier:
45
189
  return ColumnTypeEnum.Uuid;
46
- case sql.Decimal:
47
- case sql.Numeric:
190
+ case sql2.Decimal:
191
+ case sql2.Numeric:
48
192
  return ColumnTypeEnum.Numeric;
49
- case sql.Date:
193
+ case sql2.Date:
50
194
  return ColumnTypeEnum.Date;
51
- case sql.Time:
195
+ case sql2.Time:
52
196
  return ColumnTypeEnum.Time;
53
- case sql.VarBinary:
54
- case sql.Binary:
55
- case sql.Image:
197
+ case sql2.VarBinary:
198
+ case sql2.Binary:
199
+ case sql2.Image:
56
200
  return ColumnTypeEnum.Bytes;
57
201
  default:
58
202
  throw new DriverAdapterError({
@@ -64,15 +208,15 @@ function mapColumnType(col) {
64
208
  function mapIsolationLevel(level) {
65
209
  switch (level) {
66
210
  case "READ COMMITTED":
67
- return sql.ISOLATION_LEVEL.READ_COMMITTED;
211
+ return sql2.ISOLATION_LEVEL.READ_COMMITTED;
68
212
  case "READ UNCOMMITTED":
69
- return sql.ISOLATION_LEVEL.READ_UNCOMMITTED;
213
+ return sql2.ISOLATION_LEVEL.READ_UNCOMMITTED;
70
214
  case "REPEATABLE READ":
71
- return sql.ISOLATION_LEVEL.REPEATABLE_READ;
215
+ return sql2.ISOLATION_LEVEL.REPEATABLE_READ;
72
216
  case "SERIALIZABLE":
73
- return sql.ISOLATION_LEVEL.SERIALIZABLE;
217
+ return sql2.ISOLATION_LEVEL.SERIALIZABLE;
74
218
  case "SNAPSHOT":
75
- return sql.ISOLATION_LEVEL.SNAPSHOT;
219
+ return sql2.ISOLATION_LEVEL.SNAPSHOT;
76
220
  default:
77
221
  throw new DriverAdapterError({
78
222
  kind: "InvalidIsolationLevel",
@@ -80,9 +224,12 @@ function mapIsolationLevel(level) {
80
224
  });
81
225
  }
82
226
  }
83
- function mapArg(arg) {
84
- if (arg instanceof Uint8Array) {
85
- return Buffer.from(arg);
227
+ function mapArg(arg, argType) {
228
+ if (arg === null) {
229
+ return null;
230
+ }
231
+ if (typeof arg === "string" && argType.scalarType === "bigint") {
232
+ arg = BigInt(arg);
86
233
  }
87
234
  if (typeof arg === "bigint") {
88
235
  if (arg >= BigInt(Number.MIN_SAFE_INTEGER) && arg <= BigInt(Number.MAX_SAFE_INTEGER)) {
@@ -90,28 +237,74 @@ function mapArg(arg) {
90
237
  }
91
238
  return arg.toString();
92
239
  }
240
+ if (typeof arg === "string" && argType.scalarType === "datetime") {
241
+ arg = new Date(arg);
242
+ }
243
+ if (arg instanceof Date) {
244
+ switch (argType.dbType) {
245
+ case "TIME":
246
+ return formatTime(arg);
247
+ case "DATE":
248
+ return formatDate(arg);
249
+ default:
250
+ return formatDateTime(arg);
251
+ }
252
+ }
253
+ if (typeof arg === "string" && argType.scalarType === "bytes") {
254
+ return Buffer.from(arg, "base64");
255
+ }
256
+ if (Array.isArray(arg) && argType.scalarType === "bytes") {
257
+ return Buffer.from(arg);
258
+ }
259
+ if (ArrayBuffer.isView(arg)) {
260
+ return Buffer.from(arg.buffer, arg.byteOffset, arg.byteLength);
261
+ }
93
262
  return arg;
94
263
  }
95
264
  function mapRow(row, columns) {
96
265
  return row.map((value, i) => {
266
+ const type = columns?.[i]?.type;
97
267
  if (value instanceof Date) {
98
- if (columns?.[i]?.type === sql.Time) {
99
- return value.toISOString().split("T").at(1)?.replace("Z", "");
268
+ if (type === sql2.Time) {
269
+ return value.toISOString().split("T")[1].replace("Z", "");
100
270
  }
101
271
  return value.toISOString();
102
272
  }
273
+ if (typeof value === "number" && type === sql2.Real) {
274
+ for (let digits = 7; digits <= 9; digits++) {
275
+ const parsed = Number.parseFloat(value.toPrecision(digits));
276
+ if (value === new Float32Array([parsed])[0]) {
277
+ return parsed;
278
+ }
279
+ }
280
+ return value;
281
+ }
103
282
  if (Buffer.isBuffer(value)) {
104
283
  return Array.from(value);
105
284
  }
106
- if (typeof value === "string" && columns?.[i].type === sql.UniqueIdentifier) {
285
+ if (typeof value === "string" && type === sql2.UniqueIdentifier) {
107
286
  return value.toLowerCase();
108
287
  }
109
- if (typeof value === "boolean" && columns?.[i]?.type === sql.Bit) {
288
+ if (typeof value === "boolean" && type === sql2.Bit) {
110
289
  return value ? 1 : 0;
111
290
  }
112
291
  return value;
113
292
  });
114
293
  }
294
+ function formatDateTime(date) {
295
+ const pad = (n, z = 2) => String(n).padStart(z, "0");
296
+ const ms = date.getUTCMilliseconds();
297
+ return date.getUTCFullYear() + "-" + pad(date.getUTCMonth() + 1) + "-" + pad(date.getUTCDate()) + " " + pad(date.getUTCHours()) + ":" + pad(date.getUTCMinutes()) + ":" + pad(date.getUTCSeconds()) + (ms ? "." + String(ms).padStart(3, "0") : "");
298
+ }
299
+ function formatDate(date) {
300
+ const pad = (n, z = 2) => String(n).padStart(z, "0");
301
+ return date.getUTCFullYear() + "-" + pad(date.getUTCMonth() + 1) + "-" + pad(date.getUTCDate());
302
+ }
303
+ function formatTime(date) {
304
+ const pad = (n, z = 2) => String(n).padStart(z, "0");
305
+ const ms = date.getUTCMilliseconds();
306
+ return pad(date.getUTCHours()) + ":" + pad(date.getUTCMinutes()) + ":" + pad(date.getUTCSeconds()) + (ms ? "." + String(ms).padStart(3, "0") : "");
307
+ }
115
308
 
116
309
  // src/errors.ts
117
310
  function convertDriverError(error) {
@@ -298,7 +491,7 @@ function isDriverError(error) {
298
491
  }
299
492
 
300
493
  // src/mssql.ts
301
- var debug = Debug("prisma:driver-adapter:mssql");
494
+ var debug2 = Debug2("prisma:driver-adapter:mssql");
302
495
  var MssqlQueryable = class {
303
496
  constructor(conn) {
304
497
  this.conn = conn;
@@ -307,7 +500,7 @@ var MssqlQueryable = class {
307
500
  adapterName = name;
308
501
  async queryRaw(query) {
309
502
  const tag = "[js::query_raw]";
310
- debug(`${tag} %O`, query);
503
+ debug2(`${tag} %O`, query);
311
504
  const { recordset, columns: columnsList } = await this.performIO(query);
312
505
  const columns = columnsList?.[0];
313
506
  return {
@@ -318,7 +511,7 @@ var MssqlQueryable = class {
318
511
  }
319
512
  async executeRaw(query) {
320
513
  const tag = "[js::execute_raw]";
321
- debug(`${tag} %O`, query);
514
+ debug2(`${tag} %O`, query);
322
515
  return (await this.performIO(query)).rowsAffected?.[0] ?? 0;
323
516
  }
324
517
  async performIO(query) {
@@ -326,7 +519,7 @@ var MssqlQueryable = class {
326
519
  const req = this.conn.request();
327
520
  req.arrayRowMode = true;
328
521
  for (let i = 0; i < query.args.length; i++) {
329
- req.input(`P${i + 1}`, mapArg(query.args[i]));
522
+ req.input(`P${i + 1}`, mapArg(query.args[i], query.argTypes[i]));
330
523
  }
331
524
  const res = await req.query(query.sql);
332
525
  return res;
@@ -335,7 +528,7 @@ var MssqlQueryable = class {
335
528
  }
336
529
  }
337
530
  onError(error) {
338
- debug("Error in performIO: %O", error);
531
+ debug2("Error in performIO: %O", error);
339
532
  throw new DriverAdapterError2(convertDriverError(error));
340
533
  }
341
534
  };
@@ -357,14 +550,14 @@ var MssqlTransaction = class extends MssqlQueryable {
357
550
  }
358
551
  }
359
552
  async commit() {
360
- debug(`[js::commit]`);
553
+ debug2(`[js::commit]`);
361
554
  await this.transaction.commit();
362
555
  }
363
556
  async rollback() {
364
- debug(`[js::rollback]`);
557
+ debug2(`[js::rollback]`);
365
558
  await this.transaction.rollback().catch((e) => {
366
559
  if (e.code === "EABORT") {
367
- debug(`[js::rollback] Transaction already aborted`);
560
+ debug2(`[js::rollback] Transaction already aborted`);
368
561
  return;
369
562
  }
370
563
  throw e;
@@ -385,8 +578,12 @@ var PrismaMssqlAdapter = class extends MssqlQueryable {
385
578
  usePhantomQuery: true
386
579
  };
387
580
  const tag = "[js::startTransaction]";
388
- debug("%s options: %O", tag, options);
581
+ debug2("%s options: %O", tag, options);
389
582
  const tx = this.pool.transaction();
583
+ tx.on("error", (err) => {
584
+ debug2("Error from pool connection: %O", err);
585
+ this.options?.onConnectionError?.(err);
586
+ });
390
587
  try {
391
588
  await tx.begin(isolationLevel !== void 0 ? mapIsolationLevel(isolationLevel) : void 0);
392
589
  return new MssqlTransaction(tx, options);
@@ -409,16 +606,31 @@ var PrismaMssqlAdapter = class extends MssqlQueryable {
409
606
  }
410
607
  };
411
608
  var PrismaMssqlAdapterFactory = class {
412
- constructor(config, options) {
413
- this.config = config;
414
- this.options = options;
415
- }
416
609
  provider = "sqlserver";
417
610
  adapterName = name;
611
+ #config;
612
+ #options;
613
+ constructor(configOrString, options) {
614
+ if (typeof configOrString === "string") {
615
+ this.#config = parseConnectionString(configOrString);
616
+ const extractedSchema = extractSchemaFromConnectionString(configOrString);
617
+ this.#options = {
618
+ ...options,
619
+ schema: options?.schema ?? extractedSchema
620
+ };
621
+ } else {
622
+ this.#config = configOrString;
623
+ this.#options = options ?? {};
624
+ }
625
+ }
418
626
  async connect() {
419
- const pool = new sql2.ConnectionPool(this.config);
627
+ const pool = new sql3.ConnectionPool(this.#config);
628
+ pool.on("error", (err) => {
629
+ debug2("Error from pool client: %O", err);
630
+ this.#options?.onPoolError?.(err);
631
+ });
420
632
  await pool.connect();
421
- return new PrismaMssqlAdapter(pool, this.options);
633
+ return new PrismaMssqlAdapter(pool, this.#options);
422
634
  }
423
635
  };
424
636
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/adapter-mssql",
3
- "version": "6.15.0-dev.3",
3
+ "version": "6.15.0-dev.30",
4
4
  "description": "Prisma's driver adapter for \"mssql\"",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -33,18 +33,15 @@
33
33
  "dependencies": {
34
34
  "mssql": "^11.0.1",
35
35
  "async-mutex": "0.5.0",
36
- "@prisma/driver-adapter-utils": "6.15.0-dev.3"
36
+ "@prisma/driver-adapter-utils": "6.15.0-dev.30"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/mssql": "9.1.7",
40
- "@swc/core": "1.11.5",
41
- "@swc/jest": "0.2.37",
42
- "jest": "29.7.0",
43
- "jest-junit": "16.0.0"
40
+ "vitest": "3.0.9"
44
41
  },
45
42
  "scripts": {
46
43
  "dev": "DEV=true tsx helpers/build.ts",
47
44
  "build": "tsx helpers/build.ts",
48
- "test": "jest"
45
+ "test": "vitest run"
49
46
  }
50
47
  }