@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.
@@ -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 += `\`${builder.Database}\`.`;
66
+ table += `${escapeIdentifier(builder.Database)}.`;
24
67
  }
25
- table += `\`${tbl ? tbl : builder.Table}\``;
68
+ table += escapeIdentifier(tbl ? tbl : builder.Table);
26
69
  if (builder.TableAlias) {
27
- table += ` as \`${builder.TableAlias}\``;
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 && _builder === undefined) {
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 sort = this._builder.getSort();
112
+ const sorts = this._builder.getSorts();
70
113
  let stmt = '';
71
114
  const bindings = [];
72
- if (sort) {
73
- stmt = ` ORDER BY \`${sort.column}\` ${sort.order}`;
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
- const lazyBindings = builder.Statements.filter((x) => x instanceof LazyQueryStatement).map((x) => x.build());
200
- builder.Statements.filter((x) => !x.IsAggregate).filter(x => !(x instanceof LazyQueryStatement))
201
- .map((x) => {
202
- return x.build();
203
- })
204
- .concat(lazyBindings)
205
- .forEach((r) => {
206
- where.push(...r.Statements);
207
- if (Array.isArray(r.Bindings)) {
208
- bindings.push(...r.Bindings);
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: where.join(` ${builder.Op.toUpperCase()} `),
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: where.join(` ${builder.Op.toUpperCase()} `),
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(`\`${prop}\` = ?`);
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
- return `\`${c}\` = ?`;
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
- const parent = this._builder.getParent();
420
- const valueMap = parent.getColumns().map((c) => c.Column);
421
- const bindings = this._builder.getColumnsToUpdate().map((c) => {
422
- if (_.isString(c)) {
423
- return parent.Values[0][valueMap.indexOf(c)];
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 \`${this._builder.Name}\` ON \`${this._builder.Table}\` (${this._builder.Columns.map((c) => `\`${c}\``).join(',')});`,
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 = val
488
- .filter((v, i) => {
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
- if (descriptor.AutoIncrement && descriptor.PrimaryKey) {
496
- if (v !== undefined && v !== null) {
497
- return true;
498
- }
499
- return false;
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._builder
523
- .getColumns()
524
- .filter((c, i) => {
525
- const descriptor = c.Descriptor;
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
- return `\`${c instanceof RawQuery ? c.Query : c}\``;
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
- const compiler = this.container.resolve(AlterColumnQueryCompiler, [c]).compile();
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: compiler.bindings,
629
- expression: `${_table} ${compiler.expression}`,
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 \`${this.builder.Table}\` ${fOut.expression}`;
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) => `\`${c.Name}\``)
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}\'`).join(',')})`,
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(`\`${this.builder.Name}\``);
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 SqlColumnQueryCompiler {
968
- constructor(builder) {
969
- super(builder);
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
- const builder = this.builder;
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: `RENAME COLUMN \`${bld.OldName}\` TO \`${bld.Name}\``,
1108
+ expression: this._rename() ?? '',
978
1109
  };
979
1110
  }
980
- const cDefinition = super.compile();
981
- if (builder.AlterType === ColumnAlterationType.Add) {
982
- return {
983
- bindings: cDefinition.bindings,
984
- expression: `ADD ${cDefinition.expression} ${builder.AfterColumn ? `AFTER \`${builder.AfterColumn}\`` : ''}`,
985
- };
986
- }
987
- if (builder.AlterType === ColumnAlterationType.Modify) {
988
- return {
989
- bindings: cDefinition.bindings,
990
- expression: `MODIFY ${cDefinition.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 cDefinition;
1124
+ return {
1125
+ bindings: cDefinition.bindings,
1126
+ expression: expression ?? '',
1127
+ };
994
1128
  }
995
1129
  };
996
1130
  SqlAlterColumnQueryCompiler = __decorate([
997
1131
  NewInstance(),
998
- __metadata("design:paramtypes", [AlterColumnQueryBuilder])
1132
+ Inject(Container),
1133
+ __metadata("design:paramtypes", [Container, AlterColumnQueryBuilder])
999
1134
  ], SqlAlterColumnQueryCompiler);
1000
1135
  export { SqlAlterColumnQueryCompiler };
1001
1136
  let SqlEventQueryCompiler = class SqlEventQueryCompiler extends SqlQueryCompiler {