@prisma/adapter-planetscale 6.10.0-dev.2 → 6.10.0-dev.4

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.js CHANGED
@@ -37,6 +37,7 @@ module.exports = __toCommonJS(index_exports);
37
37
  // src/planetscale.ts
38
38
  var planetScale = __toESM(require("@planetscale/database"));
39
39
  var import_driver_adapter_utils2 = require("@prisma/driver-adapter-utils");
40
+ var import_async_mutex = require("async-mutex");
40
41
 
41
42
  // package.json
42
43
  var name = "@prisma/adapter-planetscale";
@@ -132,6 +133,117 @@ function createDeferred() {
132
133
  ];
133
134
  }
134
135
 
136
+ // src/errors.ts
137
+ function convertDriverError(error) {
138
+ switch (error.code) {
139
+ case 1062: {
140
+ const index = error.message.split(" ").pop()?.split("'").at(1)?.split(".").pop();
141
+ return {
142
+ kind: "UniqueConstraintViolation",
143
+ constraint: index !== void 0 ? { index } : void 0
144
+ };
145
+ }
146
+ case 1451:
147
+ case 1452: {
148
+ const field = error.message.split(" ").at(17)?.split("`").at(1);
149
+ return {
150
+ kind: "ForeignKeyConstraintViolation",
151
+ constraint: field !== void 0 ? { fields: [field] } : void 0
152
+ };
153
+ }
154
+ case 1263: {
155
+ const index = error.message.split(" ").pop()?.split("'").at(1);
156
+ return {
157
+ kind: "NullConstraintViolation",
158
+ constraint: index !== void 0 ? { index } : void 0
159
+ };
160
+ }
161
+ case 1264:
162
+ return {
163
+ kind: "ValueOutOfRange",
164
+ cause: error.message
165
+ };
166
+ case 1364:
167
+ case 1048: {
168
+ const field = error.message.split(" ").at(1)?.split("'").at(1);
169
+ return {
170
+ kind: "NullConstraintViolation",
171
+ constraint: field !== void 0 ? { fields: [field] } : void 0
172
+ };
173
+ }
174
+ case 1049: {
175
+ const db = error.message.split(" ").pop()?.split("'").at(1);
176
+ return {
177
+ kind: "DatabaseDoesNotExist",
178
+ db
179
+ };
180
+ }
181
+ case 1007: {
182
+ const db = error.message.split(" ").at(3)?.split("'").at(1);
183
+ return {
184
+ kind: "DatabaseAlreadyExists",
185
+ db
186
+ };
187
+ }
188
+ case 1044: {
189
+ const db = error.message.split(" ").pop()?.split("'").at(1);
190
+ return {
191
+ kind: "DatabaseAccessDenied",
192
+ db
193
+ };
194
+ }
195
+ case 1045: {
196
+ const user = error.message.split(" ").at(4)?.split("@").at(0)?.split("'").at(1);
197
+ return {
198
+ kind: "AuthenticationFailed",
199
+ user
200
+ };
201
+ }
202
+ case 1146: {
203
+ const table = error.message.split(" ").at(1)?.split("'").at(1)?.split(".").pop();
204
+ return {
205
+ kind: "TableDoesNotExist",
206
+ table
207
+ };
208
+ }
209
+ case 1054: {
210
+ const column = error.message.split(" ").at(2)?.split("'").at(1);
211
+ return {
212
+ kind: "ColumnNotFound",
213
+ column
214
+ };
215
+ }
216
+ case 1406: {
217
+ const column = error.message.split(" ").flatMap((part) => part.split("'")).at(6);
218
+ return {
219
+ kind: "LengthMismatch",
220
+ column
221
+ };
222
+ }
223
+ case 1191:
224
+ return {
225
+ kind: "MissingFullTextSearchIndex"
226
+ };
227
+ case 1213:
228
+ return {
229
+ kind: "TransactionWriteConflict"
230
+ };
231
+ case 1040:
232
+ case 1203:
233
+ return {
234
+ kind: "TooManyConnections",
235
+ cause: error.message
236
+ };
237
+ default:
238
+ return {
239
+ kind: "mysql",
240
+ code: error.code,
241
+ message: error.message,
242
+ state: error.state
243
+ };
244
+ }
245
+ }
246
+
135
247
  // src/planetscale.ts
136
248
  var debug = (0, import_driver_adapter_utils2.Debug)("prisma:driver-adapter:planetscale");
