@spinajs/orm-sql 2.0.481 → 2.0.484
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/README.md +61 -3
- package/lib/cjs/compilers.d.ts +79 -3
- package/lib/cjs/compilers.d.ts.map +1 -1
- package/lib/cjs/compilers.js +243 -105
- package/lib/cjs/compilers.js.map +1 -1
- package/lib/cjs/converters.d.ts.map +1 -1
- package/lib/cjs/converters.js +1 -4
- package/lib/cjs/converters.js.map +1 -1
- package/lib/cjs/index.d.ts +13 -0
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +31 -2
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/statements.d.ts.map +1 -1
- package/lib/cjs/statements.js +84 -24
- package/lib/cjs/statements.js.map +1 -1
- package/lib/mjs/compilers.d.ts +79 -3
- package/lib/mjs/compilers.d.ts.map +1 -1
- package/lib/mjs/compilers.js +241 -106
- package/lib/mjs/compilers.js.map +1 -1
- package/lib/mjs/converters.d.ts.map +1 -1
- package/lib/mjs/converters.js +1 -4
- package/lib/mjs/converters.js.map +1 -1
- package/lib/mjs/index.d.ts +13 -0
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +31 -2
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/statements.d.ts.map +1 -1
- package/lib/mjs/statements.js +85 -25
- package/lib/mjs/statements.js.map +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +14 -14
package/lib/mjs/compilers.js
CHANGED
|
@@ -12,19 +12,62 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
12
12
|
/* eslint-disable @typescript-eslint/no-empty-interface */
|
|
13
13
|
/* eslint-disable prettier/prettier */
|
|
14
14
|
import { InvalidOperation, InvalidArgument } from '@spinajs/exceptions';
|
|
15
|
-
import { LimitBuilder, DropTableQueryBuilder, AlterColumnQueryBuilder, TableCloneQueryCompiler, OnDuplicateQueryBuilder, DeleteQueryBuilder, LimitQueryCompiler, InsertQueryBuilder, OrderByBuilder, SelectQueryBuilder, UpdateQueryBuilder, SelectQueryCompiler, TableQueryCompiler, TableQueryBuilder, ColumnQueryBuilder, ColumnQueryCompiler, RawQuery, OrderByQueryCompiler, OnDuplicateQueryCompiler, IndexQueryCompiler, IndexQueryBuilder, ForeignKeyBuilder, ForeignKeyQueryCompiler, AlterTableQueryBuilder, CloneTableQueryBuilder, AlterTableQueryCompiler, ColumnAlterationType, AlterColumnQueryCompiler, TableAliasCompiler, DropTableCompiler, DropEventQueryBuilder, TableHistoryQueryCompiler, EventQueryBuilder, LazyQueryStatement, RawSchemaQueryCompiler, RawSchemaQueryBuilder, DropViewQueryBuilder, DropViewCompiler } from '@spinajs/orm';
|
|
15
|
+
import { LimitBuilder, DropTableQueryBuilder, AlterColumnQueryBuilder, TableCloneQueryCompiler, OnDuplicateQueryBuilder, DeleteQueryBuilder, LimitQueryCompiler, InsertQueryBuilder, OrderByBuilder, SelectQueryBuilder, UpdateQueryBuilder, SelectQueryCompiler, TableQueryCompiler, TableQueryBuilder, ColumnQueryBuilder, ColumnQueryCompiler, RawQuery, OrderByQueryCompiler, OnDuplicateQueryCompiler, IndexQueryCompiler, IndexQueryBuilder, ForeignKeyBuilder, ForeignKeyQueryCompiler, AlterTableQueryBuilder, CloneTableQueryBuilder, AlterTableQueryCompiler, ColumnAlterationType, AlterColumnQueryCompiler, TableAliasCompiler, DropTableCompiler, DropEventQueryBuilder, TableHistoryQueryCompiler, EventQueryBuilder, LazyQueryStatement, WhereBoolean, RawSchemaQueryCompiler, RawSchemaQueryBuilder, DropViewQueryBuilder, DropViewCompiler } from '@spinajs/orm';
|
|
16
16
|
import { use } from 'typescript-mix';
|
|
17
17
|
import { NewInstance, Inject, Container } from '@spinajs/di';
|
|
18
18
|
import _ from 'lodash';
|
|
19
|
+
/**
|
|
20
|
+
* Central identifier escaping for the MySQL-flavoured dialect.
|
|
21
|
+
*
|
|
22
|
+
* Wraps a table/column/alias/schema name in backticks and escapes any embedded
|
|
23
|
+
* backtick by doubling it (the MySQL rule). For a normal identifier (no special
|
|
24
|
+
* characters) the output is byte-identical to the previous raw interpolation
|
|
25
|
+
* (`` `${name}` ``), so existing generated SQL is unchanged; only names that
|
|
26
|
+
* actually contain a backtick produce different — and now safe — output.
|
|
27
|
+
*
|
|
28
|
+
* Dialects that quote differently (e.g. `[name]` with `]`-doubling) override
|
|
29
|
+
* this by shadowing the relevant compiler/statement, the same way
|
|
30
|
+
* {@link SqlTableAliasCompiler} is already overridden per driver.
|
|
31
|
+
*/
|
|
32
|
+
export function escapeIdentifier(name) {
|
|
33
|
+
return '`' + String(name).replace(/`/g, '``') + '`';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Escapes a possibly schema-qualified table name, quoting each dot-separated part
|
|
37
|
+
* on its own: `schema.table` becomes `` `schema`.`table` ``, not
|
|
38
|
+
* `` `schema.table` `` — the latter is a single identifier and MySQL rejects it
|
|
39
|
+
* with "Failed to open the referenced table".
|
|
40
|
+
*
|
|
41
|
+
* Needed because {@link TableQueryBuilder.references} takes the parent table as one
|
|
42
|
+
* string, so cross-schema foreign keys arrive here already qualified. A dot is
|
|
43
|
+
* therefore read as a qualifier separator; a table whose name genuinely contains a
|
|
44
|
+
* dot cannot be expressed through that API (it never could).
|
|
45
|
+
*/
|
|
46
|
+
export function escapeQualifiedIdentifier(name) {
|
|
47
|
+
return String(name)
|
|
48
|
+
.split('.')
|
|
49
|
+
.map((part) => escapeIdentifier(part))
|
|
50
|
+
.join('.');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Escapes a DDL string *literal* (SET/ENUM member, COMMENT, CHARACTER SET,
|
|
54
|
+
* COLLATE, string DEFAULT). These are single-quoted string literals, NOT
|
|
55
|
+
* identifiers, so the correct escaping is doubling embedded single quotes.
|
|
56
|
+
* Returns the value WITHOUT the surrounding quotes so callers keep their exact
|
|
57
|
+
* quoting; for a value with no quote the output is byte-identical.
|
|
58
|
+
*/
|
|
59
|
+
export function escapeStringLiteral(value) {
|
|
60
|
+
return String(value).replace(/'/g, "''");
|
|
61
|
+
}
|
|
19
62
|
let SqlTableAliasCompiler = class SqlTableAliasCompiler {
|
|
20
63
|
compile(builder, tbl) {
|
|
21
64
|
let table = '';
|
|
22
65
|
if (builder.Database) {
|
|
23
|
-
table +=
|
|
66
|
+
table += `${escapeIdentifier(builder.Database)}.`;
|
|
24
67
|
}
|
|
25
|
-
table +=
|
|
68
|
+
table += escapeIdentifier(tbl ? tbl : builder.Table);
|
|
26
69
|
if (builder.TableAlias) {
|
|
27
|
-
table += ` as
|
|
70
|
+
table += ` as ${escapeIdentifier(builder.TableAlias)}`;
|
|
28
71
|
}
|
|
29
72
|
return table;
|
|
30
73
|
}
|
|
@@ -38,7 +81,7 @@ let SqlQueryCompiler = class SqlQueryCompiler extends SelectQueryCompiler {
|
|
|
38
81
|
super();
|
|
39
82
|
this._builder = _builder;
|
|
40
83
|
this._container = _container;
|
|
41
|
-
if (_builder === null
|
|
84
|
+
if (_builder === null || _builder === undefined) {
|
|
42
85
|
throw new InvalidArgument('builder cannot be null or undefined');
|
|
43
86
|
}
|
|
44
87
|
}
|
|
@@ -66,11 +109,11 @@ let SqlOrderByQueryCompiler = class SqlOrderByQueryCompiler extends OrderByQuery
|
|
|
66
109
|
this._builder = builder;
|
|
67
110
|
}
|
|
68
111
|
compile() {
|
|
69
|
-
const
|
|
112
|
+
const sorts = this._builder.getSorts();
|
|
70
113
|
let stmt = '';
|
|
71
114
|
const bindings = [];
|
|
72
|
-
if (
|
|
73
|
-
stmt = ` ORDER BY
|
|
115
|
+
if (sorts.length > 0) {
|
|
116
|
+
stmt = ` ORDER BY ${sorts.map((s) => `${escapeIdentifier(s.column)} ${s.order}`).join(', ')}`;
|
|
74
117
|
}
|
|
75
118
|
return {
|
|
76
119
|
bindings,
|
|
@@ -111,7 +154,7 @@ let SqlForeignKeyQueryCompiler = class SqlForeignKeyQueryCompiler {
|
|
|
111
154
|
}
|
|
112
155
|
}
|
|
113
156
|
compile() {
|
|
114
|
-
const exprr = `FOREIGN KEY (${this._builder.ForeignKeyField}) REFERENCES ${this._builder.Table}(${this._builder.PrimaryKey}) ON DELETE ${this._builder.OnDeleteAction} ON UPDATE ${this._builder.OnUpdateAction}`;
|
|
157
|
+
const exprr = `FOREIGN KEY (${escapeIdentifier(this._builder.ForeignKeyField)}) REFERENCES ${escapeQualifiedIdentifier(this._builder.Table)}(${escapeIdentifier(this._builder.PrimaryKey)}) ON DELETE ${this._builder.OnDeleteAction} ON UPDATE ${this._builder.OnUpdateAction}`;
|
|
115
158
|
return {
|
|
116
159
|
bindings: [],
|
|
117
160
|
expression: exprr,
|
|
@@ -194,23 +237,24 @@ SqlColumnsCompiler = __decorate([
|
|
|
194
237
|
export { SqlColumnsCompiler };
|
|
195
238
|
let SqlWhereCompiler = class SqlWhereCompiler {
|
|
196
239
|
where(builder) {
|
|
197
|
-
const where = [];
|
|
198
240
|
const bindings = [];
|
|
199
|
-
|
|
200
|
-
builder
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
241
|
+
// Lazy statements must be built FIRST: their build() may have side effects
|
|
242
|
+
// that append further statements to the builder (e.g. a correlated EXISTS
|
|
243
|
+
// sub-query registers its correlation predicate lazily). Building them now and
|
|
244
|
+
// reusing the result guarantees the side effect runs exactly once, and lets the
|
|
245
|
+
// non-lazy pass below pick up any statements the lazy build appended. The lazy
|
|
246
|
+
// results are then emitted after the non-lazy ones (preserving legacy ordering).
|
|
247
|
+
const lazyEntries = builder.Statements.filter((x) => x instanceof LazyQueryStatement).map((stmt) => ({
|
|
248
|
+
boolean: stmt.Boolean,
|
|
249
|
+
result: stmt.build(),
|
|
250
|
+
}));
|
|
251
|
+
const nonLazyEntries = builder.Statements
|
|
252
|
+
.filter((x) => !x.IsAggregate)
|
|
253
|
+
.filter((x) => !(x instanceof LazyQueryStatement))
|
|
254
|
+
.map((stmt) => ({ boolean: stmt.Boolean, result: stmt.build() }));
|
|
211
255
|
return {
|
|
212
256
|
bindings,
|
|
213
|
-
expression:
|
|
257
|
+
expression: joinBuiltStatements(nonLazyEntries.concat(lazyEntries), bindings),
|
|
214
258
|
};
|
|
215
259
|
}
|
|
216
260
|
};
|
|
@@ -218,23 +262,41 @@ SqlWhereCompiler = __decorate([
|
|
|
218
262
|
NewInstance()
|
|
219
263
|
], SqlWhereCompiler);
|
|
220
264
|
export { SqlWhereCompiler };
|
|
265
|
+
/**
|
|
266
|
+
* Joins a list of already-built where/having statements into a single SQL
|
|
267
|
+
* expression, prefixing every statement after the first with its OWN boolean
|
|
268
|
+
* connector. This yields correct mixed grouping such as
|
|
269
|
+
* `a AND b OR c` for `where(a).where(b).orWhere(c)`, instead of applying a single
|
|
270
|
+
* builder-level connector uniformly. Bindings are collected in statement order.
|
|
271
|
+
*/
|
|
272
|
+
function joinBuiltStatements(statements, bindings) {
|
|
273
|
+
const parts = [];
|
|
274
|
+
statements.forEach(({ boolean, result }) => {
|
|
275
|
+
if (Array.isArray(result.Bindings)) {
|
|
276
|
+
bindings.push(...result.Bindings);
|
|
277
|
+
}
|
|
278
|
+
// A single statement always builds to one fragment; guard multi-fragment
|
|
279
|
+
// statements by joining them with AND (they carry no per-fragment connector).
|
|
280
|
+
const fragment = result.Statements.join(' AND ');
|
|
281
|
+
if (fragment === '') {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (parts.length === 0) {
|
|
285
|
+
parts.push(fragment);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
parts.push(`${(boolean ?? WhereBoolean.AND).toUpperCase()} ${fragment}`);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
return parts.join(' ');
|
|
292
|
+
}
|
|
221
293
|
let SqlHavingCompiler = class SqlHavingCompiler {
|
|
222
294
|
having(builder) {
|
|
223
|
-
const where = [];
|
|
224
295
|
const bindings = [];
|
|
225
|
-
builder.Statements.filter((x) => x.IsAggregate)
|
|
226
|
-
.map((x) => {
|
|
227
|
-
return x.build();
|
|
228
|
-
})
|
|
229
|
-
.forEach((r) => {
|
|
230
|
-
where.push(...r.Statements);
|
|
231
|
-
if (Array.isArray(r.Bindings)) {
|
|
232
|
-
bindings.push(...r.Bindings);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
296
|
+
const aggregates = builder.Statements.filter((x) => x.IsAggregate).map((stmt) => ({ boolean: stmt.Boolean, result: stmt.build() }));
|
|
235
297
|
return {
|
|
236
298
|
bindings,
|
|
237
|
-
expression:
|
|
299
|
+
expression: joinBuiltStatements(aggregates, bindings),
|
|
238
300
|
};
|
|
239
301
|
}
|
|
240
302
|
};
|
|
@@ -342,7 +404,7 @@ let SqlUpdateQueryCompiler = class SqlUpdateQueryCompiler extends SqlQueryCompil
|
|
|
342
404
|
const exprr = [];
|
|
343
405
|
for (const prop of Object.keys(this._builder.Value)) {
|
|
344
406
|
const v = this._builder.Value[`${prop}`];
|
|
345
|
-
exprr.push(
|
|
407
|
+
exprr.push(`${escapeIdentifier(prop)} = ?`);
|
|
346
408
|
bindings = bindings.concat(this.tryConvertValue(v));
|
|
347
409
|
}
|
|
348
410
|
return {
|
|
@@ -409,22 +471,22 @@ let SqlOnDuplicateQueryCompiler = class SqlOnDuplicateQueryCompiler {
|
|
|
409
471
|
.getColumnsToUpdate()
|
|
410
472
|
.map((c) => {
|
|
411
473
|
if (_.isString(c)) {
|
|
412
|
-
|
|
474
|
+
// Reference the row being inserted via VALUES(col) instead of
|
|
475
|
+
// re-binding one specific row's value. Binding `parent.Values[0]`
|
|
476
|
+
// applied the FIRST row's values to every conflicting row in a
|
|
477
|
+
// multi row upsert.
|
|
478
|
+
return `${escapeIdentifier(c)} = VALUES(${escapeIdentifier(c)})`;
|
|
413
479
|
}
|
|
414
480
|
else {
|
|
415
481
|
return c.Query;
|
|
416
482
|
}
|
|
417
483
|
})
|
|
418
484
|
.join(',');
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
return c.Bindings;
|
|
427
|
-
}
|
|
485
|
+
// Only RawQuery update columns contribute bindings - VALUES(col) needs
|
|
486
|
+
// none. flatMap keeps raw bindings flat; returning the array itself
|
|
487
|
+
// nested them and shifted every following placeholder.
|
|
488
|
+
const bindings = _.flatMap(this._builder.getColumnsToUpdate(), (c) => {
|
|
489
|
+
return _.isString(c) ? [] : c.Bindings ?? [];
|
|
428
490
|
});
|
|
429
491
|
return {
|
|
430
492
|
bindings,
|
|
@@ -445,7 +507,7 @@ let SqlIndexQueryCompiler = class SqlIndexQueryCompiler extends IndexQueryCompil
|
|
|
445
507
|
compile() {
|
|
446
508
|
return {
|
|
447
509
|
bindings: [],
|
|
448
|
-
expression: `CREATE ${this._builder.Unique ? 'UNIQUE ' : ''}INDEX
|
|
510
|
+
expression: `CREATE ${this._builder.Unique ? 'UNIQUE ' : ''}INDEX ${escapeIdentifier(this._builder.Name)} ON ${escapeIdentifier(this._builder.Table)} (${this._builder.Columns.map((c) => escapeIdentifier(c)).join(',')});`,
|
|
449
511
|
};
|
|
450
512
|
}
|
|
451
513
|
};
|
|
@@ -477,31 +539,54 @@ let SqlInsertQueryCompiler = class SqlInsertQueryCompiler extends SqlQueryCompil
|
|
|
477
539
|
expression: '',
|
|
478
540
|
};
|
|
479
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* Indices of the columns that take part in this INSERT.
|
|
544
|
+
*
|
|
545
|
+
* An auto increment primary key is omitted only when NO row supplies a value
|
|
546
|
+
* for it. If at least one row supplies one the column stays, and the rows
|
|
547
|
+
* that don't emit NULL so the engine assigns the value.
|
|
548
|
+
*
|
|
549
|
+
* Both {@link columns} and {@link values} MUST derive their shape from this
|
|
550
|
+
* single decision - deciding per-row in one place and per-batch in the other
|
|
551
|
+
* produced value tuples whose arity did not match the column list.
|
|
552
|
+
*/
|
|
553
|
+
keptColumnIndices() {
|
|
554
|
+
return this._builder
|
|
555
|
+
.getColumns()
|
|
556
|
+
.map((c, i) => ({ c, i }))
|
|
557
|
+
.filter(({ c, i }) => {
|
|
558
|
+
const descriptor = c.Descriptor;
|
|
559
|
+
if (descriptor && descriptor.AutoIncrement && descriptor.PrimaryKey) {
|
|
560
|
+
return this._builder.Values.some((x) => x[i] !== undefined && x[i] !== null);
|
|
561
|
+
}
|
|
562
|
+
return true;
|
|
563
|
+
})
|
|
564
|
+
.map(({ i }) => i);
|
|
565
|
+
}
|
|
480
566
|
values() {
|
|
481
567
|
if (this._builder.Values.length === 0) {
|
|
482
568
|
throw new InvalidArgument('values count invalid');
|
|
483
569
|
}
|
|
570
|
+
const kept = this.keptColumnIndices();
|
|
484
571
|
const bindings = [];
|
|
485
572
|
let data = 'VALUES ';
|
|
486
573
|
data += this._builder.Values.map((val) => {
|
|
487
|
-
const toInsert =
|
|
488
|
-
|
|
574
|
+
const toInsert = kept.map((i) => {
|
|
575
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
576
|
+
const v = val[i];
|
|
489
577
|
// eslint-disable-next-line security/detect-object-injection
|
|
490
578
|
const descriptor = this._builder.getColumns()[i].Descriptor;
|
|
491
579
|
if (descriptor) {
|
|
492
580
|
if (!descriptor.Nullable && (v === null || v === undefined) && !descriptor.AutoIncrement) {
|
|
493
581
|
throw new InvalidArgument(`value column ${descriptor.Name} cannot be null`);
|
|
494
582
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
return
|
|
583
|
+
// Auto increment PK this row does not supply. NULL (not DEFAULT) lets
|
|
584
|
+
// the engine assign it and is portable - sqlite does not accept the
|
|
585
|
+
// DEFAULT keyword inside a VALUES tuple.
|
|
586
|
+
if (descriptor.AutoIncrement && descriptor.PrimaryKey && (v === undefined || v === null)) {
|
|
587
|
+
return 'NULL';
|
|
500
588
|
}
|
|
501
589
|
}
|
|
502
|
-
return true;
|
|
503
|
-
})
|
|
504
|
-
.map((v) => {
|
|
505
590
|
if (v === undefined) {
|
|
506
591
|
return 'DEFAULT';
|
|
507
592
|
}
|
|
@@ -519,23 +604,15 @@ let SqlInsertQueryCompiler = class SqlInsertQueryCompiler extends SqlQueryCompil
|
|
|
519
604
|
};
|
|
520
605
|
}
|
|
521
606
|
columns() {
|
|
522
|
-
const columns = this.
|
|
523
|
-
.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (descriptor && descriptor.AutoIncrement && descriptor.PrimaryKey) {
|
|
527
|
-
if (this._builder.Values.every((x) => x[i] !== undefined && x[i] !== null)) {
|
|
528
|
-
return true;
|
|
529
|
-
}
|
|
530
|
-
return false;
|
|
531
|
-
}
|
|
532
|
-
return true;
|
|
533
|
-
})
|
|
534
|
-
.map((c) => {
|
|
535
|
-
return c.Column;
|
|
607
|
+
const columns = this.keptColumnIndices()
|
|
608
|
+
.map((i) => {
|
|
609
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
610
|
+
return this._builder.getColumns()[i].Column;
|
|
536
611
|
})
|
|
537
612
|
.map((c) => {
|
|
538
|
-
|
|
613
|
+
// RawQuery columns carry raw SQL - preserve the existing (unescaped)
|
|
614
|
+
// backtick wrapping. String columns go through the identifier escaper.
|
|
615
|
+
return c instanceof RawQuery ? `\`${c.Query}\`` : escapeIdentifier(c);
|
|
539
616
|
});
|
|
540
617
|
if (columns.length === 0) {
|
|
541
618
|
throw new InvalidArgument('invalid column count');
|
|
@@ -623,10 +700,22 @@ let SqlAlterTableQueryCompiler = class SqlAlterTableQueryCompiler extends AlterT
|
|
|
623
700
|
}
|
|
624
701
|
if (this.builder.Columns.length !== 0) {
|
|
625
702
|
_outputs = _outputs.concat(this.builder.Columns.map((c) => {
|
|
626
|
-
|
|
703
|
+
// keep the compiler instance around - it carries the dialect's answer to
|
|
704
|
+
// "is my expression already a complete statement ?" (see IsStandaloneStatement)
|
|
705
|
+
const compiler = this.container.resolve(AlterColumnQueryCompiler, [c]);
|
|
706
|
+
return { compiler, output: compiler.compile() };
|
|
707
|
+
})
|
|
708
|
+
// a driver may legitimately skip an alteration by compiling it to nothing.
|
|
709
|
+
// emitting it anyway would yield a dangling `ALTER TABLE \`x\`` - invalid SQL.
|
|
710
|
+
.filter(({ output }) => (output.expression ?? '').trim().length !== 0)
|
|
711
|
+
.map(({ compiler, output }) => {
|
|
712
|
+
// some dialects cannot express an alteration as a suffix of `ALTER TABLE x`
|
|
713
|
+
// eg. MSSQL renames a column with `EXEC sp_rename '[t].[old]', 'new', 'COLUMN'`,
|
|
714
|
+
// which is a complete statement and must be emitted verbatim.
|
|
715
|
+
const standalone = compiler.IsStandaloneStatement === true;
|
|
627
716
|
return {
|
|
628
|
-
bindings:
|
|
629
|
-
expression: `${_table} ${
|
|
717
|
+
bindings: output.bindings,
|
|
718
|
+
expression: standalone ? output.expression : `${_table} ${output.expression}`,
|
|
630
719
|
};
|
|
631
720
|
}));
|
|
632
721
|
}
|
|
@@ -663,7 +752,7 @@ let SqlTableCloneQueryCompiler = class SqlTableCloneQueryCompiler extends TableC
|
|
|
663
752
|
// if no filter is provided, copy all the data
|
|
664
753
|
expression: `SELECT * FROM ${_tblName}`,
|
|
665
754
|
};
|
|
666
|
-
const fExprr = `INSERT INTO
|
|
755
|
+
const fExprr = `INSERT INTO ${escapeIdentifier(this.builder.Table)} ${fOut.expression}`;
|
|
667
756
|
return [
|
|
668
757
|
out1,
|
|
669
758
|
{
|
|
@@ -845,7 +934,7 @@ let SqlTableQueryCompiler = class SqlTableQueryCompiler extends TableQueryCompil
|
|
|
845
934
|
}
|
|
846
935
|
_primaryKeys() {
|
|
847
936
|
const _keys = this.builder.Columns.filter((x) => x.PrimaryKey)
|
|
848
|
-
.map((c) =>
|
|
937
|
+
.map((c) => escapeIdentifier(c.Name))
|
|
849
938
|
.join(',');
|
|
850
939
|
if (!_.isEmpty(_keys)) {
|
|
851
940
|
return `PRIMARY KEY (${_keys})`;
|
|
@@ -866,7 +955,7 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
|
|
|
866
955
|
constructor(builder) {
|
|
867
956
|
this.builder = builder;
|
|
868
957
|
this._statementsMappings = {
|
|
869
|
-
set: (builder) => `SET(${builder.Args[0].map((a) => `'${a}
|
|
958
|
+
set: (builder) => `SET(${builder.Args[0].map((a) => `'${escapeStringLiteral(a)}'`).join(',')})`,
|
|
870
959
|
string: (builder) => `VARCHAR(${builder.Args[0] ? builder.Args[0] : 255})`,
|
|
871
960
|
boolean: () => `BOOLEAN`,
|
|
872
961
|
float: (builder) => {
|
|
@@ -876,8 +965,8 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
|
|
|
876
965
|
},
|
|
877
966
|
double: (builder) => this._statementsMappings.float(builder),
|
|
878
967
|
decimal: (builder) => this._statementsMappings.float(builder),
|
|
879
|
-
enum: (builder) => `${builder.Type.toUpperCase()}(${builder.Args[0].map((a) => `'${a}'`).join(',')})`,
|
|
880
|
-
binary: (builder) => `BINARY(${builder.Args[0] ?? 255}`,
|
|
968
|
+
enum: (builder) => `${builder.Type.toUpperCase()}(${builder.Args[0].map((a) => `'${escapeStringLiteral(a)}'`).join(',')})`,
|
|
969
|
+
binary: (builder) => `BINARY(${builder.Args[0] ?? 255})`,
|
|
881
970
|
smallint: (builder) => builder.Type.toUpperCase(),
|
|
882
971
|
tinyint: (builder) => builder.Type.toUpperCase(),
|
|
883
972
|
mediumint: (builder) => builder.Type.toUpperCase(),
|
|
@@ -898,12 +987,12 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
|
|
|
898
987
|
longblob: (builder) => builder.Type.toUpperCase(),
|
|
899
988
|
// COLUMN ADDITIONA PROPS
|
|
900
989
|
unsigned: () => 'UNSIGNED',
|
|
901
|
-
charset: (builder) => `CHARACTER SET '${builder.Charset}'`,
|
|
902
|
-
collation: (builder) => `COLLATE '${builder.Collation}'`,
|
|
990
|
+
charset: (builder) => `CHARACTER SET '${escapeStringLiteral(builder.Charset)}'`,
|
|
991
|
+
collation: (builder) => `COLLATE '${escapeStringLiteral(builder.Collation)}'`,
|
|
903
992
|
notnull: () => `NOT NULL`,
|
|
904
993
|
default: () => this._defaultCompiler(),
|
|
905
994
|
autoincrement: () => `AUTO_INCREMENT`,
|
|
906
|
-
comment: (builder) => `COMMENT '${builder.Comment}'`,
|
|
995
|
+
comment: (builder) => `COMMENT '${escapeStringLiteral(builder.Comment)}'`,
|
|
907
996
|
};
|
|
908
997
|
if (!builder) {
|
|
909
998
|
throw new Error('column query builder cannot be null');
|
|
@@ -911,7 +1000,7 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
|
|
|
911
1000
|
}
|
|
912
1001
|
compile() {
|
|
913
1002
|
const _stmt = [];
|
|
914
|
-
_stmt.push(
|
|
1003
|
+
_stmt.push(escapeIdentifier(this.builder.Name));
|
|
915
1004
|
_stmt.push(this._statementsMappings[this.builder.Type](this.builder));
|
|
916
1005
|
if (this.builder.Unsigned) {
|
|
917
1006
|
_stmt.push(this._statementsMappings.unsigned());
|
|
@@ -948,7 +1037,7 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
|
|
|
948
1037
|
return _stmt;
|
|
949
1038
|
}
|
|
950
1039
|
if (_.isString(this.builder.Default.Value)) {
|
|
951
|
-
_stmt = `DEFAULT '${this.builder.Default.Value.trim()}'`;
|
|
1040
|
+
_stmt = `DEFAULT '${escapeStringLiteral(this.builder.Default.Value.trim())}'`;
|
|
952
1041
|
}
|
|
953
1042
|
else if (_.isNumber(this.builder.Default.Value)) {
|
|
954
1043
|
_stmt = `DEFAULT ${this.builder.Default.Value}`;
|
|
@@ -964,38 +1053,84 @@ SqlColumnQueryCompiler = __decorate([
|
|
|
964
1053
|
__metadata("design:paramtypes", [ColumnQueryBuilder])
|
|
965
1054
|
], SqlColumnQueryCompiler);
|
|
966
1055
|
export { SqlColumnQueryCompiler };
|
|
967
|
-
let SqlAlterColumnQueryCompiler = class SqlAlterColumnQueryCompiler extends
|
|
968
|
-
constructor(builder) {
|
|
969
|
-
super(
|
|
1056
|
+
let SqlAlterColumnQueryCompiler = class SqlAlterColumnQueryCompiler extends AlterColumnQueryCompiler {
|
|
1057
|
+
constructor(container, builder) {
|
|
1058
|
+
super();
|
|
1059
|
+
this.container = container;
|
|
1060
|
+
this.builder = builder;
|
|
1061
|
+
if (!builder) {
|
|
1062
|
+
throw new InvalidArgument('builder cannot be null or undefined');
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Renders the column body using the DRIVER's own column compiler, resolved
|
|
1067
|
+
* from the container (late binding) - exactly like the CREATE TABLE path does.
|
|
1068
|
+
* Previously this was `super.compile()`, which statically bound every driver
|
|
1069
|
+
* to orm-sql's MySQL dialect.
|
|
1070
|
+
*/
|
|
1071
|
+
_columnDefinition() {
|
|
1072
|
+
return this.container.resolve(ColumnQueryCompiler, [this.builder]).compile();
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Whether the expression produced by `compile()` is already a complete, standalone
|
|
1076
|
+
* statement. When true, SqlAlterTableQueryCompiler emits it verbatim instead of
|
|
1077
|
+
* prefixing it with `ALTER TABLE <table> `.
|
|
1078
|
+
*
|
|
1079
|
+
* Needed by dialects whose alteration is not expressible as a suffix of ALTER TABLE,
|
|
1080
|
+
* eg. MSSQL's `EXEC sp_rename '[t].[old]', 'new', 'COLUMN'`.
|
|
1081
|
+
*
|
|
1082
|
+
* It is read AFTER `compile()`, so a subclass may decide per `builder.AlterType`.
|
|
1083
|
+
* Defaults to false - MySQL (this class) always emits a suffix.
|
|
1084
|
+
*/
|
|
1085
|
+
get IsStandaloneStatement() {
|
|
1086
|
+
return false;
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Dialect hooks. Returning `null` means "emit no statement at all" - the
|
|
1090
|
+
* parent AlterTableQueryCompiler filters such columns out.
|
|
1091
|
+
*/
|
|
1092
|
+
_add(definition) {
|
|
1093
|
+
// escapeIdentifier, not raw backticks: an identifier containing a backtick would
|
|
1094
|
+
// otherwise terminate the quote and splice arbitrary SQL into the DDL ( A3 ).
|
|
1095
|
+
return `ADD ${definition} ${this.builder.AfterColumn ? `AFTER ${escapeIdentifier(this.builder.AfterColumn)}` : ''}`;
|
|
1096
|
+
}
|
|
1097
|
+
_modify(definition) {
|
|
1098
|
+
return `MODIFY ${definition}`;
|
|
1099
|
+
}
|
|
1100
|
+
_rename() {
|
|
1101
|
+
return `RENAME COLUMN ${escapeIdentifier(this.builder.OldName)} TO ${escapeIdentifier(this.builder.Name)}`;
|
|
970
1102
|
}
|
|
971
1103
|
compile() {
|
|
972
|
-
|
|
973
|
-
if (builder.AlterType === ColumnAlterationType.Rename) {
|
|
974
|
-
const bld = this.builder;
|
|
1104
|
+
// rename never renders a column body, so it must not resolve one
|
|
1105
|
+
if (this.builder.AlterType === ColumnAlterationType.Rename) {
|
|
975
1106
|
return {
|
|
976
1107
|
bindings: [],
|
|
977
|
-
expression:
|
|
1108
|
+
expression: this._rename() ?? '',
|
|
978
1109
|
};
|
|
979
1110
|
}
|
|
980
|
-
const cDefinition =
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
expression
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
expression
|
|
991
|
-
|
|
1111
|
+
const cDefinition = this._columnDefinition();
|
|
1112
|
+
let expression;
|
|
1113
|
+
switch (this.builder.AlterType) {
|
|
1114
|
+
case ColumnAlterationType.Add:
|
|
1115
|
+
expression = this._add(cDefinition.expression);
|
|
1116
|
+
break;
|
|
1117
|
+
case ColumnAlterationType.Modify:
|
|
1118
|
+
expression = this._modify(cDefinition.expression);
|
|
1119
|
+
break;
|
|
1120
|
+
default:
|
|
1121
|
+
expression = cDefinition.expression;
|
|
1122
|
+
break;
|
|
992
1123
|
}
|
|
993
|
-
return
|
|
1124
|
+
return {
|
|
1125
|
+
bindings: cDefinition.bindings,
|
|
1126
|
+
expression: expression ?? '',
|
|
1127
|
+
};
|
|
994
1128
|
}
|
|
995
1129
|
};
|
|
996
1130
|
SqlAlterColumnQueryCompiler = __decorate([
|
|
997
1131
|
NewInstance(),
|
|
998
|
-
|
|
1132
|
+
Inject(Container),
|
|
1133
|
+
__metadata("design:paramtypes", [Container, AlterColumnQueryBuilder])
|
|
999
1134
|
], SqlAlterColumnQueryCompiler);
|
|
1000
1135
|
export { SqlAlterColumnQueryCompiler };
|
|
1001
1136
|
let SqlEventQueryCompiler = class SqlEventQueryCompiler extends SqlQueryCompiler {
|