@deterministic-code/datasources-mysql 0.1.18 → 0.1.29
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/DefaultValueConverter.d.ts +25 -0
- package/dist/DefaultValueConverter.js +172 -0
- package/dist/DefaultValueOptions.d.ts +11 -0
- package/dist/DefaultValueOptions.js +1 -0
- package/dist/MysqlAlterTableStatementRenderer.d.ts +1 -0
- package/dist/MysqlAlterTableStatementRenderer.js +6 -3
- package/dist/MysqlCreateTableStatementRenderer.d.ts +1 -0
- package/dist/MysqlCreateTableStatementRenderer.js +6 -3
- package/dist/MysqlCreateTriggerStatementRenderer.js +3 -2
- package/dist/MysqlDriver.d.ts +15 -2
- package/dist/MysqlDriver.js +51 -37
- package/dist/MysqlDropIndexStatementRenderer.js +2 -1
- package/dist/MysqlQuotedRenderers.d.ts +10 -0
- package/dist/MysqlQuotedRenderers.js +23 -0
- package/dist/MysqlSqlSeedStatementRenderer.d.ts +4 -0
- package/dist/MysqlSqlSeedStatementRenderer.js +49 -0
- package/dist/MysqlStatementRenderer.d.ts +5 -1
- package/dist/MysqlStatementRenderer.js +14 -0
- package/dist/MysqlStoredProcedureStatementRenderer.js +16 -14
- package/dist/TypeMap.d.ts +9 -0
- package/dist/TypeMap.js +43 -0
- package/dist/applyKeywords.d.ts +1 -0
- package/dist/applyKeywords.js +13 -0
- package/dist/procedureSpecs.d.ts +10 -0
- package/dist/procedureSpecs.js +17 -0
- package/package.json +2 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DefaultValueOptions } from "./DefaultValueOptions.js";
|
|
2
|
+
export declare const EMPTY_UUID = "00000000-0000-0000-0000-000000000000";
|
|
3
|
+
export type SpecToken = {
|
|
4
|
+
spec: RegExp;
|
|
5
|
+
specText?: (arg: string) => string;
|
|
6
|
+
};
|
|
7
|
+
export type DefaultEntry = SpecToken & {
|
|
8
|
+
native: (arg: string) => string;
|
|
9
|
+
reverse: RegExp;
|
|
10
|
+
};
|
|
11
|
+
type DefaultToken = {
|
|
12
|
+
token: string;
|
|
13
|
+
arg?: string | boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare const sqlStringLiteral: (value: string | number | boolean | null | undefined) => string;
|
|
16
|
+
export declare class DefaultValueConverter {
|
|
17
|
+
static readonly spec: Record<string, Record<string, SpecToken>>;
|
|
18
|
+
readonly dictionary: Record<string, Record<string, DefaultEntry>>;
|
|
19
|
+
constructor(options: DefaultValueOptions);
|
|
20
|
+
static parse(specType: string, value: unknown): DefaultToken;
|
|
21
|
+
convert: (specType: string, text: string) => string | null;
|
|
22
|
+
reverseConvert: (specType: string, native: string) => string;
|
|
23
|
+
private static dictionaryOf;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
export const EMPTY_UUID = "00000000-0000-0000-0000-000000000000";
|
|
2
|
+
const NUMERIC_TYPES = [
|
|
3
|
+
"integer",
|
|
4
|
+
"unsignedinteger",
|
|
5
|
+
"smallinteger",
|
|
6
|
+
"unsignedsmallinteger",
|
|
7
|
+
"biginteger",
|
|
8
|
+
"unsignedbiginteger",
|
|
9
|
+
"float",
|
|
10
|
+
"decimal",
|
|
11
|
+
];
|
|
12
|
+
const NUMERIC_LITERAL_TYPES = new Set([...NUMERIC_TYPES, "reference"]);
|
|
13
|
+
const CALL_FORM = /^([A-Za-z]+)\('(.*)'\)$/;
|
|
14
|
+
const quotedReverse = /^'(.*)'$/;
|
|
15
|
+
const identity = (arg) => arg;
|
|
16
|
+
const intLit = { spec: /^-?[0-9]+$/, specText: identity };
|
|
17
|
+
const uintLit = { spec: /^[0-9]+$/, specText: identity };
|
|
18
|
+
const floatLit = { spec: /^-?[0-9]+(\.[0-9]+)?$/, specText: identity };
|
|
19
|
+
export const sqlStringLiteral = (value) => `'${String(value).replace(/'/g, "''")}'`;
|
|
20
|
+
const escapeRe = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
21
|
+
const exact = (text) => new RegExp(`^${escapeRe(text)}$`);
|
|
22
|
+
const quoted = (arg) => sqlStringLiteral(arg);
|
|
23
|
+
const callTokenName = (name) => (name === "uuid" ? "Uuid" : name);
|
|
24
|
+
const token = (specToken, native, reverse) => ({
|
|
25
|
+
...specToken,
|
|
26
|
+
native,
|
|
27
|
+
reverse,
|
|
28
|
+
});
|
|
29
|
+
export class DefaultValueConverter {
|
|
30
|
+
static spec = {
|
|
31
|
+
string: { String: { spec: /^.*$/, specText: identity } },
|
|
32
|
+
character: { String: { spec: /^.+$/, specText: identity } },
|
|
33
|
+
integer: { Numeric: intLit },
|
|
34
|
+
unsignedinteger: { Numeric: uintLit },
|
|
35
|
+
smallinteger: { Numeric: intLit },
|
|
36
|
+
unsignedsmallinteger: { Numeric: uintLit },
|
|
37
|
+
biginteger: { Numeric: intLit },
|
|
38
|
+
unsignedbiginteger: { Numeric: uintLit },
|
|
39
|
+
float: { Numeric: floatLit },
|
|
40
|
+
decimal: { Numeric: floatLit, String: { spec: /^.*$/, specText: identity } },
|
|
41
|
+
boolean: { Boolean: { spec: /^(true|false)$/, specText: identity } },
|
|
42
|
+
datetime: {
|
|
43
|
+
Now: { spec: /^Now$/ },
|
|
44
|
+
UtcNow: { spec: /^UtcNow$/ },
|
|
45
|
+
DateTime: {
|
|
46
|
+
spec: /^DateTime\('[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:[0-9]{2})'\)$/,
|
|
47
|
+
specText: (arg) => `DateTime('${arg}')`,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
binary: {
|
|
51
|
+
Hex: { spec: /^Hex\('([0-9a-fA-F]{2})*'\)$/, specText: (arg) => `Hex('${arg}')` },
|
|
52
|
+
},
|
|
53
|
+
uuid: {
|
|
54
|
+
NewId: { spec: /^NewId$/ },
|
|
55
|
+
Empty: { spec: /^Empty$/ },
|
|
56
|
+
Uuid: {
|
|
57
|
+
spec: /^uuid\('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'\)$/,
|
|
58
|
+
specText: (arg) => `uuid('${arg}')`,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
reference: {},
|
|
62
|
+
};
|
|
63
|
+
dictionary;
|
|
64
|
+
constructor(options) {
|
|
65
|
+
this.dictionary = DefaultValueConverter.dictionaryOf(options);
|
|
66
|
+
}
|
|
67
|
+
static parse(specType, value) {
|
|
68
|
+
if (value === undefined || value === null)
|
|
69
|
+
return { token: "None" };
|
|
70
|
+
const v = String(value);
|
|
71
|
+
const call = CALL_FORM.exec(v);
|
|
72
|
+
if (call !== null)
|
|
73
|
+
return { token: callTokenName(call[1]), arg: call[2] };
|
|
74
|
+
if (specType === "boolean") {
|
|
75
|
+
return { token: "Boolean", arg: typeof value === "boolean" ? value : v === "true" };
|
|
76
|
+
}
|
|
77
|
+
if (NUMERIC_LITERAL_TYPES.has(specType))
|
|
78
|
+
return { token: "Numeric", arg: v };
|
|
79
|
+
if (specType === "datetime") {
|
|
80
|
+
if (v === "Now")
|
|
81
|
+
return { token: "Now" };
|
|
82
|
+
if (v === "UtcNow")
|
|
83
|
+
return { token: "UtcNow" };
|
|
84
|
+
return { token: "DateTime", arg: v };
|
|
85
|
+
}
|
|
86
|
+
if (specType === "uuid") {
|
|
87
|
+
if (v === "NewId")
|
|
88
|
+
return { token: "NewId" };
|
|
89
|
+
if (v === "Empty")
|
|
90
|
+
return { token: "Empty" };
|
|
91
|
+
return { token: "Uuid", arg: v };
|
|
92
|
+
}
|
|
93
|
+
if (specType === "binary")
|
|
94
|
+
return { token: "Hex", arg: v };
|
|
95
|
+
return { token: "String", arg: v };
|
|
96
|
+
}
|
|
97
|
+
convert = (specType, text) => {
|
|
98
|
+
const parsed = DefaultValueConverter.parse(specType, text);
|
|
99
|
+
if (parsed.token === "None")
|
|
100
|
+
return null;
|
|
101
|
+
const entries = this.dictionary[specType];
|
|
102
|
+
if (entries === undefined)
|
|
103
|
+
throw new Error(`Unknown spec field type: ${specType}`);
|
|
104
|
+
const entry = entries[parsed.token];
|
|
105
|
+
if (entry === undefined)
|
|
106
|
+
return null;
|
|
107
|
+
if (!entry.spec.test(text)) {
|
|
108
|
+
throw new Error(`default_value '${text}' is not a valid ${specType} ${parsed.token} default`);
|
|
109
|
+
}
|
|
110
|
+
const expr = entry.native(parsed.arg === undefined ? "" : String(parsed.arg));
|
|
111
|
+
return expr === "" ? null : expr;
|
|
112
|
+
};
|
|
113
|
+
reverseConvert = (specType, native) => {
|
|
114
|
+
const entries = this.dictionary[specType];
|
|
115
|
+
if (entries === undefined)
|
|
116
|
+
throw new Error(`Unknown spec field type: ${specType}`);
|
|
117
|
+
for (const [name, entry] of Object.entries(entries)) {
|
|
118
|
+
const match = entry.reverse.exec(native);
|
|
119
|
+
if (match === null)
|
|
120
|
+
continue;
|
|
121
|
+
const arg = match.slice(1).find((group) => group !== undefined) ?? "";
|
|
122
|
+
return entry.specText === undefined ? name : entry.specText(arg);
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`Native default '${native}' is not a valid ${specType} default`);
|
|
125
|
+
};
|
|
126
|
+
static dictionaryOf = (opts) => {
|
|
127
|
+
const spec = DefaultValueConverter.spec;
|
|
128
|
+
const newId = opts.newId();
|
|
129
|
+
const now = opts.now();
|
|
130
|
+
const utcNow = opts.utcNow();
|
|
131
|
+
const numericEntries = (type) => {
|
|
132
|
+
const numeric = spec[type].Numeric;
|
|
133
|
+
return {
|
|
134
|
+
Numeric: token(numeric, (arg) => arg, numeric.spec),
|
|
135
|
+
String: token(numeric, (arg) => arg, numeric.spec),
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
const quote = opts.quote ?? quoted;
|
|
139
|
+
const quoteReverse = opts.quoteReverse ?? quotedReverse;
|
|
140
|
+
const quotedString = {
|
|
141
|
+
String: token(spec.string.String, quote, quoteReverse),
|
|
142
|
+
Numeric: token(spec.string.String, quote, quoteReverse),
|
|
143
|
+
};
|
|
144
|
+
const dictionary = {
|
|
145
|
+
string: quotedString,
|
|
146
|
+
character: {
|
|
147
|
+
String: token(spec.character.String, quote, quoteReverse),
|
|
148
|
+
Numeric: token(spec.character.String, quote, quoteReverse),
|
|
149
|
+
},
|
|
150
|
+
boolean: {
|
|
151
|
+
Boolean: token(spec.boolean.Boolean, (arg) => (arg === "true" ? opts.booleanOn : opts.booleanOff), new RegExp(`^(${escapeRe(opts.booleanOn)}|${escapeRe(opts.booleanOff)})$`)),
|
|
152
|
+
},
|
|
153
|
+
datetime: {
|
|
154
|
+
Now: token(spec.datetime.Now, () => now, exact(now)),
|
|
155
|
+
UtcNow: token(spec.datetime.UtcNow, () => utcNow, exact(utcNow)),
|
|
156
|
+
DateTime: token(spec.datetime.DateTime, quoted, quotedReverse),
|
|
157
|
+
},
|
|
158
|
+
binary: {
|
|
159
|
+
Hex: token(spec.binary.Hex, opts.hex, opts.hexReverse),
|
|
160
|
+
},
|
|
161
|
+
uuid: {
|
|
162
|
+
NewId: token(spec.uuid.NewId, () => newId, exact(newId)),
|
|
163
|
+
Empty: token(spec.uuid.Empty, () => quoted(EMPTY_UUID), exact(quoted(EMPTY_UUID))),
|
|
164
|
+
Uuid: token(spec.uuid.Uuid, quoted, quotedReverse),
|
|
165
|
+
},
|
|
166
|
+
reference: {},
|
|
167
|
+
};
|
|
168
|
+
for (const type of NUMERIC_TYPES)
|
|
169
|
+
dictionary[type] = numericEntries(type);
|
|
170
|
+
return dictionary;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type DefaultValueOptions = {
|
|
2
|
+
newId: () => string;
|
|
3
|
+
now: () => string;
|
|
4
|
+
utcNow: () => string;
|
|
5
|
+
booleanOn: string;
|
|
6
|
+
booleanOff: string;
|
|
7
|
+
hex: (arg: string) => string;
|
|
8
|
+
hexReverse: RegExp;
|
|
9
|
+
quote?: (arg: string) => string;
|
|
10
|
+
quoteReverse?: RegExp;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AlterTableStatementTextRenderer, type AlterAction, type Column, type NotNull } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class MysqlAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
3
|
+
protected quote(name: string): string;
|
|
3
4
|
protected typeName(column: Column): string;
|
|
4
5
|
protected columnDefault(column: Column): string;
|
|
5
6
|
protected notNullSql(value?: NotNull): string;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { AlterTableStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
-
import {
|
|
2
|
+
import { MysqlDriver } from "./MysqlDriver.js";
|
|
3
3
|
export class MysqlAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
4
|
+
quote(name) {
|
|
5
|
+
return MysqlDriver.quote(name);
|
|
6
|
+
}
|
|
4
7
|
typeName(column) {
|
|
5
|
-
return
|
|
8
|
+
return MysqlDriver.nativeType(column);
|
|
6
9
|
}
|
|
7
10
|
columnDefault(column) {
|
|
8
|
-
return column.default == null ? "" : ` DEFAULT ${column
|
|
11
|
+
return column.default == null ? "" : ` DEFAULT ${MysqlDriver.nativeDefault(column)}`;
|
|
9
12
|
}
|
|
10
13
|
notNullSql(value) {
|
|
11
14
|
return value == null ? "" : " NOT NULL";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CreateTableStatementTextRenderer, type Column, type NotNull } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class MysqlCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
3
|
+
protected quote(name: string): string;
|
|
3
4
|
protected typeName(column: Column): string;
|
|
4
5
|
protected columnDefault(column: Column): string;
|
|
5
6
|
protected notNullSql(value?: NotNull): string;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { CreateTableStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
-
import {
|
|
2
|
+
import { MysqlDriver } from "./MysqlDriver.js";
|
|
3
3
|
export class MysqlCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
4
|
+
quote(name) {
|
|
5
|
+
return MysqlDriver.quote(name);
|
|
6
|
+
}
|
|
4
7
|
typeName(column) {
|
|
5
|
-
return
|
|
8
|
+
return MysqlDriver.nativeType(column);
|
|
6
9
|
}
|
|
7
10
|
columnDefault(column) {
|
|
8
|
-
return column.default == null ? "" : ` DEFAULT ${column
|
|
11
|
+
return column.default == null ? "" : ` DEFAULT ${MysqlDriver.nativeDefault(column)}`;
|
|
9
12
|
}
|
|
10
13
|
notNullSql(value) {
|
|
11
14
|
return value == null ? "" : " NOT NULL";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CreateTriggerStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
import { MysqlDriver } from "./MysqlDriver.js";
|
|
2
3
|
export class MysqlCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
|
|
3
4
|
render(statement) {
|
|
4
|
-
return `CREATE TRIGGER ${statement.name} BEFORE UPDATE ON ${statement.table}
|
|
5
|
-
FOR EACH ROW SET NEW.${statement.updatedColumn} = ${
|
|
5
|
+
return `CREATE TRIGGER ${MysqlDriver.quote(statement.name)} BEFORE UPDATE ON ${MysqlDriver.quote(statement.table)}
|
|
6
|
+
FOR EACH ROW SET NEW.${MysqlDriver.quote(statement.updatedColumn)} = ${MysqlDriver.utcNow()}`;
|
|
6
7
|
}
|
|
7
8
|
}
|
package/dist/MysqlDriver.d.ts
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StatementTextRenderer, type Cell, type Column, type DatasourceOptions, type ICommand, type IConnection, type IDriver, type Information, type ITransaction, type IsolationLevel, type RoutineParameter, type TypeConverter } from "@deterministic-code/datasources";
|
|
2
2
|
import { columnTypes } from "./columnTypes.js";
|
|
3
|
+
import { TypeMap } from "./TypeMap.js";
|
|
4
|
+
import { DefaultValueConverter } from "./DefaultValueConverter.js";
|
|
3
5
|
export { columnTypes };
|
|
4
6
|
export declare class MysqlDriver implements IDriver {
|
|
7
|
+
static readonly procedurePayloadType = "JSON";
|
|
8
|
+
static procedureSpecs: (entityName: string, byGroups: string[][], casing: {
|
|
9
|
+
routineName: (stem: string) => string;
|
|
10
|
+
pluralStem: (entity: string) => string;
|
|
11
|
+
}) => import("./procedureSpecs.js").ProcSpec[];
|
|
12
|
+
static applyKeywords: (sql: string, keyword: (word: string) => string) => string;
|
|
13
|
+
static quote: (name: string) => string;
|
|
14
|
+
static nativeType: (column: Column) => string;
|
|
15
|
+
static nativeDefault: (column: Column) => string;
|
|
16
|
+
static utcNow: () => string;
|
|
17
|
+
static nativeParam: (parameter: RoutineParameter) => string;
|
|
5
18
|
readonly statementRenderer: StatementTextRenderer;
|
|
6
19
|
readonly information: Information;
|
|
7
20
|
rowTypeConverters: Record<string, TypeConverter<Cell, Cell>>;
|
|
8
21
|
readonly quoteLeft = "`";
|
|
9
22
|
readonly quoteRight = "`";
|
|
10
|
-
readonly typeConverter:
|
|
23
|
+
readonly typeConverter: TypeMap;
|
|
11
24
|
readonly defaultValueConverter: DefaultValueConverter;
|
|
12
25
|
columnType: (name: string) => string;
|
|
13
26
|
connection: (options?: DatasourceOptions) => IConnection;
|
package/dist/MysqlDriver.js
CHANGED
|
@@ -1,13 +1,61 @@
|
|
|
1
|
-
import { DatasourceError,
|
|
1
|
+
import { DatasourceError, StatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
2
|
import { MysqlConnection } from "./connection.js";
|
|
3
3
|
import { MysqlCommand } from "./command.js";
|
|
4
4
|
import { MysqlTransaction } from "./transaction.js";
|
|
5
5
|
import { MysqlRowTypeConverters } from "./converters/MysqlRowTypeConverters.js";
|
|
6
6
|
import { MysqlStatementRenderer } from "./MysqlStatementRenderer.js";
|
|
7
7
|
import { columnTypes } from "./columnTypes.js";
|
|
8
|
+
import { procedureSpecs } from "./procedureSpecs.js";
|
|
9
|
+
import { applyKeywords } from "./applyKeywords.js";
|
|
10
|
+
import { TypeMap } from "./TypeMap.js";
|
|
11
|
+
import { DefaultValueConverter, sqlStringLiteral } from "./DefaultValueConverter.js";
|
|
8
12
|
export { columnTypes };
|
|
9
13
|
const statements = new StatementTextRenderer(t => new MysqlStatementRenderer(t));
|
|
14
|
+
const types = new TypeMap({
|
|
15
|
+
string: "TEXT",
|
|
16
|
+
character: "CHAR(1)",
|
|
17
|
+
integer: "INT",
|
|
18
|
+
unsignedinteger: "INT UNSIGNED",
|
|
19
|
+
smallinteger: "SMALLINT",
|
|
20
|
+
unsignedsmallinteger: "SMALLINT UNSIGNED",
|
|
21
|
+
biginteger: "BIGINT",
|
|
22
|
+
unsignedbiginteger: "BIGINT UNSIGNED",
|
|
23
|
+
float: "DOUBLE",
|
|
24
|
+
decimal: "DECIMAL(10, 2)",
|
|
25
|
+
boolean: "TINYINT(1)",
|
|
26
|
+
datetime: "DATETIME(3)",
|
|
27
|
+
binary: "BLOB",
|
|
28
|
+
uuid: "CHAR(36)",
|
|
29
|
+
reference: "INT",
|
|
30
|
+
});
|
|
31
|
+
const defaults = new DefaultValueConverter({
|
|
32
|
+
newId: () => `(UUID())`,
|
|
33
|
+
now: () => `(NOW(3))`,
|
|
34
|
+
utcNow: () => `(UTC_TIMESTAMP(3))`,
|
|
35
|
+
booleanOn: "1",
|
|
36
|
+
booleanOff: "0",
|
|
37
|
+
hex: (arg) => `(X'${arg}')`,
|
|
38
|
+
hexReverse: /^(?:\(X'(.*)'\)|X'(.*)')$/,
|
|
39
|
+
quote: (arg) => `(${sqlStringLiteral(arg)})`,
|
|
40
|
+
quoteReverse: /^(?:\('(.*)'\)|'(.*)')$/,
|
|
41
|
+
});
|
|
10
42
|
export class MysqlDriver {
|
|
43
|
+
static procedurePayloadType = "JSON";
|
|
44
|
+
static procedureSpecs = procedureSpecs;
|
|
45
|
+
static applyKeywords = applyKeywords;
|
|
46
|
+
static quote = (name) => `\`${name}\``;
|
|
47
|
+
static nativeType = (column) => column.field ? types.convertField(column.field) : columnTypes[column.type] ?? column.type;
|
|
48
|
+
static nativeDefault = (column) => column.default == null
|
|
49
|
+
? ""
|
|
50
|
+
: column.field === undefined
|
|
51
|
+
? column.default.value
|
|
52
|
+
: defaults.convert(column.field.type, column.default.value) ?? column.default.value;
|
|
53
|
+
static utcNow = () => defaults.convert("datetime", "UtcNow") ?? "";
|
|
54
|
+
static nativeParam = (parameter) => parameter.type === "payload"
|
|
55
|
+
? MysqlDriver.procedurePayloadType
|
|
56
|
+
: parameter.field
|
|
57
|
+
? types.convertField(parameter.field)
|
|
58
|
+
: types.dictionary[parameter.type] ?? parameter.type;
|
|
11
59
|
statementRenderer = statements;
|
|
12
60
|
information = {
|
|
13
61
|
descriptions: [
|
|
@@ -31,42 +79,8 @@ export class MysqlDriver {
|
|
|
31
79
|
rowTypeConverters = MysqlRowTypeConverters;
|
|
32
80
|
quoteLeft = "`";
|
|
33
81
|
quoteRight = "`";
|
|
34
|
-
typeConverter =
|
|
35
|
-
|
|
36
|
-
character: "CHAR(1)",
|
|
37
|
-
integer: "INT",
|
|
38
|
-
unsignedinteger: "INT UNSIGNED",
|
|
39
|
-
smallinteger: "SMALLINT",
|
|
40
|
-
unsignedsmallinteger: "SMALLINT UNSIGNED",
|
|
41
|
-
biginteger: "BIGINT",
|
|
42
|
-
unsignedbiginteger: "BIGINT UNSIGNED",
|
|
43
|
-
float: "DOUBLE",
|
|
44
|
-
decimal: "DECIMAL(10, 2)",
|
|
45
|
-
boolean: "TINYINT(1)",
|
|
46
|
-
datetime: "DATETIME(3)",
|
|
47
|
-
binary: "BLOB",
|
|
48
|
-
uuid: "CHAR(36)",
|
|
49
|
-
reference: "INT",
|
|
50
|
-
}, {
|
|
51
|
-
dialectName: "mysql",
|
|
52
|
-
stringUnlimited: "TEXT",
|
|
53
|
-
stringSized: (n) => (n <= 16383 ? `VARCHAR(${n})` : "LONGTEXT"),
|
|
54
|
-
character: "CHAR",
|
|
55
|
-
decimal: { kind: "optional", type: "DECIMAL", fallback: "DECIMAL(10, 2)" },
|
|
56
|
-
binaryUnlimited: "BLOB",
|
|
57
|
-
binarySized: (n) => (n <= 65535 ? `VARBINARY(${n})` : "LONGBLOB"),
|
|
58
|
-
});
|
|
59
|
-
defaultValueConverter = new DefaultValueConverter({
|
|
60
|
-
newId: () => `(UUID())`,
|
|
61
|
-
now: () => `(NOW(3))`,
|
|
62
|
-
utcNow: () => `(UTC_TIMESTAMP(3))`,
|
|
63
|
-
booleanOn: "1",
|
|
64
|
-
booleanOff: "0",
|
|
65
|
-
hex: (arg) => `(X'${arg}')`,
|
|
66
|
-
hexReverse: /^(?:\(X'(.*)'\)|X'(.*)')$/,
|
|
67
|
-
quote: (arg) => `(${sqlStringLiteral(arg)})`,
|
|
68
|
-
quoteReverse: /^(?:\('(.*)'\)|'(.*)')$/,
|
|
69
|
-
});
|
|
82
|
+
typeConverter = types;
|
|
83
|
+
defaultValueConverter = defaults;
|
|
70
84
|
columnType = (name) => columnTypes[name] ??
|
|
71
85
|
(() => {
|
|
72
86
|
throw new DatasourceError(`Unknown mysql type "${name}"`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DropIndexStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
import { MysqlDriver } from "./MysqlDriver.js";
|
|
2
3
|
export class MysqlDropIndexStatementRenderer extends DropIndexStatementTextRenderer {
|
|
3
4
|
render(statement) {
|
|
4
|
-
return `DROP INDEX ${statement.name} ON ${statement.table}`;
|
|
5
|
+
return `DROP INDEX ${MysqlDriver.quote(statement.name)} ON ${MysqlDriver.quote(statement.table)}`;
|
|
5
6
|
}
|
|
6
7
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CreateIndexStatementTextRenderer, DropTableStatementTextRenderer, type ForeignKeyStatement } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class MysqlDropTableStatementRenderer extends DropTableStatementTextRenderer {
|
|
3
|
+
protected quote(name: string): string;
|
|
4
|
+
}
|
|
5
|
+
export declare class MysqlCreateIndexStatementRenderer extends CreateIndexStatementTextRenderer {
|
|
6
|
+
protected quote(name: string): string;
|
|
7
|
+
}
|
|
8
|
+
export declare class MysqlForeignKeyStatementRenderer {
|
|
9
|
+
render(statement: ForeignKeyStatement): string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CreateIndexStatementTextRenderer, DropTableStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { MysqlDriver } from "./MysqlDriver.js";
|
|
3
|
+
export class MysqlDropTableStatementRenderer extends DropTableStatementTextRenderer {
|
|
4
|
+
quote(name) {
|
|
5
|
+
return MysqlDriver.quote(name);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class MysqlCreateIndexStatementRenderer extends CreateIndexStatementTextRenderer {
|
|
9
|
+
quote(name) {
|
|
10
|
+
return MysqlDriver.quote(name);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class MysqlForeignKeyStatementRenderer {
|
|
14
|
+
render(statement) {
|
|
15
|
+
const table = MysqlDriver.quote(statement.table);
|
|
16
|
+
const name = MysqlDriver.quote(statement.name);
|
|
17
|
+
if (statement.kind === "drop")
|
|
18
|
+
return `ALTER TABLE ${table} DROP FOREIGN KEY ${name}`;
|
|
19
|
+
const cols = statement.columns.map(MysqlDriver.quote).join(", ");
|
|
20
|
+
const refs = statement.referencesColumns.map(MysqlDriver.quote).join(", ");
|
|
21
|
+
return `ALTER TABLE ${table} ADD CONSTRAINT ${name} FOREIGN KEY (${cols}) REFERENCES ${MysqlDriver.quote(statement.referencesTable)} (${refs})`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const quote = (ident) => `\`${ident}\``;
|
|
2
|
+
const quoteString = (value) => `'${String(value).replace(/'/g, "''")}'`;
|
|
3
|
+
const truthy = (value) => value === true || value === "true" || value === "True" || value === 1;
|
|
4
|
+
const numeric = new Set([
|
|
5
|
+
"integer",
|
|
6
|
+
"unsignedinteger",
|
|
7
|
+
"smallinteger",
|
|
8
|
+
"unsignedsmallinteger",
|
|
9
|
+
"biginteger",
|
|
10
|
+
"unsignedbiginteger",
|
|
11
|
+
"reference",
|
|
12
|
+
"float",
|
|
13
|
+
"decimal",
|
|
14
|
+
]);
|
|
15
|
+
const defaultSql = (type, def) => {
|
|
16
|
+
const text = String(def);
|
|
17
|
+
if (type === "datetime" && text === "UtcNow")
|
|
18
|
+
return "(UTC_TIMESTAMP(3))";
|
|
19
|
+
if (type === "datetime" && text === "Now")
|
|
20
|
+
return "(NOW(3))";
|
|
21
|
+
if (type === "uuid" && text === "NewId")
|
|
22
|
+
return "(UUID())";
|
|
23
|
+
if (type === "boolean")
|
|
24
|
+
return text === "true" || text === "True" ? "1" : "0";
|
|
25
|
+
return quoteString(text);
|
|
26
|
+
};
|
|
27
|
+
const literal = (column) => {
|
|
28
|
+
if (column.value == null)
|
|
29
|
+
return column.field.defaultValue != null ? defaultSql(column.field.type, column.field.defaultValue) : "NULL";
|
|
30
|
+
if (column.field.type === "boolean")
|
|
31
|
+
return truthy(column.value) ? "1" : "0";
|
|
32
|
+
if (numeric.has(column.field.type))
|
|
33
|
+
return String(column.value);
|
|
34
|
+
if (column.field.type === "binary")
|
|
35
|
+
return `(X'${column.value}')`;
|
|
36
|
+
return quoteString(column.value);
|
|
37
|
+
};
|
|
38
|
+
export class MysqlSqlSeedStatementRenderer {
|
|
39
|
+
render(statement) {
|
|
40
|
+
const table = quote(statement.table);
|
|
41
|
+
if (statement.kind === "delete") {
|
|
42
|
+
const key = statement.columns[0];
|
|
43
|
+
return `DELETE FROM ${table} WHERE ${quote(key.column)} = ${literal(key)}`;
|
|
44
|
+
}
|
|
45
|
+
const cols = statement.columns.map((c) => quote(c.column)).join(", ");
|
|
46
|
+
const values = statement.columns.map(literal).join(", ");
|
|
47
|
+
return `INSERT INTO ${table} (${cols}) VALUES (${values})`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AlterTableStatement, BulkUpdateStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateTableStatement, CreateTriggerStatement, CreateUpdateStoredProcedureStatement, DropIndexStatement, DropRoutineStatement, SelectInsertedIdStatement, StatementRenderer, type StatementTextRenderer } from "@deterministic-code/datasources";
|
|
1
|
+
import { AlterTableStatement, BulkUpdateStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateTableStatement, CreateTriggerStatement, CreateUpdateStoredProcedureStatement, CreateIndexStatement, DropIndexStatement, DropRoutineStatement, DropTableStatement, ForeignKeyStatement, SelectInsertedIdStatement, SqlSeedStatement, StatementRenderer, type StatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class MysqlStatementRenderer extends StatementRenderer {
|
|
3
3
|
constructor(text: StatementTextRenderer);
|
|
4
4
|
protected visitSelectInsertedId(inserted: SelectInsertedIdStatement): string;
|
|
@@ -16,4 +16,8 @@ export declare class MysqlStatementRenderer extends StatementRenderer {
|
|
|
16
16
|
protected visitCreateBulkCreateStoredProcedureStatement(statement: CreateBulkCreateStoredProcedureStatement): string;
|
|
17
17
|
protected visitCreateBulkUpdateStoredProcedureStatement(statement: CreateBulkUpdateStoredProcedureStatement): string;
|
|
18
18
|
protected visitCreateBulkDeleteStoredProcedureStatement(statement: CreateBulkDeleteStoredProcedureStatement): string;
|
|
19
|
+
protected visitSqlSeed(seed: SqlSeedStatement): string;
|
|
20
|
+
protected visitDropTable(drop: DropTableStatement): string;
|
|
21
|
+
protected visitCreateIndex(index: CreateIndexStatement): string;
|
|
22
|
+
protected visitForeignKey(key: ForeignKeyStatement): string;
|
|
19
23
|
}
|
|
@@ -6,6 +6,8 @@ import { MysqlCreateTriggerStatementRenderer } from "./MysqlCreateTriggerStateme
|
|
|
6
6
|
import { MysqlDropIndexStatementRenderer } from "./MysqlDropIndexStatementRenderer.js";
|
|
7
7
|
import { MysqlDropRoutineStatementRenderer } from "./MysqlDropRoutineStatementRenderer.js";
|
|
8
8
|
import { MysqlStoredProcedureStatementRenderer } from "./MysqlStoredProcedureStatementRenderer.js";
|
|
9
|
+
import { MysqlSqlSeedStatementRenderer } from "./MysqlSqlSeedStatementRenderer.js";
|
|
10
|
+
import { MysqlCreateIndexStatementRenderer, MysqlDropTableStatementRenderer, MysqlForeignKeyStatementRenderer, } from "./MysqlQuotedRenderers.js";
|
|
9
11
|
class MysqlSelectInsertedIdStatementTextRenderer extends SelectInsertedIdStatementTextRenderer {
|
|
10
12
|
render(statement) {
|
|
11
13
|
return `SELECT LAST_INSERT_ID() AS ${statement.column}`;
|
|
@@ -60,4 +62,16 @@ export class MysqlStatementRenderer extends StatementRenderer {
|
|
|
60
62
|
visitCreateBulkDeleteStoredProcedureStatement(statement) {
|
|
61
63
|
return new MysqlStoredProcedureStatementRenderer().renderBulkDelete(statement);
|
|
62
64
|
}
|
|
65
|
+
visitSqlSeed(seed) {
|
|
66
|
+
return new MysqlSqlSeedStatementRenderer().render(seed);
|
|
67
|
+
}
|
|
68
|
+
visitDropTable(drop) {
|
|
69
|
+
return new MysqlDropTableStatementRenderer().render(drop);
|
|
70
|
+
}
|
|
71
|
+
visitCreateIndex(index) {
|
|
72
|
+
return new MysqlCreateIndexStatementRenderer().render(index);
|
|
73
|
+
}
|
|
74
|
+
visitForeignKey(key) {
|
|
75
|
+
return new MysqlForeignKeyStatementRenderer().render(key);
|
|
76
|
+
}
|
|
63
77
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { DefaultStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
-
|
|
2
|
+
import { MysqlDriver } from "./MysqlDriver.js";
|
|
3
|
+
const typeOf = (parameter) => MysqlDriver.nativeParam(parameter);
|
|
4
|
+
const params = (list) => list.map((p) => ` IN ${p.name} ${typeOf(p)}`).join(",\n");
|
|
3
5
|
const where = (columns) => columns.map((c) => `t.${c} = ${c}`).join(" AND ");
|
|
4
|
-
const jsonColumns = (columns) => columns.map((c) => `${c.name} ${c
|
|
6
|
+
const jsonColumns = (columns) => columns.map((c) => `${c.name} ${typeOf(c)} PATH '$.${c.name}'`).join(", ");
|
|
5
7
|
const procedure = (name, signature, body) => `DROP PROCEDURE IF EXISTS ${name};
|
|
6
8
|
GO
|
|
7
9
|
CREATE PROCEDURE ${name}(${signature})
|
|
@@ -19,10 +21,10 @@ export class MysqlStoredProcedureStatementRenderer {
|
|
|
19
21
|
const body = generated
|
|
20
22
|
? ` DECLARE new_id VARCHAR(36);
|
|
21
23
|
SET new_id = UUID();
|
|
22
|
-
INSERT INTO ${statement.table} (${insertCols.join(", ")})
|
|
24
|
+
INSERT INTO ${MysqlDriver.quote(statement.table)} (${insertCols.join(", ")})
|
|
23
25
|
VALUES (${vals.join(", ")});
|
|
24
26
|
SELECT new_id AS ${statement.key};`
|
|
25
|
-
: ` INSERT INTO ${statement.table} (${insertCols.join(", ")})
|
|
27
|
+
: ` INSERT INTO ${MysqlDriver.quote(statement.table)} (${insertCols.join(", ")})
|
|
26
28
|
VALUES (${vals.join(", ")});
|
|
27
29
|
SELECT LAST_INSERT_ID() AS ${statement.key};`;
|
|
28
30
|
return procedure(statement.name, `\n${params(statement.parameters)}\n`, body);
|
|
@@ -30,22 +32,22 @@ export class MysqlStoredProcedureStatementRenderer {
|
|
|
30
32
|
renderNestedRead(statement) {
|
|
31
33
|
const select = DefaultStatementTextRenderer.instance.render(statement.select);
|
|
32
34
|
const signature = statement.arguments
|
|
33
|
-
.map((argument) => `IN ${argument.parameter.name} ${argument.parameter
|
|
35
|
+
.map((argument) => `IN ${argument.parameter.name} ${typeOf(argument.parameter)}`)
|
|
34
36
|
.join(", ");
|
|
35
37
|
return procedure(statement.name, signature, ` ${select};`);
|
|
36
38
|
}
|
|
37
39
|
renderRead(statement) {
|
|
38
40
|
const aliased = statement.columns.map((c) => `t.${c}`).join(", ");
|
|
39
41
|
if (statement.byProperties.length === 0) {
|
|
40
|
-
return procedure(statement.name, "", ` SELECT ${aliased} FROM ${statement.table} t ORDER BY t.${statement.key};`);
|
|
42
|
+
return procedure(statement.name, "", ` SELECT ${aliased} FROM ${MysqlDriver.quote(statement.table)} t ORDER BY t.${statement.key};`);
|
|
41
43
|
}
|
|
42
44
|
const signature = statement.byProperties
|
|
43
45
|
.map((c) => {
|
|
44
|
-
const type = statement.parameters.find((p) => p.name === c)
|
|
46
|
+
const type = typeOf(statement.parameters.find((p) => p.name === c) ?? { name: c, type: statement.keyType });
|
|
45
47
|
return `IN ${c} ${type}`;
|
|
46
48
|
})
|
|
47
49
|
.join(", ");
|
|
48
|
-
return procedure(statement.name, signature, ` SELECT ${aliased} FROM ${statement.table} t WHERE ${where(statement.byProperties)};`);
|
|
50
|
+
return procedure(statement.name, signature, ` SELECT ${aliased} FROM ${MysqlDriver.quote(statement.table)} t WHERE ${where(statement.byProperties)};`);
|
|
49
51
|
}
|
|
50
52
|
renderUpdate(statement) {
|
|
51
53
|
const sets = statement.writable.map((c) => ` t.${c} = ${c}`);
|
|
@@ -55,7 +57,7 @@ export class MysqlStoredProcedureStatementRenderer {
|
|
|
55
57
|
sets.push(` t.${statement.auditUpdated} = ${auditParam.name}`);
|
|
56
58
|
}
|
|
57
59
|
const by = statement.byProperties.length > 0 ? statement.byProperties : [statement.key];
|
|
58
|
-
return procedure(statement.name, `\n${params(statement.parameters)}\n`, ` UPDATE ${statement.table} t
|
|
60
|
+
return procedure(statement.name, `\n${params(statement.parameters)}\n`, ` UPDATE ${MysqlDriver.quote(statement.table)} t
|
|
59
61
|
SET
|
|
60
62
|
${sets.join(",\n")}
|
|
61
63
|
WHERE ${where(by)};
|
|
@@ -65,16 +67,16 @@ ${sets.join(",\n")}
|
|
|
65
67
|
const by = statement.byProperties.length > 0 ? statement.byProperties : [statement.key];
|
|
66
68
|
const signature = by
|
|
67
69
|
.map((c) => {
|
|
68
|
-
const type = statement.parameters.find((p) => p.name === c)
|
|
70
|
+
const type = typeOf(statement.parameters.find((p) => p.name === c) ?? { name: c, type: statement.keyType });
|
|
69
71
|
return `IN ${c} ${type}`;
|
|
70
72
|
})
|
|
71
73
|
.join(", ");
|
|
72
|
-
return procedure(statement.name, signature, ` DELETE t FROM ${statement.table} t WHERE ${where(by)};
|
|
74
|
+
return procedure(statement.name, signature, ` DELETE t FROM ${MysqlDriver.quote(statement.table)} t WHERE ${where(by)};
|
|
73
75
|
SELECT ROW_COUNT() AS affected;`);
|
|
74
76
|
}
|
|
75
77
|
renderBulkCreate(statement) {
|
|
76
78
|
const cols = statement.columns.map((c) => c.name).join(", ");
|
|
77
|
-
return procedure(statement.name, `IN ${statement.payload.name} ${statement.payload
|
|
79
|
+
return procedure(statement.name, `IN ${statement.payload.name} ${typeOf(statement.payload)}, OUT ids JSON`, ` INSERT INTO ${MysqlDriver.quote(statement.table)} (${cols})
|
|
78
80
|
SELECT ${cols}
|
|
79
81
|
FROM JSON_TABLE(${statement.payload.name}, '$[*]' COLUMNS (
|
|
80
82
|
${jsonColumns(statement.columns)}
|
|
@@ -93,7 +95,7 @@ ${sets.join(",\n")}
|
|
|
93
95
|
.filter((c) => c.name !== statement.key)
|
|
94
96
|
.map((c) => `c.${c.name} = payload_rows.${c.name}`)
|
|
95
97
|
.join(", ");
|
|
96
|
-
return procedure(statement.name, `IN ${statement.payload.name} ${statement.payload
|
|
98
|
+
return procedure(statement.name, `IN ${statement.payload.name} ${typeOf(statement.payload)}`, ` UPDATE ${MysqlDriver.quote(statement.table)} c
|
|
97
99
|
JOIN JSON_TABLE(${statement.payload.name}, '$[*]' COLUMNS (
|
|
98
100
|
${jsonColumns(statement.columns)}
|
|
99
101
|
)) AS payload_rows ON c.${statement.key} = payload_rows.${statement.key}
|
|
@@ -104,6 +106,6 @@ ${sets.join(",\n")}
|
|
|
104
106
|
name: statement.key,
|
|
105
107
|
type: statement.keyType,
|
|
106
108
|
};
|
|
107
|
-
return procedure(statement.name, `IN ${statement.payload.name} ${statement.payload
|
|
109
|
+
return procedure(statement.name, `IN ${statement.payload.name} ${typeOf(statement.payload)}`, ` DELETE c FROM ${MysqlDriver.quote(statement.table)} c JOIN JSON_TABLE(${statement.payload.name}, '$[*]' COLUMNS (${key.name} ${typeOf(key)} PATH '$.${key.name}')) AS payload_rows ON c.${statement.key} = payload_rows.${statement.key};`);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IMapping, SpecField } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class TypeMap implements IMapping<string, string> {
|
|
3
|
+
readonly dictionary: Record<string, string>;
|
|
4
|
+
private readonly reverse;
|
|
5
|
+
constructor(dictionary: Record<string, string>);
|
|
6
|
+
convert: (from: string) => string;
|
|
7
|
+
convertField: (field: SpecField) => string;
|
|
8
|
+
reverseConvert: (native: string) => string;
|
|
9
|
+
}
|
package/dist/TypeMap.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class TypeMap {
|
|
2
|
+
dictionary;
|
|
3
|
+
reverse = new Map();
|
|
4
|
+
constructor(dictionary) {
|
|
5
|
+
this.dictionary = dictionary;
|
|
6
|
+
for (const [spec, native] of Object.entries(dictionary)) {
|
|
7
|
+
if (!this.reverse.has(native))
|
|
8
|
+
this.reverse.set(native, spec);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
convert = (from) => this.dictionary[from] ??
|
|
12
|
+
(() => {
|
|
13
|
+
throw new Error(`Unknown spec field type: ${from}`);
|
|
14
|
+
})();
|
|
15
|
+
convertField = (field) => {
|
|
16
|
+
if (field.type === "string")
|
|
17
|
+
return field.size == null || field.size === "unlimited"
|
|
18
|
+
? "TEXT"
|
|
19
|
+
: Number(field.size) <= 16383
|
|
20
|
+
? `VARCHAR(${field.size})`
|
|
21
|
+
: "LONGTEXT";
|
|
22
|
+
if (field.type === "character")
|
|
23
|
+
return `CHAR(${field.size == null ? 1 : Number(field.size)})`;
|
|
24
|
+
if (field.type === "decimal") {
|
|
25
|
+
if (field.size == null)
|
|
26
|
+
return "DECIMAL(10, 2)";
|
|
27
|
+
if (Array.isArray(field.size) && field.size.length === 2)
|
|
28
|
+
return `DECIMAL(${field.size[0]}, ${field.size[1]})`;
|
|
29
|
+
throw new Error(`decimal field "${field.name}" size must be [precision, scale] tuple; got ${field.size}`);
|
|
30
|
+
}
|
|
31
|
+
if (field.type === "binary")
|
|
32
|
+
return field.size == null || field.size === "unlimited"
|
|
33
|
+
? "BLOB"
|
|
34
|
+
: Number(field.size) <= 65535
|
|
35
|
+
? `VARBINARY(${field.size})`
|
|
36
|
+
: "LONGBLOB";
|
|
37
|
+
return this.convert(field.type);
|
|
38
|
+
};
|
|
39
|
+
reverseConvert = (native) => this.reverse.get(native) ??
|
|
40
|
+
(() => {
|
|
41
|
+
throw new Error(`Unknown native field type: ${native}`);
|
|
42
|
+
})();
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const applyKeywords: (sql: string, keyword: (word: string) => string) => string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const KEYWORDS = new Set([
|
|
2
|
+
"AFTER", "ALTER", "AND", "AS", "AUTO_INCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BIGINT",
|
|
3
|
+
"BLOB", "BOOLEAN", "BY", "CASCADE", "CAST", "CHAR", "CONSTRAINT", "CREATE", "DATETIME",
|
|
4
|
+
"DECIMAL", "DECLARE", "DEFAULT", "DELETE", "DOUBLE", "DROP", "EACH", "ELSE", "END",
|
|
5
|
+
"EXISTS", "FALSE", "FLOAT", "FOR", "FOREIGN", "FROM", "GENERATED", "IF", "IN", "INDEX",
|
|
6
|
+
"INNER", "INSERT", "INT", "INTEGER", "INTO", "JOIN", "KEY", "LAST_INSERT_ID", "LONGBLOB",
|
|
7
|
+
"LONGTEXT", "NOT", "NOW", "NULL", "NUMERIC", "ON", "OR", "ORDER", "PRIMARY", "PROCEDURE",
|
|
8
|
+
"REFERENCES", "REPLACE", "RETURN", "ROW", "ROW_COUNT", "SELECT", "SET", "SMALLINT", "TABLE",
|
|
9
|
+
"TEXT", "THEN", "TIMESTAMP", "TINYINT", "TRIGGER", "TRUE", "UNIQUE", "UNSIGNED", "UPDATE",
|
|
10
|
+
"UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR", "WHEN", "WHERE",
|
|
11
|
+
]);
|
|
12
|
+
const TOKEN = /('(?:''|[^'])*')|(`(?:``|[^`])*`)|("(?:""|[^"])*")|(--[^\n]*)|(\/\*[\s\S]*?\*\/)|(?<word>[A-Za-z_][A-Za-z0-9_]*)/g;
|
|
13
|
+
export const applyKeywords = (sql, keyword) => sql.replace(TOKEN, (match, _s, _b, _d, _lc, _bc, word) => word !== undefined && KEYWORDS.has(word.toUpperCase()) ? keyword(word) : match);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ProcOp = "create" | "read" | "update" | "delete" | "bulkCreate" | "bulkUpdate" | "bulkDelete";
|
|
2
|
+
export type ProcSpec = {
|
|
3
|
+
op: ProcOp;
|
|
4
|
+
name: string;
|
|
5
|
+
byProperties: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare const procedureSpecs: (entityName: string, byGroups: string[][], casing: {
|
|
8
|
+
routineName: (stem: string) => string;
|
|
9
|
+
pluralStem: (entity: string) => string;
|
|
10
|
+
}) => ProcSpec[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const procedureSpecs = (entityName, byGroups, casing) => {
|
|
2
|
+
const name = (stem) => casing.routineName(stem);
|
|
3
|
+
const plural = casing.pluralStem(entityName);
|
|
4
|
+
const bySuffix = (props) => props.length === 0 ? "" : `_by_${props.join("_")}`;
|
|
5
|
+
return [
|
|
6
|
+
{ op: "create", name: name(`create_${plural}`), byProperties: [] },
|
|
7
|
+
{ op: "read", name: name(`read_${plural}`), byProperties: [] },
|
|
8
|
+
...byGroups.flatMap((props) => [
|
|
9
|
+
{ op: "read", name: name(`read_${plural}${bySuffix(props)}`), byProperties: props },
|
|
10
|
+
{ op: "update", name: name(`update_${plural}${bySuffix(props)}`), byProperties: props },
|
|
11
|
+
{ op: "delete", name: name(`delete_${plural}${bySuffix(props)}`), byProperties: props },
|
|
12
|
+
]),
|
|
13
|
+
{ op: "bulkCreate", name: name(`bulk_create_${plural}`), byProperties: [] },
|
|
14
|
+
{ op: "bulkUpdate", name: name(`bulk_update_${plural}`), byProperties: [] },
|
|
15
|
+
{ op: "bulkDelete", name: name(`bulk_delete_${plural}`), byProperties: [] },
|
|
16
|
+
];
|
|
17
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deterministic-code/datasources-mysql",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@deterministic-code/datasources": "^0.1.
|
|
21
|
+
"@deterministic-code/datasources": "^0.1.29",
|
|
22
22
|
"mysql2": "^3.14.0"
|
|
23
23
|
}
|
|
24
24
|
}
|