@simplysm/orm-common 14.0.1 → 14.0.4

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 (59) hide show
  1. package/README.md +135 -0
  2. package/dist/exec/queryable.js +18 -18
  3. package/dist/exec/queryable.js.map +1 -1
  4. package/dist/expr/expr.d.ts +102 -102
  5. package/dist/expr/expr.js +105 -105
  6. package/dist/expr/expr.js.map +1 -1
  7. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +1 -1
  8. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  9. package/dist/query-builder/mssql/mssql-expr-renderer.js +17 -17
  10. package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
  11. package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
  12. package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
  13. package/dist/query-builder/mssql/mssql-query-builder.js +26 -26
  14. package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
  15. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +1 -1
  16. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  17. package/dist/query-builder/mysql/mysql-expr-renderer.js +14 -14
  18. package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
  19. package/dist/query-builder/mysql/mysql-query-builder.d.ts +10 -10
  20. package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
  21. package/dist/query-builder/mysql/mysql-query-builder.js +67 -67
  22. package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
  23. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +1 -1
  24. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  25. package/dist/query-builder/postgresql/postgresql-expr-renderer.js +13 -13
  26. package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
  27. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +8 -8
  28. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  29. package/dist/query-builder/postgresql/postgresql-query-builder.js +47 -47
  30. package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
  31. package/dist/schema/factory/relation-builder.d.ts +8 -8
  32. package/dist/schema/factory/relation-builder.js +9 -9
  33. package/dist/schema/factory/relation-builder.js.map +1 -1
  34. package/dist/schema/view-builder.js +2 -2
  35. package/dist/schema/view-builder.js.map +1 -1
  36. package/dist/types/column.d.ts +21 -21
  37. package/dist/types/column.js +5 -5
  38. package/dist/utils/result-parser.d.ts +11 -11
  39. package/dist/utils/result-parser.js +48 -48
  40. package/dist/utils/result-parser.js.map +1 -1
  41. package/docs/core.md +206 -0
  42. package/docs/expression.md +217 -0
  43. package/docs/query-builder.md +126 -0
  44. package/docs/queryable.md +236 -0
  45. package/docs/schema-builders.md +352 -0
  46. package/docs/types.md +501 -0
  47. package/package.json +7 -3
  48. package/src/exec/queryable.ts +18 -18
  49. package/src/expr/expr.ts +105 -105
  50. package/src/query-builder/mssql/mssql-expr-renderer.ts +17 -17
  51. package/src/query-builder/mssql/mssql-query-builder.ts +26 -26
  52. package/src/query-builder/mysql/mysql-expr-renderer.ts +14 -14
  53. package/src/query-builder/mysql/mysql-query-builder.ts +67 -67
  54. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +13 -13
  55. package/src/query-builder/postgresql/postgresql-query-builder.ts +47 -47
  56. package/src/schema/factory/relation-builder.ts +9 -9
  57. package/src/schema/view-builder.ts +2 -2
  58. package/src/types/column.ts +23 -23
  59. package/src/utils/result-parser.ts +49 -49
@@ -116,7 +116,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
116
116
  throw new Error(`알 수 없는 값 타입: ${typeof value}`);
117
117
  }
118
118
 
