@simplysm/orm-common 13.0.83 → 13.0.85

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.
Files changed (73) hide show
  1. package/dist/ddl/initialize.d.ts +2 -2
  2. package/dist/ddl/initialize.js +1 -1
  3. package/dist/ddl/initialize.js.map +1 -1
  4. package/dist/ddl/table-ddl.d.ts +1 -1
  5. package/dist/exec/queryable.d.ts +115 -115
  6. package/dist/exec/queryable.js +68 -68
  7. package/dist/exec/queryable.js.map +1 -1
  8. package/dist/expr/expr.d.ts +248 -248
  9. package/dist/expr/expr.js +250 -250
  10. package/dist/query-builder/base/expr-renderer-base.d.ts +7 -7
  11. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +3 -3
  12. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  13. package/dist/query-builder/mssql/mssql-expr-renderer.js +5 -5
  14. package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
  15. package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
  16. package/dist/query-builder/mssql/mssql-query-builder.js +7 -7
  17. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +2 -2
  18. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  19. package/dist/query-builder/mysql/mysql-expr-renderer.js +4 -4
  20. package/dist/query-builder/mysql/mysql-query-builder.d.ts +10 -10
  21. package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
  22. package/dist/query-builder/mysql/mysql-query-builder.js +4 -4
  23. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +2 -2
  24. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  25. package/dist/query-builder/postgresql/postgresql-expr-renderer.js +4 -4
  26. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +8 -8
  27. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  28. package/dist/query-builder/postgresql/postgresql-query-builder.js +7 -7
  29. package/dist/query-builder/query-builder.d.ts +1 -1
  30. package/dist/schema/factory/column-builder.d.ts +46 -46
  31. package/dist/schema/factory/column-builder.js +25 -25
  32. package/dist/schema/factory/index-builder.d.ts +22 -22
  33. package/dist/schema/factory/index-builder.js +14 -14
  34. package/dist/schema/factory/relation-builder.d.ts +93 -93
  35. package/dist/schema/factory/relation-builder.d.ts.map +1 -1
  36. package/dist/schema/factory/relation-builder.js +37 -37
  37. package/dist/schema/procedure-builder.d.ts +38 -38
  38. package/dist/schema/procedure-builder.d.ts.map +1 -1
  39. package/dist/schema/procedure-builder.js +26 -26
  40. package/dist/schema/table-builder.d.ts +38 -38
  41. package/dist/schema/table-builder.d.ts.map +1 -1
  42. package/dist/schema/table-builder.js +29 -29
  43. package/dist/schema/view-builder.d.ts +26 -26
  44. package/dist/schema/view-builder.d.ts.map +1 -1
  45. package/dist/schema/view-builder.js +18 -18
  46. package/dist/types/db.d.ts +40 -40
  47. package/dist/types/expr.d.ts +75 -75
  48. package/dist/types/expr.d.ts.map +1 -1
  49. package/dist/types/query-def.d.ts +32 -32
  50. package/dist/types/query-def.d.ts.map +1 -1
  51. package/package.json +2 -2
  52. package/src/ddl/initialize.ts +16 -16
  53. package/src/ddl/table-ddl.ts +1 -1
  54. package/src/exec/queryable.ts +163 -163
  55. package/src/expr/expr.ts +257 -257
  56. package/src/query-builder/base/expr-renderer-base.ts +8 -8
  57. package/src/query-builder/mssql/mssql-expr-renderer.ts +20 -20
  58. package/src/query-builder/mssql/mssql-query-builder.ts +28 -28
  59. package/src/query-builder/mysql/mysql-expr-renderer.ts +22 -22
  60. package/src/query-builder/mysql/mysql-query-builder.ts +65 -65
  61. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +15 -15
  62. package/src/query-builder/postgresql/postgresql-query-builder.ts +43 -43
  63. package/src/query-builder/query-builder.ts +1 -1
  64. package/src/schema/factory/column-builder.ts +48 -48
  65. package/src/schema/factory/index-builder.ts +22 -22
  66. package/src/schema/factory/relation-builder.ts +95 -95
  67. package/src/schema/procedure-builder.ts +38 -38
  68. package/src/schema/table-builder.ts +38 -38
  69. package/src/schema/view-builder.ts +28 -28
  70. package/src/types/db.ts +41 -41
  71. package/src/types/expr.ts +79 -79
  72. package/src/types/query-def.ts +37 -37
  73. package/tests/ddl/basic.expected.ts +8 -8
