oak-db 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,912 +1,897 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MySqlTranslator = void 0;
4
- var tslib_1 = require("tslib");
5
- var assert_1 = tslib_1.__importDefault(require("assert"));
6
- var util_1 = require("util");
7
- var lodash_1 = require("lodash");
8
- var sqlTranslator_1 = require("../sqlTranslator");
9
- var GeoTypes = [
10
- {
11
- type: 'point',
12
- name: "Point"
13
- },
14
- {
15
- type: 'path',
16
- name: "LineString",
17
- element: 'point',
18
- },
19
- {
20
- name: "MultiLineString",
21
- element: "path",
22
- multiple: true,
23
- },
24
- {
25
- type: 'polygon',
26
- name: "Polygon",
27
- element: "path"
28
- },
29
- {
30
- name: "MultiPoint",
31
- element: "point",
32
- multiple: true,
33
- },
34
- {
35
- name: "MultiPolygon",
36
- element: "polygon",
37
- multiple: true,
38
- }
39
- ];
40
- function transformGeoData(data) {
41
- if (data instanceof Array) {
42
- var element_1 = data[0];
43
- if (element_1 instanceof Array) {
44
- return " GeometryCollection(".concat(data.map(function (ele) { return transformGeoData(ele); }).join(','), ")");
45
- }
46
- else {
47
- var geoType_1 = GeoTypes.find(function (ele) { return ele.type === element_1.type; });
48
- if (!geoType_1) {
49
- throw new Error("".concat(element_1.type, " is not supported in MySQL"));
50
- }
51
- var multiGeoType = GeoTypes.find(function (ele) { return ele.element === geoType_1.type && ele.multiple; });
52
- return " ".concat(multiGeoType.name, "(").concat(data.map(function (ele) { return transformGeoData(ele); }).join(','), ")");
53
- }
54
- }
55
- else {
56
- var type_1 = data.type, coordinate = data.coordinate;
57
- var geoType = GeoTypes.find(function (ele) { return ele.type === type_1; });
58
- if (!geoType) {
59
- throw new Error("".concat(data.type, " is not supported in MySQL"));
60
- }
61
- var element_2 = geoType.element, name_1 = geoType.name;
62
- if (!element_2) {
63
- // Point
64
- return " ".concat(name_1, "(").concat(coordinate.join(','), ")");
65
- }
66
- // Polygon or Linestring
67
- return " ".concat(name_1, "(").concat(coordinate.map(function (ele) { return transformGeoData({
68
- type: element_2,
69
- coordinate: ele,
70
- }); }), ")");
71
- }
72
- }
73
- var MySqlTranslator = /** @class */ (function (_super) {
74
- tslib_1.__extends(MySqlTranslator, _super);
75
- function MySqlTranslator(schema) {
76
- var _this = _super.call(this, schema) || this;
77
- _this.maxAliasLength = 63;
78
- // MySQL为geometry属性默认创建索引
79
- _this.makeUpSchema();
80
- return _this;
81
- }
82
- MySqlTranslator.prototype.getDefaultSelectFilter = function (alias, option) {
83
- if (option === null || option === void 0 ? void 0 : option.includedDeleted) {
84
- return '';
85
- }
86
- return " (`".concat(alias, "`.`$$deleteAt$$` is null)");
87
- };
88
- MySqlTranslator.prototype.makeUpSchema = function () {
89
- for (var entity in this.schema) {
90
- var _a = this.schema[entity], attributes = _a.attributes, indexes = _a.indexes;
91
- var geoIndexes = [];
92
- var _loop_1 = function (attr) {
93
- if (attributes[attr].type === 'geometry') {
94
- var geoIndex = indexes === null || indexes === void 0 ? void 0 : indexes.find(function (idx) {
95
- var _a;
96
- return ((_a = idx.config) === null || _a === void 0 ? void 0 : _a.type) === 'spatial' && idx.attributes.find(function (attrDef) { return attrDef.name === attr; });
97
- });
98
- if (!geoIndex) {
99
- geoIndexes.push({
100
- name: "".concat(entity, "_geo_").concat(attr),
101
- attributes: [{
102
- name: attr,
103
- }],
104
- config: {
105
- type: 'spatial',
106
- }
107
- });
108
- }
109
- }
110
- };
111
- for (var attr in attributes) {
112
- _loop_1(attr);
113
- }
114
- if (geoIndexes.length > 0) {
115
- if (indexes) {
116
- indexes.push.apply(indexes, geoIndexes);
117
- }
118
- else {
119
- (0, lodash_1.assign)(this.schema[entity], {
120
- indexes: geoIndexes,
121
- });
122
- }
123
- }
124
- }
125
- };
126
- MySqlTranslator.prototype.populateDataTypeDef = function (type, params, enumeration) {
127
- if (['date', 'datetime', 'time', 'sequence'].includes(type)) {
128
- return 'bigint ';
129
- }
130
- if (['object', 'array'].includes(type)) {
131
- return 'json ';
132
- }
133
- if (['image', 'function'].includes(type)) {
134
- return 'text ';
135
- }
136
- if (type === 'ref') {
137
- return 'char(36)';
138
- }
139
- if (type === 'money') {
140
- return 'bigint';
141
- }
142
- if (type === 'enum') {
143
- (0, assert_1.default)(enumeration);
144
- return "enum(".concat(enumeration.map(function (ele) { return "'".concat(ele, "'"); }).join(','), ")");
145
- }
146
- if (MySqlTranslator.withLengthDataTypes.includes(type)) {
147
- if (params) {
148
- var length_1 = params.length;
149
- return "".concat(type, "(").concat(length_1, ") ");
150
- }
151
- else {
152
- var length_2 = MySqlTranslator.dataTypeDefaults[type].length;
153
- return "".concat(type, "(").concat(length_2, ") ");
154
- }
155
- }
156
- if (MySqlTranslator.withPrecisionDataTypes.includes(type)) {
157
- if (params) {
158
- var precision = params.precision, scale = params.scale;
159
- if (typeof scale === 'number') {
160
- return "".concat(type, "(").concat(precision, ", ").concat(scale, ") ");
161
- }
162
- return "".concat(type, "(").concat(precision, ")");
163
- }
164
- else {
165
- var _a = MySqlTranslator.dataTypeDefaults[type], precision = _a.precision, scale = _a.scale;
166
- if (typeof scale === 'number') {
167
- return "".concat(type, "(").concat(precision, ", ").concat(scale, ") ");
168
- }
169
- return "".concat(type, "(").concat(precision, ")");
170
- }
171
- }
172
- if (MySqlTranslator.withWidthDataTypes.includes(type)) {
173
- (0, assert_1.default)(type === 'int');
174
- var width = params.width;
175
- switch (width) {
176
- case 1: {
177
- return 'tinyint';
178
- }
179
- case 2: {
180
- return 'smallint';
181
- }
182
- case 3: {
183
- return 'mediumint';
184
- }
185
- case 4: {
186
- return 'int';
187
- }
188
- default: {
189
- return 'bigint';
190
- }
191
- }
192
- }
193
- return "".concat(type, " ");
194
- };
195
- MySqlTranslator.prototype.translateAttrProjection = function (dataType, alias, attr) {
196
- switch (dataType) {
197
- case 'geometry': {
198
- return " st_astext(`".concat(alias, "`.`").concat(attr, "`)");
199
- }
200
- default: {
201
- return " `".concat(alias, "`.`").concat(attr, "`");
202
- }
203
- }
204
- };
205
- MySqlTranslator.prototype.translateObjectPredicate = function (predicate, alias, attr) {
206
- var _this = this;
207
- var stmt = '';
208
- var translatePredicate = function (o, p) {
209
- var predicate2 = Object.keys(o)[0];
210
- if (predicate2.startsWith('$')) {
211
- if (stmt) {
212
- stmt += ' and ';
213
- }
214
- // todo
215
- if (predicate2 === '$contains') {
216
- // json_contains,多值的包含关系
217
- var value = JSON.stringify(o[predicate2]);
218
- stmt += "JSON_CONTAINS(".concat(alias, ".").concat(attr, "->>\"$").concat(p, "\", CAST('").concat(value, "' AS JSON)) ");
219
- }
220
- else if (predicate2 === '$overlaps') {
221
- // json_overlaps,多值的交叉关系
222
- var value = JSON.stringify(o[predicate2]);
223
- stmt += "JSON_OVERLAPS(".concat(alias, ".").concat(attr, "->>\"$").concat(p, "\", CAST('").concat(value, "' AS JSON)) ");
224
- }
225
- else {
226
- stmt += "".concat(alias, ".").concat(attr, "->>\"$").concat(p, "\" ").concat(_this.translatePredicate(predicate2, o[predicate2]));
227
- }
228
- }
229
- else {
230
- // 继续子对象解构
231
- translateInner(o, p);
232
- }
233
- };
234
- var translateInner = function (o, p) {
235
- if (o instanceof Array) {
236
- o.forEach(function (item, idx) {
237
- var p2 = "".concat(p, "[").concat(idx, "]");
238
- if (typeof item !== 'object') {
239
- if (item !== null && item !== undefined) {
240
- if (stmt) {
241
- stmt += ' and ';
242
- }
243
- stmt += "".concat(alias, ".").concat(attr, "->>\"$").concat(p2, "\"");
244
- if (typeof item === 'string') {
245
- stmt += " = '".concat(item, "'");
246
- }
247
- else {
248
- stmt += " = ".concat(item);
249
- }
250
- }
251
- }
252
- else {
253
- translatePredicate(item, p2);
254
- }
255
- });
256
- }
257
- else {
258
- for (var key in o) {
259
- var p2 = "".concat(p, ".").concat(key);
260
- if (typeof o[key] !== 'object') {
261
- if (o[key] !== null && o[key] !== undefined) {
262
- if (stmt) {
263
- stmt += ', ';
264
- }
265
- stmt += "".concat(alias, ".").concat(attr, "->>\"$").concat(p2, "\"");
266
- if (typeof o[key] === 'string') {
267
- stmt += " = '".concat(o[key], "'");
268
- }
269
- else {
270
- stmt += " = ".concat(o[key]);
271
- }
272
- }
273
- }
274
- else {
275
- translatePredicate(o[key], p2);
276
- }
277
- }
278
- }
279
- };
280
- translatePredicate(predicate, '');
281
- return stmt;
282
- };
283
- MySqlTranslator.prototype.translateObjectProjection = function (projection, alias, attr, prefix) {
284
- var stmt = '';
285
- var translateInner = function (o, p) {
286
- if (o instanceof Array) {
287
- o.forEach(function (item, idx) {
288
- var p2 = "".concat(p, "[").concat(idx, "]");
289
- if (typeof item === 'number') {
290
- if (stmt) {
291
- stmt += ', ';
292
- }
293
- stmt += "".concat(alias, ".").concat(attr, "->>\"$").concat(p2, "\"");
294
- stmt += prefix ? " as `".concat(prefix, ".").concat(attr).concat(p2, "`") : " as `".concat(attr).concat(p2, "`");
295
- }
296
- else if (typeof item === 'object') {
297
- translateInner(item, p2);
298
- }
299
- });
300
- }
301
- else {
302
- for (var key in o) {
303
- var p2 = "".concat(p, ".").concat(key);
304
- if (typeof o[key] === 'number') {
305
- if (stmt) {
306
- stmt += ', ';
307
- }
308
- stmt += "".concat(alias, ".").concat(attr, "->>\"$").concat(p2, "\"");
309
- stmt += prefix ? " as `".concat(prefix, ".").concat(attr).concat(p2, "`") : " as `".concat(attr).concat(p2, "`");
310
- }
311
- else {
312
- translateInner(o[key], p2);
313
- }
314
- }
315
- }
316
- };
317
- translateInner(projection, '');
318
- return stmt;
319
- };
320
- MySqlTranslator.prototype.translateAttrValue = function (dataType, value) {
321
- if (value === null || value === undefined) {
322
- return 'null';
323
- }
324
- switch (dataType) {
325
- case 'geometry': {
326
- return transformGeoData(value);
327
- }
328
- case 'datetime':
329
- case 'time':
330
- case 'date': {
331
- if (value instanceof Date) {
332
- return "".concat(value.valueOf());
333
- }
334
- else if (typeof value === 'number') {
335
- return "".concat(value);
336
- }
337
- return "'".concat((new Date(value)).valueOf(), "'");
338
- }
339
- case 'object':
340
- case 'array': {
341
- return this.escapeStringValue(JSON.stringify(value));
342
- }
343
- /* case 'function': {
344
- return `'${Buffer.from(value.toString()).toString('base64')}'`;
345
- } */
346
- default: {
347
- if (typeof value === 'string') {
348
- return this.escapeStringValue(value);
349
- }
350
- return value;
351
- }
352
- }
353
- };
354
- MySqlTranslator.prototype.translateFullTextSearch = function (value, entity, alias) {
355
- var $search = value.$search;
356
- var indexes = this.schema[entity].indexes;
357
- var ftIndex = indexes && indexes.find(function (ele) {
358
- var config = ele.config;
359
- return config && config.type === 'fulltext';
360
- });
361
- (0, assert_1.default)(ftIndex);
362
- var attributes = ftIndex.attributes;
363
- var columns2 = attributes.map(function (_a) {
364
- var name = _a.name;
365
- return "".concat(alias, ".").concat(name);
366
- });
367
- return " match(".concat(columns2.join(','), ") against ('").concat($search, "' in natural language mode)");
368
- };
369
- MySqlTranslator.prototype.translateCreateEntity = function (entity, options) {
370
- var _this = this;
371
- var replace = options === null || options === void 0 ? void 0 : options.replace;
372
- var schema = this.schema;
373
- var entityDef = schema[entity];
374
- var storageName = entityDef.storageName, attributes = entityDef.attributes, indexes = entityDef.indexes, view = entityDef.view;
375
- var hasSequence = false;
376
- // todo view暂还不支持
377
- var entityType = view ? 'view' : 'table';
378
- var sql = "create ".concat(entityType, " ");
379
- if (storageName) {
380
- sql += "`".concat(storageName, "` ");
381
- }
382
- else {
383
- sql += "`".concat(entity, "` ");
384
- }
385
- if (view) {
386
- throw new Error(' view unsupported yet');
387
- }
388
- else {
389
- sql += '(';
390
- // 翻译所有的属性
391
- Object.keys(attributes).forEach(function (attr, idx) {
392
- var attrDef = attributes[attr];
393
- var type = attrDef.type, params = attrDef.params, defaultValue = attrDef.default, unique = attrDef.unique, notNull = attrDef.notNull, sequenceStart = attrDef.sequenceStart, enumeration = attrDef.enumeration;
394
- sql += "`".concat(attr, "` ");
395
- sql += _this.populateDataTypeDef(type, params, enumeration);
396
- if (notNull || type === 'geometry') {
397
- sql += ' not null ';
398
- }
399
- if (unique) {
400
- sql += ' unique ';
401
- }
402
- if (sequenceStart) {
403
- if (hasSequence) {
404
- throw new Error("\u300C".concat(entity, "\u300D\u53EA\u80FD\u6709\u4E00\u4E2Asequence\u5217"));
405
- }
406
- hasSequence = sequenceStart;
407
- sql += ' auto_increment unique ';
408
- }
409
- if (defaultValue !== undefined) {
410
- (0, assert_1.default)(type !== 'ref');
411
- sql += " default ".concat(_this.translateAttrValue(type, defaultValue));
412
- }
413
- if (attr === 'id') {
414
- sql += ' primary key';
415
- }
416
- if (idx < Object.keys(attributes).length - 1) {
417
- sql += ',\n';
418
- }
419
- });
420
- // 翻译索引信息
421
- if (indexes) {
422
- sql += ',\n';
423
- indexes.forEach(function (_a, idx) {
424
- var name = _a.name, attributes = _a.attributes, config = _a.config;
425
- var _b = config || {}, unique = _b.unique, type = _b.type, parser = _b.parser;
426
- // 因为有deleteAt的存在,这里的unique没意义,只能框架自己去建立checker来处理
427
- /* if (unique) {
428
- sql += ' unique ';
429
- }
430
- else */ if (type === 'fulltext') {
431
- sql += ' fulltext ';
432
- }
433
- else if (type === 'spatial') {
434
- sql += ' spatial ';
435
- }
436
- sql += "index ".concat(name, " ");
437
- if (type === 'hash') {
438
- sql += " using hash ";
439
- }
440
- sql += '(';
441
- var includeDeleteAt = false;
442
- attributes.forEach(function (_a, idx2) {
443
- var name = _a.name, size = _a.size, direction = _a.direction;
444
- sql += "`".concat(name, "`");
445
- if (size) {
446
- sql += " (".concat(size, ")");
447
- }
448
- if (direction) {
449
- sql += " ".concat(direction);
450
- }
451
- if (idx2 < attributes.length - 1) {
452
- sql += ',';
453
- }
454
- if (name === '$$deleteAt$$') {
455
- includeDeleteAt = true;
456
- }
457
- });
458
- if (!includeDeleteAt && !type) {
459
- sql += ', $$deleteAt$$';
460
- }
461
- sql += ')';
462
- if (parser) {
463
- sql += " with parser ".concat(parser);
464
- }
465
- if (idx < indexes.length - 1) {
466
- sql += ',\n';
467
- }
468
- });
469
- }
470
- }
471
- sql += ')';
472
- if (typeof hasSequence === 'number') {
473
- sql += "auto_increment = ".concat(hasSequence);
474
- }
475
- if (!replace) {
476
- return [sql];
477
- }
478
- return ["drop ".concat(entityType, " if exists `").concat(storageName || entity, "`;"), sql];
479
- };
480
- MySqlTranslator.prototype.translateFnName = function (fnName, argumentNumber) {
481
- switch (fnName) {
482
- case '$add': {
483
- var result = '%s';
484
- while (--argumentNumber > 0) {
485
- result += ' + %s';
486
- }
487
- return result;
488
- }
489
- case '$subtract': {
490
- (0, assert_1.default)(argumentNumber === 2);
491
- return '%s - %s';
492
- }
493
- case '$multiply': {
494
- var result = '%s';
495
- while (--argumentNumber > 0) {
496
- result += ' * %s';
497
- }
498
- return result;
499
- }
500
- case '$divide': {
501
- (0, assert_1.default)(argumentNumber === 2);
502
- return '%s / %s';
503
- }
504
- case '$abs': {
505
- return 'ABS(%s)';
506
- }
507
- case '$round': {
508
- (0, assert_1.default)(argumentNumber === 2);
509
- return 'ROUND(%s, %s)';
510
- }
511
- case '$ceil': {
512
- return 'CEIL(%s)';
513
- }
514
- case '$floor': {
515
- return 'FLOOR(%s)';
516
- }
517
- case '$pow': {
518
- (0, assert_1.default)(argumentNumber === 2);
519
- return 'POW(%s, %s)';
520
- }
521
- case '$gt': {
522
- (0, assert_1.default)(argumentNumber === 2);
523
- return '%s > %s';
524
- }
525
- case '$gte': {
526
- (0, assert_1.default)(argumentNumber === 2);
527
- return '%s >= %s';
528
- }
529
- case '$lt': {
530
- (0, assert_1.default)(argumentNumber === 2);
531
- return '%s < %s';
532
- }
533
- case '$lte': {
534
- return '%s <= %s';
535
- }
536
- case '$eq': {
537
- (0, assert_1.default)(argumentNumber === 2);
538
- return '%s = %s';
539
- }
540
- case '$ne': {
541
- (0, assert_1.default)(argumentNumber === 2);
542
- return '%s <> %s';
543
- }
544
- case '$startsWith': {
545
- (0, assert_1.default)(argumentNumber === 2);
546
- return '%s like CONCAT(%s, \'%\')';
547
- }
548
- case '$endsWith': {
549
- (0, assert_1.default)(argumentNumber === 2);
550
- return '%s like CONCAT(\'%\', %s)';
551
- }
552
- case '$includes': {
553
- (0, assert_1.default)(argumentNumber === 2);
554
- return '%s like CONCAT(\'%\', %s, \'%\')';
555
- }
556
- case '$true': {
557
- return '%s = true';
558
- }
559
- case '$false': {
560
- return '%s = false';
561
- }
562
- case '$and': {
563
- var result = '';
564
- for (var iter = 0; iter < argumentNumber; iter++) {
565
- result += '%s';
566
- if (iter < argumentNumber - 1) {
567
- result += ' and ';
568
- }
569
- }
570
- return result;
571
- }
572
- case '$or': {
573
- var result = '';
574
- for (var iter = 0; iter < argumentNumber; iter++) {
575
- result += '%s';
576
- if (iter < argumentNumber - 1) {
577
- result += ' or ';
578
- }
579
- }
580
- return result;
581
- }
582
- case '$not': {
583
- return 'not %s';
584
- }
585
- case '$year': {
586
- return 'YEAR(%s)';
587
- }
588
- case '$month': {
589
- return 'MONTH(%s)';
590
- }
591
- case '$weekday': {
592
- return 'WEEKDAY(%s)';
593
- }
594
- case '$weekOfYear': {
595
- return 'WEEKOFYEAR(%s)';
596
- }
597
- case '$day': {
598
- return 'DAY(%s)';
599
- }
600
- case '$dayOfMonth': {
601
- return 'DAYOFMONTH(%s)';
602
- }
603
- case '$dayOfWeek': {
604
- return 'DAYOFWEEK(%s)';
605
- }
606
- case '$dayOfYear': {
607
- return 'DAYOFYEAR(%s)';
608
- }
609
- case '$dateDiff': {
610
- (0, assert_1.default)(argumentNumber === 3);
611
- return 'DATEDIFF(%s, %s, %s)';
612
- }
613
- case '$contains': {
614
- (0, assert_1.default)(argumentNumber === 2);
615
- return 'ST_CONTAINS(%s, %s)';
616
- }
617
- case '$distance': {
618
- (0, assert_1.default)(argumentNumber === 2);
619
- return 'ST_DISTANCE(%s, %s)';
620
- }
621
- case '$concat': {
622
- var result = ' concat(%s';
623
- while (--argumentNumber > 0) {
624
- result += ', %s';
625
- }
626
- result += ')';
627
- return result;
628
- }
629
- default: {
630
- throw new Error("unrecoganized function ".concat(fnName));
631
- }
632
- }
633
- };
634
- MySqlTranslator.prototype.translateAttrInExpression = function (entity, attr, exprText) {
635
- var attributes = this.schema[entity].attributes;
636
- var type = attributes[attr].type;
637
- if (['date', 'time', 'datetime'].includes(type)) {
638
- // 从unix时间戵转成date类型参加expr的运算
639
- return "from_unixtime(".concat(exprText, " / 1000)");
640
- }
641
- return exprText;
642
- };
643
- MySqlTranslator.prototype.translateExpression = function (entity, alias, expression, refDict) {
644
- var _this = this;
645
- var translateConstant = function (constant) {
646
- if (constant instanceof Date) {
647
- return " from_unixtime(".concat(constant.valueOf(), "/1000)");
648
- }
649
- else if (typeof constant === 'string') {
650
- return " '".concat(constant, "'");
651
- }
652
- else {
653
- (0, assert_1.default)(typeof constant === 'number');
654
- return " ".concat(constant);
655
- }
656
- };
657
- var translateInner = function (expr) {
658
- var k = Object.keys(expr);
659
- var result;
660
- if (k.includes('#attr')) {
661
- var attrText = "`".concat(alias, "`.`").concat((expr)['#attr'], "`");
662
- result = _this.translateAttrInExpression(entity, (expr)['#attr'], attrText);
663
- }
664
- else if (k.includes('#refId')) {
665
- var refId = (expr)['#refId'];
666
- var refAttr = (expr)['#refAttr'];
667
- (0, assert_1.default)(refDict[refId]);
668
- var attrText = "`".concat(refDict[refId][0], "`.`").concat(refAttr, "`");
669
- result = _this.translateAttrInExpression(entity, (expr)['#refAttr'], attrText);
670
- }
671
- else {
672
- (0, assert_1.default)(k.length === 1);
673
- if ((expr)[k[0]] instanceof Array) {
674
- var fnName = _this.translateFnName(k[0], (expr)[k[0]].length);
675
- var args = [fnName];
676
- args.push.apply(args, (expr)[k[0]].map(function (ele) {
677
- if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
678
- return translateConstant(ele);
679
- }
680
- else {
681
- return translateInner(ele);
682
- }
683
- }));
684
- result = util_1.format.apply(null, args);
685
- }
686
- else {
687
- var fnName = _this.translateFnName(k[0], 1);
688
- var args = [fnName];
689
- var arg = (expr)[k[0]];
690
- if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
691
- args.push(translateConstant(arg));
692
- }
693
- else {
694
- args.push(translateInner(arg));
695
- }
696
- result = util_1.format.apply(null, args);
697
- }
698
- }
699
- return result;
700
- };
701
- return translateInner(expression);
702
- };
703
- MySqlTranslator.prototype.populateSelectStmt = function (projectionText, fromText, aliasDict, filterText, sorterText, groupByText, indexFrom, count, option) {
704
- // todo hint of use index
705
- var sql = "select ".concat(projectionText, " from ").concat(fromText);
706
- if (filterText) {
707
- sql += " where ".concat(filterText);
708
- }
709
- if (sorterText) {
710
- sql += " order by ".concat(sorterText);
711
- }
712
- if (groupByText) {
713
- sql += " group by ".concat(groupByText);
714
- }
715
- if (typeof indexFrom === 'number') {
716
- (0, assert_1.default)(typeof count === 'number');
717
- sql += " limit ".concat(indexFrom, ", ").concat(count);
718
- }
719
- if (option === null || option === void 0 ? void 0 : option.forUpdate) {
720
- sql += ' for update';
721
- }
722
- return sql;
723
- };
724
- MySqlTranslator.prototype.populateUpdateStmt = function (updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option) {
725
- // todo using index
726
- (0, assert_1.default)(updateText);
727
- var sql = "update ".concat(fromText, " set ").concat(updateText);
728
- if (filterText) {
729
- sql += " where ".concat(filterText);
730
- }
731
- if (sorterText) {
732
- sql += " order by ".concat(sorterText);
733
- }
734
- if (typeof indexFrom === 'number') {
735
- (0, assert_1.default)(typeof count === 'number');
736
- sql += " limit ".concat(indexFrom, ", ").concat(count);
737
- }
738
- return sql;
739
- };
740
- MySqlTranslator.prototype.populateRemoveStmt = function (removeText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option) {
741
- // todo using index
742
- var alias = aliasDict['./'];
743
- if (option === null || option === void 0 ? void 0 : option.deletePhysically) {
744
- var sql_1 = "delete ".concat(alias, " from ").concat(fromText, " ");
745
- if (filterText) {
746
- sql_1 += " where ".concat(filterText);
747
- }
748
- if (sorterText) {
749
- sql_1 += " order by ".concat(sorterText);
750
- }
751
- if (typeof indexFrom === 'number') {
752
- (0, assert_1.default)(typeof count === 'number');
753
- sql_1 += " limit ".concat(indexFrom, ", ").concat(count);
754
- }
755
- return sql_1;
756
- }
757
- var now = Date.now();
758
- var sql = "update ".concat(fromText, " set `").concat(alias, "`.`$$deleteAt$$` = '").concat(now, "'");
759
- if (filterText) {
760
- sql += " where ".concat(filterText);
761
- }
762
- if (sorterText) {
763
- sql += " order by ".concat(sorterText);
764
- }
765
- if (typeof indexFrom === 'number') {
766
- (0, assert_1.default)(typeof count === 'number');
767
- sql += " limit ".concat(indexFrom, ", ").concat(count);
768
- }
769
- return sql;
770
- };
771
- MySqlTranslator.supportedDataTypes = [
772
- // numeric types
773
- "bit",
774
- "int",
775
- "integer",
776
- "tinyint",
777
- "smallint",
778
- "mediumint",
779
- "bigint",
780
- "float",
781
- "double",
782
- "double precision",
783
- "real",
784
- "decimal",
785
- "dec",
786
- "numeric",
787
- "fixed",
788
- "bool",
789
- "boolean",
790
- // date and time types
791
- "date",
792
- "datetime",
793
- "timestamp",
794
- "time",
795
- "year",
796
- // string types
797
- "char",
798
- "nchar",
799
- "national char",
800
- "varchar",
801
- "nvarchar",
802
- "national varchar",
803
- "blob",
804
- "text",
805
- "tinyblob",
806
- "tinytext",
807
- "mediumblob",
808
- "mediumtext",
809
- "longblob",
810
- "longtext",
811
- "enum",
812
- "set",
813
- "binary",
814
- "varbinary",
815
- // json data type
816
- "json",
817
- // spatial data types
818
- "geometry",
819
- "point",
820
- "linestring",
821
- "polygon",
822
- "multipoint",
823
- "multilinestring",
824
- "multipolygon",
825
- "geometrycollection"
826
- ];
827
- MySqlTranslator.spatialTypes = [
828
- "geometry",
829
- "point",
830
- "linestring",
831
- "polygon",
832
- "multipoint",
833
- "multilinestring",
834
- "multipolygon",
835
- "geometrycollection"
836
- ];
837
- MySqlTranslator.withLengthDataTypes = [
838
- "char",
839
- "varchar",
840
- "nvarchar",
841
- "binary",
842
- "varbinary"
843
- ];
844
- MySqlTranslator.withPrecisionDataTypes = [
845
- "decimal",
846
- "dec",
847
- "numeric",
848
- "fixed",
849
- "float",
850
- "double",
851
- "double precision",
852
- "real",
853
- "time",
854
- "datetime",
855
- "timestamp"
856
- ];
857
- MySqlTranslator.withScaleDataTypes = [
858
- "decimal",
859
- "dec",
860
- "numeric",
861
- "fixed",
862
- "float",
863
- "double",
864
- "double precision",
865
- "real"
866
- ];
867
- MySqlTranslator.unsignedAndZerofillTypes = [
868
- "int",
869
- "integer",
870
- "smallint",
871
- "tinyint",
872
- "mediumint",
873
- "bigint",
874
- "decimal",
875
- "dec",
876
- "numeric",
877
- "fixed",
878
- "float",
879
- "double",
880
- "double precision",
881
- "real"
882
- ];
883
- MySqlTranslator.withWidthDataTypes = [
884
- 'int',
885
- ];
886
- MySqlTranslator.dataTypeDefaults = {
887
- "varchar": { length: 255 },
888
- "nvarchar": { length: 255 },
889
- "national varchar": { length: 255 },
890
- "char": { length: 1 },
891
- "binary": { length: 1 },
892
- "varbinary": { length: 255 },
893
- "decimal": { precision: 10, scale: 0 },
894
- "dec": { precision: 10, scale: 0 },
895
- "numeric": { precision: 10, scale: 0 },
896
- "fixed": { precision: 10, scale: 0 },
897
- "float": { precision: 12 },
898
- "double": { precision: 22 },
899
- "time": { precision: 0 },
900
- "datetime": { precision: 0 },
901
- "timestamp": { precision: 0 },
902
- "bit": { width: 1 },
903
- "int": { width: 11 },
904
- "integer": { width: 11 },
905
- "tinyint": { width: 4 },
906
- "smallint": { width: 6 },
907
- "mediumint": { width: 9 },
908
- "bigint": { width: 20 }
909
- };
910
- return MySqlTranslator;
911
- }(sqlTranslator_1.SqlTranslator));
912
- exports.MySqlTranslator = MySqlTranslator;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MySqlTranslator = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const assert_1 = tslib_1.__importDefault(require("assert"));
6
+ const util_1 = require("util");
7
+ const lodash_1 = require("lodash");
8
+ const sqlTranslator_1 = require("../sqlTranslator");
9
+ const GeoTypes = [
10
+ {
11
+ type: 'point',
12
+ name: "Point"
13
+ },
14
+ {
15
+ type: 'path',
16
+ name: "LineString",
17
+ element: 'point',
18
+ },
19
+ {
20
+ name: "MultiLineString",
21
+ element: "path",
22
+ multiple: true,
23
+ },
24
+ {
25
+ type: 'polygon',
26
+ name: "Polygon",
27
+ element: "path"
28
+ },
29
+ {
30
+ name: "MultiPoint",
31
+ element: "point",
32
+ multiple: true,
33
+ },
34
+ {
35
+ name: "MultiPolygon",
36
+ element: "polygon",
37
+ multiple: true,
38
+ }
39
+ ];
40
+ function transformGeoData(data) {
41
+ if (data instanceof Array) {
42
+ const element = data[0];
43
+ if (element instanceof Array) {
44
+ return ` GeometryCollection(${data.map(ele => transformGeoData(ele)).join(',')})`;
45
+ }
46
+ else {
47
+ const geoType = GeoTypes.find(ele => ele.type === element.type);
48
+ if (!geoType) {
49
+ throw new Error(`${element.type} is not supported in MySQL`);
50
+ }
51
+ const multiGeoType = GeoTypes.find(ele => ele.element === geoType.type && ele.multiple);
52
+ return ` ${multiGeoType.name}(${data.map(ele => transformGeoData(ele)).join(',')})`;
53
+ }
54
+ }
55
+ else {
56
+ const { type, coordinate } = data;
57
+ const geoType = GeoTypes.find(ele => ele.type === type);
58
+ if (!geoType) {
59
+ throw new Error(`${data.type} is not supported in MySQL`);
60
+ }
61
+ const { element, name } = geoType;
62
+ if (!element) {
63
+ // Point
64
+ return ` ${name}(${coordinate.join(',')})`;
65
+ }
66
+ // Polygon or Linestring
67
+ return ` ${name}(${coordinate.map((ele) => transformGeoData({
68
+ type: element,
69
+ coordinate: ele,
70
+ }))})`;
71
+ }
72
+ }
73
+ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
74
+ getDefaultSelectFilter(alias, option) {
75
+ if (option?.includedDeleted) {
76
+ return '';
77
+ }
78
+ return ` (\`${alias}\`.\`$$deleteAt$$\` is null)`;
79
+ }
80
+ makeUpSchema() {
81
+ for (const entity in this.schema) {
82
+ const { attributes, indexes } = this.schema[entity];
83
+ const geoIndexes = [];
84
+ for (const attr in attributes) {
85
+ if (attributes[attr].type === 'geometry') {
86
+ const geoIndex = indexes?.find((idx) => idx.config?.type === 'spatial' && idx.attributes.find((attrDef) => attrDef.name === attr));
87
+ if (!geoIndex) {
88
+ geoIndexes.push({
89
+ name: `${entity}_geo_${attr}`,
90
+ attributes: [{
91
+ name: attr,
92
+ }],
93
+ config: {
94
+ type: 'spatial',
95
+ }
96
+ });
97
+ }
98
+ }
99
+ }
100
+ if (geoIndexes.length > 0) {
101
+ if (indexes) {
102
+ indexes.push(...geoIndexes);
103
+ }
104
+ else {
105
+ (0, lodash_1.assign)(this.schema[entity], {
106
+ indexes: geoIndexes,
107
+ });
108
+ }
109
+ }
110
+ }
111
+ }
112
+ constructor(schema) {
113
+ super(schema);
114
+ // MySQL为geometry属性默认创建索引
115
+ this.makeUpSchema();
116
+ }
117
+ static supportedDataTypes = [
118
+ // numeric types
119
+ "bit",
120
+ "int",
121
+ "integer",
122
+ "tinyint",
123
+ "smallint",
124
+ "mediumint",
125
+ "bigint",
126
+ "float",
127
+ "double",
128
+ "double precision",
129
+ "real",
130
+ "decimal",
131
+ "dec",
132
+ "numeric",
133
+ "fixed",
134
+ "bool",
135
+ "boolean",
136
+ // date and time types
137
+ "date",
138
+ "datetime",
139
+ "timestamp",
140
+ "time",
141
+ "year",
142
+ // string types
143
+ "char",
144
+ "nchar",
145
+ "national char",
146
+ "varchar",
147
+ "nvarchar",
148
+ "national varchar",
149
+ "blob",
150
+ "text",
151
+ "tinyblob",
152
+ "tinytext",
153
+ "mediumblob",
154
+ "mediumtext",
155
+ "longblob",
156
+ "longtext",
157
+ "enum",
158
+ "set",
159
+ "binary",
160
+ "varbinary",
161
+ // json data type
162
+ "json",
163
+ // spatial data types
164
+ "geometry",
165
+ "point",
166
+ "linestring",
167
+ "polygon",
168
+ "multipoint",
169
+ "multilinestring",
170
+ "multipolygon",
171
+ "geometrycollection"
172
+ ];
173
+ static spatialTypes = [
174
+ "geometry",
175
+ "point",
176
+ "linestring",
177
+ "polygon",
178
+ "multipoint",
179
+ "multilinestring",
180
+ "multipolygon",
181
+ "geometrycollection"
182
+ ];
183
+ static withLengthDataTypes = [
184
+ "char",
185
+ "varchar",
186
+ "nvarchar",
187
+ "binary",
188
+ "varbinary"
189
+ ];
190
+ static withPrecisionDataTypes = [
191
+ "decimal",
192
+ "dec",
193
+ "numeric",
194
+ "fixed",
195
+ "float",
196
+ "double",
197
+ "double precision",
198
+ "real",
199
+ "time",
200
+ "datetime",
201
+ "timestamp"
202
+ ];
203
+ static withScaleDataTypes = [
204
+ "decimal",
205
+ "dec",
206
+ "numeric",
207
+ "fixed",
208
+ "float",
209
+ "double",
210
+ "double precision",
211
+ "real"
212
+ ];
213
+ static unsignedAndZerofillTypes = [
214
+ "int",
215
+ "integer",
216
+ "smallint",
217
+ "tinyint",
218
+ "mediumint",
219
+ "bigint",
220
+ "decimal",
221
+ "dec",
222
+ "numeric",
223
+ "fixed",
224
+ "float",
225
+ "double",
226
+ "double precision",
227
+ "real"
228
+ ];
229
+ static withWidthDataTypes = [
230
+ 'int',
231
+ ];
232
+ static dataTypeDefaults = {
233
+ "varchar": { length: 255 },
234
+ "nvarchar": { length: 255 },
235
+ "national varchar": { length: 255 },
236
+ "char": { length: 1 },
237
+ "binary": { length: 1 },
238
+ "varbinary": { length: 255 },
239
+ "decimal": { precision: 10, scale: 0 },
240
+ "dec": { precision: 10, scale: 0 },
241
+ "numeric": { precision: 10, scale: 0 },
242
+ "fixed": { precision: 10, scale: 0 },
243
+ "float": { precision: 12 },
244
+ "double": { precision: 22 },
245
+ "time": { precision: 0 },
246
+ "datetime": { precision: 0 },
247
+ "timestamp": { precision: 0 },
248
+ "bit": { width: 1 },
249
+ "int": { width: 11 },
250
+ "integer": { width: 11 },
251
+ "tinyint": { width: 4 },
252
+ "smallint": { width: 6 },
253
+ "mediumint": { width: 9 },
254
+ "bigint": { width: 20 }
255
+ };
256
+ maxAliasLength = 63;
257
+ populateDataTypeDef(type, params, enumeration) {
258
+ if (['date', 'datetime', 'time', 'sequence'].includes(type)) {
259
+ return 'bigint ';
260
+ }
261
+ if (['object', 'array'].includes(type)) {
262
+ return 'json ';
263
+ }
264
+ if (['image', 'function'].includes(type)) {
265
+ return 'text ';
266
+ }
267
+ if (type === 'ref') {
268
+ return 'char(36)';
269
+ }
270
+ if (type === 'money') {
271
+ return 'bigint';
272
+ }
273
+ if (type === 'enum') {
274
+ (0, assert_1.default)(enumeration);
275
+ return `enum(${enumeration.map(ele => `'${ele}'`).join(',')})`;
276
+ }
277
+ if (MySqlTranslator.withLengthDataTypes.includes(type)) {
278
+ if (params) {
279
+ const { length } = params;
280
+ return `${type}(${length}) `;
281
+ }
282
+ else {
283
+ const { length } = MySqlTranslator.dataTypeDefaults[type];
284
+ return `${type}(${length}) `;
285
+ }
286
+ }
287
+ if (MySqlTranslator.withPrecisionDataTypes.includes(type)) {
288
+ if (params) {
289
+ const { precision, scale } = params;
290
+ if (typeof scale === 'number') {
291
+ return `${type}(${precision}, ${scale}) `;
292
+ }
293
+ return `${type}(${precision})`;
294
+ }
295
+ else {
296
+ const { precision, scale } = MySqlTranslator.dataTypeDefaults[type];
297
+ if (typeof scale === 'number') {
298
+ return `${type}(${precision}, ${scale}) `;
299
+ }
300
+ return `${type}(${precision})`;
301
+ }
302
+ }
303
+ if (MySqlTranslator.withWidthDataTypes.includes(type)) {
304
+ (0, assert_1.default)(type === 'int');
305
+ const { width } = params;
306
+ switch (width) {
307
+ case 1: {
308
+ return 'tinyint';
309
+ }
310
+ case 2: {
311
+ return 'smallint';
312
+ }
313
+ case 3: {
314
+ return 'mediumint';
315
+ }
316
+ case 4: {
317
+ return 'int';
318
+ }
319
+ default: {
320
+ return 'bigint';
321
+ }
322
+ }
323
+ }
324
+ return `${type} `;
325
+ }
326
+ translateAttrProjection(dataType, alias, attr) {
327
+ switch (dataType) {
328
+ case 'geometry': {
329
+ return ` st_astext(\`${alias}\`.\`${attr}\`)`;
330
+ }
331
+ default: {
332
+ return ` \`${alias}\`.\`${attr}\``;
333
+ }
334
+ }
335
+ }
336
+ translateObjectPredicate(predicate, alias, attr) {
337
+ let stmt = '';
338
+ const translatePredicate = (o, p) => {
339
+ const predicate2 = Object.keys(o)[0];
340
+ if (predicate2.startsWith('$')) {
341
+ if (stmt) {
342
+ stmt += ' and ';
343
+ }
344
+ // todo
345
+ if (predicate2 === '$contains') {
346
+ // json_contains,多值的包含关系
347
+ const value = JSON.stringify(o[predicate2]);
348
+ stmt += `JSON_CONTAINS(${alias}.${attr}->>"$${p}", CAST('${value}' AS JSON)) `;
349
+ }
350
+ else if (predicate2 === '$overlaps') {
351
+ // json_overlaps,多值的交叉关系
352
+ const value = JSON.stringify(o[predicate2]);
353
+ stmt += `JSON_OVERLAPS(${alias}.${attr}->>"$${p}", CAST('${value}' AS JSON)) `;
354
+ }
355
+ else {
356
+ stmt += `${alias}.${attr}->>"$${p}" ${this.translatePredicate(predicate2, o[predicate2])}`;
357
+ }
358
+ }
359
+ else {
360
+ // 继续子对象解构
361
+ translateInner(o, p);
362
+ }
363
+ };
364
+ const translateInner = (o, p) => {
365
+ if (o instanceof Array) {
366
+ o.forEach((item, idx) => {
367
+ const p2 = `${p}[${idx}]`;
368
+ if (typeof item !== 'object') {
369
+ if (item !== null && item !== undefined) {
370
+ if (stmt) {
371
+ stmt += ' and ';
372
+ }
373
+ stmt += `${alias}.${attr}->>"$${p2}"`;
374
+ if (typeof item === 'string') {
375
+ stmt += ` = '${item}'`;
376
+ }
377
+ else {
378
+ stmt += ` = ${item}`;
379
+ }
380
+ }
381
+ }
382
+ else {
383
+ translatePredicate(item, p2);
384
+ }
385
+ });
386
+ }
387
+ else {
388
+ for (const key in o) {
389
+ const p2 = `${p}.${key}`;
390
+ if (typeof o[key] !== 'object') {
391
+ if (o[key] !== null && o[key] !== undefined) {
392
+ if (stmt) {
393
+ stmt += ', ';
394
+ }
395
+ stmt += `${alias}.${attr}->>"$${p2}"`;
396
+ if (typeof o[key] === 'string') {
397
+ stmt += ` = '${o[key]}'`;
398
+ }
399
+ else {
400
+ stmt += ` = ${o[key]}`;
401
+ }
402
+ }
403
+ }
404
+ else {
405
+ translatePredicate(o[key], p2);
406
+ }
407
+ }
408
+ }
409
+ };
410
+ translatePredicate(predicate, '');
411
+ return stmt;
412
+ }
413
+ translateObjectProjection(projection, alias, attr, prefix) {
414
+ let stmt = '';
415
+ const translateInner = (o, p) => {
416
+ if (o instanceof Array) {
417
+ o.forEach((item, idx) => {
418
+ const p2 = `${p}[${idx}]`;
419
+ if (typeof item === 'number') {
420
+ if (stmt) {
421
+ stmt += ', ';
422
+ }
423
+ stmt += `${alias}.${attr}->>"$${p2}"`;
424
+ stmt += prefix ? ` as \`${prefix}.${attr}${p2}\`` : ` as \`${attr}${p2}\``;
425
+ }
426
+ else if (typeof item === 'object') {
427
+ translateInner(item, p2);
428
+ }
429
+ });
430
+ }
431
+ else {
432
+ for (const key in o) {
433
+ const p2 = `${p}.${key}`;
434
+ if (typeof o[key] === 'number') {
435
+ if (stmt) {
436
+ stmt += ', ';
437
+ }
438
+ stmt += `${alias}.${attr}->>"$${p2}"`;
439
+ stmt += prefix ? ` as \`${prefix}.${attr}${p2}\`` : ` as \`${attr}${p2}\``;
440
+ }
441
+ else {
442
+ translateInner(o[key], p2);
443
+ }
444
+ }
445
+ }
446
+ };
447
+ translateInner(projection, '');
448
+ return stmt;
449
+ }
450
+ translateAttrValue(dataType, value) {
451
+ if (value === null || value === undefined) {
452
+ return 'null';
453
+ }
454
+ switch (dataType) {
455
+ case 'geometry': {
456
+ return transformGeoData(value);
457
+ }
458
+ case 'datetime':
459
+ case 'time':
460
+ case 'date': {
461
+ if (value instanceof Date) {
462
+ return `${value.valueOf()}`;
463
+ }
464
+ else if (typeof value === 'number') {
465
+ return `${value}`;
466
+ }
467
+ return `'${(new Date(value)).valueOf()}'`;
468
+ }
469
+ case 'object':
470
+ case 'array': {
471
+ return this.escapeStringValue(JSON.stringify(value));
472
+ }
473
+ /* case 'function': {
474
+ return `'${Buffer.from(value.toString()).toString('base64')}'`;
475
+ } */
476
+ default: {
477
+ if (typeof value === 'string') {
478
+ return this.escapeStringValue(value);
479
+ }
480
+ return value;
481
+ }
482
+ }
483
+ }
484
+ translateFullTextSearch(value, entity, alias) {
485
+ const { $search } = value;
486
+ const { indexes } = this.schema[entity];
487
+ const ftIndex = indexes && indexes.find((ele) => {
488
+ const { config } = ele;
489
+ return config && config.type === 'fulltext';
490
+ });
491
+ (0, assert_1.default)(ftIndex);
492
+ const { attributes } = ftIndex;
493
+ const columns2 = attributes.map(({ name }) => `${alias}.${name}`);
494
+ return ` match(${columns2.join(',')}) against ('${$search}' in natural language mode)`;
495
+ }
496
+ translateCreateEntity(entity, options) {
497
+ const replace = options?.replace;
498
+ const { schema } = this;
499
+ const entityDef = schema[entity];
500
+ const { storageName, attributes, indexes, view } = entityDef;
501
+ let hasSequence = false;
502
+ // todo view暂还不支持
503
+ const entityType = view ? 'view' : 'table';
504
+ let sql = `create ${entityType} `;
505
+ if (storageName) {
506
+ sql += `\`${storageName}\` `;
507
+ }
508
+ else {
509
+ sql += `\`${entity}\` `;
510
+ }
511
+ if (view) {
512
+ throw new Error(' view unsupported yet');
513
+ }
514
+ else {
515
+ sql += '(';
516
+ // 翻译所有的属性
517
+ Object.keys(attributes).forEach((attr, idx) => {
518
+ const attrDef = attributes[attr];
519
+ const { type, params, default: defaultValue, unique, notNull, sequenceStart, enumeration, } = attrDef;
520
+ sql += `\`${attr}\` `;
521
+ sql += this.populateDataTypeDef(type, params, enumeration);
522
+ if (notNull || type === 'geometry') {
523
+ sql += ' not null ';
524
+ }
525
+ if (unique) {
526
+ sql += ' unique ';
527
+ }
528
+ if (sequenceStart) {
529
+ if (hasSequence) {
530
+ throw new Error(`「${entity}」只能有一个sequence列`);
531
+ }
532
+ hasSequence = sequenceStart;
533
+ sql += ' auto_increment unique ';
534
+ }
535
+ if (defaultValue !== undefined) {
536
+ (0, assert_1.default)(type !== 'ref');
537
+ sql += ` default ${this.translateAttrValue(type, defaultValue)}`;
538
+ }
539
+ if (attr === 'id') {
540
+ sql += ' primary key';
541
+ }
542
+ if (idx < Object.keys(attributes).length - 1) {
543
+ sql += ',\n';
544
+ }
545
+ });
546
+ // 翻译索引信息
547
+ if (indexes) {
548
+ sql += ',\n';
549
+ indexes.forEach(({ name, attributes, config }, idx) => {
550
+ const { unique, type, parser } = config || {};
551
+ // 因为有deleteAt的存在,这里的unique没意义,只能框架自己去建立checker来处理
552
+ /* if (unique) {
553
+ sql += ' unique ';
554
+ }
555
+ else */ if (type === 'fulltext') {
556
+ sql += ' fulltext ';
557
+ }
558
+ else if (type === 'spatial') {
559
+ sql += ' spatial ';
560
+ }
561
+ sql += `index ${name} `;
562
+ if (type === 'hash') {
563
+ sql += ` using hash `;
564
+ }
565
+ sql += '(';
566
+ let includeDeleteAt = false;
567
+ attributes.forEach(({ name, size, direction }, idx2) => {
568
+ sql += `\`${name}\``;
569
+ if (size) {
570
+ sql += ` (${size})`;
571
+ }
572
+ if (direction) {
573
+ sql += ` ${direction}`;
574
+ }
575
+ if (idx2 < attributes.length - 1) {
576
+ sql += ',';
577
+ }
578
+ if (name === '$$deleteAt$$') {
579
+ includeDeleteAt = true;
580
+ }
581
+ });
582
+ if (!includeDeleteAt && !type) {
583
+ sql += ', $$deleteAt$$';
584
+ }
585
+ sql += ')';
586
+ if (parser) {
587
+ sql += ` with parser ${parser}`;
588
+ }
589
+ if (idx < indexes.length - 1) {
590
+ sql += ',\n';
591
+ }
592
+ });
593
+ }
594
+ }
595
+ sql += ')';
596
+ if (typeof hasSequence === 'number') {
597
+ sql += `auto_increment = ${hasSequence}`;
598
+ }
599
+ if (!replace) {
600
+ return [sql];
601
+ }
602
+ return [`drop ${entityType} if exists \`${storageName || entity}\`;`, sql];
603
+ }
604
+ translateFnName(fnName, argumentNumber) {
605
+ switch (fnName) {
606
+ case '$add': {
607
+ let result = '%s';
608
+ while (--argumentNumber > 0) {
609
+ result += ' + %s';
610
+ }
611
+ return result;
612
+ }
613
+ case '$subtract': {
614
+ (0, assert_1.default)(argumentNumber === 2);
615
+ return '%s - %s';
616
+ }
617
+ case '$multiply': {
618
+ let result = '%s';
619
+ while (--argumentNumber > 0) {
620
+ result += ' * %s';
621
+ }
622
+ return result;
623
+ }
624
+ case '$divide': {
625
+ (0, assert_1.default)(argumentNumber === 2);
626
+ return '%s / %s';
627
+ }
628
+ case '$abs': {
629
+ return 'ABS(%s)';
630
+ }
631
+ case '$round': {
632
+ (0, assert_1.default)(argumentNumber === 2);
633
+ return 'ROUND(%s, %s)';
634
+ }
635
+ case '$ceil': {
636
+ return 'CEIL(%s)';
637
+ }
638
+ case '$floor': {
639
+ return 'FLOOR(%s)';
640
+ }
641
+ case '$pow': {
642
+ (0, assert_1.default)(argumentNumber === 2);
643
+ return 'POW(%s, %s)';
644
+ }
645
+ case '$gt': {
646
+ (0, assert_1.default)(argumentNumber === 2);
647
+ return '%s > %s';
648
+ }
649
+ case '$gte': {
650
+ (0, assert_1.default)(argumentNumber === 2);
651
+ return '%s >= %s';
652
+ }
653
+ case '$lt': {
654
+ (0, assert_1.default)(argumentNumber === 2);
655
+ return '%s < %s';
656
+ }
657
+ case '$lte': {
658
+ return '%s <= %s';
659
+ }
660
+ case '$eq': {
661
+ (0, assert_1.default)(argumentNumber === 2);
662
+ return '%s = %s';
663
+ }
664
+ case '$ne': {
665
+ (0, assert_1.default)(argumentNumber === 2);
666
+ return '%s <> %s';
667
+ }
668
+ case '$startsWith': {
669
+ (0, assert_1.default)(argumentNumber === 2);
670
+ return '%s like CONCAT(%s, \'%\')';
671
+ }
672
+ case '$endsWith': {
673
+ (0, assert_1.default)(argumentNumber === 2);
674
+ return '%s like CONCAT(\'%\', %s)';
675
+ }
676
+ case '$includes': {
677
+ (0, assert_1.default)(argumentNumber === 2);
678
+ return '%s like CONCAT(\'%\', %s, \'%\')';
679
+ }
680
+ case '$true': {
681
+ return '%s = true';
682
+ }
683
+ case '$false': {
684
+ return '%s = false';
685
+ }
686
+ case '$and': {
687
+ let result = '';
688
+ for (let iter = 0; iter < argumentNumber; iter++) {
689
+ result += '%s';
690
+ if (iter < argumentNumber - 1) {
691
+ result += ' and ';
692
+ }
693
+ }
694
+ return result;
695
+ }
696
+ case '$or': {
697
+ let result = '';
698
+ for (let iter = 0; iter < argumentNumber; iter++) {
699
+ result += '%s';
700
+ if (iter < argumentNumber - 1) {
701
+ result += ' or ';
702
+ }
703
+ }
704
+ return result;
705
+ }
706
+ case '$not': {
707
+ return 'not %s';
708
+ }
709
+ case '$year': {
710
+ return 'YEAR(%s)';
711
+ }
712
+ case '$month': {
713
+ return 'MONTH(%s)';
714
+ }
715
+ case '$weekday': {
716
+ return 'WEEKDAY(%s)';
717
+ }
718
+ case '$weekOfYear': {
719
+ return 'WEEKOFYEAR(%s)';
720
+ }
721
+ case '$day': {
722
+ return 'DAY(%s)';
723
+ }
724
+ case '$dayOfMonth': {
725
+ return 'DAYOFMONTH(%s)';
726
+ }
727
+ case '$dayOfWeek': {
728
+ return 'DAYOFWEEK(%s)';
729
+ }
730
+ case '$dayOfYear': {
731
+ return 'DAYOFYEAR(%s)';
732
+ }
733
+ case '$dateDiff': {
734
+ (0, assert_1.default)(argumentNumber === 3);
735
+ return 'DATEDIFF(%s, %s, %s)';
736
+ }
737
+ case '$contains': {
738
+ (0, assert_1.default)(argumentNumber === 2);
739
+ return 'ST_CONTAINS(%s, %s)';
740
+ }
741
+ case '$distance': {
742
+ (0, assert_1.default)(argumentNumber === 2);
743
+ return 'ST_DISTANCE(%s, %s)';
744
+ }
745
+ case '$concat': {
746
+ let result = ' concat(%s';
747
+ while (--argumentNumber > 0) {
748
+ result += ', %s';
749
+ }
750
+ result += ')';
751
+ return result;
752
+ }
753
+ default: {
754
+ throw new Error(`unrecoganized function ${fnName}`);
755
+ }
756
+ }
757
+ }
758
+ translateAttrInExpression(entity, attr, exprText) {
759
+ const { attributes } = this.schema[entity];
760
+ const { type } = attributes[attr];
761
+ if (['date', 'time', 'datetime'].includes(type)) {
762
+ // 从unix时间戵转成date类型参加expr的运算
763
+ return `from_unixtime(${exprText} / 1000)`;
764
+ }
765
+ return exprText;
766
+ }
767
+ translateExpression(entity, alias, expression, refDict) {
768
+ const translateConstant = (constant) => {
769
+ if (constant instanceof Date) {
770
+ return ` from_unixtime(${constant.valueOf()}/1000)`;
771
+ }
772
+ else if (typeof constant === 'string') {
773
+ return ` '${constant}'`;
774
+ }
775
+ else {
776
+ (0, assert_1.default)(typeof constant === 'number');
777
+ return ` ${constant}`;
778
+ }
779
+ };
780
+ const translateInner = (expr) => {
781
+ const k = Object.keys(expr);
782
+ let result;
783
+ if (k.includes('#attr')) {
784
+ const attrText = `\`${alias}\`.\`${(expr)['#attr']}\``;
785
+ result = this.translateAttrInExpression(entity, (expr)['#attr'], attrText);
786
+ }
787
+ else if (k.includes('#refId')) {
788
+ const refId = (expr)['#refId'];
789
+ const refAttr = (expr)['#refAttr'];
790
+ (0, assert_1.default)(refDict[refId]);
791
+ const attrText = `\`${refDict[refId][0]}\`.\`${refAttr}\``;
792
+ result = this.translateAttrInExpression(entity, (expr)['#refAttr'], attrText);
793
+ }
794
+ else {
795
+ (0, assert_1.default)(k.length === 1);
796
+ if ((expr)[k[0]] instanceof Array) {
797
+ const fnName = this.translateFnName(k[0], (expr)[k[0]].length);
798
+ const args = [fnName];
799
+ args.push(...(expr)[k[0]].map((ele) => {
800
+ if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
801
+ return translateConstant(ele);
802
+ }
803
+ else {
804
+ return translateInner(ele);
805
+ }
806
+ }));
807
+ result = util_1.format.apply(null, args);
808
+ }
809
+ else {
810
+ const fnName = this.translateFnName(k[0], 1);
811
+ const args = [fnName];
812
+ const arg = (expr)[k[0]];
813
+ if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
814
+ args.push(translateConstant(arg));
815
+ }
816
+ else {
817
+ args.push(translateInner(arg));
818
+ }
819
+ result = util_1.format.apply(null, args);
820
+ }
821
+ }
822
+ return result;
823
+ };
824
+ return translateInner(expression);
825
+ }
826
+ populateSelectStmt(projectionText, fromText, aliasDict, filterText, sorterText, groupByText, indexFrom, count, option) {
827
+ // todo hint of use index
828
+ let sql = `select ${projectionText} from ${fromText}`;
829
+ if (filterText) {
830
+ sql += ` where ${filterText}`;
831
+ }
832
+ if (sorterText) {
833
+ sql += ` order by ${sorterText}`;
834
+ }
835
+ if (groupByText) {
836
+ sql += ` group by ${groupByText}`;
837
+ }
838
+ if (typeof indexFrom === 'number') {
839
+ (0, assert_1.default)(typeof count === 'number');
840
+ sql += ` limit ${indexFrom}, ${count}`;
841
+ }
842
+ if (option?.forUpdate) {
843
+ sql += ' for update';
844
+ }
845
+ return sql;
846
+ }
847
+ populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option) {
848
+ // todo using index
849
+ (0, assert_1.default)(updateText);
850
+ let sql = `update ${fromText} set ${updateText}`;
851
+ if (filterText) {
852
+ sql += ` where ${filterText}`;
853
+ }
854
+ if (sorterText) {
855
+ sql += ` order by ${sorterText}`;
856
+ }
857
+ if (typeof indexFrom === 'number') {
858
+ (0, assert_1.default)(typeof count === 'number');
859
+ sql += ` limit ${indexFrom}, ${count}`;
860
+ }
861
+ return sql;
862
+ }
863
+ populateRemoveStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option) {
864
+ // todo using index
865
+ const alias = aliasDict['./'];
866
+ if (option?.deletePhysically) {
867
+ (0, assert_1.default)(!updateText, 'physically delete does not support setting trigger data');
868
+ let sql = `delete ${alias} from ${fromText} `;
869
+ if (filterText) {
870
+ sql += ` where ${filterText}`;
871
+ }
872
+ if (sorterText) {
873
+ sql += ` order by ${sorterText}`;
874
+ }
875
+ if (typeof indexFrom === 'number') {
876
+ (0, assert_1.default)(typeof count === 'number');
877
+ sql += ` limit ${indexFrom}, ${count}`;
878
+ }
879
+ return sql;
880
+ }
881
+ const now = Date.now();
882
+ const updateText2 = updateText ? `${updateText}, \`${alias}\`.\`$$deleteAt$$\` = '${now}'` : `\`${alias}\`.\`$$deleteAt$$\` = '${now}'`;
883
+ let sql = `update ${fromText} set ${updateText2}`;
884
+ if (filterText) {
885
+ sql += ` where ${filterText}`;
886
+ }
887
+ if (sorterText) {
888
+ sql += ` order by ${sorterText}`;
889
+ }
890
+ if (typeof indexFrom === 'number') {
891
+ (0, assert_1.default)(typeof count === 'number');
892
+ sql += ` limit ${indexFrom}, ${count}`;
893
+ }
894
+ return sql;
895
+ }
896
+ }
897
+ exports.MySqlTranslator = MySqlTranslator;