@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.
@@ -13,6 +13,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.SqlRawSchemaQueryCompiler = exports.SqlDropEventQueryCompiler = exports.SqlEventQueryCompiler = exports.SqlAlterColumnQueryCompiler = exports.SqlColumnQueryCompiler = exports.SqlTableQueryCompiler = exports.SqlTableHistoryQueryCompiler = exports.SqlTruncateTableQueryCompiler = exports.SqlTableCloneQueryCompiler = exports.SqlAlterTableQueryCompiler = exports.SqlDropViewQueryCompiler = exports.SqlDropTableQueryCompiler = exports.SqlInsertQueryCompiler = exports.SqlIndexQueryCompiler = exports.SqlOnDuplicateQueryCompiler = exports.SqlDeleteQueryCompiler = exports.SqlUpdateQueryCompiler = exports.SqlSelectQueryCompiler = exports.SqlJoinCompiler = exports.SqlHavingCompiler = exports.SqlWhereCompiler = exports.SqlColumnsCompiler = exports.SqlGroupByCompiler = exports.SqlLimitQueryCompiler = exports.SqlForeignKeyQueryCompiler = exports.SqlWithRecursiveCompiler = exports.SqlOrderByQueryCompiler = exports.SqlQueryCompiler = exports.SqlTableAliasCompiler = void 0;
16
+ exports.escapeIdentifier = escapeIdentifier;
17
+ exports.escapeQualifiedIdentifier = escapeQualifiedIdentifier;
18
+ exports.escapeStringLiteral = escapeStringLiteral;
16
19
  /* eslint-disable @typescript-eslint/no-unsafe-call */
17
20
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
18
21
  /* eslint-disable @typescript-eslint/no-empty-interface */
@@ -22,15 +25,58 @@ const orm_1 = require("@spinajs/orm");
22
25
  const typescript_mix_1 = require("typescript-mix");
23
26
  const di_1 = require("@spinajs/di");
24
27
  const lodash_1 = __importDefault(require("lodash"));
