@eggjs/dal-runtime 4.0.0-beta.6 → 4.0.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +243 -14
- package/dist/index.js +1265 -14
- package/package.json +5 -5
- package/dist/BaseSqlMap.d.ts +0 -18
- package/dist/BaseSqlMap.js +0 -247
- package/dist/CodeGenerator.d.ts +0 -17
- package/dist/CodeGenerator.js +0 -114
- package/dist/DaoLoader.d.ts +0 -8
- package/dist/DaoLoader.js +0 -16
- package/dist/DataSource.d.ts +0 -32
- package/dist/DataSource.js +0 -72
- package/dist/DatabaseForker.d.ts +0 -16
- package/dist/DatabaseForker.js +0 -47
- package/dist/MySqlDataSource.d.ts +0 -27
- package/dist/MySqlDataSource.js +0 -49
- package/dist/NunjucksConverter.d.ts +0 -76
- package/dist/NunjucksConverter.js +0 -90
- package/dist/NunjucksUtil.d.ts +0 -9
- package/dist/NunjucksUtil.js +0 -64
- package/dist/SqlGenerator.d.ts +0 -13
- package/dist/SqlGenerator.js +0 -224
- package/dist/SqlMapLoader.d.ts +0 -14
- package/dist/SqlMapLoader.js +0 -22
- package/dist/SqlUtil.d.ts +0 -6
- package/dist/SqlUtil.js +0 -193
- package/dist/TableModelInstanceBuilder.d.ts +0 -10
- package/dist/TableModelInstanceBuilder.js +0 -25
- package/dist/TableSqlMap.d.ts +0 -18
- package/dist/TableSqlMap.js +0 -81
- package/dist/TemplateUtil.d.ts +0 -22
- package/dist/TemplateUtil.js +0 -78
package/dist/index.js
CHANGED
|
@@ -1,16 +1,1267 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { ColumnModel, DaoInfoUtil, SpatialHelper, TableModel } from "@eggjs/dal-decorator";
|
|
3
|
+
import { ColumnType, EggLoadUnitType, IndexType, SqlType, Templates } from "@eggjs/tegg-types";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import js_beautify from "js-beautify";
|
|
7
|
+
import nunjucks, { Template } from "nunjucks";
|
|
8
|
+
import { PrototypeUtil } from "@eggjs/core-decorator";
|
|
9
|
+
import "@eggjs/tegg-types/dal";
|
|
10
|
+
import { LoaderFactory } from "@eggjs/tegg-loader";
|
|
11
|
+
import assert from "node:assert";
|
|
12
|
+
import { RDSClient } from "@eggjs/rds";
|
|
13
|
+
import { Base } from "sdk-base";
|
|
14
|
+
import sqlstring from "sqlstring";
|
|
15
15
|
|
|
16
|
+
//#region src/TemplateUtil.ts
|
|
17
|
+
var TemplateUtil = class TemplateUtil {
|
|
18
|
+
static isSpatialType(columnModel) {
|
|
19
|
+
switch (columnModel.type.type) {
|
|
20
|
+
case ColumnType.GEOMETRY:
|
|
21
|
+
case ColumnType.POINT:
|
|
22
|
+
case ColumnType.LINESTRING:
|
|
23
|
+
case ColumnType.POLYGON:
|
|
24
|
+
case ColumnType.MULTIPOINT:
|
|
25
|
+
case ColumnType.MULTILINESTRING:
|
|
26
|
+
case ColumnType.MULTIPOLYGON:
|
|
27
|
+
case ColumnType.GEOMETRYCOLLECTION: return true;
|
|
28
|
+
default: return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
static importPath(tableModelPath, currentPath) {
|
|
32
|
+
return path.relative(currentPath, tableModelPath);
|
|
33
|
+
}
|
|
34
|
+
static dbTypeToTsType(columnType) {
|
|
35
|
+
return `ColumnTsType['${columnType}']`;
|
|
36
|
+
}
|
|
37
|
+
static toJson(value) {
|
|
38
|
+
return JSON.stringify(JSON.stringify(value));
|
|
39
|
+
}
|
|
40
|
+
static toPoint(point) {
|
|
41
|
+
if (typeof point.x !== "number" || typeof point.y !== "number") throw new Error(`invalidate point ${JSON.stringify(point)}`);
|
|
42
|
+
return `Point(${point.x}, ${point.y})`;
|
|
43
|
+
}
|
|
44
|
+
static toLine(val) {
|
|
45
|
+
return `LINESTRING(${val.map((t) => TemplateUtil.toPoint(t)).join(",")})`;
|
|
46
|
+
}
|
|
47
|
+
static toPolygon(val) {
|
|
48
|
+
return `POLYGON(${val.map((t) => TemplateUtil.toLine(t)).join(",")})`;
|
|
49
|
+
}
|
|
50
|
+
static toGeometry(val) {
|
|
51
|
+
const type = SpatialHelper.getGeometryType(val);
|
|
52
|
+
const filterName = TemplateUtil.getSpatialFilter(type);
|
|
53
|
+
return TemplateUtil[filterName](val);
|
|
54
|
+
}
|
|
55
|
+
static toMultiPoint(val) {
|
|
56
|
+
return `MULTIPOINT(${val.map((t) => TemplateUtil.toPoint(t)).join(",")})`;
|
|
57
|
+
}
|
|
58
|
+
static toMultiLine(val) {
|
|
59
|
+
return `MULTILINESTRING(${val.map((t) => TemplateUtil.toLine(t)).join(",")})`;
|
|
60
|
+
}
|
|
61
|
+
static toMultiPolygon(val) {
|
|
62
|
+
return `MULTIPOLYGON(${val.map((t) => TemplateUtil.toPolygon(t)).join(",")})`;
|
|
63
|
+
}
|
|
64
|
+
static toGeometryCollection(val) {
|
|
65
|
+
return `GEOMETRYCOLLECTION(${val.map((t) => {
|
|
66
|
+
return TemplateUtil.toGeometry(t);
|
|
67
|
+
}).join(",")})`;
|
|
68
|
+
}
|
|
69
|
+
static {
|
|
70
|
+
this.spatialFilter = {
|
|
71
|
+
[ColumnType.POINT]: "toPoint",
|
|
72
|
+
[ColumnType.LINESTRING]: "toLine",
|
|
73
|
+
[ColumnType.POLYGON]: "toPolygon",
|
|
74
|
+
[ColumnType.GEOMETRY]: "toGeometry",
|
|
75
|
+
[ColumnType.MULTIPOINT]: "toMultiPoint",
|
|
76
|
+
[ColumnType.MULTILINESTRING]: "toMultiLine",
|
|
77
|
+
[ColumnType.MULTIPOLYGON]: "toMultiPolygon",
|
|
78
|
+
[ColumnType.GEOMETRYCOLLECTION]: "toGeometryCollection"
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
static getSpatialFilter(columnType) {
|
|
82
|
+
const filter = TemplateUtil.spatialFilter[columnType];
|
|
83
|
+
if (!filter) throw new Error(`type ${columnType} is not spatial type`);
|
|
84
|
+
return filter;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/BaseSqlMap.ts
|
|
90
|
+
var BaseSqlMapGenerator = class {
|
|
91
|
+
constructor(tableModel, logger) {
|
|
92
|
+
this.tableModel = tableModel;
|
|
93
|
+
this.logger = logger;
|
|
94
|
+
}
|
|
95
|
+
generateAllColumns(countIf) {
|
|
96
|
+
const str = this.tableModel.columns.map((t) => `\`${t.columnName}\``).join(",");
|
|
97
|
+
return countIf ? `{% if $$count == true %}0{% else %}${str}{% endif %}` : str;
|
|
98
|
+
}
|
|
99
|
+
generateFindByPrimary() {
|
|
100
|
+
const result = [];
|
|
101
|
+
const primary = this.tableModel.getPrimary();
|
|
102
|
+
if (!primary) {
|
|
103
|
+
this.logger.warn(`表 \`${this.tableModel.name}\` 没有主键,无法生成主键查询语句。`);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
let sql = `SELECT ${this.generateAllColumns(true)}
|
|
107
|
+
FROM \`${this.tableModel.name}\`
|
|
108
|
+
WHERE `;
|
|
109
|
+
sql += primary.keys.map((indexKey) => `\`${indexKey.columnName}\` = {{$${indexKey.propertyName}}}`).join(" AND ");
|
|
110
|
+
if (primary.keys.length === 1) result.push({
|
|
111
|
+
type: SqlType.SELECT,
|
|
112
|
+
name: `findBy${_.upperFirst(primary.keys[0].propertyName)}`,
|
|
113
|
+
sql
|
|
114
|
+
});
|
|
115
|
+
result.push({
|
|
116
|
+
name: "findByPrimary",
|
|
117
|
+
type: SqlType.SELECT,
|
|
118
|
+
sql
|
|
119
|
+
});
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
generateFindByIndexes() {
|
|
123
|
+
const sqlMaps = [];
|
|
124
|
+
for (const index of this.tableModel.indices) {
|
|
125
|
+
if (index.type === IndexType.PRIMARY) continue;
|
|
126
|
+
let sql = `SELECT ${this.generateAllColumns(true)}
|
|
127
|
+
FROM \`${this.tableModel.name}\`
|
|
128
|
+
WHERE `;
|
|
129
|
+
sql += index.keys.map((indexKey) => {
|
|
130
|
+
return `\`${indexKey.columnName}\` {{ "IS" if $${indexKey.propertyName} == null else "=" }} {{$${indexKey.propertyName}}}`;
|
|
131
|
+
}).join(" AND ");
|
|
132
|
+
const tempName = _.upperFirst(_.camelCase(index.keys.length === 1 ? index.keys[0].propertyName : index.name));
|
|
133
|
+
sqlMaps.push({
|
|
134
|
+
name: `findBy${tempName}`,
|
|
135
|
+
type: SqlType.SELECT,
|
|
136
|
+
sql
|
|
137
|
+
});
|
|
138
|
+
sqlMaps.push({
|
|
139
|
+
name: `findOneBy${tempName}`,
|
|
140
|
+
type: SqlType.SELECT,
|
|
141
|
+
sql: `${sql} LIMIT 0, 1`
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return sqlMaps;
|
|
145
|
+
}
|
|
146
|
+
generateInsert() {
|
|
147
|
+
let sql = `INSERT INTO \`${this.tableModel.name}\` `;
|
|
148
|
+
sql += "{% set ___first = true %}";
|
|
149
|
+
const keys = [];
|
|
150
|
+
const values = [];
|
|
151
|
+
for (const column of this.tableModel.columns) {
|
|
152
|
+
const { propertyName, columnName, type } = column;
|
|
153
|
+
if (column.propertyName !== "gmtCreate" && column.propertyName !== "gmtModified") {
|
|
154
|
+
keys.push(`
|
|
155
|
+
{% if $${propertyName} !== undefined %}
|
|
156
|
+
{% if ___first %}
|
|
157
|
+
{% set ___first = false %}
|
|
158
|
+
{% else %}
|
|
159
|
+
,
|
|
160
|
+
{% endif %}
|
|
161
|
+
|
|
162
|
+
\`${columnName}\`
|
|
163
|
+
{% endif %}
|
|
164
|
+
`.trim());
|
|
165
|
+
if (TemplateUtil.isSpatialType(column)) {
|
|
166
|
+
const filter = TemplateUtil.getSpatialFilter(column.type.type);
|
|
167
|
+
values.push(`
|
|
168
|
+
{% if $${propertyName} !== undefined %}
|
|
169
|
+
{% if ___first %}
|
|
170
|
+
{% set ___first = false %}
|
|
171
|
+
{% else %}
|
|
172
|
+
,
|
|
173
|
+
{% endif %}
|
|
174
|
+
|
|
175
|
+
{{$${propertyName} | ${filter}}}
|
|
176
|
+
{% endif %}
|
|
177
|
+
`.trim());
|
|
178
|
+
} else if (column.type.type === ColumnType.JSON) values.push(`
|
|
179
|
+
{% if $${propertyName} !== undefined %}
|
|
180
|
+
{% if ___first %}
|
|
181
|
+
{% set ___first = false %}
|
|
182
|
+
{% else %}
|
|
183
|
+
,
|
|
184
|
+
{% endif %}
|
|
185
|
+
|
|
186
|
+
{{$${propertyName} | toJson}}
|
|
187
|
+
{% endif %}
|
|
188
|
+
`.trim());
|
|
189
|
+
else values.push(`
|
|
190
|
+
{% if $${propertyName} !== undefined %}
|
|
191
|
+
{% if ___first %}
|
|
192
|
+
{% set ___first = false %}
|
|
193
|
+
{% else %}
|
|
194
|
+
,
|
|
195
|
+
{% endif %}
|
|
196
|
+
|
|
197
|
+
{{$${propertyName}}}
|
|
198
|
+
{% endif %}
|
|
199
|
+
`.trim());
|
|
200
|
+
} else {
|
|
201
|
+
let now;
|
|
202
|
+
if (type.type === ColumnType.INT) now = "UNIX_TIMESTAMP()";
|
|
203
|
+
else if (type.type === ColumnType.BIGINT) now = "ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000)";
|
|
204
|
+
else if (type.type === ColumnType.DATETIME || type.type === ColumnType.TIMESTAMP) now = type.precision ? `NOW(${type.precision})` : "NOW()";
|
|
205
|
+
else this.logger.warn(`unknown type ${type.type} for ${propertyName}`);
|
|
206
|
+
keys.push(`
|
|
207
|
+
{% if ___first %}
|
|
208
|
+
{% set ___first = false %}
|
|
209
|
+
{% else %}
|
|
210
|
+
,
|
|
211
|
+
{% endif %}
|
|
212
|
+
|
|
213
|
+
\`${columnName}\`
|
|
214
|
+
`.trim());
|
|
215
|
+
values.push(`
|
|
216
|
+
{% if ___first %}
|
|
217
|
+
{% set ___first = false %}
|
|
218
|
+
{% else %}
|
|
219
|
+
,
|
|
220
|
+
{% endif %}
|
|
221
|
+
|
|
222
|
+
{{ $${propertyName} if $${propertyName} !== undefined else '${now}' }}
|
|
223
|
+
`.trim());
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
sql += `(${keys.join("")})`;
|
|
227
|
+
sql += "{% set ___first = true %}";
|
|
228
|
+
sql += `VALUES(${values.join("")});`;
|
|
229
|
+
return sql;
|
|
230
|
+
}
|
|
231
|
+
generateUpdate() {
|
|
232
|
+
const primary = this.tableModel.getPrimary();
|
|
233
|
+
if (!primary) {
|
|
234
|
+
this.logger.warn(`表 \`${this.tableModel.name}\` 没有主键,无法生成主键更新语句。`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
let sql = `UPDATE \`${this.tableModel.name}\` SET`;
|
|
238
|
+
sql += "{% set ___first = true %}";
|
|
239
|
+
const kv = [];
|
|
240
|
+
for (const column of this.tableModel.columns) {
|
|
241
|
+
const { type, propertyName, columnName } = column;
|
|
242
|
+
let now;
|
|
243
|
+
if (type.type === ColumnType.INT) now = "UNIX_TIMESTAMP()";
|
|
244
|
+
else if (type.type === ColumnType.BIGINT) now = "ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000)";
|
|
245
|
+
else if (type.type === ColumnType.TIMESTAMP || type.type === ColumnType.DATETIME) now = type.precision ? `NOW(${type.precision})` : "NOW()";
|
|
246
|
+
const temp = propertyName !== "gmtModified" ? `
|
|
247
|
+
{% if $${propertyName} !== undefined %}
|
|
248
|
+
{% if ___first %}
|
|
249
|
+
{% set ___first = false %}
|
|
250
|
+
{% else %}
|
|
251
|
+
,
|
|
252
|
+
{% endif %}
|
|
253
|
+
|
|
254
|
+
\`${columnName}\` = {{$${propertyName}}}
|
|
255
|
+
{% endif %}
|
|
256
|
+
` : `
|
|
257
|
+
{% if ___first %}
|
|
258
|
+
{% set ___first = false %}
|
|
259
|
+
{% else %}
|
|
260
|
+
,
|
|
261
|
+
{% endif %}
|
|
262
|
+
|
|
263
|
+
\`${columnName}\` =
|
|
264
|
+
{{ $${propertyName} if $${propertyName} !== undefined else '${now}' }}
|
|
265
|
+
`;
|
|
266
|
+
kv.push(temp);
|
|
267
|
+
}
|
|
268
|
+
sql += kv.join("");
|
|
269
|
+
sql += `WHERE ${primary.keys.map((indexKey) => `\`${indexKey.columnName}\` = {{primary.${indexKey.propertyName}}}`).join(" AND ")}`;
|
|
270
|
+
return sql;
|
|
271
|
+
}
|
|
272
|
+
generateDelete() {
|
|
273
|
+
const primary = this.tableModel.getPrimary();
|
|
274
|
+
if (!primary) {
|
|
275
|
+
this.logger.warn(`表 \`${this.tableModel.name}\` 没有主键,无法生成主键删除语句。`);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
let sql = `DELETE
|
|
279
|
+
FROM \`${this.tableModel.name}\`
|
|
280
|
+
WHERE `;
|
|
281
|
+
sql += primary.keys.map((indexKey) => `\`${indexKey.columnName}\` = {{${indexKey.propertyName}}}`).join(" AND ");
|
|
282
|
+
return sql;
|
|
283
|
+
}
|
|
284
|
+
load() {
|
|
285
|
+
const map = {};
|
|
286
|
+
map.allColumns = {
|
|
287
|
+
type: SqlType.BLOCK,
|
|
288
|
+
content: this.generateAllColumns(false)
|
|
289
|
+
};
|
|
290
|
+
const sqlMaps = [
|
|
291
|
+
...this.generateFindByPrimary(),
|
|
292
|
+
...this.generateFindByIndexes(),
|
|
293
|
+
(
|
|
294
|
+
/**
|
|
295
|
+
* 插入
|
|
296
|
+
*/
|
|
297
|
+
{
|
|
298
|
+
name: "insert",
|
|
299
|
+
type: SqlType.INSERT,
|
|
300
|
+
sql: this.generateInsert()
|
|
301
|
+
}),
|
|
302
|
+
(
|
|
303
|
+
/**
|
|
304
|
+
* 主键更新
|
|
305
|
+
*/
|
|
306
|
+
{
|
|
307
|
+
name: "update",
|
|
308
|
+
type: SqlType.UPDATE,
|
|
309
|
+
sql: this.generateUpdate()
|
|
310
|
+
}),
|
|
311
|
+
(
|
|
312
|
+
/**
|
|
313
|
+
* 主键删除
|
|
314
|
+
*/
|
|
315
|
+
{
|
|
316
|
+
name: "delete",
|
|
317
|
+
type: SqlType.DELETE,
|
|
318
|
+
sql: this.generateDelete()
|
|
319
|
+
})
|
|
320
|
+
];
|
|
321
|
+
for (const sqlMap of sqlMaps) map[sqlMap.name] = {
|
|
322
|
+
type: sqlMap.type,
|
|
323
|
+
sql: sqlMap.sql
|
|
324
|
+
};
|
|
325
|
+
return map;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
//#endregion
|
|
330
|
+
//#region src/SqlGenerator.ts
|
|
331
|
+
var SqlGenerator = class {
|
|
332
|
+
formatComment(comment) {
|
|
333
|
+
return comment.replace(/\n/g, "\\n");
|
|
334
|
+
}
|
|
335
|
+
generateColumn(column) {
|
|
336
|
+
const sqls = [
|
|
337
|
+
" ",
|
|
338
|
+
column.columnName,
|
|
339
|
+
this.generateColumnType(column.type)
|
|
340
|
+
];
|
|
341
|
+
if (column.canNull) sqls.push("NULL");
|
|
342
|
+
else sqls.push("NOT NULL");
|
|
343
|
+
if ([
|
|
344
|
+
ColumnType.POINT,
|
|
345
|
+
ColumnType.GEOMETRY,
|
|
346
|
+
ColumnType.POINT,
|
|
347
|
+
ColumnType.LINESTRING,
|
|
348
|
+
ColumnType.POLYGON,
|
|
349
|
+
ColumnType.MULTIPOINT,
|
|
350
|
+
ColumnType.MULTILINESTRING,
|
|
351
|
+
ColumnType.MULTIPOLYGON,
|
|
352
|
+
ColumnType.GEOMETRYCOLLECTION
|
|
353
|
+
].includes(column.type.type)) {
|
|
354
|
+
const SRID = column.type.SRID;
|
|
355
|
+
if (SRID) sqls.push(`SRID ${SRID}`);
|
|
356
|
+
}
|
|
357
|
+
if (typeof column.default !== "undefined") sqls.push(`DEFAULT ${column.default}`);
|
|
358
|
+
if (column.autoIncrement) sqls.push("AUTO_INCREMENT");
|
|
359
|
+
if (column.uniqueKey) sqls.push("UNIQUE KEY");
|
|
360
|
+
if (column.primaryKey) sqls.push("PRIMARY KEY");
|
|
361
|
+
if (column.comment) sqls.push(`COMMENT '${this.formatComment(column.comment)}'`);
|
|
362
|
+
if (column.collate) sqls.push(`COLLATE ${column.collate}`);
|
|
363
|
+
if (column.columnFormat) sqls.push(`COLUMN_FORMAT ${column.columnFormat}`);
|
|
364
|
+
if (column.engineAttribute) sqls.push(`ENGINE_ATTRIBUTE='${column.engineAttribute}'`);
|
|
365
|
+
if (column.secondaryEngineAttribute) sqls.push(`SECONDARY_ENGINE_ATTRIBUTE='${column.secondaryEngineAttribute}'`);
|
|
366
|
+
return sqls.join(" ");
|
|
367
|
+
}
|
|
368
|
+
generateColumnType(columnType) {
|
|
369
|
+
const sqls = [];
|
|
370
|
+
switch (columnType.type) {
|
|
371
|
+
case ColumnType.BOOL:
|
|
372
|
+
sqls.push("BOOL");
|
|
373
|
+
break;
|
|
374
|
+
case ColumnType.BIT:
|
|
375
|
+
if (columnType.length) sqls.push(`BIT(${columnType.length})`);
|
|
376
|
+
else sqls.push("BIT");
|
|
377
|
+
break;
|
|
378
|
+
case ColumnType.TINYINT:
|
|
379
|
+
case ColumnType.SMALLINT:
|
|
380
|
+
case ColumnType.MEDIUMINT:
|
|
381
|
+
case ColumnType.INT:
|
|
382
|
+
case ColumnType.BIGINT:
|
|
383
|
+
if (typeof columnType.length === "number") sqls.push(`${columnType.type}(${columnType.length})`);
|
|
384
|
+
else sqls.push(columnType.type);
|
|
385
|
+
if (columnType.unsigned) sqls.push("UNSIGNED");
|
|
386
|
+
if (columnType.zeroFill) sqls.push("ZEROFILL");
|
|
387
|
+
break;
|
|
388
|
+
case ColumnType.DECIMAL:
|
|
389
|
+
case ColumnType.FLOAT:
|
|
390
|
+
case ColumnType.DOUBLE:
|
|
391
|
+
if (typeof columnType.length === "number" && typeof columnType.fractionalLength === "number") sqls.push(`${columnType.type}(${columnType.length},${columnType.fractionalLength})`);
|
|
392
|
+
else if (typeof columnType.length === "number") sqls.push(`${columnType.type}(${columnType.length})`);
|
|
393
|
+
else sqls.push("TINYINT");
|
|
394
|
+
if (columnType.unsigned) sqls.push("UNSIGNED");
|
|
395
|
+
if (columnType.zeroFill) sqls.push("ZEROFILL");
|
|
396
|
+
break;
|
|
397
|
+
case ColumnType.DATE:
|
|
398
|
+
sqls.push("DATE");
|
|
399
|
+
break;
|
|
400
|
+
case ColumnType.DATETIME:
|
|
401
|
+
case ColumnType.TIMESTAMP:
|
|
402
|
+
if (columnType.precision) sqls.push(`${columnType.type}(${columnType.precision})`);
|
|
403
|
+
else sqls.push(columnType.type);
|
|
404
|
+
if (columnType.autoUpdate) if (columnType.precision) sqls.push(`ON UPDATE CURRENT_TIMESTAMP(${columnType.precision})`);
|
|
405
|
+
else sqls.push("ON UPDATE CURRENT_TIMESTAMP");
|
|
406
|
+
break;
|
|
407
|
+
case ColumnType.TIME:
|
|
408
|
+
if (columnType.precision) sqls.push(`${columnType.type}(${columnType.precision})`);
|
|
409
|
+
else sqls.push(columnType.type);
|
|
410
|
+
break;
|
|
411
|
+
case ColumnType.YEAR:
|
|
412
|
+
sqls.push("YEAR");
|
|
413
|
+
break;
|
|
414
|
+
case ColumnType.CHAR:
|
|
415
|
+
case ColumnType.TEXT:
|
|
416
|
+
if (columnType.length) sqls.push(`${columnType.type}(${columnType.length})`);
|
|
417
|
+
else sqls.push(columnType.type);
|
|
418
|
+
if (columnType.characterSet) sqls.push(`CHARACTER SET ${columnType.characterSet}`);
|
|
419
|
+
if (columnType.collate) sqls.push(`COLLATE ${columnType.collate}`);
|
|
420
|
+
break;
|
|
421
|
+
case ColumnType.VARCHAR:
|
|
422
|
+
sqls.push(`${columnType.type}(${columnType.length})`);
|
|
423
|
+
if (columnType.characterSet) sqls.push(`CHARACTER SET ${columnType.characterSet}`);
|
|
424
|
+
if (columnType.collate) sqls.push(`COLLATE ${columnType.collate}`);
|
|
425
|
+
break;
|
|
426
|
+
case ColumnType.BINARY:
|
|
427
|
+
if (columnType.length) sqls.push(`${columnType.type}(${columnType.length})`);
|
|
428
|
+
else sqls.push(columnType.type);
|
|
429
|
+
break;
|
|
430
|
+
case ColumnType.VARBINARY:
|
|
431
|
+
sqls.push(`${columnType.type}(${columnType.length})`);
|
|
432
|
+
break;
|
|
433
|
+
case ColumnType.TINYBLOB:
|
|
434
|
+
sqls.push("TINYBLOB");
|
|
435
|
+
break;
|
|
436
|
+
case ColumnType.TINYTEXT:
|
|
437
|
+
case ColumnType.MEDIUMTEXT:
|
|
438
|
+
case ColumnType.LONGTEXT:
|
|
439
|
+
sqls.push(columnType.type);
|
|
440
|
+
if (columnType.characterSet) sqls.push(`CHARACTER SET ${columnType.characterSet}`);
|
|
441
|
+
if (columnType.collate) sqls.push(`COLLATE ${columnType.collate}`);
|
|
442
|
+
break;
|
|
443
|
+
case ColumnType.BLOB:
|
|
444
|
+
if (columnType.length) sqls.push(`${columnType.type}(${columnType.length})`);
|
|
445
|
+
else sqls.push(columnType.type);
|
|
446
|
+
break;
|
|
447
|
+
case ColumnType.MEDIUMBLOB:
|
|
448
|
+
sqls.push("MEDIUMBLOB");
|
|
449
|
+
break;
|
|
450
|
+
case ColumnType.LONGBLOB:
|
|
451
|
+
sqls.push("LONGBLOB");
|
|
452
|
+
break;
|
|
453
|
+
case ColumnType.ENUM: {
|
|
454
|
+
const enumValue = columnType.enums.map((t) => `'${t}'`).join(",");
|
|
455
|
+
sqls.push(`ENUM(${enumValue})`);
|
|
456
|
+
if (columnType.characterSet) sqls.push(`CHARACTER SET ${columnType.characterSet}`);
|
|
457
|
+
if (columnType.collate) sqls.push(`COLLATE ${columnType.collate}`);
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
case ColumnType.SET: {
|
|
461
|
+
const enumValue = columnType.enums.map((t) => `'${t}'`).join(",");
|
|
462
|
+
sqls.push(`SET(${enumValue})`);
|
|
463
|
+
if (columnType.characterSet) sqls.push(`CHARACTER SET ${columnType.characterSet}`);
|
|
464
|
+
if (columnType.collate) sqls.push(`COLLATE ${columnType.collate}`);
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case ColumnType.JSON:
|
|
468
|
+
sqls.push("JSON");
|
|
469
|
+
break;
|
|
470
|
+
case ColumnType.GEOMETRY:
|
|
471
|
+
case ColumnType.POINT:
|
|
472
|
+
case ColumnType.LINESTRING:
|
|
473
|
+
case ColumnType.POLYGON:
|
|
474
|
+
case ColumnType.MULTIPOINT:
|
|
475
|
+
case ColumnType.MULTILINESTRING:
|
|
476
|
+
case ColumnType.MULTIPOLYGON:
|
|
477
|
+
case ColumnType.GEOMETRYCOLLECTION:
|
|
478
|
+
sqls.push(columnType.type);
|
|
479
|
+
break;
|
|
480
|
+
default: throw new Error(`unknown ColumnType ${columnType}`);
|
|
481
|
+
}
|
|
482
|
+
return sqls.join(" ");
|
|
483
|
+
}
|
|
484
|
+
generateIndex(indexModel) {
|
|
485
|
+
const indexSql = [" "];
|
|
486
|
+
switch (indexModel.type) {
|
|
487
|
+
case IndexType.INDEX:
|
|
488
|
+
indexSql.push("KEY");
|
|
489
|
+
break;
|
|
490
|
+
case IndexType.UNIQUE:
|
|
491
|
+
indexSql.push("UNIQUE KEY");
|
|
492
|
+
break;
|
|
493
|
+
case IndexType.PRIMARY:
|
|
494
|
+
indexSql.push("PRIMARY KEY");
|
|
495
|
+
break;
|
|
496
|
+
case IndexType.FULLTEXT:
|
|
497
|
+
indexSql.push("FULLTEXT KEY");
|
|
498
|
+
break;
|
|
499
|
+
case IndexType.SPATIAL:
|
|
500
|
+
indexSql.push("SPATIAL KEY");
|
|
501
|
+
break;
|
|
502
|
+
default: throw new Error(`unknown IndexType ${indexModel.type}`);
|
|
503
|
+
}
|
|
504
|
+
indexSql.push(indexModel.name);
|
|
505
|
+
indexSql.push(`(${indexModel.keys.map((t) => t.columnName).join(",")})`);
|
|
506
|
+
if (indexModel.storeType) indexSql.push(`USING ${indexModel.storeType}`);
|
|
507
|
+
if (indexModel.parser) indexSql.push(`WITH PARSER ${indexModel.parser}`);
|
|
508
|
+
if (indexModel.comment) indexSql.push(`COMMENT '${this.formatComment(indexModel.comment)}'`);
|
|
509
|
+
if (indexModel.engineAttribute) indexSql.push(`ENGINE_ATTRIBUTE='${indexModel.engineAttribute}'`);
|
|
510
|
+
if (indexModel.secondaryEngineAttribute) indexSql.push(`SECONDARY_ENGINE_ATTRIBUTE='${indexModel.secondaryEngineAttribute}'`);
|
|
511
|
+
return indexSql.join(" ");
|
|
512
|
+
}
|
|
513
|
+
generateTableOptions(tableModel) {
|
|
514
|
+
const sqls = [];
|
|
515
|
+
if (tableModel.autoExtendSize) sqls.push(`AUTOEXTEND_SIZE=${tableModel.autoExtendSize}`);
|
|
516
|
+
if (tableModel.autoIncrement) sqls.push(`AUTO_INCREMENT=${tableModel.autoIncrement}`);
|
|
517
|
+
if (tableModel.avgRowLength) sqls.push(`AVG_ROW_LENGTH=${tableModel.avgRowLength}`);
|
|
518
|
+
if (tableModel.characterSet) sqls.push(`DEFAULT CHARACTER SET ${tableModel.characterSet}`);
|
|
519
|
+
if (tableModel.collate) sqls.push(`DEFAULT COLLATE ${tableModel.collate}`);
|
|
520
|
+
if (tableModel.comment) sqls.push(`COMMENT='${this.formatComment(tableModel.comment)}'`);
|
|
521
|
+
if (tableModel.compression) sqls.push(`COMPRESSION='${tableModel.compression}'`);
|
|
522
|
+
if (typeof tableModel.encryption !== "undefined") sqls.push(`ENCRYPTION='${tableModel.encryption ? "Y" : "N"}'`);
|
|
523
|
+
if (typeof tableModel.engine !== "undefined") sqls.push(`ENGINE=${tableModel.engine}`);
|
|
524
|
+
if (tableModel.engineAttribute) sqls.push(`ENGINE_ATTRIBUTE='${tableModel.engineAttribute}'`);
|
|
525
|
+
if (tableModel.secondaryEngineAttribute) sqls.push(`SECONDARY_ENGINE_ATTRIBUTE = '${tableModel.secondaryEngineAttribute}'`);
|
|
526
|
+
if (tableModel.insertMethod) sqls.push(`INSERT_METHOD=${tableModel.insertMethod}`);
|
|
527
|
+
if (tableModel.keyBlockSize) sqls.push(`KEY_BLOCK_SIZE=${tableModel.keyBlockSize}`);
|
|
528
|
+
if (tableModel.maxRows) sqls.push(`MAX_ROWS=${tableModel.maxRows}`);
|
|
529
|
+
if (tableModel.minRows) sqls.push(`MIN_ROWS=${tableModel.minRows}`);
|
|
530
|
+
if (tableModel.rowFormat) sqls.push(`ROW_FORMAT=${tableModel.rowFormat}`);
|
|
531
|
+
return sqls.join(", ");
|
|
532
|
+
}
|
|
533
|
+
generate(tableModel) {
|
|
534
|
+
const createSql = [];
|
|
535
|
+
createSql.push(`CREATE TABLE IF NOT EXISTS ${tableModel.name} (`);
|
|
536
|
+
const columnSql = [];
|
|
537
|
+
for (const column of tableModel.columns) columnSql.push(this.generateColumn(column));
|
|
538
|
+
const indexSql = [];
|
|
539
|
+
for (const index of tableModel.indices) indexSql.push(this.generateIndex(index));
|
|
540
|
+
if (indexSql.length) {
|
|
541
|
+
createSql.push(columnSql.join(",\n") + ",");
|
|
542
|
+
createSql.push(indexSql.join(",\n"));
|
|
543
|
+
} else createSql.push(columnSql.join(",\n"));
|
|
544
|
+
createSql.push(`) ${this.generateTableOptions(tableModel)};`);
|
|
545
|
+
return createSql.join("\n");
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
//#endregion
|
|
550
|
+
//#region src/CodeGenerator.ts
|
|
551
|
+
var CodeGenerator = class {
|
|
552
|
+
constructor(options) {
|
|
553
|
+
this.moduleDir = options.moduleDir;
|
|
554
|
+
this.moduleName = options.moduleName;
|
|
555
|
+
this.teggPkg = options.teggPkg ?? "@eggjs/tegg";
|
|
556
|
+
this.dalPkg = options.dalPkg ?? "@eggjs/tegg/dal";
|
|
557
|
+
this.createNunjucksEnv();
|
|
558
|
+
}
|
|
559
|
+
createNunjucksEnv() {
|
|
560
|
+
this.njkEnv = nunjucks.configure(path.join(__dirname, "./templates"), { autoescape: false });
|
|
561
|
+
this.njkEnv.addFilter("pascalCase", (name) => _.upperFirst(_.camelCase(name)));
|
|
562
|
+
this.njkEnv.addFilter("camelCase", (name) => _.camelCase(name));
|
|
563
|
+
this.njkEnv.addFilter("dbTypeToTSType", TemplateUtil.dbTypeToTsType);
|
|
564
|
+
}
|
|
565
|
+
genCode(tplName, filePath, tableModel) {
|
|
566
|
+
let tableModelAbsolutePath = PrototypeUtil.getFilePath(tableModel.clazz);
|
|
567
|
+
tableModelAbsolutePath = tableModelAbsolutePath.substring(0, tableModelAbsolutePath.length - 3);
|
|
568
|
+
const data = {
|
|
569
|
+
table: tableModel,
|
|
570
|
+
file: filePath,
|
|
571
|
+
fileName: path.basename(filePath),
|
|
572
|
+
clazzName: tableModel.clazz.name,
|
|
573
|
+
moduleName: this.moduleName,
|
|
574
|
+
teggPkg: this.teggPkg,
|
|
575
|
+
dalPkg: this.dalPkg,
|
|
576
|
+
id: tableModel.columns.find((t) => t.propertyName === "id"),
|
|
577
|
+
primaryIndex: tableModel.getPrimary(),
|
|
578
|
+
tableModelPath: TemplateUtil.importPath(tableModelAbsolutePath, path.dirname(filePath)),
|
|
579
|
+
extensionPath: `../../extension/${tableModel.clazz.name}Extension`,
|
|
580
|
+
structurePath: `../../structure/${tableModel.clazz.name}.json`,
|
|
581
|
+
sqlPath: `../../structure/${tableModel.clazz.name}.sql`,
|
|
582
|
+
columnMap: tableModel.columns.reduce((p, c) => {
|
|
583
|
+
p[c.propertyName] = c;
|
|
584
|
+
return p;
|
|
585
|
+
}, {})
|
|
586
|
+
};
|
|
587
|
+
return this.njkEnv.render(`${tplName}.njk`, data);
|
|
588
|
+
}
|
|
589
|
+
async generate(tableModel) {
|
|
590
|
+
let dalDir;
|
|
591
|
+
try {
|
|
592
|
+
await fs.access(path.join(this.moduleDir, "src"));
|
|
593
|
+
dalDir = path.join(this.moduleDir, "src/dal");
|
|
594
|
+
} catch {
|
|
595
|
+
dalDir = path.join(this.moduleDir, "dal");
|
|
596
|
+
}
|
|
597
|
+
const clazzFileName = path.basename(PrototypeUtil.getFilePath(tableModel.clazz));
|
|
598
|
+
const baseFileName = path.basename(clazzFileName, ".ts");
|
|
599
|
+
const paths = {
|
|
600
|
+
baseBizDAO: path.join(dalDir, `dao/base/Base${baseFileName}DAO.ts`),
|
|
601
|
+
bizDAO: path.join(dalDir, `dao/${baseFileName}DAO.ts`),
|
|
602
|
+
extension: path.join(dalDir, `extension/${baseFileName}Extension.ts`),
|
|
603
|
+
structure: path.join(dalDir, `structure/${baseFileName}.json`),
|
|
604
|
+
structureSql: path.join(dalDir, `structure/${baseFileName}.sql`)
|
|
605
|
+
};
|
|
606
|
+
await fs.mkdir(path.dirname(paths.structure), { recursive: true });
|
|
607
|
+
await fs.writeFile(paths.structure, JSON.stringify(tableModel, null, 2), "utf8");
|
|
608
|
+
const structureSql = new SqlGenerator().generate(tableModel);
|
|
609
|
+
await fs.writeFile(paths.structureSql, structureSql, "utf8");
|
|
610
|
+
const codes = [
|
|
611
|
+
{
|
|
612
|
+
templates: Templates.BASE_DAO,
|
|
613
|
+
filePath: paths.baseBizDAO,
|
|
614
|
+
beautify: true,
|
|
615
|
+
overwrite: true
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
templates: Templates.DAO,
|
|
619
|
+
filePath: paths.bizDAO,
|
|
620
|
+
beautify: true,
|
|
621
|
+
overwrite: false
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
templates: Templates.EXTENSION,
|
|
625
|
+
filePath: paths.extension,
|
|
626
|
+
beautify: false,
|
|
627
|
+
overwrite: false
|
|
628
|
+
}
|
|
629
|
+
];
|
|
630
|
+
for (const { templates, filePath, beautify, overwrite } of codes) {
|
|
631
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
632
|
+
const code = this.genCode(templates, filePath, tableModel);
|
|
633
|
+
let beautified;
|
|
634
|
+
if (beautify) beautified = js_beautify(code, {
|
|
635
|
+
brace_style: "preserve-inline",
|
|
636
|
+
indent_size: 2,
|
|
637
|
+
jslint_happy: true,
|
|
638
|
+
preserve_newlines: false
|
|
639
|
+
});
|
|
640
|
+
else beautified = code;
|
|
641
|
+
beautified = beautified.replace(/( )*\/\/ empty-line( )*/g, "").replace(/Promise( )*<( )*(.+?)( )*>/g, "Promise<$3>").replace(/Optional( )*<( )*(.+?)( )*>/g, "Optional<$3>").replace(/Record( )*<( )*(.+?)( )*>/g, "Record<$3>").replace(/Partial( )*<( )*(.+?)( )*>/g, "Partial<$3>").replace(/DataSource( )*<( )*(.+?)( )*>/g, "DataSource<$3>").replace(/ \? :/g, "?:");
|
|
642
|
+
if (overwrite !== true) try {
|
|
643
|
+
await fs.access(filePath);
|
|
644
|
+
continue;
|
|
645
|
+
} catch {}
|
|
646
|
+
await fs.writeFile(filePath, beautified, "utf8");
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region src/DaoLoader.ts
|
|
653
|
+
var DaoLoader = class {
|
|
654
|
+
static async loadDaos(moduleDir) {
|
|
655
|
+
return (await LoaderFactory.createLoader(moduleDir, EggLoadUnitType.MODULE).load()).filter((t) => {
|
|
656
|
+
return DaoInfoUtil.getIsDao(t);
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
//#endregion
|
|
662
|
+
//#region src/MySqlDataSource.ts
|
|
663
|
+
const DEFAULT_OPTIONS = {
|
|
664
|
+
supportBigNumbers: true,
|
|
665
|
+
bigNumberStrings: true,
|
|
666
|
+
trace: true
|
|
667
|
+
};
|
|
668
|
+
var MysqlDataSource = class extends Base {
|
|
669
|
+
#initRetryTimes;
|
|
670
|
+
#logger;
|
|
671
|
+
constructor(options) {
|
|
672
|
+
super({ initMethod: "_init" });
|
|
673
|
+
const { name, initSql, forkDb, initRetryTimes, logger,...mysqlOptions } = options;
|
|
674
|
+
this.#logger = logger;
|
|
675
|
+
this.forkDb = forkDb;
|
|
676
|
+
this.initSql = initSql ?? "SELECT 1 + 1";
|
|
677
|
+
this.#initRetryTimes = initRetryTimes;
|
|
678
|
+
this.name = name;
|
|
679
|
+
this.timezone = options.timezone;
|
|
680
|
+
this.rdsOptions = Object.assign({}, DEFAULT_OPTIONS, mysqlOptions);
|
|
681
|
+
this.client = new RDSClient(this.rdsOptions);
|
|
682
|
+
}
|
|
683
|
+
async _init() {
|
|
684
|
+
if (this.initSql) await this.#doInit(1);
|
|
685
|
+
}
|
|
686
|
+
async #doInit(tryTimes) {
|
|
687
|
+
try {
|
|
688
|
+
this.#logger?.log(`${tryTimes} try to initialize dataSource ${this.name}`);
|
|
689
|
+
const st = Date.now();
|
|
690
|
+
await this.client.query(this.initSql);
|
|
691
|
+
this.#logger?.info(`dataSource initialization cost: ${Date.now() - st}, tryTimes: ${tryTimes}`);
|
|
692
|
+
} catch (e) {
|
|
693
|
+
this.#logger?.warn(`failed to initialize dataSource ${this.name}, tryTimes ${tryTimes}`, e);
|
|
694
|
+
if (!this.#initRetryTimes || tryTimes >= this.#initRetryTimes) throw e;
|
|
695
|
+
await this.#doInit(tryTimes + 1);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async query(sql) {
|
|
699
|
+
return this.client.query(sql);
|
|
700
|
+
}
|
|
701
|
+
async beginTransactionScope(scope) {
|
|
702
|
+
return await this.client.beginTransactionScope(scope);
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
//#endregion
|
|
707
|
+
//#region src/DatabaseForker.ts
|
|
708
|
+
var DatabaseForker = class {
|
|
709
|
+
constructor(env, options) {
|
|
710
|
+
this.env = env;
|
|
711
|
+
this.options = options;
|
|
712
|
+
}
|
|
713
|
+
shouldFork() {
|
|
714
|
+
return this.env === "unittest" && this.options.forkDb;
|
|
715
|
+
}
|
|
716
|
+
async forkDb(moduleDir) {
|
|
717
|
+
assert(this.shouldFork(), "fork db only run in unittest");
|
|
718
|
+
const { name, initSql, forkDb, database,...mysqlOptions } = this.options;
|
|
719
|
+
const client = new RDSClient(Object.assign(mysqlOptions));
|
|
720
|
+
const conn = await client.getConnection();
|
|
721
|
+
await this.doCreateUtDb(conn);
|
|
722
|
+
await this.forkTables(conn, moduleDir);
|
|
723
|
+
conn.release();
|
|
724
|
+
await client.end();
|
|
725
|
+
}
|
|
726
|
+
async forkTables(conn, moduleDir) {
|
|
727
|
+
const daoClazzList = await DaoLoader.loadDaos(moduleDir);
|
|
728
|
+
for (const clazz of daoClazzList) await this.doForkTable(conn, clazz.tableSql);
|
|
729
|
+
}
|
|
730
|
+
async doForkTable(conn, sqlFile) {
|
|
731
|
+
const sqls = sqlFile.split(";").filter((t) => !!t.trim());
|
|
732
|
+
for (const sql of sqls) await conn.query(sql);
|
|
733
|
+
}
|
|
734
|
+
async doCreateUtDb(conn) {
|
|
735
|
+
await conn.query(`CREATE DATABASE IF NOT EXISTS ${this.options.database};`);
|
|
736
|
+
await conn.query(`use ${this.options.database};`);
|
|
737
|
+
}
|
|
738
|
+
async destroy() {
|
|
739
|
+
assert(this.shouldFork(), "fork db only run in unittest");
|
|
740
|
+
const { name, initSql, forkDb, database,...mysqlOptions } = this.options;
|
|
741
|
+
const client = new RDSClient(Object.assign(mysqlOptions));
|
|
742
|
+
await client.query(`DROP DATABASE ${database}`);
|
|
743
|
+
await client.end();
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
//#endregion
|
|
748
|
+
//#region src/NunjucksConverter.ts
|
|
749
|
+
var NunjucksConverter = class {
|
|
750
|
+
/**
|
|
751
|
+
* 将变量 HTML 转义的逻辑改为 MySQL 防注入转义
|
|
752
|
+
*
|
|
753
|
+
* eg:
|
|
754
|
+
*
|
|
755
|
+
* output += runtime.suppressValue(runtime.contextOrFrameLookup(context, frame, "allColumns")
|
|
756
|
+
*
|
|
757
|
+
* 转换为
|
|
758
|
+
*
|
|
759
|
+
* output += runtime.escapeSQL.call(this, "allColumns", runtime.contextOrFrameLookup(context, frame, "allColumns")
|
|
760
|
+
*
|
|
761
|
+
* @param {String} code 转换前的代码
|
|
762
|
+
* @return {String} 转换后的代码
|
|
763
|
+
*/
|
|
764
|
+
static convertNormalVariableCode(code) {
|
|
765
|
+
return code.replace(/\Woutput\W*?\+=\W*?runtime\.suppressValue\(runtime\.contextOrFrameLookup\((.+?),(.*?),\W*?"(.+?)"\)/g, "\noutput += runtime.escapeSQL.call(this, \"$3\", runtime.contextOrFrameLookup($1, $2, \"$3\")");
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* 三目运算的 MySQL 防注入转义
|
|
769
|
+
*
|
|
770
|
+
* eg:
|
|
771
|
+
*
|
|
772
|
+
* output += runtime.suppressValue((runtime.contextOrFrameLookup(context, frame, "$gmtCreate") !== \
|
|
773
|
+
* runtime.contextOrFrameLookup(context, frame, "undefined")?runtime.contextOrFrameLookup(context,\
|
|
774
|
+
* frame, "$gmtCreate"):"NOW()"), env.opts.autoescape);
|
|
775
|
+
*
|
|
776
|
+
* 转换为
|
|
777
|
+
*
|
|
778
|
+
* output += runtime.suppressValue((runtime.contextOrFrameLookup(...) != ...) ?
|
|
779
|
+
* runtime.escapeSQL.call(this, "...", runtime.contextOrFrameLookup(...)) :
|
|
780
|
+
* ...)
|
|
781
|
+
*
|
|
782
|
+
* @param {String} code 转换前的代码
|
|
783
|
+
* @return {String} 转换后的代码
|
|
784
|
+
*/
|
|
785
|
+
static convertTernaryCode(code) {
|
|
786
|
+
const ternaryBefore = code.match(/\Woutput\W*?\+=\W*?runtime\.suppressValue\(\(.*\W*?\?\W*?.*?:.*\),\W*?env\.opts\.autoescape/g) || [];
|
|
787
|
+
const ternaryAfter = ternaryBefore.map((str) => {
|
|
788
|
+
return str.replace(/([?:])runtime\.contextOrFrameLookup\((.+?),(.*?),\W*?"(.+?)"\)/g, "$1runtime.escapeSQL.call(this, \"$4\", runtime.contextOrFrameLookup($2, $3, \"$4\"))").replace(/env.opts.autoescape$/g, "false");
|
|
789
|
+
});
|
|
790
|
+
for (let i = 0; i < ternaryBefore.length; i++) code = code.replace(ternaryBefore[i], ternaryAfter[i]);
|
|
791
|
+
return code;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* 对象的属性,如 `user.id` 防注入转义
|
|
795
|
+
*
|
|
796
|
+
* eg:
|
|
797
|
+
* output += runtime.suppressValue(runtime.memberLookup(\
|
|
798
|
+
* (runtime.contextOrFrameLookup(context, frame, "user")),"id"), env.opts.autoescape);
|
|
799
|
+
*
|
|
800
|
+
* 转换为
|
|
801
|
+
*
|
|
802
|
+
* output += runtime.escapeSQL.call(this, "<...>", runtime.memberLookup(...), env.opts.autoescape);
|
|
803
|
+
*
|
|
804
|
+
* 由于 escapeSQL 中是根据 key 与预定义 block 匹配决定是否转义,而 memberLookup 的状态下总的 key 肯定不会匹配,
|
|
805
|
+
* 所以找一个绝对不会匹配的 "<...>" 传入。事实上它可以是任意一个不会被匹配的字符串,比如说 ">_<" 等。
|
|
806
|
+
*
|
|
807
|
+
* @param {String} code 转换前的代码
|
|
808
|
+
* @return {String} 转换后的代码
|
|
809
|
+
*/
|
|
810
|
+
static convertNestedObjectCode(code) {
|
|
811
|
+
return code.replace(/\Woutput\W*?\+=\W*?runtime\.suppressValue\(runtime\.memberLookup\((.+?)\), env\.opts\.autoescape\)/g, "\noutput += runtime.escapeSQL.call(this, \"<...>\", runtime.memberLookup($1), env.opts.autoescape)");
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* For 中的 `t_xxx` 要被转义:
|
|
815
|
+
*
|
|
816
|
+
* eg:
|
|
817
|
+
* frame.set("...", t_...);
|
|
818
|
+
* ...
|
|
819
|
+
* output += runtime.suppressValue(t_.., env.opts.autoscape);
|
|
820
|
+
*
|
|
821
|
+
* 转换为
|
|
822
|
+
*
|
|
823
|
+
* output += runtime.escapeSQL.call(this, "for.t_...", t_..., env.opts.autoescape);
|
|
824
|
+
*
|
|
825
|
+
* 由于 escapeSQL 中是根据 key 与预定义 block 匹配决定是否转义,而 memberLookup 的状态下总的 key 肯定不会匹配,
|
|
826
|
+
* 所以找一个绝对不会匹配的 "for.t_..." 传入。事实上它可以是任意一个不会被匹配的字符串,比如说 ">_<" 等。
|
|
827
|
+
*
|
|
828
|
+
* @param {String} code 转换前的代码
|
|
829
|
+
* @return {String} 转换后的代码
|
|
830
|
+
*/
|
|
831
|
+
static convertValueInsideFor(code) {
|
|
832
|
+
return code.replace(/\Woutput\W*?\+=\W*?runtime\.suppressValue\((t_\d+), env\.opts\.autoescape\)/g, "\noutput += runtime.escapeSQL.call(this, \"for.$1\", $1, env.opts.autoescape)");
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/SqlUtil.ts
|
|
838
|
+
function isWhiteChar(ch) {
|
|
839
|
+
return ch === " " || ch === "\n" || ch === "\r" || ch === " ";
|
|
840
|
+
}
|
|
841
|
+
const COMMENT_CHARS = "-#/";
|
|
842
|
+
const MUL_CHAR_LEADING_COMMENT_FIRST_CHAR = {
|
|
843
|
+
MAY_BE_FIRST_COMMENT: "-",
|
|
844
|
+
MAY_BE_FIRST_BLOCK_COMMENT: "/"
|
|
845
|
+
};
|
|
846
|
+
const MUL_CHAR_LEADING_COMMENT_VERIFIER = {
|
|
847
|
+
MAY_BE_FIRST_COMMENT: "-",
|
|
848
|
+
MAY_BE_FIRST_BLOCK_COMMENT: "*"
|
|
849
|
+
};
|
|
850
|
+
const MUL_CHAR_LEADING_COMMENT_NEXT_STATE = {
|
|
851
|
+
MAY_BE_FIRST_COMMENT: "IN_COMMENT_WAIT_HINT",
|
|
852
|
+
MAY_BE_FIRST_BLOCK_COMMENT: "IN_BLOCK_COMMENT_WAIT_HINT"
|
|
853
|
+
};
|
|
854
|
+
var SqlUtil = class {
|
|
855
|
+
static minify(sql) {
|
|
856
|
+
let ret = "";
|
|
857
|
+
let state = "START";
|
|
858
|
+
let tempNextState;
|
|
859
|
+
for (let i = 0; i < sql.length; i++) {
|
|
860
|
+
const ch = sql[i];
|
|
861
|
+
switch (state) {
|
|
862
|
+
case "MAY_BE_FIRST_COMMENT":
|
|
863
|
+
case "MAY_BE_FIRST_BLOCK_COMMENT":
|
|
864
|
+
switch (ch) {
|
|
865
|
+
case "\"":
|
|
866
|
+
tempNextState = "DOUBLE_QUOTE";
|
|
867
|
+
break;
|
|
868
|
+
case "'":
|
|
869
|
+
tempNextState = "SINGLE_QUOTE";
|
|
870
|
+
break;
|
|
871
|
+
case MUL_CHAR_LEADING_COMMENT_VERIFIER[state]:
|
|
872
|
+
tempNextState = MUL_CHAR_LEADING_COMMENT_NEXT_STATE[state];
|
|
873
|
+
break;
|
|
874
|
+
default:
|
|
875
|
+
tempNextState = "CONTENT";
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
if (ch !== MUL_CHAR_LEADING_COMMENT_VERIFIER[state]) ret += `${MUL_CHAR_LEADING_COMMENT_FIRST_CHAR[state]}${ch}`;
|
|
879
|
+
state = tempNextState;
|
|
880
|
+
break;
|
|
881
|
+
case "IN_COMMENT_WAIT_HINT":
|
|
882
|
+
if (ch !== "+") state = "IN_COMMENT";
|
|
883
|
+
else {
|
|
884
|
+
state = "IN_COMMENT_HINT";
|
|
885
|
+
ret += "--+";
|
|
886
|
+
}
|
|
887
|
+
break;
|
|
888
|
+
case "IN_BLOCK_COMMENT_WAIT_HINT":
|
|
889
|
+
if (ch !== "+") state = "IN_BLOCK_COMMENT";
|
|
890
|
+
else {
|
|
891
|
+
state = "IN_BLOCK_COMMENT_HINT";
|
|
892
|
+
ret += "/*+";
|
|
893
|
+
}
|
|
894
|
+
break;
|
|
895
|
+
case "MAY_BE_LAST_BLOCK_COMMENT":
|
|
896
|
+
if (ch === "/") {
|
|
897
|
+
if (ret && !isWhiteChar(ret[ret.length - 1])) ret += " ";
|
|
898
|
+
state = "IN_SPACE";
|
|
899
|
+
} else state = "IN_BLOCK_COMMENT";
|
|
900
|
+
break;
|
|
901
|
+
case "MAY_BE_LAST_BLOCK_COMMENT_HINT":
|
|
902
|
+
ret += ch;
|
|
903
|
+
if (ch === "/") {
|
|
904
|
+
state = "IN_SPACE";
|
|
905
|
+
if (isWhiteChar(sql[i + 1])) ret += sql[i + 1];
|
|
906
|
+
} else state = "IN_BLOCK_COMMENT_HINT";
|
|
907
|
+
break;
|
|
908
|
+
case "IN_COMMENT":
|
|
909
|
+
if (ch === "\n" || ch === "\r") {
|
|
910
|
+
if (ret && !isWhiteChar(ret[ret.length - 1])) ret += " ";
|
|
911
|
+
state = "IN_SPACE";
|
|
912
|
+
}
|
|
913
|
+
break;
|
|
914
|
+
case "IN_COMMENT_HINT":
|
|
915
|
+
ret += ch;
|
|
916
|
+
if (ch === "\n" || ch === "\r") state = "IN_SPACE";
|
|
917
|
+
break;
|
|
918
|
+
case "IN_BLOCK_COMMENT":
|
|
919
|
+
if (ch === "*") state = "MAY_BE_LAST_BLOCK_COMMENT";
|
|
920
|
+
break;
|
|
921
|
+
case "IN_BLOCK_COMMENT_HINT":
|
|
922
|
+
ret += ch;
|
|
923
|
+
if (ch === "*") state = "MAY_BE_LAST_BLOCK_COMMENT_HINT";
|
|
924
|
+
break;
|
|
925
|
+
case "START":
|
|
926
|
+
if (isWhiteChar(ch)) continue;
|
|
927
|
+
switch (ch) {
|
|
928
|
+
case "\"":
|
|
929
|
+
state = "DOUBLE_QUOTE";
|
|
930
|
+
break;
|
|
931
|
+
case "'":
|
|
932
|
+
state = "SINGLE_QUOTE";
|
|
933
|
+
break;
|
|
934
|
+
case "-":
|
|
935
|
+
state = "MAY_BE_FIRST_COMMENT";
|
|
936
|
+
break;
|
|
937
|
+
case "#":
|
|
938
|
+
state = "IN_COMMENT";
|
|
939
|
+
break;
|
|
940
|
+
case "/":
|
|
941
|
+
state = "MAY_BE_FIRST_BLOCK_COMMENT";
|
|
942
|
+
break;
|
|
943
|
+
default:
|
|
944
|
+
state = "CONTENT";
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
947
|
+
if (!COMMENT_CHARS.includes(ch)) ret += ch;
|
|
948
|
+
break;
|
|
949
|
+
case "DOUBLE_QUOTE":
|
|
950
|
+
case "SINGLE_QUOTE":
|
|
951
|
+
switch (ch) {
|
|
952
|
+
case "\\":
|
|
953
|
+
state = `BACKSLASH_AFTER_${state}`;
|
|
954
|
+
break;
|
|
955
|
+
case "'":
|
|
956
|
+
if (state === "SINGLE_QUOTE") state = "QUOTE_DONE";
|
|
957
|
+
break;
|
|
958
|
+
case "\"":
|
|
959
|
+
if (state === "DOUBLE_QUOTE") state = "QUOTE_DONE";
|
|
960
|
+
break;
|
|
961
|
+
default: break;
|
|
962
|
+
}
|
|
963
|
+
ret += ch;
|
|
964
|
+
break;
|
|
965
|
+
case "BACKSLASH_AFTER_SINGLE_QUOTE":
|
|
966
|
+
case "BACKSLASH_AFTER_DOUBLE_QUOTE":
|
|
967
|
+
ret += ch;
|
|
968
|
+
state = state.substr(16);
|
|
969
|
+
break;
|
|
970
|
+
case "QUOTE_DONE":
|
|
971
|
+
case "CONTENT":
|
|
972
|
+
switch (ch) {
|
|
973
|
+
case "'":
|
|
974
|
+
state = "SINGLE_QUOTE";
|
|
975
|
+
break;
|
|
976
|
+
case "\"":
|
|
977
|
+
state = "DOUBLE_QUOTE";
|
|
978
|
+
break;
|
|
979
|
+
case "-":
|
|
980
|
+
state = "MAY_BE_FIRST_COMMENT";
|
|
981
|
+
break;
|
|
982
|
+
case "#":
|
|
983
|
+
state = "IN_COMMENT";
|
|
984
|
+
break;
|
|
985
|
+
case "/":
|
|
986
|
+
state = "MAY_BE_FIRST_BLOCK_COMMENT";
|
|
987
|
+
break;
|
|
988
|
+
default:
|
|
989
|
+
if (isWhiteChar(ch)) {
|
|
990
|
+
state = "IN_SPACE";
|
|
991
|
+
ret += " ";
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
state = "CONTENT";
|
|
995
|
+
}
|
|
996
|
+
if (!COMMENT_CHARS.includes(ch)) ret += ch;
|
|
997
|
+
break;
|
|
998
|
+
case "IN_SPACE":
|
|
999
|
+
switch (ch) {
|
|
1000
|
+
case "'":
|
|
1001
|
+
state = "SINGLE_QUOTE";
|
|
1002
|
+
break;
|
|
1003
|
+
case "\"":
|
|
1004
|
+
state = "DOUBLE_QUOTE";
|
|
1005
|
+
break;
|
|
1006
|
+
case "-":
|
|
1007
|
+
state = "MAY_BE_FIRST_COMMENT";
|
|
1008
|
+
break;
|
|
1009
|
+
case "#":
|
|
1010
|
+
state = "IN_COMMENT";
|
|
1011
|
+
break;
|
|
1012
|
+
case "/":
|
|
1013
|
+
state = "MAY_BE_FIRST_BLOCK_COMMENT";
|
|
1014
|
+
break;
|
|
1015
|
+
default:
|
|
1016
|
+
if (isWhiteChar(ch)) continue;
|
|
1017
|
+
state = "CONTENT";
|
|
1018
|
+
}
|
|
1019
|
+
if (!COMMENT_CHARS.includes(ch)) ret += ch;
|
|
1020
|
+
break;
|
|
1021
|
+
default: throw new Error("Unexpected state machine while minifying SQL.");
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
return ret.trim();
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
//#endregion
|
|
1029
|
+
//#region src/NunjucksUtil.ts
|
|
1030
|
+
const compiler = nunjucks.compiler;
|
|
1031
|
+
const envs = {};
|
|
1032
|
+
const ROOT_RENDER_FUNC = Symbol("rootRenderFunc");
|
|
1033
|
+
const RUNTIME = Object.assign({}, nunjucks.runtime, { escapeSQL: function escapeSQL(key, value) {
|
|
1034
|
+
if (this.env.globals[key]) return value;
|
|
1035
|
+
return sqlstring.escape(value, true, this.env.timezone);
|
|
1036
|
+
} });
|
|
1037
|
+
function _replaceCodeWithSQLFeature(source) {
|
|
1038
|
+
return [
|
|
1039
|
+
"convertNormalVariableCode",
|
|
1040
|
+
"convertTernaryCode",
|
|
1041
|
+
"convertNestedObjectCode",
|
|
1042
|
+
"convertValueInsideFor"
|
|
1043
|
+
].reduce((source$1, func) => NunjucksConverter[func](source$1), source);
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* compile the string into function
|
|
1047
|
+
* @see https://github.com/mozilla/nunjucks/blob/2fd547f/src/environment.js#L571-L592
|
|
1048
|
+
*/
|
|
1049
|
+
function _compile() {
|
|
1050
|
+
let source = compiler.compile(this.tmplStr, this.env.asyncFilters, this.env.extensionsList, this.path, this.env.opts);
|
|
1051
|
+
/**
|
|
1052
|
+
* 将一些 Nunjucks 的 HTML 转义的代码转换成 SQL 防注入的代码
|
|
1053
|
+
*/
|
|
1054
|
+
source = _replaceCodeWithSQLFeature(source);
|
|
1055
|
+
const props = new Function(source)();
|
|
1056
|
+
this.blocks = this._getBlocks(props);
|
|
1057
|
+
this[ROOT_RENDER_FUNC] = props.root;
|
|
1058
|
+
this.rootRenderFunc = function(env, context, frame, _runtime, cb) {
|
|
1059
|
+
/**
|
|
1060
|
+
* 1. 将 runtime 遗弃,用新的
|
|
1061
|
+
* 2. 移除 SQL 语句中多余空白符
|
|
1062
|
+
*/
|
|
1063
|
+
return this[ROOT_RENDER_FUNC](env, context, frame, RUNTIME, function(err, ret) {
|
|
1064
|
+
// istanbul ignore if
|
|
1065
|
+
if (err) return cb(err, ret);
|
|
1066
|
+
return cb(err, SqlUtil.minify(ret || ""));
|
|
1067
|
+
});
|
|
1068
|
+
};
|
|
1069
|
+
this.compiled = true;
|
|
1070
|
+
}
|
|
1071
|
+
var NunjucksUtils = class {
|
|
1072
|
+
static createEnv(modelName) {
|
|
1073
|
+
if (envs[modelName]) return envs[modelName];
|
|
1074
|
+
return envs[modelName] = nunjucks.configure({ autoescape: false });
|
|
1075
|
+
}
|
|
1076
|
+
static compile(modelName, sqlName, sql) {
|
|
1077
|
+
// istanbul ignore if
|
|
1078
|
+
if (!envs[modelName]) throw new Error(`you should create an Environment for ${modelName} first.`);
|
|
1079
|
+
const template = new Template(sql, envs[modelName], `egg-dal:MySQL:${modelName}:${sqlName}`, false);
|
|
1080
|
+
template._compile = _compile;
|
|
1081
|
+
template.compile();
|
|
1082
|
+
return template;
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
//#endregion
|
|
1087
|
+
//#region src/TableSqlMap.ts
|
|
1088
|
+
var TableSqlMap = class {
|
|
1089
|
+
constructor(name, map) {
|
|
1090
|
+
this.name = name;
|
|
1091
|
+
this.map = map;
|
|
1092
|
+
const env = NunjucksUtils.createEnv(name);
|
|
1093
|
+
const extracted = this.#extract(this.map);
|
|
1094
|
+
this.blocks = extracted.blocks;
|
|
1095
|
+
this.sqlGenerator = extracted.sqlGenerator;
|
|
1096
|
+
for (const key in this.blocks) {
|
|
1097
|
+
// istanbul ignore if
|
|
1098
|
+
if (!this.blocks.hasOwnProperty(key)) continue;
|
|
1099
|
+
env.addGlobal(key, this.blocks[key]);
|
|
1100
|
+
}
|
|
1101
|
+
env.addFilter("toJson", TemplateUtil.toJson);
|
|
1102
|
+
env.addFilter("toPoint", TemplateUtil.toPoint);
|
|
1103
|
+
env.addFilter("toLine", TemplateUtil.toLine);
|
|
1104
|
+
env.addFilter("toPolygon", TemplateUtil.toPolygon);
|
|
1105
|
+
env.addFilter("toGeometry", TemplateUtil.toGeometry);
|
|
1106
|
+
env.addFilter("toMultiPoint", TemplateUtil.toMultiPoint);
|
|
1107
|
+
env.addFilter("toMultiLine", TemplateUtil.toMultiLine);
|
|
1108
|
+
env.addFilter("toMultiPolygon", TemplateUtil.toMultiPolygon);
|
|
1109
|
+
env.addFilter("toGeometryCollection", TemplateUtil.toGeometryCollection);
|
|
1110
|
+
}
|
|
1111
|
+
#extract(map) {
|
|
1112
|
+
const ret = {
|
|
1113
|
+
blocks: {},
|
|
1114
|
+
sqlGenerator: {}
|
|
1115
|
+
};
|
|
1116
|
+
for (const key in map) {
|
|
1117
|
+
// istanbul ignore if
|
|
1118
|
+
if (!map.hasOwnProperty(key)) continue;
|
|
1119
|
+
const sqlMap = map[key];
|
|
1120
|
+
switch (sqlMap.type) {
|
|
1121
|
+
case SqlType.BLOCK:
|
|
1122
|
+
ret.blocks[key] = sqlMap.content || "";
|
|
1123
|
+
break;
|
|
1124
|
+
case SqlType.INSERT:
|
|
1125
|
+
case SqlType.SELECT:
|
|
1126
|
+
case SqlType.UPDATE:
|
|
1127
|
+
case SqlType.DELETE:
|
|
1128
|
+
default:
|
|
1129
|
+
ret.sqlGenerator[key] = {
|
|
1130
|
+
type: sqlMap.type,
|
|
1131
|
+
template: NunjucksUtils.compile(this.name, key, sqlMap.sql || ""),
|
|
1132
|
+
raw: sqlMap.sql
|
|
1133
|
+
};
|
|
1134
|
+
break;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return ret;
|
|
1138
|
+
}
|
|
1139
|
+
generate(name, data, timezone) {
|
|
1140
|
+
const generator = this.sqlGenerator[name];
|
|
1141
|
+
// istanbul ignore if
|
|
1142
|
+
if (!generator) throw new Error(`No sql map named '${name}' in '${name}'.`);
|
|
1143
|
+
const template = generator.template;
|
|
1144
|
+
template.env.timezone = timezone;
|
|
1145
|
+
return template.render(data);
|
|
1146
|
+
}
|
|
1147
|
+
getType(name) {
|
|
1148
|
+
const generator = this.sqlGenerator[name];
|
|
1149
|
+
// istanbul ignore if
|
|
1150
|
+
if (!generator) throw new Error(`No sql map named '${name}' in '${name}'.`);
|
|
1151
|
+
return generator.type;
|
|
1152
|
+
}
|
|
1153
|
+
getTemplateString(name) {
|
|
1154
|
+
const generator = this.sqlGenerator[name];
|
|
1155
|
+
// istanbul ignore if
|
|
1156
|
+
if (!generator) throw new Error(`No sql map named '${name}' in '${name}'.`);
|
|
1157
|
+
return generator.raw;
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
|
|
1161
|
+
//#endregion
|
|
1162
|
+
//#region src/TableModelInstanceBuilder.ts
|
|
1163
|
+
var TableModelInstanceBuilder = class TableModelInstanceBuilder {
|
|
1164
|
+
constructor(tableModel, row) {
|
|
1165
|
+
for (const [key, value] of Object.entries(row)) {
|
|
1166
|
+
const column = tableModel.columns.find((t) => t.columnName === key);
|
|
1167
|
+
Reflect.set(this, column?.propertyName ?? key, value);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
static buildInstance(tableModel, row) {
|
|
1171
|
+
return Reflect.construct(TableModelInstanceBuilder, [tableModel, row], tableModel.clazz);
|
|
1172
|
+
}
|
|
1173
|
+
static buildRow(instance, tableModel) {
|
|
1174
|
+
const result = {};
|
|
1175
|
+
for (const column of tableModel.columns) {
|
|
1176
|
+
const columnValue = Reflect.get(instance, column.propertyName);
|
|
1177
|
+
if (typeof columnValue !== "undefined") result[`$${column.propertyName}`] = columnValue;
|
|
1178
|
+
}
|
|
1179
|
+
return result;
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
//#endregion
|
|
1184
|
+
//#region src/DataSource.ts
|
|
1185
|
+
const PAGINATE_COUNT_WRAPPER = ["SELECT COUNT(0) as count FROM (", ") AS T"];
|
|
1186
|
+
var DataSource = class {
|
|
1187
|
+
constructor(tableModel, mysqlDataSource, sqlMap) {
|
|
1188
|
+
this.tableModel = tableModel;
|
|
1189
|
+
this.mysqlDataSource = mysqlDataSource;
|
|
1190
|
+
this.sqlMap = sqlMap;
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* public for aop execute to implement sql hint append
|
|
1194
|
+
* @param sqlName - sql name
|
|
1195
|
+
* @param data - sql data
|
|
1196
|
+
*/
|
|
1197
|
+
async generateSql(sqlName, data) {
|
|
1198
|
+
const sql = this.sqlMap.generate(sqlName, data, this.mysqlDataSource.timezone);
|
|
1199
|
+
const sqlType = this.sqlMap.getType(sqlName);
|
|
1200
|
+
const template = this.sqlMap.getTemplateString(sqlName);
|
|
1201
|
+
return {
|
|
1202
|
+
sql,
|
|
1203
|
+
sqlType,
|
|
1204
|
+
template
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
async count(sqlName, data) {
|
|
1208
|
+
const newData = Object.assign({ $$count: true }, data);
|
|
1209
|
+
const executeSql = await this.generateSql(sqlName, newData);
|
|
1210
|
+
return await this.#paginateCount(executeSql.sql);
|
|
1211
|
+
}
|
|
1212
|
+
async execute(sqlName, data) {
|
|
1213
|
+
const executeSql = await this.generateSql(sqlName, data);
|
|
1214
|
+
return (await this.mysqlDataSource.query(executeSql.sql)).map((t) => {
|
|
1215
|
+
return TableModelInstanceBuilder.buildInstance(this.tableModel, t);
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
async executeRaw(sqlName, data) {
|
|
1219
|
+
const executeSql = await this.generateSql(sqlName, data);
|
|
1220
|
+
return await this.mysqlDataSource.query(executeSql.sql);
|
|
1221
|
+
}
|
|
1222
|
+
async executeScalar(sqlName, data) {
|
|
1223
|
+
const ret = await this.execute(sqlName, data);
|
|
1224
|
+
if (!Array.isArray(ret)) return ret || null;
|
|
1225
|
+
return ret[0] || null;
|
|
1226
|
+
}
|
|
1227
|
+
async executeRawScalar(sqlName, data) {
|
|
1228
|
+
const ret = await this.executeRaw(sqlName, data);
|
|
1229
|
+
if (!Array.isArray(ret)) return ret || null;
|
|
1230
|
+
return ret[0] || null;
|
|
1231
|
+
}
|
|
1232
|
+
async paginate(sqlName, data, currentPage, perPageCount) {
|
|
1233
|
+
const limit = `LIMIT ${(currentPage - 1) * perPageCount}, ${perPageCount}`;
|
|
1234
|
+
const sql = (await this.generateSql(sqlName, data)).sql + " " + limit;
|
|
1235
|
+
const countSql = (await this.generateSql(sqlName, Object.assign({ $$count: true }, data))).sql;
|
|
1236
|
+
const ret = await Promise.all([this.mysqlDataSource.query(sql), this.#paginateCount(countSql)]);
|
|
1237
|
+
return {
|
|
1238
|
+
total: Number(ret[1]),
|
|
1239
|
+
pageNum: currentPage,
|
|
1240
|
+
rows: ret[0].map((t) => TableModelInstanceBuilder.buildInstance(this.tableModel, t))
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
async #paginateCount(baseSQL) {
|
|
1244
|
+
const sql = `${PAGINATE_COUNT_WRAPPER[0]}${baseSQL}${PAGINATE_COUNT_WRAPPER[1]}`;
|
|
1245
|
+
return (await this.mysqlDataSource.query(sql))[0].count;
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
//#endregion
|
|
1250
|
+
//#region src/SqlMapLoader.ts
|
|
1251
|
+
var SqlMapLoader = class {
|
|
1252
|
+
constructor(tableModel, baseDaoClazz, logger) {
|
|
1253
|
+
this.clazzExtension = baseDaoClazz.clazzExtension;
|
|
1254
|
+
this.logger = logger;
|
|
1255
|
+
this.tableModel = tableModel;
|
|
1256
|
+
}
|
|
1257
|
+
load() {
|
|
1258
|
+
const sqlMap = {
|
|
1259
|
+
...new BaseSqlMapGenerator(this.tableModel, this.logger).load(),
|
|
1260
|
+
...this.clazzExtension
|
|
1261
|
+
};
|
|
1262
|
+
return new TableSqlMap(this.tableModel.clazz.name, sqlMap);
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
//#endregion
|
|
16
1267
|
export { BaseSqlMapGenerator, CodeGenerator, DaoLoader, DataSource, DatabaseForker, MysqlDataSource, NunjucksConverter, NunjucksUtils, SqlGenerator, SqlMapLoader, SqlUtil, TableModelInstanceBuilder, TableSqlMap, TemplateUtil };
|