137
249
  var RollbackError = class _RollbackError extends Error {
@@ -197,23 +309,28 @@ function onError(error) {
197
309
  if (error.name === "DatabaseError") {
198
310
  const parsed = parseErrorMessage(error.message);
199
311
  if (parsed) {
200
- throw new import_driver_adapter_utils2.DriverAdapterError({
201
- kind: "mysql",
202
- ...parsed
203
- });
312
+ throw new import_driver_adapter_utils2.DriverAdapterError(convertDriverError(parsed));
204
313
  }
205
314
  }
206
315
  debug("Error in performIO: %O", error);
207
316
  throw error;
208
317
  }
209
- function parseErrorMessage(message) {
318
+ function parseErrorMessage(error) {
210
319
  const regex = /^(.*) \(errno (\d+)\) \(sqlstate ([A-Z0-9]+)\)/;
211
- const match = message.match(regex);
212
- if (match) {
213
- const [, message2, codeAsString, sqlstate] = match;
320
+ let match = null;
321
+ while (true) {
322
+ const result = error.match(regex);
323
+ if (result === null) {
324
+ break;
325
+ }
326
+ match = result;
327
+ error = match[1];
328
+ }
329
+ if (match !== null) {
330
+ const [, message, codeAsString, sqlstate] = match;
214
331
  const code = Number.parseInt(codeAsString, 10);
215
332
  return {
216
- message: message2,
333
+ message,
217
334
  code,
218
335
  state: sqlstate
219
336
  };
@@ -221,6 +338,7 @@ function parseErrorMessage(message) {
221
338
  return void 0;
222
339
  }
223
340
  }
341
+ var LOCK_TAG = Symbol();
224
342
  var PlanetScaleTransaction = class extends PlanetScaleQueryable {
225
343
  constructor(tx, options, txDeferred, txResultPromise) {
226
344
  super(tx);
@@ -228,6 +346,20 @@ var PlanetScaleTransaction = class extends PlanetScaleQueryable {
228
346
  this.txDeferred = txDeferred;
229
347
  this.txResultPromise = txResultPromise;
230
348
  }
349
+ // The PlanetScale connection objects are not meant to be used concurrently,
350
+ // so we override the `performIO` method to synchronize access to it with a mutex.
351
+ // See: https://github.com/mattrobenolt/ps-http-sim/issues/7
352
+ [LOCK_TAG] = new import_async_mutex.Mutex();
353
+ async performIO(query) {
354
+ const release = await this[LOCK_TAG].acquire();
355
+ try {
356
+ return await super.performIO(query);
357
+ } catch (e) {
358
+ onError(e);
359
+ } finally {
360
+ release();
361
+ }
362
+ }
231
363
  async commit() {
232
364
  debug(`[js::commit]`);
233
365
  this.txDeferred.resolve();
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/planetscale.ts
2
2
  import * as planetScale from "@planetscale/database";
3
3
  import { Debug, DriverAdapterError } from "@prisma/driver-adapter-utils";
4
+ import { Mutex } from "async-mutex";
4
5
 
5
6
  // package.json
6
7
  var name = "@prisma/adapter-planetscale";
@@ -96,6 +97,117 @@ function createDeferred() {
96
97
  ];
97
98
  }
98
99
 
100
+ // src/errors.ts
101
+ function convertDriverError(error) {
102
+ switch (error.code) {
103
+ case 1062: {
104
+ const index = error.message.split(" ").pop()?.split("'").at(1)?.split(".").pop();
105
+ return {
106
+ kind: "UniqueConstraintViolation",
107
+ constraint: index !== void 0 ? { index } : void 0
108
+ };
109
+ }
110
+ case 1451:
111
+ case 1452: {
112
+ const field = error.message.split(" ").at(17)?.split("`").at(1);
113
+ return {
114
+ kind: "ForeignKeyConstraintViolation",
115
+ constraint: field !== void 0 ? { fields: [field] } : void 0
116
+ };
117
+ }
118
+ case 1263: {
119
+ const index = error.message.split(" ").pop()?.split("'").at(1);
120
+ return {
121
+ kind: "NullConstraintViolation",
122
+ constraint: index !== void 0 ? { index } : void 0
123
+ };
124
+ }
125
+ case 1264:
126
+ return {
127
+ kind: "ValueOutOfRange",
128
+ cause: error.message
129
+ };
130
+ case 1364:
131
+ case 1048: {
132
+ const field = error.message.split(" ").at(1)?.split("'").at(1);
133
+ return {
134
+ kind: "NullConstraintViolation",
135
+ constraint: field !== void 0 ? { fields: [field] } : void 0
136
+ };
137
+ }
138
+ case 1049: {
139
+ const db = error.message.split(" ").pop()?.split("'").at(1);
140
+ return {
141
+ kind: "DatabaseDoesNotExist",
142
+ db
143
+ };
144
+ }
145
+ case 1007: {
146
+ const db = error.message.split(" ").at(3)?.split("'").at(1);
147
+ return {
148
+ kind: "DatabaseAlreadyExists",
149
+ db
150
+ };
151
+ }
152
+ case 1044: {
153
+ const db = error.message.split(" ").pop()?.split("'").at(1);
154
+ return {
155
+ kind: "DatabaseAccessDenied",
156
+ db
157
+ };
158
+ }
159
+ case 1045: {
160
+ const user = error.message.split(" ").at(4)?.split("@").at(0)?.split("'").at(1);
161
+ return {
162
+ kind: "AuthenticationFailed",
163
+ user
164
+ };
165
+ }
166
+ case 1146: {
167
+ const table = error.message.split(" ").at(1)?.split("'").at(1)?.split(".").pop();
168
+ return {
169
+ kind: "TableDoesNotExist",
170
+ table
171
+ };
172
+ }
173
+ case 1054: {
174
+ const column = error.message.split(" ").at(2)?.split("'").at(1);
175
+ return {
176
+ kind: "ColumnNotFound",
177
+ column
178
+ };
179
+ }
180
+ case 1406: {
181
+ const column = error.message.split(" ").flatMap((part) => part.split("'")).at(6);
182
+ return {
183
+ kind: "LengthMismatch",
184
+ column
185
+ };
186
+ }
187
+ case 1191:
188
+ return {
189
+ kind: "MissingFullTextSearchIndex"
190
+ };
191
+ case 1213:
192
+ return {
193
+ kind: "TransactionWriteConflict"
194
+ };
195
+ case 1040:
196
+ case 1203:
197
+ return {
198
+ kind: "TooManyConnections",
199
+ cause: error.message
200
+ };
201
+ default:
202
+ return {
203
+ kind: "mysql",
204
+ code: error.code,
205
+ message: error.message,
206
+ state: error.state
207
+ };
208
+ }
209
+ }
210
+
99
211
  // src/planetscale.ts
100
212
  var debug = Debug("prisma:driver-adapter:planetscale");
101
213
  var RollbackError = class _RollbackError extends Error {
@@ -161,23 +273,28 @@ function onError(error) {
161
273
  if (error.name === "DatabaseError") {
162
274
  const parsed = parseErrorMessage(error.message);
163
275
  if (parsed) {
164
- throw new DriverAdapterError({
165
- kind: "mysql",
166
- ...parsed
167
- });
276
+ throw new DriverAdapterError(convertDriverError(parsed));
168
277
  }
169
278
  }
170
279
  debug("Error in performIO: %O", error);
171
280
  throw error;
172
281
  }
173
- function parseErrorMessage(message) {
282
+ function parseErrorMessage(error) {
174
283
  const regex = /^(.*) \(errno (\d+)\) \(sqlstate ([A-Z0-9]+)\)/;
175
- const match = message.match(regex);
176
- if (match) {
177
- const [, message2, codeAsString, sqlstate] = match;
284
+ let match = null;
285
+ while (true) {
286
+ const result = error.match(regex);
287
+ if (result === null) {
288
+ break;
289
+ }
290
+ match = result;
291
+ error = match[1];
292
+ }
293
+ if (match !== null) {
294
+ const [, message, codeAsString, sqlstate] = match;
178
295
  const code = Number.parseInt(codeAsString, 10);
179
296
  return {
180
- message: message2,
297
+ message,
181
298
  code,
182
299
  state: sqlstate
183
300
  };
@@ -185,6 +302,7 @@ function parseErrorMessage(message) {
185
302
  return void 0;
186
303
  }
187
304
  }
305
+ var LOCK_TAG = Symbol();
188
306
  var PlanetScaleTransaction = class extends PlanetScaleQueryable {
189
307
  constructor(tx, options, txDeferred, txResultPromise) {
190
308
  super(tx);
@@ -192,6 +310,20 @@ var PlanetScaleTransaction = class extends PlanetScaleQueryable {
192
310
  this.txDeferred = txDeferred;
193
311
  this.txResultPromise = txResultPromise;
194
312
  }
313
+ // The PlanetScale connection objects are not meant to be used concurrently,
314
+ // so we override the `performIO` method to synchronize access to it with a mutex.
315
+ // See: https://github.com/mattrobenolt/ps-http-sim/issues/7
316
+ [LOCK_TAG] = new Mutex();
317
+ async performIO(query) {
318
+ const release = await this[LOCK_TAG].acquire();
319
+ try {
320
+ return await super.performIO(query);
321
+ } catch (e) {
322
+ onError(e);
323
+ } finally {
324
+ release();
325
+ }
326
+ }
195
327
  async commit() {
196
328
  debug(`[js::commit]`);
197
329
  this.txDeferred.resolve();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/adapter-planetscale",
3
- "version": "6.10.0-dev.2",
3
+ "version": "6.10.0-dev.4",
4
4
  "description": "Prisma's driver adapter for \"@planetscale/database\"",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -31,7 +31,8 @@
31
31
  "license": "Apache-2.0",
32
32
  "sideEffects": false,
33
33
  "dependencies": {
34
- "@prisma/driver-adapter-utils": "6.10.0-dev.2"
34
+ "async-mutex": "0.5.0",
35
+ "@prisma/driver-adapter-utils": "6.10.0-dev.4"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@planetscale/database": "1.19.0",