@kysera/dialects 0.8.2 → 0.8.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.
@@ -2,15 +2,33 @@
2
2
  * Microsoft SQL Server Dialect Adapter
3
3
  *
4
4
  * Supports SQL Server 2017+, Azure SQL Database, and Azure SQL Edge
5
+ * with full schema support (default: 'dbo')
5
6
  */
6
7
 
7
8
  import type { Kysely } from 'kysely'
8
9
  import { sql } from 'kysely'
9
- import type { DialectAdapter, DatabaseErrorLike } from '../types.js'
10
- import { assertValidIdentifier } from '../helpers.js'
10
+ import { silentLogger, type KyseraLogger } from '@kysera/core'
11
+ import type { DialectAdapter, DialectAdapterOptions, SchemaOptions } from '../types.js'
12
+ import { assertValidIdentifier, resolveSchema as resolveSchemaUtil, errorMatchers } from '../helpers.js'
13
+
14
+ /**
15
+ * MSSQL-specific adapter options
16
+ */
17
+ export interface MSSQLAdapterOptions extends DialectAdapterOptions {
18
+ /** Logger instance for error reporting */
19
+ logger?: KyseraLogger
20
+ }
11
21
 
12
22
  export class MSSQLAdapter implements DialectAdapter {
13
23
  readonly dialect = 'mssql' as const
24
+ readonly defaultSchema: string
25
+ private logger: KyseraLogger
26
+
27
+ constructor(options: MSSQLAdapterOptions = {}) {
28
+ // MSSQL uses 'dbo' as the default schema
29
+ this.defaultSchema = options.defaultSchema ?? 'dbo'
30
+ this.logger = options.logger ?? silentLogger
31
+ }
14
32
 
15
33
  getDefaultPort(): number {
16
34
  return 1433
@@ -31,51 +49,39 @@ export class MSSQLAdapter implements DialectAdapter {
31
49
  }
32
50
 
33
51
  isUniqueConstraintError(error: unknown): boolean {
34
- const e = error as DatabaseErrorLike
35
- const message = e.message?.toLowerCase() || ''
36
- const code = e.code || ''
37
- // MSSQL error 2627: Violation of PRIMARY KEY/UNIQUE constraint
38
- // MSSQL error 2601: Cannot insert duplicate key row
39
- return (
40
- code === '2627' ||
41
- code === '2601' ||
42
- message.includes('violation of unique key constraint') ||
43
- message.includes('cannot insert duplicate key') ||
44
- message.includes('unique constraint')
45
- )
52
+ return errorMatchers.mssql.uniqueConstraint(error)
46
53
  }
47
54
 
48
55
  isForeignKeyError(error: unknown): boolean {
49
- const e = error as DatabaseErrorLike
50
- const message = e.message?.toLowerCase() || ''
51
- const code = e.code || ''
52
- // MSSQL error 547: FOREIGN KEY constraint violation
53
- return (
54
- code === '547' ||
55
- message.includes('foreign key constraint') ||
56
- message.includes('conflicted with the foreign key')
57
- )
56
+ return errorMatchers.mssql.foreignKey(error)
58
57
  }
59
58
 
60
59
  isNotNullError(error: unknown): boolean {
61
- const e = error as DatabaseErrorLike
62
- const message = e.message?.toLowerCase() || ''
63
- const code = e.code || ''
64
- // MSSQL error 515: Cannot insert NULL value
65
- return (
66
- code === '515' ||
67
- message.includes('cannot insert the value null') ||
68
- message.includes('does not allow nulls')
69
- )
70
- }
71
-
72
- async tableExists(db: Kysely<any>, tableName: string): Promise<boolean> {
60
+ return errorMatchers.mssql.notNull(error)
61
+ }
62
+
63
+ /**
64
+ * Resolve the schema to use for an operation.
65
+ * Uses the shared resolveSchema utility from helpers.ts.
66
+ */
67
+ private resolveSchema(options?: SchemaOptions): string {
68
+ return resolveSchemaUtil(this.defaultSchema, options)
69
+ }
70
+
71
+ async tableExists(
72
+ db: Kysely<any>,
73
+ tableName: string,
74
+ options?: SchemaOptions
75
+ ): Promise<boolean> {
73
76
  assertValidIdentifier(tableName, 'table name')
77
+ const schema = this.resolveSchema(options)
78
+
74
79
  try {
75
80
  const result = await db
76
81
  .selectFrom('INFORMATION_SCHEMA.TABLES')
77
82
  .select('TABLE_NAME')
78
83
  .where('TABLE_NAME', '=', tableName)
84
+ .where('TABLE_SCHEMA', '=', schema)
79
85
  .where('TABLE_TYPE', '=', 'BASE TABLE')
80
86
  .executeTakeFirst()
81
87
  return !!result
@@ -84,13 +90,20 @@ export class MSSQLAdapter implements DialectAdapter {
84
90
  }
85
91
  }
86
92
 
87
- async getTableColumns(db: Kysely<any>, tableName: string): Promise<string[]> {
93
+ async getTableColumns(
94
+ db: Kysely<any>,
95
+ tableName: string,
96
+ options?: SchemaOptions
97
+ ): Promise<string[]> {
88
98
  assertValidIdentifier(tableName, 'table name')
99
+ const schema = this.resolveSchema(options)
100
+
89
101
  try {
90
102
  const results = await db
91
103
  .selectFrom('INFORMATION_SCHEMA.COLUMNS')
92
104
  .select('COLUMN_NAME')
93
105
  .where('TABLE_NAME', '=', tableName)
106
+ .where('TABLE_SCHEMA', '=', schema)
94
107
  .execute()
95
108
  return results.map(r => (r as { COLUMN_NAME: string }).COLUMN_NAME)
96
109
  } catch {
@@ -98,13 +111,15 @@ export class MSSQLAdapter implements DialectAdapter {
98
111
  }
99
112
  }
100
113
 
101
- async getTables(db: Kysely<any>): Promise<string[]> {
114
+ async getTables(db: Kysely<any>, options?: SchemaOptions): Promise<string[]> {
115
+ const schema = this.resolveSchema(options)
116
+
102
117
  try {
103
118
  const results = await db
104
119
  .selectFrom('INFORMATION_SCHEMA.TABLES')
105
120
  .select('TABLE_NAME')
106
121
  .where('TABLE_TYPE', '=', 'BASE TABLE')
107
- .where('TABLE_SCHEMA', '=', 'dbo')
122
+ .where('TABLE_SCHEMA', '=', schema)
108
123
  .execute()
109
124
  return results.map(r => (r as { TABLE_NAME: string }).TABLE_NAME)
110
125
  } catch {
@@ -127,24 +142,28 @@ export class MSSQLAdapter implements DialectAdapter {
127
142
  }
128
143
  }
129
144
 
130
- async truncateTable(db: Kysely<any>, tableName: string): Promise<boolean> {
145
+ async truncateTable(
146
+ db: Kysely<any>,
147
+ tableName: string,
148
+ options?: SchemaOptions
149
+ ): Promise<boolean> {
131
150
  assertValidIdentifier(tableName, 'table name')
151
+ const schema = this.resolveSchema(options)
152
+
132
153
  try {
154
+ const qualifiedTable = `${this.escapeIdentifier(schema)}.${this.escapeIdentifier(tableName)}`
155
+
133
156
  // MSSQL: First try TRUNCATE, fall back to DELETE if FK constraints exist
134
157
  try {
135
- await sql.raw(`TRUNCATE TABLE ${this.escapeIdentifier(tableName)}`).execute(db)
158
+ await sql.raw(`TRUNCATE TABLE ${qualifiedTable}`).execute(db)
136
159
  } catch (truncateError) {
137
160
  // If truncate fails due to FK, use DELETE
138
161
  const errorMsg = String(truncateError)
139
162
  if (errorMsg.includes('FOREIGN KEY') || errorMsg.includes('Cannot truncate')) {
140
- await sql.raw(`DELETE FROM ${this.escapeIdentifier(tableName)}`).execute(db)
163
+ await sql.raw(`DELETE FROM ${qualifiedTable}`).execute(db)
141
164
  // Reset identity if table has one
142
165
  try {
143
- // Use escaped identifier for SQL injection prevention
144
- const escapedTableName = this.escapeIdentifier(tableName)
145
- await sql
146
- .raw(`DBCC CHECKIDENT (${escapedTableName}, RESEED, 0)`)
147
- .execute(db)
166
+ await sql.raw(`DBCC CHECKIDENT (${qualifiedTable}, RESEED, 0)`).execute(db)
148
167
  } catch {
149
168
  // Ignore if table doesn't have identity column
150
169
  }
@@ -161,21 +180,26 @@ export class MSSQLAdapter implements DialectAdapter {
161
180
  ) {
162
181
  return false
163
182
  }
164
- // Re-throw with context (logging should be handled by caller)
165
- throw new Error(`Failed to truncate MSSQL table "${tableName}": ${String(error)}`)
183
+ // Log and rethrow with context
184
+ this.logger.error(`Failed to truncate MSSQL table "${schema}.${tableName}":`, error)
185
+ throw new Error(`Failed to truncate MSSQL table "${schema}.${tableName}": ${String(error)}`)
166
186
  }
167
187
  }
168
188
 
169
- async truncateAllTables(db: Kysely<any>, exclude: string[] = []): Promise<void> {
170
- const tables = await this.getTables(db)
189
+ async truncateAllTables(
190
+ db: Kysely<any>,
191
+ exclude: string[] = [],
192
+ options?: SchemaOptions
193
+ ): Promise<void> {
194
+ const tables = await this.getTables(db, options)
195
+ const schema = this.resolveSchema(options)
171
196
 
172
197
  // MSSQL: Disable all FK constraints first
173
198
  for (const table of tables) {
174
199
  if (!exclude.includes(table)) {
175
200
  try {
176
- await sql
177
- .raw(`ALTER TABLE ${this.escapeIdentifier(table)} NOCHECK CONSTRAINT ALL`)
178
- .execute(db)
201
+ const qualifiedTable = `${this.escapeIdentifier(schema)}.${this.escapeIdentifier(table)}`
202
+ await sql.raw(`ALTER TABLE ${qualifiedTable} NOCHECK CONSTRAINT ALL`).execute(db)
179
203
  } catch {
180
204
  // Ignore errors for tables without constraints
181
205
  }
@@ -185,7 +209,7 @@ export class MSSQLAdapter implements DialectAdapter {
185
209
  // Truncate all tables
186
210
  for (const table of tables) {
187
211
  if (!exclude.includes(table)) {
188
- await this.truncateTable(db, table)
212
+ await this.truncateTable(db, table, options)
189
213
  }
190
214
  }
191
215
 
@@ -193,15 +217,137 @@ export class MSSQLAdapter implements DialectAdapter {
193
217
  for (const table of tables) {
194
218
  if (!exclude.includes(table)) {
195
219
  try {
196
- await sql
197
- .raw(`ALTER TABLE ${this.escapeIdentifier(table)} CHECK CONSTRAINT ALL`)
198
- .execute(db)
220
+ const qualifiedTable = `${this.escapeIdentifier(schema)}.${this.escapeIdentifier(table)}`
221
+ await sql.raw(`ALTER TABLE ${qualifiedTable} CHECK CONSTRAINT ALL`).execute(db)
199
222
  } catch {
200
223
  // Ignore errors
201
224
  }
202
225
  }
203
226
  }
204
227
  }
228
+
229
+ /**
230
+ * Check if a schema exists in the database
231
+ *
232
+ * @param db - Kysely database instance
233
+ * @param schemaName - Name of the schema to check
234
+ * @returns true if schema exists, false otherwise
235
+ */
236
+ async schemaExists(db: Kysely<any>, schemaName: string): Promise<boolean> {
237
+ assertValidIdentifier(schemaName, 'schema name')
238
+
239
+ try {
240
+ const result = await db
241
+ .selectFrom('INFORMATION_SCHEMA.SCHEMATA')
242
+ .select('SCHEMA_NAME')
243
+ .where('SCHEMA_NAME', '=', schemaName)
244
+ .executeTakeFirst()
245
+ return !!result
246
+ } catch {
247
+ return false
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Get all schemas in the database (excluding system schemas)
253
+ *
254
+ * @param db - Kysely database instance
255
+ * @returns Array of schema names
256
+ */
257
+ async getSchemas(db: Kysely<any>): Promise<string[]> {
258
+ try {
259
+ const results = await db
260
+ .selectFrom('INFORMATION_SCHEMA.SCHEMATA')
261
+ .select('SCHEMA_NAME')
262
+ .where('SCHEMA_NAME', 'not in', [
263
+ 'INFORMATION_SCHEMA',
264
+ 'sys',
265
+ 'guest',
266
+ 'db_owner',
267
+ 'db_accessadmin',
268
+ 'db_securityadmin',
269
+ 'db_ddladmin',
270
+ 'db_backupoperator',
271
+ 'db_datareader',
272
+ 'db_datawriter',
273
+ 'db_denydatareader',
274
+ 'db_denydatawriter'
275
+ ])
276
+ .execute()
277
+ return results.map(r => (r as { SCHEMA_NAME: string }).SCHEMA_NAME)
278
+ } catch {
279
+ return []
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Create a new schema in the database
285
+ *
286
+ * @param db - Kysely database instance
287
+ * @param schemaName - Name of the schema to create
288
+ * @returns true if schema was created, false if it already exists
289
+ */
290
+ async createSchema(db: Kysely<any>, schemaName: string): Promise<boolean> {
291
+ assertValidIdentifier(schemaName, 'schema name')
292
+
293
+ try {
294
+ await sql.raw(`CREATE SCHEMA ${this.escapeIdentifier(schemaName)}`).execute(db)
295
+ return true
296
+ } catch (error) {
297
+ const errorMessage = String(error)
298
+ if (errorMessage.includes('already exists')) {
299
+ return false
300
+ }
301
+ this.logger.error(`Failed to create schema "${schemaName}":`, error)
302
+ throw error
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Drop a schema from the database
308
+ *
309
+ * @param db - Kysely database instance
310
+ * @param schemaName - Name of the schema to drop
311
+ * @returns true if schema was dropped, false if it doesn't exist
312
+ */
313
+ async dropSchema(db: Kysely<any>, schemaName: string): Promise<boolean> {
314
+ assertValidIdentifier(schemaName, 'schema name')
315
+
316
+ // Prevent dropping protected schemas
317
+ const protectedSchemas = ['dbo', 'sys', 'INFORMATION_SCHEMA', 'guest']
318
+ if (protectedSchemas.includes(schemaName)) {
319
+ throw new Error(`Cannot drop protected schema: ${schemaName}`)
320
+ }
321
+
322
+ try {
323
+ await sql.raw(`DROP SCHEMA ${this.escapeIdentifier(schemaName)}`).execute(db)
324
+ return true
325
+ } catch (error) {
326
+ const errorMessage = String(error)
327
+ if (errorMessage.includes('does not exist') || errorMessage.includes('Cannot find')) {
328
+ return false
329
+ }
330
+ this.logger.error(`Failed to drop schema "${schemaName}":`, error)
331
+ throw error
332
+ }
333
+ }
205
334
  }
206
335
 
336
+ /**
337
+ * Default MSSQL adapter instance with 'dbo' schema
338
+ */
207
339
  export const mssqlAdapter = new MSSQLAdapter()
340
+
341
+ /**
342
+ * Create a new MSSQL adapter with custom configuration
343
+ *
344
+ * @param options - Adapter configuration options
345
+ * @returns Configured MSSQLAdapter instance
346
+ *
347
+ * @example
348
+ * // Create adapter with custom default schema
349
+ * const adapter = createMSSQLAdapter({ defaultSchema: 'app' })
350
+ */
351
+ export function createMSSQLAdapter(options?: MSSQLAdapterOptions): MSSQLAdapter {
352
+ return new MSSQLAdapter(options)
353
+ }
@@ -1,19 +1,34 @@
1
1
  /**
2
2
  * MySQL Dialect Adapter
3
+ *
4
+ * Note: In MySQL, "schema" and "database" are synonymous.
5
+ * The schema option maps to the current database context.
3
6
  */
4
7
 
5
8
  import type { Kysely } from 'kysely'
6
9
  import { sql } from 'kysely'
7
10
  import { silentLogger, type KyseraLogger } from '@kysera/core'
8
- import type { DialectAdapter, DatabaseErrorLike } from '../types.js'
9
- import { assertValidIdentifier } from '../helpers.js'
11
+ import type { DialectAdapter, DialectAdapterOptions, SchemaOptions } from '../types.js'
12
+ import { assertValidIdentifier, errorMatchers } from '../helpers.js'
13
+
14
+ /**
15
+ * MySQL-specific adapter options
16
+ */
17
+ export interface MySQLAdapterOptions extends DialectAdapterOptions {
18
+ /** Logger instance for error reporting */
19
+ logger?: KyseraLogger
20
+ }
10
21
 
11
22
  export class MySQLAdapter implements DialectAdapter {
12
23
  readonly dialect = 'mysql' as const
24
+ readonly defaultSchema: string
13
25
  private logger: KyseraLogger
14
26
 
15
- constructor(logger: KyseraLogger = silentLogger) {
16
- this.logger = logger
27
+ constructor(options: MySQLAdapterOptions = {}) {
28
+ // In MySQL, defaultSchema is the database name
29
+ // Empty string means use current database (DATABASE())
30
+ this.defaultSchema = options.defaultSchema ?? ''
31
+ this.logger = options.logger ?? silentLogger
17
32
  }
18
33
 
19
34
  getDefaultPort(): number {
@@ -34,67 +49,91 @@ export class MySQLAdapter implements DialectAdapter {
34
49
  }
35
50
 
36
51
  isUniqueConstraintError(error: unknown): boolean {
37
- const e = error as DatabaseErrorLike
38
- const message = e.message?.toLowerCase() || ''
39
- const code = e.code || ''
40
- return code === 'ER_DUP_ENTRY' || code === '1062' || message.includes('duplicate entry')
52
+ return errorMatchers.mysql.uniqueConstraint(error)
41
53
  }
42
54
 
43
55
  isForeignKeyError(error: unknown): boolean {
44
- const e = error as DatabaseErrorLike
45
- const code = e.code || ''
46
- return (
47
- code === 'ER_ROW_IS_REFERENCED' ||
48
- code === '1451' ||
49
- code === 'ER_NO_REFERENCED_ROW' ||
50
- code === '1452'
51
- )
56
+ return errorMatchers.mysql.foreignKey(error)
52
57
  }
53
58
 
54
59
  isNotNullError(error: unknown): boolean {
55
- const e = error as DatabaseErrorLike
56
- const code = e.code || ''
57
- return code === 'ER_BAD_NULL_ERROR' || code === '1048'
60
+ return errorMatchers.mysql.notNull(error)
61
+ }
62
+
63
+ /**
64
+ * Get the schema (database) filter for queries.
65
+ * In MySQL, schema = database, so we use DATABASE() if not specified.
66
+ */
67
+ private getSchemaFilter(options?: SchemaOptions): ReturnType<typeof sql> | string {
68
+ const schema = options?.schema ?? this.defaultSchema
69
+ if (schema) {
70
+ assertValidIdentifier(schema, 'schema/database name')
71
+ return schema
72
+ }
73
+ return sql`DATABASE()`
58
74
  }
59
75
 
60
- async tableExists(db: Kysely<any>, tableName: string): Promise<boolean> {
76
+ async tableExists(
77
+ db: Kysely<any>,
78
+ tableName: string,
79
+ options?: SchemaOptions
80
+ ): Promise<boolean> {
61
81
  assertValidIdentifier(tableName, 'table name')
82
+ const schemaFilter = this.getSchemaFilter(options)
83
+
62
84
  try {
63
- const result = await db
85
+ const query = db
64
86
  .selectFrom('information_schema.tables')
65
87
  .select('table_name')
66
88
  .where('table_name', '=', tableName)
67
- .where('table_schema', '=', sql`DATABASE()`)
68
- .executeTakeFirst()
89
+
90
+ const result = typeof schemaFilter === 'string'
91
+ ? await query.where('table_schema', '=', schemaFilter).executeTakeFirst()
92
+ : await query.where('table_schema', '=', schemaFilter).executeTakeFirst()
93
+
69
94
  return !!result
70
95
  } catch {
71
96
  return false
72
97
  }
73
98
  }
74
99
 
75
- async getTableColumns(db: Kysely<any>, tableName: string): Promise<string[]> {
100
+ async getTableColumns(
101
+ db: Kysely<any>,
102
+ tableName: string,
103
+ options?: SchemaOptions
104
+ ): Promise<string[]> {
76
105
  assertValidIdentifier(tableName, 'table name')
106
+ const schemaFilter = this.getSchemaFilter(options)
107
+
77
108
  try {
78
- const results = await db
109
+ const query = db
79
110
  .selectFrom('information_schema.columns')
80
111
  .select('column_name')
81
112
  .where('table_name', '=', tableName)
82
- .where('table_schema', '=', sql`DATABASE()`)
83
- .execute()
113
+
114
+ const results = typeof schemaFilter === 'string'
115
+ ? await query.where('table_schema', '=', schemaFilter).execute()
116
+ : await query.where('table_schema', '=', schemaFilter).execute()
117
+
84
118
  return results.map(r => r.column_name as string)
85
119
  } catch {
86
120
  return []
87
121
  }
88
122
  }
89
123
 
90
- async getTables(db: Kysely<any>): Promise<string[]> {
124
+ async getTables(db: Kysely<any>, options?: SchemaOptions): Promise<string[]> {
125
+ const schemaFilter = this.getSchemaFilter(options)
126
+
91
127
  try {
92
- const results = await db
128
+ const query = db
93
129
  .selectFrom('information_schema.tables')
94
130
  .select('table_name')
95
- .where('table_schema', '=', sql`DATABASE()`)
96
131
  .where('table_type', '=', 'BASE TABLE')
97
- .execute()
132
+
133
+ const results = typeof schemaFilter === 'string'
134
+ ? await query.where('table_schema', '=', schemaFilter).execute()
135
+ : await query.where('table_schema', '=', schemaFilter).execute()
136
+
98
137
  return results.map(r => r.table_name as string)
99
138
  } catch {
100
139
  return []
@@ -126,12 +165,21 @@ export class MySQLAdapter implements DialectAdapter {
126
165
  }
127
166
  }
128
167
 
129
- async truncateTable(db: Kysely<any>, tableName: string): Promise<boolean> {
168
+ async truncateTable(
169
+ db: Kysely<any>,
170
+ tableName: string,
171
+ options?: SchemaOptions
172
+ ): Promise<boolean> {
130
173
  assertValidIdentifier(tableName, 'table name')
174
+ const schema = options?.schema ?? this.defaultSchema
175
+
131
176
  try {
132
177
  await sql.raw('SET FOREIGN_KEY_CHECKS = 0').execute(db)
133
178
  try {
134
- await sql.raw(`TRUNCATE TABLE ${this.escapeIdentifier(tableName)}`).execute(db)
179
+ const qualifiedTable = schema
180
+ ? `${this.escapeIdentifier(schema)}.${this.escapeIdentifier(tableName)}`
181
+ : this.escapeIdentifier(tableName)
182
+ await sql.raw(`TRUNCATE TABLE ${qualifiedTable}`).execute(db)
135
183
  return true
136
184
  } finally {
137
185
  // Always try to re-enable FK checks
@@ -152,14 +200,35 @@ export class MySQLAdapter implements DialectAdapter {
152
200
  }
153
201
  }
154
202
 
155
- async truncateAllTables(db: Kysely<any>, exclude: string[] = []): Promise<void> {
156
- const tables = await this.getTables(db)
203
+ async truncateAllTables(
204
+ db: Kysely<any>,
205
+ exclude: string[] = [],
206
+ options?: SchemaOptions
207
+ ): Promise<void> {
208
+ const tables = await this.getTables(db, options)
157
209
  for (const table of tables) {
158
210
  if (!exclude.includes(table)) {
159
- await this.truncateTable(db, table)
211
+ await this.truncateTable(db, table, options)
160
212
  }
161
213
  }
162
214
  }
163
215
  }
164
216
 
217
+ /**
218
+ * Default MySQL adapter instance
219
+ */
165
220
  export const mysqlAdapter = new MySQLAdapter()
221
+
222
+ /**
223
+ * Create a new MySQL adapter with custom configuration
224
+ *
225
+ * @param options - Adapter configuration options
226
+ * @returns Configured MySQLAdapter instance
227
+ *
228
+ * @example
229
+ * // Create adapter with specific database as default
230
+ * const adapter = createMySQLAdapter({ defaultSchema: 'my_database' })
231
+ */
232
+ export function createMySQLAdapter(options?: MySQLAdapterOptions): MySQLAdapter {
233
+ return new MySQLAdapter(options)
234
+ }