@@ -68,12 +68,12 @@ import type {
68
68
  import type { SelectQueryDef } from "../../types/query-def";
69
69
 
70
70
  /**
71
- * Expr → SQL Render 추상 Basic class
71
+ * Expr → SQL Render abstract base class
72
72
  *
73
73
  * Base principles:
74
74
  * - Implement only 100% identical logic across all dialects (dispatch)
75
75
  * - If different at all, make it abstract
76
- * - Method명은 expr.type 동일 (동적 dispatch 가능)
76
+ * - Method names match expr.type (enables dynamic dispatch)
77
77
  */
78
78
  export abstract class ExprRendererBase {
79
79
  constructor(protected buildSelect: (def: SelectQueryDef) => string) {}
@@ -81,20 +81,20 @@ export abstract class ExprRendererBase {
81
81
  //#region ========== Public Utilities ==========
82
82
 
83
83
  /**
84
- * 식별자 감싸기 (table명, column )
84
+ * Wrap identifier (table name, column name, etc.)
85
85
  * MySQL: `name`, MSSQL: [name], PostgreSQL: "name"
86
86
  */
87
87
  abstract wrap(name: string): string;
88
88
 
89
89
  /**
90
- * SQL 문자열 리터럴용 escape
91
- * 동적 SQL이나 System query에서 문자열 값으로 사용될 호출
92
- * 예: WHERE schema_name = 'escaped_value'
90
+ * Escape for SQL string literals
91
+ * Called when used as a string value in dynamic SQL or system queries
92
+ * e.g.: WHERE schema_name = 'escaped_value'
93
93
  */
94
94
  abstract escapeString(value: string): string;
95
95
 
96
96
  /**
97
- * value escape (타입에 따라 적절한 SQL 리터럴로 Transform)
97
+ * Value escape (transform to appropriate SQL literal based on type)
98
98
  */
99
99
  abstract escapeValue(value: unknown): string;
100
100
 
@@ -150,7 +150,7 @@ export abstract class ExprRendererBase {
150
150
 
151
151
  //#endregion
152
152
 
153
- //#region ========== Abstract - 문자열 (null processing required) ==========
153
+ //#region ========== Abstract - String (null processing required) ==========
154
154
 
155
155
  protected abstract concat(expr: ExprConcat): string;
156
156
  protected abstract left(expr: ExprLeft): string;
@@ -72,19 +72,19 @@ import { ExprRendererBase } from "../base/expr-renderer-base";
72
72
  * MSSQL expression renderer
73
73
  */
74
74
  export class MssqlExprRenderer extends ExprRendererBase {
75
- //#region ========== 유틸리티 (public - QueryBuilder에서도 사용) ==========
75
+ //#region ========== Utilities (public - also used by QueryBuilder) ==========
76
76
 
77
- /** 식별자 감싸기 */
77
+ /** Wrap identifier */
78
78
  wrap(name: string): string {
79
79
  return `[${name.replace(/]/g, "]]")}]`;
80
80
  }
81
81
 
82
- /** SQL 문자열 리터럴용 escape (따옴표 없이 return) */
82
+ /** Escape for SQL string literals (returns without quotes) */
83
83
  escapeString(value: string): string {
84
84
  return value.replace(/'/g, "''");
85
85
  }
86
86
 
87
- /** value escape */
87
+ /** Value escape */
88
88
  escapeValue(value: unknown): string {
89
89
  if (value == null) {
90
90
  return "NULL";
@@ -217,18 +217,18 @@ export class MssqlExprRenderer extends ExprRendererBase {
217
217
  }
218
218
 
219
219
  protected like(expr: ExprLike): string {
220
- // ESCAPE '\' 항상 Add
220
+ // Always add ESCAPE '\'
221
221
  return `${this.render(expr.source)} LIKE ${this.render(expr.pattern)} ESCAPE '\\'`;
222
222
  }
223
223
 
224
224
  protected regexp(_expr: ExprRegexp): string {
225
- // MSSQL REGEXP 미지원 - LIKE pattern이나 CLR 사용 필요
225
+ // MSSQL does not support REGEXP - needs LIKE pattern or CLR
226
226
  throw new Error("MSSQL does not natively support REGEXP.");
227
227
  }
228
228
 
229
229
  protected in(expr: ExprIn): string {
230
230
  if (expr.values.length === 0) {
231
- return "1=0"; // IN 항상 false
231
+ return "1=0"; // empty IN is always false
232
232
  }
233
233
  const values = expr.values.map((v) => this.render(v)).join(", ");
234
234
  return `${this.render(expr.source)} IN (${values})`;
@@ -239,7 +239,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
239
239
  }
240
240
 
241
241
  protected exists(expr: ExprExists): string {
242
- // SELECT 1로 Render
242
+ // Render as SELECT 1
243
243
  const subquery = this.buildSelect({
244
244
  ...expr.query,
245
245
  select: { _: { type: "value", value: 1 } },
@@ -267,10 +267,10 @@ export class MssqlExprRenderer extends ExprRendererBase {
267
267
 
268
268
  //#endregion
269
269
 
270
- //#region ========== 문자열 (null Process) ==========
270
+ //#region ========== String (null handling) ==========
271
271
 
272
272
  protected concat(expr: ExprConcat): string {
273
- // MSSQL 2012+: CONCAT 함수는 NULL automatic으로 문자열로 processing
273
+ // MSSQL 2012+: CONCAT function automatically treats NULL as empty string
274
274
  const args = expr.args.map((a) => this.render(a)).join(", ");
275
275
  return `CONCAT(${args})`;
276
276
  }
@@ -308,12 +308,12 @@ export class MssqlExprRenderer extends ExprRendererBase {
308
308
  }
309
309
 
310
310
  protected length(expr: ExprLength): string {
311
- // MSSQL: LEN() (null Process)
311
+ // MSSQL: LEN() (null handling)
312
312
  return `LEN(ISNULL(${this.render(expr.arg)}, N''))`;
313
313
  }
314
314
 
315
315
  protected byteLength(expr: ExprByteLength): string {
316
- // MSSQL: DATALENGTH() (null Process)
316
+ // MSSQL: DATALENGTH() (null handling)
317
317
  return `DATALENGTH(ISNULL(${this.render(expr.arg)}, N''))`;
318
318
  }
319
319
 
@@ -321,7 +321,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
321
321
  if (expr.length != null) {
322
322
  return `SUBSTRING(${this.render(expr.source)}, ${this.render(expr.start)}, ${this.render(expr.length)})`;
323
323
  }
324
- // MSSQL: length 없으면 끝까지
324
+ // MSSQL: if no length, go to end
325
325
  return `SUBSTRING(${this.render(expr.source)}, ${this.render(expr.start)}, LEN(${this.render(expr.source)}))`;
326
326
  }
327
327
 
@@ -384,9 +384,9 @@ export class MssqlExprRenderer extends ExprRendererBase {
384
384
 
385
385
  protected isoWeekStartDate(expr: ExprIsoWeekStartDate): string {
386
386
  const src = this.render(expr.arg);
387
- // ISO 주의 시작일 (월요일) - @@DATEFIRST 무관하게 항상 월요일 return
388
- // 원리: DATEDIFF(DAY, 0, date) 1900-01-01(월요일)부터의 일수
389
- // (일수 + 6) % 7 + 1 = 1(), 2(), ..., 7()
387
+ // ISO week start date (Monday) - always returns Monday regardless of @@DATEFIRST
388
+ // Principle: DATEDIFF(DAY, 0, date) is the number of days from 1900-01-01 (Monday)
389
+ // (days + 6) % 7 + 1 = 1(Mon), 2(Tue), ..., 7(Sun)
390
390
  const weekDay = `((DATEDIFF(DAY, 0, ${src}) + 6) % 7 + 1)`;
391
391
  return `DATEADD(DAY, 1 - ${weekDay}, CAST(${src} AS DATE))`;
392
392
  }
@@ -434,7 +434,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
434
434
  }
435
435
 
436
436
  private convertDateFormat(format: string): string {
437
- // MSSQL FORMAT 함수용 (동일한 포맷 사용)
437
+ // For MSSQL FORMAT function (uses the same format)
438
438
  return format;
439
439
  }
440
440
 
@@ -504,7 +504,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
504
504
  protected greatest(expr: ExprGreatest): string {
505
505
  if (expr.args.length === 0) throw new Error("greatest requires at least one argument.");
506
506
  if (expr.args.length === 1) return this.render(expr.args[0]);
507
- // MSSQL 2012+: VALUES + MAX 방식
507
+ // MSSQL 2012+: VALUES + MAX approach
508
508
  const values = expr.args.map((a) => `(${this.render(a)})`).join(", ");
509
509
  return `(SELECT MAX(v) FROM (VALUES ${values}) AS t(v))`;
510
510
  }
@@ -512,7 +512,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
512
512
  protected least(expr: ExprLeast): string {
513
513
  if (expr.args.length === 0) throw new Error("least requires at least one argument.");
514
514
  if (expr.args.length === 1) return this.render(expr.args[0]);
515
- // MSSQL 2012+: VALUES + MIN 방식
515
+ // MSSQL 2012+: VALUES + MIN approach
516
516
  const values = expr.args.map((a) => `(${this.render(a)})`).join(", ");
517
517
  return `(SELECT MIN(v) FROM (VALUES ${values}) AS t(v))`;
518
518
  }
@@ -537,7 +537,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
537
537
  const fn = this.renderWindowFn(expr.fn);
538
538
  let over = this.renderWindowSpec(expr.spec);
539
539
 
540
- // LAST_VALUE Basic 프레임이 CURRENT ROW까지만 보므로 전체 프레임 명시 필요
540
+ // LAST_VALUE default frame only sees up to CURRENT ROW, so full frame must be specified
541
541
  if (expr.fn.type === "lastValue" && over.length > 0) {
542
542
  over += " ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING";
543
543
  }
@@ -41,9 +41,9 @@ import { MssqlExprRenderer } from "./mssql-expr-renderer";
41
41
  export class MssqlQueryBuilder extends QueryBuilderBase {
42
42
  protected expr = new MssqlExprRenderer((def) => this.select(def).sql);
43
43
 
44
- //#region ========== 유틸리티 ==========
44
+ //#region ========== Utilities ==========
45
45
 
46
- /** Table명 Render */
46
+ /** Render table name */
47
47
  protected tableName(obj: QueryDefObjectName): string {
48
48
  const parts: string[] = [];
49
49
  if (obj.database != null) {
@@ -58,7 +58,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
58
58
  return parts.join(".");
59
59
  }
60
60
 
61
- /** OFFSET...FETCH Render */
61
+ /** Render OFFSET...FETCH clause */
62
62
  protected renderLimit(limit: [number, number] | undefined): string {
63
63
  if (limit == null) return "";
64
64
  const [offset, count] = limit;
@@ -68,15 +68,15 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
68
68
  protected renderJoin(join: SelectQueryDefJoin): string {
69
69
  const alias = this.expr.wrap(join.as);
70
70
 
71
- // LATERAL JOIN 필요 여부 감지 → MSSQL OUTER APPLY 사용
71
+ // Detect if LATERAL JOIN is needed → MSSQL uses OUTER APPLY
72
72
  if (this.needsLateral(join)) {
73
- // from 배열(UNION ALL)이면 renderFrom(join.from),
74
- // (orderBy, top, select ) renderFrom(join)으로 Subquery Generate
73
+ // If from is an array (UNION ALL), use renderFrom(join.from),
74
+ // otherwise (orderBy, top, select, etc.) use renderFrom(join) to generate subquery
75
75
  const from = Array.isArray(join.from) ? this.renderFrom(join.from) : this.renderFrom(join);
76
76
  return ` OUTER APPLY ${from} AS ${alias}`;
77
77
  }
78
78
 
79
- // 일반 JOIN
79
+ // Normal JOIN
80
80
  const from = this.renderFrom(join.from);
81
81
  const where =
82
82
  join.where != null && join.where.length > 0
@@ -122,7 +122,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
122
122
  const from = this.renderFrom(def.from);
123
123
  sql += ` FROM ${from} AS ${this.expr.wrap(def.as)}`;
124
124
 
125
- // LOCK (ROWLOCK으로 row 수준 강제 - MySQL/PostgreSQL FOR UPDATE와 동일 Behavior)
125
+ // LOCK (force row-level lock with ROWLOCK - same behavior as MySQL/PostgreSQL FOR UPDATE)
126
126
  if (def.lock) {
127
127
  sql += " WITH (UPDLOCK, ROWLOCK)";
128
128
  }
@@ -165,14 +165,14 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
165
165
 
166
166
  let sql = "";
167
167
 
168
- // IDENTITY_INSERT ON (AI column에 explicit value 삽입 )
168
+ // IDENTITY_INSERT ON (when inserting explicit values into AI column)
169
169
  if (def.overrideIdentity) {
170
170
  sql += `SET IDENTITY_INSERT ${table} ON;\n`;
171
171
  }
172
172
 
173
173
  sql += `INSERT INTO ${table} (${colList})`;
174
174
 
175
- // OUTPUT (MSSQL 네이티브 지원)
175
+ // OUTPUT (MSSQL native support)
176
176
  if (def.output != null) {
177
177
  const outputCols = def.output.columns.map((c) => `INSERTED.${this.expr.wrap(c)}`).join(", ");
178
178
  sql += ` OUTPUT ${outputCols}`;
@@ -191,7 +191,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
191
191
  sql += `;\nSET IDENTITY_INSERT ${table} OFF;`;
192
192
  }
193
193
 
194
- // overrideIdentity 시: SET ON → results[0], INSERT → results[1], SET OFF → results[2]
194
+ // With overrideIdentity: SET ON → results[0], INSERT → results[1], SET OFF → results[2]
195
195
  return { sql, resultSetIndex: def.overrideIdentity ? 1 : undefined };
196
196
  }
197
197
 
@@ -202,7 +202,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
202
202
  const colList = columns.map((c) => this.expr.wrap(c)).join(", ");
203
203
  const values = columns.map((c) => this.expr.escapeValue(def.record[c])).join(", ");
204
204
 
205
- // existsSelectQuery SELECT 1 AS _ 형태로 Render
205
+ // Render existsSelectQuery as SELECT 1 AS _
206
206
  const existsQuerySql = this.select({
207
207
  ...def.existsSelectQuery,
208
208
  select: { _: { type: "value", value: 1 } },
@@ -210,7 +210,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
210
210
 
211
211
  let sql = `INSERT INTO ${table} (${colList})`;
212
212
 
213
- // OUTPUT (MSSQL: OUTPUT SELECT 앞에 위치해야 )
213
+ // OUTPUT (MSSQL: OUTPUT must be placed before SELECT)
214
214
  if (def.output != null) {
215
215
  const outputCols = def.output.columns.map((c) => `INSERTED.${this.expr.wrap(c)}`).join(", ");
216
216
  sql += ` OUTPUT ${outputCols}`;
@@ -225,7 +225,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
225
225
  const table = this.tableName(def.table);
226
226
  const selectSql = this.select(def.recordsSelectQuery).sql;
227
227
 
228
- // INSERT INTO SELECT에서 columns 추출
228
+ // Extract columns from INSERT INTO SELECT
229
229
  const selectDef = def.recordsSelectQuery;
230
230
  const colList =
231
231
  selectDef.select != null
@@ -310,7 +310,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
310
310
  //#region ========== DML - UPSERT ==========
311
311
 
312
312
  protected upsert(def: UpsertQueryDef): QueryBuildResult {
313
- // MSSQL: MERGE 사용
313
+ // MSSQL: Use MERGE
314
314
  const table = this.tableName(def.table);
315
315
  const alias = this.expr.wrap(def.existsSelectQuery.as);
316
316
  const existsWhere = def.existsSelectQuery.where;
@@ -389,14 +389,14 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
389
389
  }
390
390
 
391
391
  protected renameTable(def: RenameTableQueryDef): QueryBuildResult {
392
- // MSSQL: sp_rename 사용
392
+ // MSSQL: Use sp_rename
393
393
  const tableName = this.expr.escapeString(this.tableName(def.table));
394
394
  const newName = this.expr.escapeString(def.newName);
395
395
  return { sql: `EXEC sp_rename '${tableName}', '${newName}'` };
396
396
  }
397
397
 
398
398
  protected truncate(def: TruncateQueryDef): QueryBuildResult {
399
- // MSSQL: TRUNCATE IDENTITY automatic 리셋
399
+ // MSSQL: TRUNCATE automatically resets IDENTITY
400
400
  return { sql: `TRUNCATE TABLE ${this.tableName(def.table)}` };
401
401
  }
402
402
 
@@ -447,13 +447,13 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
447
447
  colSql += " NOT NULL";
448
448
  }
449
449
 
450
- // MSSQL: ALTER COLUMN (IDENTITY DEFAULT 별도 processing 필요)
450
+ // MSSQL: ALTER COLUMN (IDENTITY and DEFAULT require separate handling)
451
451
  return { sql: `ALTER TABLE ${table} ALTER COLUMN ${colSql}` };
452
452
  }
453
453
 
454
454
  protected renameColumn(def: RenameColumnQueryDef): QueryBuildResult {
455
455
  const table = this.tableName(def.table);
456
- // MSSQL: sp_rename 사용
456
+ // MSSQL: Use sp_rename
457
457
  const tableCol = this.expr.escapeString(`${table}.${def.column}`);
458
458
  const newName = this.expr.escapeString(def.newName);
459
459
  return { sql: `EXEC sp_rename '${tableCol}', '${newName}', 'COLUMN'` };
@@ -487,7 +487,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
487
487
 
488
488
  let sql = `ALTER TABLE ${table} ADD CONSTRAINT ${this.expr.wrap(fk.name)} FOREIGN KEY (${fkCols}) REFERENCES ${targetTable} (${targetCols})`;
489
489
 
490
- // MSSQL/PostgreSQL: FK용 Index 별도 Generate 필요
490
+ // MSSQL/PostgreSQL: separate index generation needed for FK
491
491
  const idxName = `IDX_${def.table.name}_${fk.name.replace(/^FK_/, "")}`;
492
492
  sql += `;\nCREATE INDEX ${this.expr.wrap(idxName)} ON ${table} (${fkCols});`;
493
493
 
@@ -530,7 +530,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
530
530
  protected createProc(def: CreateProcQueryDef): QueryBuildResult {
531
531
  const proc = this.tableName(def.procedure);
532
532
 
533
- // params processing
533
+ // Process params
534
534
  const paramList =
535
535
  def.params
536
536
  ?.map((p) => {
@@ -574,7 +574,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
574
574
  //#region ========== Utils ==========
575
575
 
576
576
  protected clearSchema(def: ClearSchemaQueryDef): QueryBuildResult {
577
- // SQL Injection 방지: 식별자 유효성 Validation
577
+ // SQL injection prevention: identifier validation
578
578
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(def.database)) {
579
579
  throw new Error(`Invalid database name: ${def.database}`);
580
580
  }
@@ -590,22 +590,22 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
590
590
  DECLARE @sql NVARCHAR(MAX);
591
591
  SET @sql = N'';
592
592
 
593
- -- FK constraint Delete
593
+ -- Drop FK constraints
594
594
  SELECT @sql = @sql + N'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' + QUOTENAME(OBJECT_NAME(parent_object_id)) + N' DROP CONSTRAINT ' + QUOTENAME(name) + N';' + CHAR(13)
595
595
  FROM ${db}.sys.foreign_keys
596
596
  WHERE OBJECT_SCHEMA_NAME(parent_object_id) = '${schema}';
597
597
 
598
- -- Drop table
598
+ -- Drop tables
599
599
  SELECT @sql = @sql + N'DROP TABLE ' + QUOTENAME(SCHEMA_NAME(schema_id)) + '.' + QUOTENAME(name) + N';' + CHAR(13)
600
600
  FROM ${db}.sys.tables
601
601
  WHERE SCHEMA_NAME(schema_id) = '${schema}';
602
602
 
603
- -- Drop view
603
+ -- Drop views
604
604
  SELECT @sql = @sql + N'DROP VIEW ' + QUOTENAME(SCHEMA_NAME(schema_id)) + '.' + QUOTENAME(name) + N';' + CHAR(13)
605
605
  FROM ${db}.sys.views
606
606
  WHERE schema_id = SCHEMA_ID('${schema}');
607
607
 
608
- -- Procedure Delete
608
+ -- Drop procedures
609
609
  SELECT @sql = @sql + N'DROP PROCEDURE ' + QUOTENAME(SCHEMA_NAME(schema_id)) + '.' + QUOTENAME(name) + N';' + CHAR(13)
610
610
  FROM ${db}.sys.procedures
611
611
  WHERE SCHEMA_NAME(schema_id) = '${schema}';
@@ -615,7 +615,7 @@ EXEC sp_executesql @sql;`,
615
615
  }
616
616
 
617
617
  protected schemaExists(def: SchemaExistsQueryDef): QueryBuildResult {
618
- // SQL Injection 방지: 식별자 유효성 Validation
618
+ // SQL injection prevention: identifier validation
619
619
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(def.database)) {
620
620
  throw new Error(`Invalid database name: ${def.database}`);
621
621
  }
@@ -626,7 +626,7 @@ EXEC sp_executesql @sql;`,
626
626
 
627
627
  const dbName = this.expr.escapeString(def.database);
628
628
  const schema = this.expr.escapeString(schemaName);
629
- // MSSQL: database 존재 확인 schema 확인 (동적 SQL 사용)
629
+ // MSSQL: check database existence then check schema (using dynamic SQL)
630
630
  return {
631
631
  sql: `DECLARE @result NVARCHAR(MAX) = NULL;
632
632
  IF EXISTS (SELECT 1 FROM sys.databases WHERE name = '${dbName}')
@@ -72,22 +72,22 @@ import { ExprRendererBase } from "../base/expr-renderer-base";
72
72
  * MySQL expression renderer
73
73
  */
74
74
  export class MysqlExprRenderer extends ExprRendererBase {
75
- //#region ========== 유틸리티 (public - QueryBuilder에서도 사용) ==========
75
+ //#region ========== Utilities (public - also used by QueryBuilder) ==========
76
76
 
77
- /** 식별자 감싸기 */
77
+ /** Wrap identifier */
78
78
  wrap(name: string): string {
79
79
  return `\`${name.replace(/`/g, "``")}\``;
80
80
  }
81
81
 
82
- /** SQL 문자열 리터럴용 escape (따옴표 없이 return) */
82
+ /** Escape for SQL string literals (returns without quotes) */
83
83
  escapeString(value: string): string {
84
84
  return value
85
- .replace(/\\/g, "\\\\") // 백슬래시 (최우선)
86
- .replace(/'/g, "''") // 따옴표
87
- .replace(/\0/g, "\\0") // NULL 바이트
88
- .replace(/\n/g, "\\n") // 줄바꿈
89
- .replace(/\r/g, "\\r") // 캐리지 리턴
90
- .replace(/\t/g, "\\t"); //
85
+ .replace(/\\/g, "\\\\") // backslash (highest priority)
86
+ .replace(/'/g, "''") // single quote
87
+ .replace(/\0/g, "\\0") // NULL byte
88
+ .replace(/\n/g, "\\n") // newline
89
+ .replace(/\r/g, "\\r") // carriage return
90
+ .replace(/\t/g, "\\t"); // tab
91
91
  }
92
92
 
93
93
  /** value escape */
@@ -221,7 +221,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
221
221
  }
222
222
 
223
223
  protected like(expr: ExprLike): string {
224
- // ESCAPE '\' 항상 Add
224
+ // Always add ESCAPE '\'
225
225
  return `${this.render(expr.source)} LIKE ${this.render(expr.pattern)} ESCAPE '\\\\'`;
226
226
  }
227
227
 
@@ -231,7 +231,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
231
231
 
232
232
  protected in(expr: ExprIn): string {
233
233
  if (expr.values.length === 0) {
234
- return "1=0"; // IN 항상 false
234
+ return "1=0"; // empty IN is always false
235
235
  }
236
236
  const values = expr.values.map((v) => this.render(v)).join(", ");
237
237
  return `${this.render(expr.source)} IN (${values})`;
@@ -242,7 +242,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
242
242
  }
243
243
 
244
244
  protected exists(expr: ExprExists): string {
245
- // SELECT 1로 Render
245
+ // Render as SELECT 1
246
246
  const subquery = this.buildSelect({
247
247
  ...expr.query,
248
248
  select: { _: { type: "value", value: 1 } },
@@ -270,10 +270,10 @@ export class MysqlExprRenderer extends ExprRendererBase {
270
270
 
271
271
  //#endregion
272
272
 
273
- //#region ========== 문자열 (null Process) ==========
273
+ //#region ========== String (null handling) ==========
274
274
 
275
275
  protected concat(expr: ExprConcat): string {
276
- // null processing: IFNULL(arg, '')
276
+ // null handling: IFNULL(arg, '')
277
277
  const args = expr.args.map((a) => `IFNULL(${this.render(a)}, '')`);
278
278
  return `CONCAT(${args.join(", ")})`;
279
279
  }
@@ -307,12 +307,12 @@ export class MysqlExprRenderer extends ExprRendererBase {
307
307
  }
308
308
 
309
309
  protected length(expr: ExprLength): string {
310
- // null processing: IFNULL(arg, '')
310
+ // null handling: IFNULL(arg, '')
311
311
  return `CHAR_LENGTH(IFNULL(${this.render(expr.arg)}, ''))`;
312
312
  }
313
313
 
314
314
  protected byteLength(expr: ExprByteLength): string {
315
- // null processing: IFNULL(arg, '')
315
+ // null handling: IFNULL(arg, '')
316
316
  return `LENGTH(IFNULL(${this.render(expr.arg)}, ''))`;
317
317
  }
318
318
 
@@ -380,7 +380,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
380
380
  }
381
381
 
382
382
  protected isoWeekStartDate(expr: ExprIsoWeekStartDate): string {
383
- // ISO 주의 시작일 (월요일)
383
+ // ISO week start date (Monday)
384
384
  return `DATE_SUB(${this.render(expr.arg)}, INTERVAL (WEEKDAY(${this.render(expr.arg)})) DAY)`;
385
385
  }
386
386
 
@@ -438,7 +438,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
438
438
  }
439
439
 
440
440
  private convertDateFormat(format: string): string {
441
- // 간단한 Transform (yyyy-MM-dd HH:mm:ss 형식)
441
+ // Simple transform (yyyy-MM-dd HH:mm:ss format)
442
442
  return format
443
443
  .replace(/yyyy/g, "%Y")
444
444
  .replace(/MM/g, "%m")
@@ -455,7 +455,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
455
455
  protected coalesce(expr: ExprCoalesce): string {
456
456
  if (expr.args.length === 0) return "NULL";
457
457
  if (expr.args.length === 1) return this.render(expr.args[0]);
458
- // COALESCE로 Render (여러 value 첫 번째 non-null)
458
+ // Render as COALESCE (first non-null among multiple values)
459
459
  return `COALESCE(${expr.args.map((a) => this.render(a)).join(", ")})`;
460
460
  }
461
461
 
@@ -522,8 +522,8 @@ export class MysqlExprRenderer extends ExprRendererBase {
522
522
  }
523
523
 
524
524
  protected rowNum(_expr: ExprRowNum): string {
525
- // MySQL에서는 변수 사용 또는 ROW_NUMBER() Window function 사용
526
- // 여기서는 ROW_NUMBER() 구현 (MySQL 8.0+)
525
+ // MySQL: use variables or ROW_NUMBER() window function
526
+ // Implemented with ROW_NUMBER() here (MySQL 8.0+)
527
527
  return "ROW_NUMBER() OVER ()";
528
528
  }
529
529
 
@@ -543,7 +543,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
543
543
  const fn = this.renderWindowFn(expr.fn);
544
544
  let over = this.renderWindowSpec(expr.spec);
545
545
 
546
- // LAST_VALUE Basic 프레임이 CURRENT ROW까지만 보므로 전체 프레임 명시 필요
546
+ // LAST_VALUE default frame only sees up to CURRENT ROW, so full frame must be specified
547
547
  if (expr.fn.type === "lastValue" && over.length > 0) {
548
548
  over += " ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING";
549
549
  }