@deterministic-code/datasources-mysql 0.1.0 → 0.1.2

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 (68) hide show
  1. package/dist/MysqlAlterTableStatementRenderer.d.ts +6 -0
  2. package/dist/MysqlAlterTableStatementRenderer.js +13 -0
  3. package/dist/MysqlBulkUpdateStatementTextRenderer.d.ts +4 -0
  4. package/dist/MysqlBulkUpdateStatementTextRenderer.js +9 -0
  5. package/dist/MysqlCreateRoutineStatementRenderer.d.ts +4 -0
  6. package/dist/MysqlCreateTableStatementRenderer.d.ts +5 -0
  7. package/dist/MysqlCreateTableStatementRenderer.js +10 -0
  8. package/dist/MysqlCreateTriggerStatementRenderer.d.ts +4 -0
  9. package/dist/MysqlCreateTriggerStatementRenderer.js +7 -0
  10. package/dist/MysqlDriver.d.ts +16 -0
  11. package/dist/MysqlDriver.js +76 -0
  12. package/dist/MysqlDropIndexStatementRenderer.d.ts +4 -0
  13. package/dist/MysqlDropIndexStatementRenderer.js +6 -0
  14. package/dist/MysqlDropRoutineStatementRenderer.d.ts +4 -0
  15. package/dist/MysqlDropRoutineStatementRenderer.js +6 -0
  16. package/dist/MysqlStatementRenderer.d.ts +18 -0
  17. package/dist/MysqlStatementRenderer.js +60 -0
  18. package/dist/MysqlStoredProcedureStatementRenderer.d.ts +10 -0
  19. package/dist/MysqlStoredProcedureStatementRenderer.js +101 -0
  20. package/dist/bind.d.ts +5 -0
  21. package/dist/bind.js +20 -0
  22. package/dist/columnTypes.d.ts +1 -0
  23. package/dist/columnTypes.js +10 -0
  24. package/dist/command.d.ts +16 -0
  25. package/dist/command.js +143 -0
  26. package/dist/connection-options.d.ts +5 -0
  27. package/dist/connection-options.js +1 -0
  28. package/dist/connection.d.ts +19 -0
  29. package/dist/connection.js +64 -0
  30. package/dist/converters/BinaryConverter.d.ts +5 -0
  31. package/dist/converters/BinaryConverter.js +6 -0
  32. package/dist/converters/BoolConverter.d.ts +5 -0
  33. package/dist/converters/BoolConverter.js +5 -0
  34. package/dist/converters/DateTimeConverter.d.ts +5 -0
  35. package/dist/converters/DateTimeConverter.js +10 -0
  36. package/dist/converters/DecimalConverter.d.ts +5 -0
  37. package/dist/converters/DecimalConverter.js +5 -0
  38. package/dist/converters/MysqlRowTypeConverters.d.ts +3 -0
  39. package/dist/converters/MysqlRowTypeConverters.js +20 -0
  40. package/dist/converters/UuidConverter.d.ts +5 -0
  41. package/dist/converters/UuidConverter.js +5 -0
  42. package/dist/i-mysql-engine.d.ts +27 -0
  43. package/dist/i-mysql-engine.js +14 -0
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.js +1 -0
  46. package/dist/scalar.js +1 -0
  47. package/dist/transaction.d.ts +9 -0
  48. package/dist/transaction.js +15 -0
  49. package/package.json +10 -7
  50. package/src/MysqlBulkUpdateStatementTextRenderer.ts +0 -10
  51. package/src/MysqlCreateTableStatementRenderer.ts +0 -12
  52. package/src/MysqlDriver.ts +0 -54
  53. package/src/MysqlStatementRenderer.ts +0 -34
  54. package/src/bind.ts +0 -31
  55. package/src/columnTypes.ts +0 -10
  56. package/src/command.ts +0 -197
  57. package/src/connection-options.ts +0 -6
  58. package/src/connection.ts +0 -91
  59. package/src/converters/BinaryConverter.ts +0 -9
  60. package/src/converters/BoolConverter.ts +0 -6
  61. package/src/converters/DateTimeConverter.ts +0 -15
  62. package/src/converters/DecimalConverter.ts +0 -7
  63. package/src/converters/MysqlRowTypeConverters.ts +0 -25
  64. package/src/converters/UuidConverter.ts +0 -6
  65. package/src/i-mysql-engine.ts +0 -58
  66. package/src/index.ts +0 -1
  67. package/src/transaction.ts +0 -21
  68. /package/{src/scalar.ts → dist/scalar.d.ts} +0 -0