119
- /** DataType → SQL type */
119
+ /** DataType → SQL 타입 변환 */
120
120
  renderDataType(dataType: DataType): string {
121
121
  switch (dataType.type) {
122
122
  case "int":
@@ -176,7 +176,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
176
176
  //#region ========== comparison (null-safe) ==========
177
177
 
178
178
  protected eq(expr: ExprEq): string {
179
- // MSSQL: null-safe equal (OR Pattern)
179
+ // MSSQL: NULL 안전 동등 비교 (OR 패턴)
180
180
  const left = this.render(expr.source);
181
181
  const right = this.render(expr.target);
182
182
  return `((${left} IS NULL AND ${right} IS NULL) OR ${left} = ${right})`;
@@ -228,7 +228,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
228
228
 
229
229
  protected in(expr: ExprIn): string {
230
230
  if (expr.values.length === 0) {
231
- return "1=0"; // empty IN is always false
231
+ return "1=0"; // IN 항상 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
- // Render as SELECT 1
242
+ // SELECT 1로 렌더링
243
243
  const subquery = this.buildSelect({
244
244
  ...expr.query,
245
245
  select: { _: { type: "value", value: 1 } },
@@ -270,7 +270,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
270
270
  //#region ========== String (null handling) ==========
271
271
 
272
272
  protected concat(expr: ExprConcat): string {
273
- // MSSQL 2012+: CONCAT function automatically treats NULL as empty string
273
+ // MSSQL 2012+: CONCAT 함수는 NULL을 자동으로 문자열로 처리
274
274
  const args = expr.args.map((a) => this.render(a)).join(", ");
275
275
  return `CONCAT(${args})`;
276
276
  }
@@ -288,7 +288,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
288
288
  }
289
289
 
290
290
  protected padStart(expr: ExprPadStart): string {
291
- // MSSQL: RIGHT(REPLICATE(fill, len) + source, len)
291
+ // MSSQL: RIGHT(REPLICATE(채움문자, 길이) + 원본, 길이)
292
292
  const source = this.render(expr.source);
293
293
  const len = this.render(expr.length);
294
294
  const fill = this.render(expr.fillString);
@@ -308,12 +308,12 @@ export class MssqlExprRenderer extends ExprRendererBase {
308
308
  }
309
309
 
310
310
  protected length(expr: ExprLength): string {
311
- // MSSQL: LEN() (null handling)
311
+ // MSSQL: LEN() (null 처리)
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 handling)
316
+ // MSSQL: DATALENGTH() (null 처리)
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: if no length, go to end
324
+ // MSSQL: 길이 미지정 끝까지
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 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)
387
+ // ISO 시작일 (월요일) - @@DATEFIRST와 무관하게 항상 월요일 반환
388
+ // 원리: DATEDIFF(DAY, 0, date) 1900-01-01(월요일)부터의 일수
389
+ // (days + 6) % 7 + 1 = 1(), 2(), ..., 7()
390
390
  const weekDay = `((DATEDIFF(DAY, 0, ${src}) + 6) % 7 + 1)`;
391
391
  return `DATEADD(DAY, 1 - ${weekDay}, CAST(${src} AS DATE))`;
392
392
  }
@@ -411,7 +411,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
411
411
  }
412
412
 
413
413
  protected formatDate(expr: ExprFormatDate): string {
414
- // JS format → MSSQL FORMAT style
414
+ // JS 포맷 → MSSQL FORMAT 스타일
415
415
  const mssqlFormat = this.convertDateFormat(expr.format);
416
416
  return `FORMAT(${this.render(expr.source)}, '${mssqlFormat}')`;
417
417
  }
@@ -434,7 +434,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
434
434
  }
435
435
 
436
436
  private convertDateFormat(format: string): string {
437
- // For MSSQL FORMAT function (uses the same format)
437
+ // MSSQL 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에는 최소 1개의 인수가 필요합니다.");
506
506
  if (expr.args.length === 1) return this.render(expr.args[0]);
507
- // MSSQL 2012+: VALUES + MAX approach
507
+ // MSSQL 2012+: VALUES + MAX 방식
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에는 최소 1개의 인수가 필요합니다.");
514
514
  if (expr.args.length === 1) return this.render(expr.args[0]);
515
- // MSSQL 2012+: VALUES + MIN approach
515
+ // MSSQL 2012+: VALUES + MIN 방식
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 default frame only sees up to CURRENT ROW, so full frame must be specified
540
+ // LAST_VALUE 기본 프레임은 CURRENT ROW까지만 보므로 전체 프레임을 지정해야
541
541
  if (expr.fn.type === "lastValue" && over.length > 0) {
542
542
  over += " ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING";
543
543
  }
@@ -43,7 +43,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
43
43
 
44
44
  //#region ========== Utilities ==========
45
45
 
46
- /** Render table name */
46
+ /** 테이블명 렌더링 */
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
- /** Render OFFSET...FETCH clause */
61
+ /** OFFSET...FETCH 렌더링 */
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
- // Detect if LATERAL JOIN is needed → MSSQL uses OUTER APPLY
71
+ // LATERAL JOIN 필요한지 감지 → MSSQL OUTER APPLY 사용
72
72
  if (this.needsLateral(join)) {
73
- // If from is an array (UNION ALL), use renderFrom(join.from),
74
- // otherwise (orderBy, top, select, etc.) use renderFrom(join) to generate subquery
73
+ // from 배열(UNION ALL)이면 renderFrom(join.from) 사용,
74
+ // (orderBy, top, select )이면 renderFrom(join)으로 서브쿼리 생성
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
- // Normal JOIN
79
+ // 일반 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 (force row-level lock with ROWLOCK - same behavior as MySQL/PostgreSQL FOR UPDATE)
125
+ // LOCK (ROWLOCK으로 수준 잠금 강제 - 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 (when inserting explicit values into AI column)
168
+ // IDENTITY_INSERT ON (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 native support)
175
+ // OUTPUT (MSSQL 네이티브 지원)
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
- // With overrideIdentity: SET ON → results[0], INSERT → results[1], SET OFF → results[2]
194
+ // 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
- // Render existsSelectQuery as SELECT 1 AS _
205
+ // existsSelectQuery 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 must be placed before SELECT)
213
+ // OUTPUT (MSSQL: OUTPUT 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
- // Extract columns from INSERT INTO SELECT
228
+ // INSERT INTO SELECT에서 column 추출
229
229
  const selectDef = def.recordsSelectQuery;
230
230
  const colList =
231
231
  selectDef.select != null
@@ -292,7 +292,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
292
292
  }
293
293
  sql += ` ${alias}`;
294
294
 
295
- // OUTPUT (MSSQL: DELETED for DELETE)
295
+ // OUTPUT (MSSQL: DELETE에는 DELETED 사용)
296
296
  if (def.output != null) {
297
297
  const outputCols = def.output.columns.map((c) => `DELETED.${this.expr.wrap(c)}`).join(", ");
298
298
  sql += ` OUTPUT ${outputCols}`;
@@ -310,17 +310,17 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
310
310
  //#region ========== DML - UPSERT ==========
311
311
 
312
312
  protected upsert(def: UpsertQueryDef): QueryBuildResult {
313
- // MSSQL: Use MERGE
313
+ // MSSQL: 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;
317
317
 
318
- // UPDATE SET part
318
+ // UPDATE SET 부분
319
319
  const updateSetParts = Object.entries(def.updateRecord).map(
320
320
  ([col, e]) => `${this.expr.wrap(col)} = ${this.expr.render(e)}`,
321
321
  );
322
322
 
323
- // INSERT part
323
+ // INSERT 부분
324
324
  const insertColumns = Object.keys(def.insertRecord);
325
325
  const insertColList = insertColumns.map((c) => this.expr.wrap(c)).join(", ");
326
326
  const insertValues = insertColumns.map((c) => this.expr.render(def.insertRecord[c])).join(", ");
@@ -356,7 +356,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
356
356
  const colDefs = def.columns.map((col) => {
357
357
  let colSql = `${this.expr.wrap(col.name)} ${this.expr.renderDataType(col.dataType)}`;
358
358
 
359
- // nullable: true → NULL, else → NOT NULL
359
+ // nullable: true → NULL, 아니면 → NOT NULL
360
360
  if (col.nullable === true) {
361
361
  colSql += " NULL";
362
362
  } else {
@@ -374,7 +374,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
374
374
  return colSql;
375
375
  });
376
376
 
377
- // Primary Key with CONSTRAINT name
377
+ // CONSTRAINT 이름이 포함된 Primary Key
378
378
  if (def.primaryKey != null && def.primaryKey.length > 0) {
379
379
  const pkCols = def.primaryKey.map((c) => this.expr.wrap(c)).join(", ");
380
380
  const pkName = this.expr.wrap(`PK_${def.table.name}`);
@@ -396,7 +396,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
396
396
  }
397
397
 
398
398
  protected truncate(def: TruncateQueryDef): QueryBuildResult {
399
- // MSSQL: TRUNCATE automatically resets IDENTITY
399
+ // MSSQL: TRUNCATE 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 and DEFAULT require separate handling)
450
+ // MSSQL: ALTER COLUMN (IDENTITY DEFAULT 별도 처리 필요)
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: Use sp_rename
456
+ // MSSQL: 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: separate index generation needed for FK
490
+ // MSSQL/PostgreSQL: 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
 
@@ -519,7 +519,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
519
519
  protected createView(def: CreateViewQueryDef): QueryBuildResult {
520
520
  const view = this.tableName(def.view);
521
521
  const selectSql = this.select(def.queryDef).sql;
522
- // MSSQL: CREATE OR ALTER VIEW (2016 SP1+)
522
+ // MSSQL: CREATE OR ALTER VIEW (2016 SP1 이상)
523
523
  return { sql: `CREATE OR ALTER VIEW ${view} AS ${selectSql}` };
524
524
  }
525
525
 
@@ -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
- // Process params
533
+ // 파라미터 처리
534
534
  const paramList =
535
535
  def.params
536
536
  ?.map((p) => {
@@ -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: check database existence then check schema (using dynamic SQL)
629
+ // MSSQL: 데이터베이스 존재 여부 확인 schema 확인 (동적 SQL 사용)
630
630
  return {
631
631
  sql: `DECLARE @result NVARCHAR(MAX) = NULL;
632
632
  IF EXISTS (SELECT 1 FROM sys.databases WHERE name = '${dbName}')
@@ -122,7 +122,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
122
122
  throw new Error(`알 수 없는 값 타입: ${typeof value}`);
123
123
  }
124
124
 
125
- /** DataType → SQL type */
125
+ /** DataType → SQL 타입 변환 */
126
126
  renderDataType(dataType: DataType): string {
127
127
  switch (dataType.type) {
128
128
  case "int":
@@ -182,7 +182,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
182
182
  //#region ========== comparison (null-safe) ==========
183
183
 
184
184
  protected eq(expr: ExprEq): string {
185
- // MySQL: <=> operator (null-safe equal)
185
+ // MySQL: <=> 연산자 (NULL 안전 동등 비교)
186
186
  return `${this.render(expr.source)} <=> ${this.render(expr.target)}`;
187
187
  }
188
188
 
@@ -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"; // empty IN is always false
234
+ return "1=0"; // IN 항상 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
- // Render as SELECT 1
245
+ // SELECT 1로 렌더링
246
246
  const subquery = this.buildSelect({
247
247
  ...expr.query,
248
248
  select: { _: { type: "value", value: 1 } },
@@ -273,7 +273,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
273
273
  //#region ========== String (null handling) ==========
274
274
 
275
275
  protected concat(expr: ExprConcat): string {
276
- // null handling: IFNULL(arg, '')
276
+ // null 처리: 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 handling: IFNULL(arg, '')
310
+ // null 처리: 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 handling: IFNULL(arg, '')
315
+ // null 처리: 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 week start date (Monday)
383
+ // ISO 시작일 (월요일)
384
384
  return `DATE_SUB(${this.render(expr.arg)}, INTERVAL (WEEKDAY(${this.render(expr.arg)})) DAY)`;
385
385
  }
386
386
 
@@ -415,7 +415,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
415
415
  }
416
416
 
417
417
  protected formatDate(expr: ExprFormatDate): string {
418
- // JS format → MySQL format
418
+ // JS 포맷 → MySQL 포맷
419
419
  const mysqlFormat = this.convertDateFormat(expr.format);
420
420
  return `DATE_FORMAT(${this.render(expr.source)}, '${mysqlFormat}')`;
421
421
  }
@@ -438,7 +438,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
438
438
  }
439
439
 
440
440
  private convertDateFormat(format: string): string {
441
- // Simple transform (yyyy-MM-dd HH:mm:ss format)
441
+ // 단순 변환 (yyyy-MM-dd HH:mm:ss 포맷)
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
- // Render as COALESCE (first non-null among multiple values)
458
+ // COALESCE로 렌더링 (여러 값 중 첫 번째 non-null 반환)
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: use variables or ROW_NUMBER() window function
526
- // Implemented with ROW_NUMBER() here (MySQL 8.0+)
525
+ // MySQL: 변수 또는 ROW_NUMBER() 윈도우 함수 사용
526
+ // 여기서는 ROW_NUMBER() 구현 (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 default frame only sees up to CURRENT ROW, so full frame must be specified
546
+ // LAST_VALUE 기본 프레임은 CURRENT ROW까지만 보므로 전체 프레임을 지정해야
547
547
  if (expr.fn.type === "lastValue" && over.length > 0) {
548
548
  over += " ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING";
549
549
  }