28
+ /**
29
+ * Central identifier escaping for the MySQL-flavoured dialect.
30
+ *
31
+ * Wraps a table/column/alias/schema name in backticks and escapes any embedded
32
+ * backtick by doubling it (the MySQL rule). For a normal identifier (no special
33
+ * characters) the output is byte-identical to the previous raw interpolation
34
+ * (`` `${name}` ``), so existing generated SQL is unchanged; only names that
35
+ * actually contain a backtick produce different — and now safe — output.
36
+ *
37
+ * Dialects that quote differently (e.g. `[name]` with `]`-doubling) override
38
+ * this by shadowing the relevant compiler/statement, the same way
39
+ * {@link SqlTableAliasCompiler} is already overridden per driver.
40
+ */
41
+ function escapeIdentifier(name) {
42
+ return '`' + String(name).replace(/`/g, '``') + '`';
43
+ }
44
+ /**
45
+ * Escapes a possibly schema-qualified table name, quoting each dot-separated part
46
+ * on its own: `schema.table` becomes `` `schema`.`table` ``, not
47
+ * `` `schema.table` `` — the latter is a single identifier and MySQL rejects it
48
+ * with "Failed to open the referenced table".
49
+ *
50
+ * Needed because {@link TableQueryBuilder.references} takes the parent table as one
51
+ * string, so cross-schema foreign keys arrive here already qualified. A dot is
52
+ * therefore read as a qualifier separator; a table whose name genuinely contains a
53
+ * dot cannot be expressed through that API (it never could).
54
+ */
55
+ function escapeQualifiedIdentifier(name) {
56
+ return String(name)
57
+ .split('.')
58
+ .map((part) => escapeIdentifier(part))
59
+ .join('.');
60
+ }
61
+ /**
62
+ * Escapes a DDL string *literal* (SET/ENUM member, COMMENT, CHARACTER SET,
63
+ * COLLATE, string DEFAULT). These are single-quoted string literals, NOT
64
+ * identifiers, so the correct escaping is doubling embedded single quotes.
65
+ * Returns the value WITHOUT the surrounding quotes so callers keep their exact
66
+ * quoting; for a value with no quote the output is byte-identical.
67
+ */
68
+ function escapeStringLiteral(value) {
69
+ return String(value).replace(/'/g, "''");
70
+ }
25
71
  let SqlTableAliasCompiler = class SqlTableAliasCompiler {
26
72
  compile(builder, tbl) {
27
73
  let table = '';
28
74
  if (builder.Database) {
29
- table += `\`${builder.Database}\`.`;
75
+ table += `${escapeIdentifier(builder.Database)}.`;
30
76
  }
31
- table += `\`${tbl ? tbl : builder.Table}\``;
77
+ table += escapeIdentifier(tbl ? tbl : builder.Table);
32
78
  if (builder.TableAlias) {
33
- table += ` as \`${builder.TableAlias}\``;
79
+ table += ` as ${escapeIdentifier(builder.TableAlias)}`;
34
80
  }
35
81
  return table;
36
82
  }
@@ -44,7 +90,7 @@ let SqlQueryCompiler = class SqlQueryCompiler extends orm_1.SelectQueryCompiler
44
90
  super();
45
91
  this._builder = _builder;
46
92
  this._container = _container;
47
- if (_builder === null && _builder === undefined) {
93
+ if (_builder === null || _builder === undefined) {
48
94
  throw new exceptions_1.InvalidArgument('builder cannot be null or undefined');
49
95
  }
50
96
  }
@@ -72,11 +118,11 @@ let SqlOrderByQueryCompiler = class SqlOrderByQueryCompiler extends orm_1.OrderB
72
118
  this._builder = builder;
73
119
  }
74
120
  compile() {
75
- const sort = this._builder.getSort();
121
+ const sorts = this._builder.getSorts();
76
122
  let stmt = '';
77
123
  const bindings = [];
78
- if (sort) {
79
- stmt = ` ORDER BY \`${sort.column}\` ${sort.order}`;
124
+ if (sorts.length > 0) {
125
+ stmt = ` ORDER BY ${sorts.map((s) => `${escapeIdentifier(s.column)} ${s.order}`).join(', ')}`;
80
126
  }
81
127
  return {
82
128
  bindings,
@@ -117,7 +163,7 @@ let SqlForeignKeyQueryCompiler = class SqlForeignKeyQueryCompiler {
117
163
  }
118
164
  }
119
165
  compile() {
120
- const exprr = `FOREIGN KEY (${this._builder.ForeignKeyField}) REFERENCES ${this._builder.Table}(${this._builder.PrimaryKey}) ON DELETE ${this._builder.OnDeleteAction} ON UPDATE ${this._builder.OnUpdateAction}`;
166
+ 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}`;
121
167
  return {
122
168
  bindings: [],
123
169
  expression: exprr,
@@ -200,23 +246,24 @@ exports.SqlColumnsCompiler = SqlColumnsCompiler = __decorate([
200
246
  ], SqlColumnsCompiler);
201
247
  let SqlWhereCompiler = class SqlWhereCompiler {
202
248
  where(builder) {
203
- const where = [];
204
249
  const bindings = [];
205
- const lazyBindings = builder.Statements.filter((x) => x instanceof orm_1.LazyQueryStatement).map((x) => x.build());
206
- builder.Statements.filter((x) => !x.IsAggregate).filter(x => !(x instanceof orm_1.LazyQueryStatement))
207
- .map((x) => {
208
- return x.build();
209
- })
210
- .concat(lazyBindings)
211
- .forEach((r) => {
212
- where.push(...r.Statements);
213
- if (Array.isArray(r.Bindings)) {
214
- bindings.push(...r.Bindings);
215
- }
216
- });
250
+ // Lazy statements must be built FIRST: their build() may have side effects
251
+ // that append further statements to the builder (e.g. a correlated EXISTS
252
+ // sub-query registers its correlation predicate lazily). Building them now and
253
+ // reusing the result guarantees the side effect runs exactly once, and lets the
254
+ // non-lazy pass below pick up any statements the lazy build appended. The lazy
255
+ // results are then emitted after the non-lazy ones (preserving legacy ordering).
256
+ const lazyEntries = builder.Statements.filter((x) => x instanceof orm_1.LazyQueryStatement).map((stmt) => ({
257
+ boolean: stmt.Boolean,
258
+ result: stmt.build(),
259
+ }));
260
+ const nonLazyEntries = builder.Statements
261
+ .filter((x) => !x.IsAggregate)
262
+ .filter((x) => !(x instanceof orm_1.LazyQueryStatement))
263
+ .map((stmt) => ({ boolean: stmt.Boolean, result: stmt.build() }));
217
264
  return {
218
265
  bindings,
219
- expression: where.join(` ${builder.Op.toUpperCase()} `),
266
+ expression: joinBuiltStatements(nonLazyEntries.concat(lazyEntries), bindings),
220
267
  };
221
268
  }
222
269
  };
@@ -224,23 +271,41 @@ exports.SqlWhereCompiler = SqlWhereCompiler;
224
271
  exports.SqlWhereCompiler = SqlWhereCompiler = __decorate([
225
272
  (0, di_1.NewInstance)()
226
273
  ], SqlWhereCompiler);
274
+ /**
275
+ * Joins a list of already-built where/having statements into a single SQL
276
+ * expression, prefixing every statement after the first with its OWN boolean
277
+ * connector. This yields correct mixed grouping such as
278
+ * `a AND b OR c` for `where(a).where(b).orWhere(c)`, instead of applying a single
279
+ * builder-level connector uniformly. Bindings are collected in statement order.
280
+ */
281
+ function joinBuiltStatements(statements, bindings) {
282
+ const parts = [];
283
+ statements.forEach(({ boolean, result }) => {
284
+ if (Array.isArray(result.Bindings)) {
285
+ bindings.push(...result.Bindings);
286
+ }
287
+ // A single statement always builds to one fragment; guard multi-fragment
288
+ // statements by joining them with AND (they carry no per-fragment connector).
289
+ const fragment = result.Statements.join(' AND ');
290
+ if (fragment === '') {
291
+ return;
292
+ }
293
+ if (parts.length === 0) {
294
+ parts.push(fragment);
295
+ }
296
+ else {
297
+ parts.push(`${(boolean ?? orm_1.WhereBoolean.AND).toUpperCase()} ${fragment}`);
298
+ }
299
+ });
300
+ return parts.join(' ');
301
+ }
227
302
  let SqlHavingCompiler = class SqlHavingCompiler {
228
303
  having(builder) {
229
- const where = [];
230
304
  const bindings = [];
231
- builder.Statements.filter((x) => x.IsAggregate)
232
- .map((x) => {
233
- return x.build();
234
- })
235
- .forEach((r) => {
236
- where.push(...r.Statements);
237
- if (Array.isArray(r.Bindings)) {
238
- bindings.push(...r.Bindings);
239
- }
240
- });
305
+ const aggregates = builder.Statements.filter((x) => x.IsAggregate).map((stmt) => ({ boolean: stmt.Boolean, result: stmt.build() }));
241
306
  return {
242
307
  bindings,
243
- expression: where.join(` ${builder.Op.toUpperCase()} `),
308
+ expression: joinBuiltStatements(aggregates, bindings),
244
309
  };
245
310
  }
246
311
  };
@@ -348,7 +413,7 @@ let SqlUpdateQueryCompiler = class SqlUpdateQueryCompiler extends SqlQueryCompil
348
413
  const exprr = [];
349
414
  for (const prop of Object.keys(this._builder.Value)) {
350
415
  const v = this._builder.Value[`${prop}`];
351
- exprr.push(`\`${prop}\` = ?`);
416
+ exprr.push(`${escapeIdentifier(prop)} = ?`);
352
417
  bindings = bindings.concat(this.tryConvertValue(v));
353
418
  }
354
419
  return {
@@ -415,22 +480,22 @@ let SqlOnDuplicateQueryCompiler = class SqlOnDuplicateQueryCompiler {
415
480
  .getColumnsToUpdate()
416
481
  .map((c) => {
417
482
  if (lodash_1.default.isString(c)) {
418
- return `\`${c}\` = ?`;
483
+ // Reference the row being inserted via VALUES(col) instead of
484
+ // re-binding one specific row's value. Binding `parent.Values[0]`
485
+ // applied the FIRST row's values to every conflicting row in a
486
+ // multi row upsert.
487
+ return `${escapeIdentifier(c)} = VALUES(${escapeIdentifier(c)})`;
419
488
  }
420
489
  else {
421
490
  return c.Query;
422
491
  }
423
492
  })
424
493
  .join(',');
425
- const parent = this._builder.getParent();
426
- const valueMap = parent.getColumns().map((c) => c.Column);
427
- const bindings = this._builder.getColumnsToUpdate().map((c) => {
428
- if (lodash_1.default.isString(c)) {
429
- return parent.Values[0][valueMap.indexOf(c)];
430
- }
431
- else {
432
- return c.Bindings;
433
- }
494
+ // Only RawQuery update columns contribute bindings - VALUES(col) needs
495
+ // none. flatMap keeps raw bindings flat; returning the array itself
496
+ // nested them and shifted every following placeholder.
497
+ const bindings = lodash_1.default.flatMap(this._builder.getColumnsToUpdate(), (c) => {
498
+ return lodash_1.default.isString(c) ? [] : c.Bindings ?? [];
434
499
  });
435
500
  return {
436
501
  bindings,
@@ -451,7 +516,7 @@ let SqlIndexQueryCompiler = class SqlIndexQueryCompiler extends orm_1.IndexQuery
451
516
  compile() {
452
517
  return {
453
518
  bindings: [],
454
- expression: `CREATE ${this._builder.Unique ? 'UNIQUE ' : ''}INDEX \`${this._builder.Name}\` ON \`${this._builder.Table}\` (${this._builder.Columns.map((c) => `\`${c}\``).join(',')});`,
519
+ expression: `CREATE ${this._builder.Unique ? 'UNIQUE ' : ''}INDEX ${escapeIdentifier(this._builder.Name)} ON ${escapeIdentifier(this._builder.Table)} (${this._builder.Columns.map((c) => escapeIdentifier(c)).join(',')});`,
455
520
  };
456
521
  }
457
522
  };
@@ -483,31 +548,54 @@ let SqlInsertQueryCompiler = class SqlInsertQueryCompiler extends SqlQueryCompil
483
548
  expression: '',
484
549
  };
485
550
  }
551
+ /**
552
+ * Indices of the columns that take part in this INSERT.
553
+ *
554
+ * An auto increment primary key is omitted only when NO row supplies a value
555
+ * for it. If at least one row supplies one the column stays, and the rows
556
+ * that don't emit NULL so the engine assigns the value.
557
+ *
558
+ * Both {@link columns} and {@link values} MUST derive their shape from this
559
+ * single decision - deciding per-row in one place and per-batch in the other
560
+ * produced value tuples whose arity did not match the column list.
561
+ */
562
+ keptColumnIndices() {
563
+ return this._builder
564
+ .getColumns()
565
+ .map((c, i) => ({ c, i }))
566
+ .filter(({ c, i }) => {
567
+ const descriptor = c.Descriptor;
568
+ if (descriptor && descriptor.AutoIncrement && descriptor.PrimaryKey) {
569
+ return this._builder.Values.some((x) => x[i] !== undefined && x[i] !== null);
570
+ }
571
+ return true;
572
+ })
573
+ .map(({ i }) => i);
574
+ }
486
575
  values() {
487
576
  if (this._builder.Values.length === 0) {
488
577
  throw new exceptions_1.InvalidArgument('values count invalid');
489
578
  }
579
+ const kept = this.keptColumnIndices();
490
580
  const bindings = [];
491
581
  let data = 'VALUES ';
492
582
  data += this._builder.Values.map((val) => {
493
- const toInsert = val
494
- .filter((v, i) => {
583
+ const toInsert = kept.map((i) => {
584
+ // eslint-disable-next-line security/detect-object-injection
585
+ const v = val[i];
495
586
  // eslint-disable-next-line security/detect-object-injection
496
587
  const descriptor = this._builder.getColumns()[i].Descriptor;
497
588
  if (descriptor) {
498
589
  if (!descriptor.Nullable && (v === null || v === undefined) && !descriptor.AutoIncrement) {
499
590
  throw new exceptions_1.InvalidArgument(`value column ${descriptor.Name} cannot be null`);
500
591
  }
501
- if (descriptor.AutoIncrement && descriptor.PrimaryKey) {
502
- if (v !== undefined && v !== null) {
503
- return true;
504
- }
505
- return false;
592
+ // Auto increment PK this row does not supply. NULL (not DEFAULT) lets
593
+ // the engine assign it and is portable - sqlite does not accept the
594
+ // DEFAULT keyword inside a VALUES tuple.
595
+ if (descriptor.AutoIncrement && descriptor.PrimaryKey && (v === undefined || v === null)) {
596
+ return 'NULL';
506
597
  }
507
598
  }
508
- return true;
509
- })
510
- .map((v) => {
511
599
  if (v === undefined) {
512
600
  return 'DEFAULT';
513
601
  }
@@ -525,23 +613,15 @@ let SqlInsertQueryCompiler = class SqlInsertQueryCompiler extends SqlQueryCompil
525
613
  };
526
614
  }
527
615
  columns() {
528
- const columns = this._builder
529
- .getColumns()
530
- .filter((c, i) => {
531
- const descriptor = c.Descriptor;
532
- if (descriptor && descriptor.AutoIncrement && descriptor.PrimaryKey) {
533
- if (this._builder.Values.every((x) => x[i] !== undefined && x[i] !== null)) {
534
- return true;
535
- }
536
- return false;
537
- }
538
- return true;
539
- })
540
- .map((c) => {
541
- return c.Column;
616
+ const columns = this.keptColumnIndices()
617
+ .map((i) => {
618
+ // eslint-disable-next-line security/detect-object-injection
619
+ return this._builder.getColumns()[i].Column;
542
620
  })
543
621
  .map((c) => {
544
- return `\`${c instanceof orm_1.RawQuery ? c.Query : c}\``;
622
+ // RawQuery columns carry raw SQL - preserve the existing (unescaped)
623
+ // backtick wrapping. String columns go through the identifier escaper.
624
+ return c instanceof orm_1.RawQuery ? `\`${c.Query}\`` : escapeIdentifier(c);
545
625
  });
546
626
  if (columns.length === 0) {
547
627
  throw new exceptions_1.InvalidArgument('invalid column count');
@@ -629,10 +709,22 @@ let SqlAlterTableQueryCompiler = class SqlAlterTableQueryCompiler extends orm_1.
629
709
  }
630
710
  if (this.builder.Columns.length !== 0) {
631
711
  _outputs = _outputs.concat(this.builder.Columns.map((c) => {
632
- const compiler = this.container.resolve(orm_1.AlterColumnQueryCompiler, [c]).compile();
712
+ // keep the compiler instance around - it carries the dialect's answer to
713
+ // "is my expression already a complete statement ?" (see IsStandaloneStatement)
714
+ const compiler = this.container.resolve(orm_1.AlterColumnQueryCompiler, [c]);
715
+ return { compiler, output: compiler.compile() };
716
+ })
717
+ // a driver may legitimately skip an alteration by compiling it to nothing.
718
+ // emitting it anyway would yield a dangling `ALTER TABLE \`x\`` - invalid SQL.
719
+ .filter(({ output }) => (output.expression ?? '').trim().length !== 0)
720
+ .map(({ compiler, output }) => {
721
+ // some dialects cannot express an alteration as a suffix of `ALTER TABLE x`
722
+ // eg. MSSQL renames a column with `EXEC sp_rename '[t].[old]', 'new', 'COLUMN'`,
723
+ // which is a complete statement and must be emitted verbatim.
724
+ const standalone = compiler.IsStandaloneStatement === true;
633
725
  return {
634
- bindings: compiler.bindings,
635
- expression: `${_table} ${compiler.expression}`,
726
+ bindings: output.bindings,
727
+ expression: standalone ? output.expression : `${_table} ${output.expression}`,
636
728
  };
637
729
  }));
638
730
  }
@@ -669,7 +761,7 @@ let SqlTableCloneQueryCompiler = class SqlTableCloneQueryCompiler extends orm_1.
669
761
  // if no filter is provided, copy all the data
670
762
  expression: `SELECT * FROM ${_tblName}`,
671
763
  };
672
- const fExprr = `INSERT INTO \`${this.builder.Table}\` ${fOut.expression}`;
764
+ const fExprr = `INSERT INTO ${escapeIdentifier(this.builder.Table)} ${fOut.expression}`;
673
765
  return [
674
766
  out1,
675
767
  {
@@ -851,7 +943,7 @@ let SqlTableQueryCompiler = class SqlTableQueryCompiler extends orm_1.TableQuery
851
943
  }
852
944
  _primaryKeys() {
853
945
  const _keys = this.builder.Columns.filter((x) => x.PrimaryKey)
854
- .map((c) => `\`${c.Name}\``)
946
+ .map((c) => escapeIdentifier(c.Name))
855
947
  .join(',');
856
948
  if (!lodash_1.default.isEmpty(_keys)) {
857
949
  return `PRIMARY KEY (${_keys})`;
@@ -872,7 +964,7 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
872
964
  constructor(builder) {
873
965
  this.builder = builder;
874
966
  this._statementsMappings = {
875
- set: (builder) => `SET(${builder.Args[0].map((a) => `'${a}\'`).join(',')})`,
967
+ set: (builder) => `SET(${builder.Args[0].map((a) => `'${escapeStringLiteral(a)}'`).join(',')})`,
876
968
  string: (builder) => `VARCHAR(${builder.Args[0] ? builder.Args[0] : 255})`,
877
969
  boolean: () => `BOOLEAN`,
878
970
  float: (builder) => {
@@ -882,8 +974,8 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
882
974
  },
883
975
  double: (builder) => this._statementsMappings.float(builder),
884
976
  decimal: (builder) => this._statementsMappings.float(builder),
885
- enum: (builder) => `${builder.Type.toUpperCase()}(${builder.Args[0].map((a) => `'${a}'`).join(',')})`,
886
- binary: (builder) => `BINARY(${builder.Args[0] ?? 255}`,
977
+ enum: (builder) => `${builder.Type.toUpperCase()}(${builder.Args[0].map((a) => `'${escapeStringLiteral(a)}'`).join(',')})`,
978
+ binary: (builder) => `BINARY(${builder.Args[0] ?? 255})`,
887
979
  smallint: (builder) => builder.Type.toUpperCase(),
888
980
  tinyint: (builder) => builder.Type.toUpperCase(),
889
981
  mediumint: (builder) => builder.Type.toUpperCase(),
@@ -904,12 +996,12 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
904
996
  longblob: (builder) => builder.Type.toUpperCase(),
905
997
  // COLUMN ADDITIONA PROPS
906
998
  unsigned: () => 'UNSIGNED',
907
- charset: (builder) => `CHARACTER SET '${builder.Charset}'`,
908
- collation: (builder) => `COLLATE '${builder.Collation}'`,
999
+ charset: (builder) => `CHARACTER SET '${escapeStringLiteral(builder.Charset)}'`,
1000
+ collation: (builder) => `COLLATE '${escapeStringLiteral(builder.Collation)}'`,
909
1001
  notnull: () => `NOT NULL`,
910
1002
  default: () => this._defaultCompiler(),
911
1003
  autoincrement: () => `AUTO_INCREMENT`,
912
- comment: (builder) => `COMMENT '${builder.Comment}'`,
1004
+ comment: (builder) => `COMMENT '${escapeStringLiteral(builder.Comment)}'`,
913
1005
  };
914
1006
  if (!builder) {
915
1007
  throw new Error('column query builder cannot be null');
@@ -917,7 +1009,7 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
917
1009
  }
918
1010
  compile() {
919
1011
  const _stmt = [];
920
- _stmt.push(`\`${this.builder.Name}\``);
1012
+ _stmt.push(escapeIdentifier(this.builder.Name));
921
1013
  _stmt.push(this._statementsMappings[this.builder.Type](this.builder));
922
1014
  if (this.builder.Unsigned) {
923
1015
  _stmt.push(this._statementsMappings.unsigned());
@@ -954,7 +1046,7 @@ let SqlColumnQueryCompiler = class SqlColumnQueryCompiler {
954
1046
  return _stmt;
955
1047
  }
956
1048
  if (lodash_1.default.isString(this.builder.Default.Value)) {
957
- _stmt = `DEFAULT '${this.builder.Default.Value.trim()}'`;
1049
+ _stmt = `DEFAULT '${escapeStringLiteral(this.builder.Default.Value.trim())}'`;
958
1050
  }
959
1051
  else if (lodash_1.default.isNumber(this.builder.Default.Value)) {
960
1052
  _stmt = `DEFAULT ${this.builder.Default.Value}`;
@@ -970,39 +1062,85 @@ exports.SqlColumnQueryCompiler = SqlColumnQueryCompiler = __decorate([
970
1062
  (0, di_1.NewInstance)(),
971
1063
  __metadata("design:paramtypes", [orm_1.ColumnQueryBuilder])
972
1064
  ], SqlColumnQueryCompiler);
973
- let SqlAlterColumnQueryCompiler = class SqlAlterColumnQueryCompiler extends SqlColumnQueryCompiler {
974
- constructor(builder) {
975
- super(builder);
1065
+ let SqlAlterColumnQueryCompiler = class SqlAlterColumnQueryCompiler extends orm_1.AlterColumnQueryCompiler {
1066
+ constructor(container, builder) {
1067
+ super();
1068
+ this.container = container;
1069
+ this.builder = builder;
1070
+ if (!builder) {
1071
+ throw new exceptions_1.InvalidArgument('builder cannot be null or undefined');
1072
+ }
1073
+ }
1074
+ /**
1075
+ * Renders the column body using the DRIVER's own column compiler, resolved
1076
+ * from the container (late binding) - exactly like the CREATE TABLE path does.
1077
+ * Previously this was `super.compile()`, which statically bound every driver
1078
+ * to orm-sql's MySQL dialect.
1079
+ */
1080
+ _columnDefinition() {
1081
+ return this.container.resolve(orm_1.ColumnQueryCompiler, [this.builder]).compile();
1082
+ }
1083
+ /**
1084
+ * Whether the expression produced by `compile()` is already a complete, standalone
1085
+ * statement. When true, SqlAlterTableQueryCompiler emits it verbatim instead of
1086
+ * prefixing it with `ALTER TABLE <table> `.
1087
+ *
1088
+ * Needed by dialects whose alteration is not expressible as a suffix of ALTER TABLE,
1089
+ * eg. MSSQL's `EXEC sp_rename '[t].[old]', 'new', 'COLUMN'`.
1090
+ *
1091
+ * It is read AFTER `compile()`, so a subclass may decide per `builder.AlterType`.
1092
+ * Defaults to false - MySQL (this class) always emits a suffix.
1093
+ */
1094
+ get IsStandaloneStatement() {
1095
+ return false;
1096
+ }
1097
+ /**
1098
+ * Dialect hooks. Returning `null` means "emit no statement at all" - the
1099
+ * parent AlterTableQueryCompiler filters such columns out.
1100
+ */
1101
+ _add(definition) {
1102
+ // escapeIdentifier, not raw backticks: an identifier containing a backtick would
1103
+ // otherwise terminate the quote and splice arbitrary SQL into the DDL ( A3 ).
1104
+ return `ADD ${definition} ${this.builder.AfterColumn ? `AFTER ${escapeIdentifier(this.builder.AfterColumn)}` : ''}`;
1105
+ }
1106
+ _modify(definition) {
1107
+ return `MODIFY ${definition}`;
1108
+ }
1109
+ _rename() {
1110
+ return `RENAME COLUMN ${escapeIdentifier(this.builder.OldName)} TO ${escapeIdentifier(this.builder.Name)}`;
976
1111
  }
977
1112
  compile() {
978
- const builder = this.builder;
979
- if (builder.AlterType === orm_1.ColumnAlterationType.Rename) {
980
- const bld = this.builder;
1113
+ // rename never renders a column body, so it must not resolve one
1114
+ if (this.builder.AlterType === orm_1.ColumnAlterationType.Rename) {
981
1115
  return {
982
1116
  bindings: [],
983
- expression: `RENAME COLUMN \`${bld.OldName}\` TO \`${bld.Name}\``,
1117
+ expression: this._rename() ?? '',
984
1118
  };
985
1119
  }
986
- const cDefinition = super.compile();
987
- if (builder.AlterType === orm_1.ColumnAlterationType.Add) {
988
- return {
989
- bindings: cDefinition.bindings,
990
- expression: `ADD ${cDefinition.expression} ${builder.AfterColumn ? `AFTER \`${builder.AfterColumn}\`` : ''}`,
991
- };
992
- }
993
- if (builder.AlterType === orm_1.ColumnAlterationType.Modify) {
994
- return {
995
- bindings: cDefinition.bindings,
996
- expression: `MODIFY ${cDefinition.expression}`,
997
- };
1120
+ const cDefinition = this._columnDefinition();
1121
+ let expression;
1122
+ switch (this.builder.AlterType) {
1123
+ case orm_1.ColumnAlterationType.Add:
1124
+ expression = this._add(cDefinition.expression);
1125
+ break;
1126
+ case orm_1.ColumnAlterationType.Modify:
1127
+ expression = this._modify(cDefinition.expression);
1128
+ break;
1129
+ default:
1130
+ expression = cDefinition.expression;
1131
+ break;
998
1132
  }
999
- return cDefinition;
1133
+ return {
1134
+ bindings: cDefinition.bindings,
1135
+ expression: expression ?? '',
1136
+ };
1000
1137
  }
1001
1138
  };
1002
1139
  exports.SqlAlterColumnQueryCompiler = SqlAlterColumnQueryCompiler;
1003
1140
  exports.SqlAlterColumnQueryCompiler = SqlAlterColumnQueryCompiler = __decorate([
1004
1141
  (0, di_1.NewInstance)(),
1005
- __metadata("design:paramtypes", [orm_1.AlterColumnQueryBuilder])
1142
+ (0, di_1.Inject)(di_1.Container),
1143
+ __metadata("design:paramtypes", [di_1.Container, orm_1.AlterColumnQueryBuilder])
1006
1144
  ], SqlAlterColumnQueryCompiler);
1007
1145
  let SqlEventQueryCompiler = class SqlEventQueryCompiler extends SqlQueryCompiler {
1008
1146
  constructor(container, builder) {