@@ -0,0 +1,6 @@
1
+ import { AlterTableStatementTextRenderer, type AlterAction, type Column } from "@deterministic-code/datasources";
2
+ export declare class MysqlAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
3
+ protected typeName(column: Column): string;
4
+ protected identity(column: Column): string;
5
+ protected actionSql(action: AlterAction): string;
6
+ }
@@ -0,0 +1,13 @@
1
+ import { AlterTableStatementTextRenderer, } from "@deterministic-code/datasources";
2
+ import { columnTypes } from "./columnTypes.js";
3
+ export class MysqlAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
4
+ typeName(column) {
5
+ return columnTypes[column.type] ?? column.type;
6
+ }
7
+ identity(column) {
8
+ return column.identity ? " AUTO_INCREMENT" : "";
9
+ }
10
+ actionSql(action) {
11
+ return action.kind === "alterColumn" ? `MODIFY COLUMN ${this.column(action.column)}` : super.actionSql(action);
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ import { BulkUpdateStatementTextRenderer, type BulkUpdateStatement } from "@deterministic-code/datasources";
2
+ export declare class MysqlBulkUpdateStatementTextRenderer extends BulkUpdateStatementTextRenderer {
3
+ render: (statement: BulkUpdateStatement) => string;
4
+ }
@@ -0,0 +1,9 @@
1
+ import { BulkUpdateStatementTextRenderer } from "@deterministic-code/datasources";
2
+ export class MysqlBulkUpdateStatementTextRenderer extends BulkUpdateStatementTextRenderer {
3
+ render = (statement) => {
4
+ const assigns = statement.set
5
+ .map((column) => `${statement.table}.${column} = data.${column}`)
6
+ .join(", ");
7
+ return `UPDATE ${statement.table} JOIN ${this.source(statement)} ON ${this.on(statement)} SET ${assigns}`;
8
+ };
9
+ }
@@ -0,0 +1,4 @@
1
+ import { CreateRoutineStatementTextRenderer, type CreateRoutineStatement } from "@datasources/core";
2
+ export declare class MysqlCreateRoutineStatementRenderer extends CreateRoutineStatementTextRenderer {
3
+ render(statement: CreateRoutineStatement): string;
4
+ }
@@ -0,0 +1,5 @@
1
+ import { CreateTableStatementTextRenderer, type Column } from "@deterministic-code/datasources";
2
+ export declare class MysqlCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
3
+ protected typeName(column: Column): string;
4
+ protected identity(column: Column): string;
5
+ }
@@ -0,0 +1,10 @@
1
+ import { CreateTableStatementTextRenderer } from "@deterministic-code/datasources";
2
+ import { columnTypes } from "./columnTypes.js";
3
+ export class MysqlCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
4
+ typeName(column) {
5
+ return columnTypes[column.type] ?? column.type;
6
+ }
7
+ identity(column) {
8
+ return column.identity ? " AUTO_INCREMENT" : "";
9
+ }
10
+ }
@@ -0,0 +1,4 @@
1
+ import { CreateTriggerStatementTextRenderer, type CreateTriggerStatement } from "@deterministic-code/datasources";
2
+ export declare class MysqlCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
3
+ render(statement: CreateTriggerStatement): string;
4
+ }
@@ -0,0 +1,7 @@
1
+ import { CreateTriggerStatementTextRenderer } from "@deterministic-code/datasources";
2
+ export class MysqlCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
3
+ render(statement) {
4
+ return `CREATE TRIGGER ${statement.name} BEFORE UPDATE ON ${statement.table}
5
+ FOR EACH ROW SET NEW.${statement.updatedColumn} = ${statement.utcNow}`;
6
+ }
7
+ }
@@ -0,0 +1,16 @@
1
+ import { DefaultValueConverter, DeterministicTypeConverter, StatementTextRenderer, type Cell, type DatasourceOptions, type ICommand, type IConnection, type IDriver, type Information, type ITransaction, type IsolationLevel, type TypeConverter } from "@deterministic-code/datasources";
2
+ import { columnTypes } from "./columnTypes.js";
3
+ export { columnTypes };
4
+ export declare class MysqlDriver implements IDriver {
5
+ readonly statementRenderer: StatementTextRenderer;
6
+ readonly information: Information;
7
+ rowTypeConverters: Record<string, TypeConverter<Cell, Cell>>;
8
+ readonly quoteLeft = "`";
9
+ readonly quoteRight = "`";
10
+ readonly typeConverter: DeterministicTypeConverter;
11
+ readonly defaultValueConverter: DefaultValueConverter;
12
+ columnType: (name: string) => string;
13
+ connection: (options?: DatasourceOptions) => IConnection;
14
+ command: (connection: IConnection) => ICommand;
15
+ transaction: (connection: IConnection, isolation?: IsolationLevel) => ITransaction;
16
+ }
@@ -0,0 +1,76 @@
1
+ import { DatasourceError, DefaultValueConverter, DeterministicTypeConverter, StatementTextRenderer, } from "@deterministic-code/datasources";
2
+ import { MysqlConnection } from "./connection.js";
3
+ import { MysqlCommand } from "./command.js";
4
+ import { MysqlTransaction } from "./transaction.js";
5
+ import { MysqlRowTypeConverters } from "./converters/MysqlRowTypeConverters.js";
6
+ import { MysqlStatementRenderer } from "./MysqlStatementRenderer.js";
7
+ import { columnTypes } from "./columnTypes.js";
8
+ export { columnTypes };
9
+ const statements = new StatementTextRenderer(t => new MysqlStatementRenderer(t));
10
+ export class MysqlDriver {
11
+ statementRenderer = statements;
12
+ information = {
13
+ descriptions: [
14
+ "MySQL / MariaDB server driver.",
15
+ "Full connection, command, transaction, and repository support.",
16
+ ],
17
+ overrides: [
18
+ "Overrides SelectInsertedIdStatement to SELECT LAST_INSERT_ID() AS {column}",
19
+ "Overrides CreateTableStatement identity to AUTO_INCREMENT",
20
+ "Overrides CreateTableStatement type mapping (int→INT, uuid→CHAR(36), string/datetime→TEXT, bool→TINYINT, decimal→DECIMAL(18,4), float→DOUBLE, binary→BLOB)",
21
+ "Overrides CreateCreateStoredProcedureStatement to PROCEDURE Create{Plural}",
22
+ "Overrides CreateReadStoredProcedureStatement to PROCEDURE Read{Plural}By{Properties}",
23
+ "Overrides CreateUpdateStoredProcedureStatement to PROCEDURE Update{Plural}By{Properties}",
24
+ "Overrides CreateDeleteStoredProcedureStatement to PROCEDURE Delete{Plural}By{Properties}",
25
+ "Overrides CreateBulkCreateStoredProcedureStatement to PROCEDURE BulkCreate{Plural}",
26
+ "Overrides CreateBulkUpdateStoredProcedureStatement to PROCEDURE BulkUpdate{Plural}",
27
+ "Overrides CreateBulkDeleteStoredProcedureStatement to PROCEDURE BulkDelete{Plural}",
28
+ ],
29
+ changeLogs: [],
30
+ };
31
+ rowTypeConverters = MysqlRowTypeConverters;
32
+ quoteLeft = "`";
33
+ quoteRight = "`";
34
+ typeConverter = new DeterministicTypeConverter({
35
+ string: "TEXT",
36
+ character: "CHAR(1)",
37
+ number: "INT",
38
+ integer: "INT",
39
+ unsignedinteger: "INT UNSIGNED",
40
+ smallinteger: "SMALLINT",
41
+ unsignedsmallinteger: "SMALLINT UNSIGNED",
42
+ biginteger: "BIGINT",
43
+ unsignedbiginteger: "BIGINT UNSIGNED",
44
+ float: "DOUBLE",
45
+ decimal: "DECIMAL(10, 2)",
46
+ boolean: "TINYINT(1)",
47
+ datetime: "DATETIME(3)",
48
+ binary: "BLOB",
49
+ uuid: "CHAR(36)",
50
+ reference: "INT",
51
+ }, {
52
+ dialectName: "mysql",
53
+ stringUnlimited: "TEXT",
54
+ stringSized: (n) => (n <= 16383 ? `VARCHAR(${n})` : "LONGTEXT"),
55
+ character: "CHAR",
56
+ decimal: { kind: "optional", type: "DECIMAL", fallback: "DECIMAL(10, 2)" },
57
+ binaryUnlimited: "BLOB",
58
+ binarySized: (n) => (n <= 65535 ? `VARBINARY(${n})` : "LONGBLOB"),
59
+ });
60
+ defaultValueConverter = new DefaultValueConverter({
61
+ newId: () => `(UUID())`,
62
+ now: () => `(NOW(3))`,
63
+ utcNow: () => `(UTC_TIMESTAMP(3))`,
64
+ booleanOn: "1",
65
+ booleanOff: "0",
66
+ hex: (arg) => `X'${arg}'`,
67
+ hexReverse: /^X'(.*)'$/,
68
+ });
69
+ columnType = (name) => columnTypes[name] ??
70
+ (() => {
71
+ throw new DatasourceError(`Unknown mysql type "${name}"`);
72
+ })();
73
+ connection = (options) => MysqlConnection.from(options);
74
+ command = (connection) => new MysqlCommand(connection, statements);
75
+ transaction = (connection, isolation = "readCommitted") => new MysqlTransaction(connection, isolation);
76
+ }
@@ -0,0 +1,4 @@
1
+ import { DropIndexStatementTextRenderer, type DropIndexStatement } from "@deterministic-code/datasources";
2
+ export declare class MysqlDropIndexStatementRenderer extends DropIndexStatementTextRenderer {
3
+ render(statement: DropIndexStatement): string;
4
+ }
@@ -0,0 +1,6 @@
1
+ import { DropIndexStatementTextRenderer } from "@deterministic-code/datasources";
2
+ export class MysqlDropIndexStatementRenderer extends DropIndexStatementTextRenderer {
3
+ render(statement) {
4
+ return `DROP INDEX ${statement.name} ON ${statement.table}`;
5
+ }
6
+ }
@@ -0,0 +1,4 @@
1
+ import { DropRoutineStatementTextRenderer, type DropRoutineStatement } from "@deterministic-code/datasources";
2
+ export declare class MysqlDropRoutineStatementRenderer extends DropRoutineStatementTextRenderer {
3
+ render(statement: DropRoutineStatement): string;
4
+ }
@@ -0,0 +1,6 @@
1
+ import { DropRoutineStatementTextRenderer } from "@deterministic-code/datasources";
2
+ export class MysqlDropRoutineStatementRenderer extends DropRoutineStatementTextRenderer {
3
+ render(statement) {
4
+ return `DROP PROCEDURE IF EXISTS ${statement.name}`;
5
+ }
6
+ }
@@ -0,0 +1,18 @@
1
+ import { AlterTableStatement, BulkUpdateStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateTableStatement, CreateTriggerStatement, CreateUpdateStoredProcedureStatement, DropIndexStatement, DropRoutineStatement, SelectInsertedIdStatement, StatementRenderer, type StatementTextRenderer } from "@deterministic-code/datasources";
2
+ export declare class MysqlStatementRenderer extends StatementRenderer {
3
+ constructor(text: StatementTextRenderer);
4
+ protected visitSelectInsertedId(inserted: SelectInsertedIdStatement): string;
5
+ protected visitCreateTable(create: CreateTableStatement): string;
6
+ protected visitAlterTable(alter: AlterTableStatement): string;
7
+ protected visitDropIndex(index: DropIndexStatement): string;
8
+ protected visitBulkUpdate(update: BulkUpdateStatement): string;
9
+ protected visitCreateTrigger(trigger: CreateTriggerStatement): string;
10
+ protected visitDropRoutine(routine: DropRoutineStatement): string;
11
+ protected visitCreateCreateStoredProcedureStatement(statement: CreateCreateStoredProcedureStatement): string;
12
+ protected visitCreateReadStoredProcedureStatement(statement: CreateReadStoredProcedureStatement): string;
13
+ protected visitCreateUpdateStoredProcedureStatement(statement: CreateUpdateStoredProcedureStatement): string;
14
+ protected visitCreateDeleteStoredProcedureStatement(statement: CreateDeleteStoredProcedureStatement): string;
15
+ protected visitCreateBulkCreateStoredProcedureStatement(statement: CreateBulkCreateStoredProcedureStatement): string;
16
+ protected visitCreateBulkUpdateStoredProcedureStatement(statement: CreateBulkUpdateStoredProcedureStatement): string;
17
+ protected visitCreateBulkDeleteStoredProcedureStatement(statement: CreateBulkDeleteStoredProcedureStatement): string;
18
+ }
@@ -0,0 +1,60 @@
1
+ import { SelectInsertedIdStatementTextRenderer, StatementRenderer, } from "@deterministic-code/datasources";
2
+ import { MysqlAlterTableStatementRenderer } from "./MysqlAlterTableStatementRenderer.js";
3
+ import { MysqlBulkUpdateStatementTextRenderer } from "./MysqlBulkUpdateStatementTextRenderer.js";
4
+ import { MysqlCreateTableStatementRenderer } from "./MysqlCreateTableStatementRenderer.js";
5
+ import { MysqlCreateTriggerStatementRenderer } from "./MysqlCreateTriggerStatementRenderer.js";
6
+ import { MysqlDropIndexStatementRenderer } from "./MysqlDropIndexStatementRenderer.js";
7
+ import { MysqlDropRoutineStatementRenderer } from "./MysqlDropRoutineStatementRenderer.js";
8
+ import { MysqlStoredProcedureStatementRenderer } from "./MysqlStoredProcedureStatementRenderer.js";
9
+ class MysqlSelectInsertedIdStatementTextRenderer extends SelectInsertedIdStatementTextRenderer {
10
+ render(statement) {
11
+ return `SELECT LAST_INSERT_ID() AS ${statement.column}`;
12
+ }
13
+ }
14
+ export class MysqlStatementRenderer extends StatementRenderer {
15
+ constructor(text) {
16
+ super(text);
17
+ }
18
+ visitSelectInsertedId(inserted) {
19
+ return new MysqlSelectInsertedIdStatementTextRenderer().render(inserted);
20
+ }
21
+ visitCreateTable(create) {
22
+ return new MysqlCreateTableStatementRenderer().render(create);
23
+ }
24
+ visitAlterTable(alter) {
25
+ return new MysqlAlterTableStatementRenderer().renderAlter(alter);
26
+ }
27
+ visitDropIndex(index) {
28
+ return new MysqlDropIndexStatementRenderer().render(index);
29
+ }
30
+ visitBulkUpdate(update) {
31
+ return new MysqlBulkUpdateStatementTextRenderer().render(update);
32
+ }
33
+ visitCreateTrigger(trigger) {
34
+ return new MysqlCreateTriggerStatementRenderer().render(trigger);
35
+ }
36
+ visitDropRoutine(routine) {
37
+ return new MysqlDropRoutineStatementRenderer().render(routine);
38
+ }
39
+ visitCreateCreateStoredProcedureStatement(statement) {
40
+ return new MysqlStoredProcedureStatementRenderer().renderCreate(statement);
41
+ }
42
+ visitCreateReadStoredProcedureStatement(statement) {
43
+ return new MysqlStoredProcedureStatementRenderer().renderRead(statement);
44
+ }
45
+ visitCreateUpdateStoredProcedureStatement(statement) {
46
+ return new MysqlStoredProcedureStatementRenderer().renderUpdate(statement);
47
+ }
48
+ visitCreateDeleteStoredProcedureStatement(statement) {
49
+ return new MysqlStoredProcedureStatementRenderer().renderDelete(statement);
50
+ }
51
+ visitCreateBulkCreateStoredProcedureStatement(statement) {
52
+ return new MysqlStoredProcedureStatementRenderer().renderBulkCreate(statement);
53
+ }
54
+ visitCreateBulkUpdateStoredProcedureStatement(statement) {
55
+ return new MysqlStoredProcedureStatementRenderer().renderBulkUpdate(statement);
56
+ }
57
+ visitCreateBulkDeleteStoredProcedureStatement(statement) {
58
+ return new MysqlStoredProcedureStatementRenderer().renderBulkDelete(statement);
59
+ }
60
+ }
@@ -0,0 +1,10 @@
1
+ import type { CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateUpdateStoredProcedureStatement } from "@deterministic-code/datasources";
2
+ export declare class MysqlStoredProcedureStatementRenderer {
3
+ renderCreate(statement: CreateCreateStoredProcedureStatement): string;
4
+ renderRead(statement: CreateReadStoredProcedureStatement): string;
5
+ renderUpdate(statement: CreateUpdateStoredProcedureStatement): string;
6
+ renderDelete(statement: CreateDeleteStoredProcedureStatement): string;
7
+ renderBulkCreate(statement: CreateBulkCreateStoredProcedureStatement): string;
8
+ renderBulkUpdate(statement: CreateBulkUpdateStoredProcedureStatement): string;
9
+ renderBulkDelete(statement: CreateBulkDeleteStoredProcedureStatement): string;
10
+ }
@@ -0,0 +1,101 @@
1
+ const params = (list) => list.map((p) => ` IN ${p.name} ${p.type}`).join(",\n");
2
+ const where = (columns) => columns.map((c) => `t.${c} = ${c}`).join(" AND ");
3
+ const jsonColumns = (columns) => columns.map((c) => `${c.name} ${c.type} PATH '$.${c.name}'`).join(", ");
4
+ const procedure = (name, signature, body) => `DROP PROCEDURE IF EXISTS ${name};
5
+ GO
6
+ CREATE PROCEDURE ${name}(${signature})
7
+ BEGIN
8
+ ${body}
9
+ END;
10
+ GO`;
11
+ export class MysqlStoredProcedureStatementRenderer {
12
+ renderCreate(statement) {
13
+ const generated = statement.generatedKey;
14
+ const insertCols = generated ? statement.columns : statement.parameters.map((p) => p.name);
15
+ const vals = generated
16
+ ? statement.columns.map((c) => (c === statement.key ? "new_id" : c))
17
+ : statement.parameters.map((p) => p.name);
18
+ const body = generated
19
+ ? ` DECLARE new_id VARCHAR(36);
20
+ SET new_id = UUID();
21
+ INSERT INTO ${statement.table} (${insertCols.join(", ")})
22
+ VALUES (${vals.join(", ")});
23
+ SELECT new_id AS ${statement.key};`
24
+ : ` INSERT INTO ${statement.table} (${insertCols.join(", ")})
25
+ VALUES (${vals.join(", ")});
26
+ SELECT LAST_INSERT_ID() AS ${statement.key};`;
27
+ return procedure(statement.name, `\n${params(statement.parameters)}\n`, body);
28
+ }
29
+ renderRead(statement) {
30
+ const aliased = statement.columns.map((c) => `t.${c}`).join(", ");
31
+ if (statement.byProperties.length === 0) {
32
+ return procedure(statement.name, "", ` SELECT ${aliased} FROM ${statement.table} t ORDER BY t.${statement.key};`);
33
+ }
34
+ const signature = statement.byProperties
35
+ .map((c) => {
36
+ const type = statement.parameters.find((p) => p.name === c)?.type ?? statement.keyType;
37
+ return `IN ${c} ${type}`;
38
+ })
39
+ .join(", ");
40
+ return procedure(statement.name, signature, ` SELECT ${aliased} FROM ${statement.table} t WHERE ${where(statement.byProperties)};`);
41
+ }
42
+ renderUpdate(statement) {
43
+ const sets = statement.writable.map((c) => ` t.${c} = ${c}`);
44
+ if (statement.auditUpdated !== undefined) {
45
+ const auditParam = statement.parameters.find((p) => p.name.includes("new_updated"));
46
+ if (auditParam)
47
+ sets.push(` t.${statement.auditUpdated} = ${auditParam.name}`);
48
+ }
49
+ const by = statement.byProperties.length > 0 ? statement.byProperties : [statement.key];
50
+ return procedure(statement.name, `\n${params(statement.parameters)}\n`, ` UPDATE ${statement.table} t
51
+ SET
52
+ ${sets.join(",\n")}
53
+ WHERE ${where(by)};
54
+ SELECT ROW_COUNT() AS affected;`);
55
+ }
56
+ renderDelete(statement) {
57
+ const by = statement.byProperties.length > 0 ? statement.byProperties : [statement.key];
58
+ const signature = by
59
+ .map((c) => {
60
+ const type = statement.parameters.find((p) => p.name === c)?.type ?? statement.keyType;
61
+ return `IN ${c} ${type}`;
62
+ })
63
+ .join(", ");
64
+ return procedure(statement.name, signature, ` DELETE t FROM ${statement.table} t WHERE ${where(by)};
65
+ SELECT ROW_COUNT() AS affected;`);
66
+ }
67
+ renderBulkCreate(statement) {
68
+ const cols = statement.columns.map((c) => c.name).join(", ");
69
+ return procedure(statement.name, `IN ${statement.payload.name} ${statement.payload.type}, OUT ids JSON`, ` INSERT INTO ${statement.table} (${cols})
70
+ SELECT ${cols}
71
+ FROM JSON_TABLE(${statement.payload.name}, '$[*]' COLUMNS (
72
+ ${jsonColumns(statement.columns)}
73
+ )) AS payload_rows;
74
+ GET DIAGNOSTICS @n = ROW_COUNT;
75
+ SET @first = LAST_INSERT_ID();
76
+ SET ids = JSON_ARRAY();
77
+ SET @i = 0;
78
+ WHILE @i < @n DO
79
+ SET ids = JSON_ARRAY_APPEND(ids, '$', @first + @i);
80
+ SET @i = @i + 1;
81
+ END WHILE;`);
82
+ }
83
+ renderBulkUpdate(statement) {
84
+ const sets = statement.columns
85
+ .filter((c) => c.name !== statement.key)
86
+ .map((c) => `c.${c.name} = payload_rows.${c.name}`)
87
+ .join(", ");
88
+ return procedure(statement.name, `IN ${statement.payload.name} ${statement.payload.type}`, ` UPDATE ${statement.table} c
89
+ JOIN JSON_TABLE(${statement.payload.name}, '$[*]' COLUMNS (
90
+ ${jsonColumns(statement.columns)}
91
+ )) AS payload_rows ON c.${statement.key} = payload_rows.${statement.key}
92
+ SET ${sets};`);
93
+ }
94
+ renderBulkDelete(statement) {
95
+ const key = statement.columns.find((c) => c.name === statement.key) ?? {
96
+ name: statement.key,
97
+ type: statement.keyType,
98
+ };
99
+ return procedure(statement.name, `IN ${statement.payload.name} ${statement.payload.type}`, ` DELETE c FROM ${statement.table} c JOIN JSON_TABLE(${statement.payload.name}, '$[*]' COLUMNS (${key.name} ${key.type} PATH '$.${key.name}')) AS payload_rows ON c.${statement.key} = payload_rows.${statement.key};`);
100
+ }
101
+ }
package/dist/bind.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type Parameter } from "@deterministic-code/datasources";
2
+ import type { Scalar } from "./scalar.js";
3
+ export declare const withoutReturning: (sql: string) => string;
4
+ export declare const toMysql: (sql: string) => string;
5
+ export declare const bindParameters: (parameters?: Parameter[] | null) => Record<string, Scalar>;
package/dist/bind.js ADDED
@@ -0,0 +1,20 @@
1
+ import { parametersToRecord } from "@deterministic-code/datasources";
2
+ import { reverse } from "./converters/MysqlRowTypeConverters.js";
3
+ const NAME = /\$([A-Za-z_][A-Za-z0-9_]*)/g;
4
+ const TOKEN = /\$[A-Za-z_][A-Za-z0-9_]*|'[^']*'|"[^"]*"|`[^`]*`|\b[A-Za-z_][A-Za-z0-9_]*\b/g;
5
+ export const withoutReturning = (sql) => sql.replace(/\s+RETURNING\b[^;]*$/i, "");
6
+ const quoteReserved = (sql) => sql.replace(TOKEN, (token, offset) => {
7
+ const skip = token.startsWith("$") || token.startsWith("'") || token.startsWith("\"") || token.startsWith("`");
8
+ const reserved = !skip && token.toLowerCase() === "key"
9
+ && !/\b(?:PRIMARY|FOREIGN|UNIQUE)\s+$/i.test(sql.slice(0, offset));
10
+ const mixed = !skip && /[A-Z]/.test(token) && /[a-z]/.test(token);
11
+ return reserved || mixed ? `\`${token}\`` : token;
12
+ });
13
+ export const toMysql = (sql) => quoteReserved(withoutReturning(sql)).replace(NAME, (_match, name) => `:${name}`);
14
+ export const bindParameters = (parameters) => {
15
+ const record = parametersToRecord(parameters);
16
+ const bound = {};
17
+ for (const [name, value] of Object.entries(record))
18
+ bound[name] = reverse(value);
19
+ return bound;
20
+ };
@@ -0,0 +1 @@
1
+ export declare const columnTypes: Record<string, string>;
@@ -0,0 +1,10 @@
1
+ export const columnTypes = {
2
+ int: "INT",
3
+ uuid: "CHAR(36)",
4
+ string: "TEXT",
5
+ datetime: "TEXT",
6
+ bool: "TINYINT",
7
+ decimal: "DECIMAL(18,4)",
8
+ float: "DOUBLE",
9
+ binary: "BLOB",
10
+ };
@@ -0,0 +1,16 @@
1
+ import { CommandMiddlewareHost, Native, StatementTextRenderer, Response, type Cells, type ICommand, type IReader, type IStatement, type Parameter } from "@deterministic-code/datasources";
2
+ import type { MysqlConnection } from "./connection.js";
3
+ export declare class MysqlCommand extends CommandMiddlewareHost implements ICommand {
4
+ readonly connection: MysqlConnection;
5
+ private readonly statements;
6
+ constructor(connection: MysqlConnection, statements?: StatementTextRenderer);
7
+ executeReaderNative(text: Native, parameters?: Parameter[]): Promise<IReader<Cells>>;
8
+ executeReaderStatement(statement: IStatement, parameters?: Parameter[]): Promise<IReader<Cells>>;
9
+ executeStatement(statement: IStatement, parameters?: Parameter[]): Promise<Response>;
10
+ private firstCell;
11
+ executeNative(text: Native, parameters?: Parameter[]): Promise<Response>;
12
+ private executeProcedure;
13
+ private runSql;
14
+ private query;
15
+ private callResults;
16
+ }
@@ -0,0 +1,143 @@
1
+ import { ArrayReader, CommandMiddlewareHost, FirstResultReader, DatasourceError, ExecuteStoredProcedureStatement, Native, ParameterDirection, ReturnStatement, SelectStatement, StatementTextRenderer, Response, splitStatements, statementParameters, statementReader, StreamingReader, } from "@deterministic-code/datasources";
2
+ import { bindParameters, toMysql } from "./bind.js";
3
+ import { MysqlStatementRenderer } from "./MysqlStatementRenderer.js";
4
+ const outputs = (parameters) => parameters?.filter((parameter) => parameter.direction && parameter.direction !== ParameterDirection.Input) ?? [];
5
+ export class MysqlCommand extends CommandMiddlewareHost {
6
+ connection;
7
+ statements;
8
+ constructor(connection, statements) {
9
+ super();
10
+ this.connection = connection;
11
+ this.statements = statements ?? new StatementTextRenderer(t => new MysqlStatementRenderer(t));
12
+ }
13
+ async executeReaderNative(text, parameters) {
14
+ return this.run(text.text, parameters, async (query, params) => {
15
+ if (!query.trim())
16
+ return { value: new ArrayReader([]), rows: [] };
17
+ this.connection.ensureOpen();
18
+ const statements = splitStatements(query);
19
+ if (statements.length === 0)
20
+ return { value: new ArrayReader([]), rows: [] };
21
+ const bound = bindParameters(params);
22
+ if (statements.some((sql) => /^\s*call\b/i.test(sql))) {
23
+ const sets = [];
24
+ for (const sql of statements)
25
+ sets.push(...(await this.callResults(sql, bound)));
26
+ return FirstResultReader.capture(resultSetsReader(sets));
27
+ }
28
+ return FirstResultReader.capture(statementReader(statements, (sql) => this.query(sql, bound)));
29
+ });
30
+ }
31
+ async executeReaderStatement(statement, parameters) {
32
+ return this.executeReaderNative(new Native(this.statements.render(statement)), parameters);
33
+ }
34
+ async executeStatement(statement, parameters) {
35
+ if (statement instanceof SelectStatement || statement instanceof ReturnStatement)
36
+ return this.firstCell(statement, parameters);
37
+ if (statement instanceof ExecuteStoredProcedureStatement && statement.name && outputs(parameters).length)
38
+ return this.executeProcedure(this.statements.render(statement), parameters);
39
+ return this.executeNative(new Native(this.statements.render(statement)), parameters);
40
+ }
41
+ async firstCell(statement, parameters) {
42
+ const reader = await this.executeReaderStatement(statement, parameters);
43
+ try {
44
+ if (!(await reader.next()))
45
+ return new Response(null, 0, parameters);
46
+ const row = reader.current;
47
+ /* v8 ignore next */
48
+ if (!row)
49
+ throw new DatasourceError("Reader current must be a row");
50
+ const values = Object.values(row);
51
+ return new Response(values.length === 0 ? null : values[0], 1, parameters);
52
+ }
53
+ finally {
54
+ await reader.dispose();
55
+ }
56
+ }
57
+ async executeNative(text, parameters) {
58
+ return this.run(text.text, parameters, async (query, params) => {
59
+ if (!query.trim())
60
+ return { value: new Response(undefined, 0, params), rows: [] };
61
+ this.connection.ensureOpen();
62
+ const named = bindParameters(params);
63
+ let rowsAffected = 0;
64
+ let parameters = params;
65
+ let result;
66
+ for (const part of splitStatements(query)) {
67
+ const names = new Set(statementParameters(part));
68
+ const used = Object.fromEntries(Object.entries(named).filter(([name]) => names.has(name.replace(/^[$:@]/, ""))));
69
+ const executed = await this.runSql(part, used, params);
70
+ rowsAffected += executed.rowsAffected;
71
+ parameters = executed.parameters ?? params;
72
+ result = executed.result;
73
+ }
74
+ return {
75
+ value: new Response(result, rowsAffected, parameters),
76
+ rows: [],
77
+ };
78
+ });
79
+ }
80
+ async executeProcedure(sql, parameters) {
81
+ return this.run(sql, parameters, async (query, params) => {
82
+ this.connection.ensureOpen();
83
+ const outs = outputs(params);
84
+ const client = this.connection.require();
85
+ for (const parameter of params) {
86
+ if (!parameter.direction ||
87
+ parameter.direction === ParameterDirection.Input ||
88
+ parameter.direction === ParameterDirection.InputOutput)
89
+ await client.query(`SET @${parameter.name} = :${parameter.name}`, {
90
+ [parameter.name]: parameter.value ?? null,
91
+ });
92
+ }
93
+ await client.query(toMysql(query).replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, "@$1"));
94
+ const [rows] = await client.query(`SELECT ${outs.map((parameter) => `@${parameter.name} AS \`${parameter.name}\``).join(", ")}`);
95
+ const row = Array.isArray(rows) ? rows[0] : undefined;
96
+ return {
97
+ value: new Response(undefined, 0, params?.map((parameter) => row && parameter.direction && parameter.direction !== ParameterDirection.Input
98
+ ? { ...parameter, value: cellValue(row[parameter.name]) }
99
+ : parameter)),
100
+ rows: [],
101
+ };
102
+ });
103
+ }
104
+ async runSql(sql, params, parameters) {
105
+ const [result] = await this.connection.require().query(toMysql(sql), params);
106
+ return Array.isArray(result)
107
+ ? { rowsAffected: result.length, parameters }
108
+ : { rowsAffected: result.affectedRows ?? 0, result: null, parameters };
109
+ }
110
+ query(sql, params) {
111
+ return this.connection.require().queryRows(toMysql(sql), params);
112
+ }
113
+ async callResults(sql, params) {
114
+ const [result] = await this.connection.require().query(toMysql(sql), params);
115
+ if (!Array.isArray(result))
116
+ return [[]];
117
+ return result.filter((item) => Array.isArray(item));
118
+ }
119
+ }
120
+ const cellValue = (cell) => {
121
+ if (cell === null || cell === undefined)
122
+ return null;
123
+ if (typeof cell === "string" || typeof cell === "number" || typeof cell === "boolean" || typeof cell === "bigint")
124
+ return cell;
125
+ if (cell instanceof Uint8Array)
126
+ return new TextDecoder().decode(cell);
127
+ return JSON.stringify(cell);
128
+ };
129
+ const resultSetsReader = (sets) => {
130
+ let set = 0;
131
+ let row = 0;
132
+ return new StreamingReader(async () => {
133
+ const current = sets[set]?.[row];
134
+ if (current === undefined)
135
+ return undefined;
136
+ row += 1;
137
+ return current;
138
+ }, async () => {
139
+ set += 1;
140
+ row = 0;
141
+ return set < sets.length;
142
+ });
143
+ };
@@ -0,0 +1,5 @@
1
+ import type { MysqlCreateConnection } from "./i-mysql-engine.js";
2
+ export interface MysqlConnectionOptions {
3
+ url?: string;
4
+ connect?: MysqlCreateConnection;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { type DatasourceOptions, type ICommand, type IConnection, type IsolationLevelName, type ITransaction } from "@deterministic-code/datasources";
2
+ import type { MysqlConnectionOptions } from "./connection-options.js";
3
+ import { type IMysqlClient } from "./i-mysql-engine.js";
4
+ export declare class MysqlConnection implements IConnection {
5
+ readonly provider = "mysql";
6
+ url: string;
7
+ isOpen: boolean;
8
+ private client?;
9
+ private readonly connect?;
10
+ static from: (options?: DatasourceOptions) => MysqlConnection;
11
+ constructor(options?: MysqlConnectionOptions);
12
+ createCommand: () => ICommand;
13
+ open(): Promise<void>;
14
+ close(): Promise<void>;
15
+ [Symbol.asyncDispose]: () => Promise<void>;
16
+ startTransaction(isolation?: IsolationLevelName): Promise<ITransaction>;
17
+ ensureOpen(): void;
18
+ require(): IMysqlClient;
19
+ }