@simplysm/orm-common 13.0.100 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/create-db-context.d.ts +10 -10
- package/dist/create-db-context.js +312 -276
- package/dist/create-db-context.js.map +1 -6
- package/dist/ddl/column-ddl.d.ts +4 -4
- package/dist/ddl/column-ddl.js +41 -35
- package/dist/ddl/column-ddl.js.map +1 -6
- package/dist/ddl/initialize.d.ts +17 -17
- package/dist/ddl/initialize.js +200 -142
- package/dist/ddl/initialize.js.map +1 -6
- package/dist/ddl/relation-ddl.d.ts +6 -6
- package/dist/ddl/relation-ddl.js +55 -48
- package/dist/ddl/relation-ddl.js.map +1 -6
- package/dist/ddl/schema-ddl.d.ts +4 -4
- package/dist/ddl/schema-ddl.js +21 -15
- package/dist/ddl/schema-ddl.js.map +1 -6
- package/dist/ddl/table-ddl.d.ts +20 -20
- package/dist/ddl/table-ddl.js +139 -93
- package/dist/ddl/table-ddl.js.map +1 -6
- package/dist/define-db-context.js +10 -13
- package/dist/define-db-context.js.map +1 -6
- package/dist/errors/db-transaction-error.d.ts +15 -15
- package/dist/errors/db-transaction-error.d.ts.map +1 -1
- package/dist/errors/db-transaction-error.js +53 -19
- package/dist/errors/db-transaction-error.js.map +1 -6
- package/dist/exec/executable.d.ts +23 -23
- package/dist/exec/executable.js +94 -40
- package/dist/exec/executable.js.map +1 -6
- package/dist/exec/queryable.d.ts +97 -97
- package/dist/exec/queryable.js +1310 -1204
- package/dist/exec/queryable.js.map +1 -6
- package/dist/exec/search-parser.d.ts +31 -31
- package/dist/exec/search-parser.d.ts.map +1 -1
- package/dist/exec/search-parser.js +158 -59
- package/dist/exec/search-parser.js.map +1 -6
- package/dist/expr/expr-unit.d.ts +4 -4
- package/dist/expr/expr-unit.js +24 -18
- package/dist/expr/expr-unit.js.map +1 -6
- package/dist/expr/expr.d.ts +6 -6
- package/dist/expr/expr.js +1872 -1844
- package/dist/expr/expr.js.map +1 -6
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -6
- package/dist/models/system-migration.js +7 -7
- package/dist/models/system-migration.js.map +1 -6
- package/dist/query-builder/base/expr-renderer-base.d.ts +10 -10
- package/dist/query-builder/base/expr-renderer-base.js +27 -21
- package/dist/query-builder/base/expr-renderer-base.js.map +1 -6
- package/dist/query-builder/base/query-builder-base.d.ts +21 -21
- package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
- package/dist/query-builder/base/query-builder-base.js +90 -80
- package/dist/query-builder/base/query-builder-base.js.map +1 -6
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.js +447 -420
- package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -6
- package/dist/query-builder/mssql/mssql-query-builder.js +483 -443
- package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -6
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.js +451 -419
- package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -6
- package/dist/query-builder/mysql/mysql-query-builder.js +570 -479
- package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -6
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js +449 -422
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -6
- package/dist/query-builder/postgresql/postgresql-query-builder.js +511 -460
- package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -6
- package/dist/query-builder/query-builder.d.ts +1 -1
- package/dist/query-builder/query-builder.js +13 -13
- package/dist/query-builder/query-builder.js.map +1 -6
- package/dist/schema/factory/column-builder.d.ts +84 -84
- package/dist/schema/factory/column-builder.js +248 -185
- package/dist/schema/factory/column-builder.js.map +1 -6
- package/dist/schema/factory/index-builder.d.ts +38 -38
- package/dist/schema/factory/index-builder.js +144 -85
- package/dist/schema/factory/index-builder.js.map +1 -6
- package/dist/schema/factory/relation-builder.d.ts +91 -91
- package/dist/schema/factory/relation-builder.d.ts.map +1 -1
- package/dist/schema/factory/relation-builder.js +274 -136
- package/dist/schema/factory/relation-builder.js.map +1 -6
- package/dist/schema/procedure-builder.d.ts +51 -51
- package/dist/schema/procedure-builder.d.ts.map +1 -1
- package/dist/schema/procedure-builder.js +205 -131
- package/dist/schema/procedure-builder.js.map +1 -6
- package/dist/schema/table-builder.d.ts +55 -55
- package/dist/schema/table-builder.d.ts.map +1 -1
- package/dist/schema/table-builder.js +274 -205
- package/dist/schema/table-builder.js.map +1 -6
- package/dist/schema/view-builder.d.ts +44 -44
- package/dist/schema/view-builder.d.ts.map +1 -1
- package/dist/schema/view-builder.js +189 -116
- package/dist/schema/view-builder.js.map +1 -6
- package/dist/types/column.js +60 -30
- package/dist/types/column.js.map +1 -6
- package/dist/types/db-context-def.d.ts +9 -9
- package/dist/types/db-context-def.js +2 -1
- package/dist/types/db-context-def.js.map +1 -6
- package/dist/types/db.d.ts +47 -47
- package/dist/types/db.js +15 -5
- package/dist/types/db.js.map +1 -6
- package/dist/types/expr.d.ts +81 -81
- package/dist/types/expr.d.ts.map +1 -1
- package/dist/types/expr.js +3 -1
- package/dist/types/expr.js.map +1 -6
- package/dist/types/query-def.d.ts +46 -46
- package/dist/types/query-def.d.ts.map +1 -1
- package/dist/types/query-def.js +31 -24
- package/dist/types/query-def.js.map +1 -6
- package/dist/utils/result-parser.js +362 -221
- package/dist/utils/result-parser.js.map +1 -6
- package/package.json +5 -7
- package/src/create-db-context.ts +31 -31
- package/src/ddl/column-ddl.ts +4 -4
- package/src/ddl/initialize.ts +38 -38
- package/src/ddl/relation-ddl.ts +6 -6
- package/src/ddl/schema-ddl.ts +4 -4
- package/src/ddl/table-ddl.ts +24 -24
- package/src/errors/db-transaction-error.ts +13 -13
- package/src/exec/executable.ts +25 -25
- package/src/exec/queryable.ts +134 -134
- package/src/exec/search-parser.ts +50 -50
- package/src/expr/expr-unit.ts +4 -4
- package/src/expr/expr.ts +13 -13
- package/src/index.ts +8 -8
- package/src/models/system-migration.ts +1 -1
- package/src/query-builder/base/expr-renderer-base.ts +21 -21
- package/src/query-builder/base/query-builder-base.ts +33 -33
- package/src/query-builder/mssql/mssql-expr-renderer.ts +11 -11
- package/src/query-builder/mssql/mssql-query-builder.ts +11 -11
- package/src/query-builder/mysql/mysql-expr-renderer.ts +15 -15
- package/src/query-builder/mysql/mysql-query-builder.ts +3 -3
- package/src/query-builder/postgresql/postgresql-expr-renderer.ts +9 -9
- package/src/query-builder/postgresql/postgresql-query-builder.ts +7 -7
- package/src/query-builder/query-builder.ts +1 -1
- package/src/schema/factory/column-builder.ts +86 -86
- package/src/schema/factory/index-builder.ts +38 -38
- package/src/schema/factory/relation-builder.ts +93 -93
- package/src/schema/procedure-builder.ts +52 -52
- package/src/schema/table-builder.ts +56 -56
- package/src/schema/view-builder.ts +45 -45
- package/src/types/column.ts +1 -1
- package/src/types/db-context-def.ts +15 -15
- package/src/types/db.ts +50 -50
- package/src/types/expr.ts +103 -103
- package/src/types/query-def.ts +50 -50
- package/src/utils/result-parser.ts +39 -39
- package/README.md +0 -192
- package/docs/core.md +0 -234
- package/docs/expression.md +0 -234
- package/docs/query-builder.md +0 -93
- package/docs/queryable.md +0 -198
- package/docs/schema-builders.md +0 -463
- package/docs/types.md +0 -445
- package/docs/utilities.md +0 -27
- package/tests/db-context/create-db-context.spec.ts +0 -193
- package/tests/db-context/define-db-context.spec.ts +0 -17
- package/tests/ddl/basic.expected.ts +0 -341
- package/tests/ddl/basic.spec.ts +0 -557
- package/tests/ddl/column-builder.expected.ts +0 -310
- package/tests/ddl/column-builder.spec.ts +0 -525
- package/tests/ddl/index-builder.expected.ts +0 -38
- package/tests/ddl/index-builder.spec.ts +0 -148
- package/tests/ddl/procedure-builder.expected.ts +0 -52
- package/tests/ddl/procedure-builder.spec.ts +0 -128
- package/tests/ddl/relation-builder.expected.ts +0 -36
- package/tests/ddl/relation-builder.spec.ts +0 -171
- package/tests/ddl/table-builder.expected.ts +0 -113
- package/tests/ddl/table-builder.spec.ts +0 -399
- package/tests/ddl/view-builder.expected.ts +0 -38
- package/tests/ddl/view-builder.spec.ts +0 -116
- package/tests/dml/delete.expected.ts +0 -96
- package/tests/dml/delete.spec.ts +0 -127
- package/tests/dml/insert.expected.ts +0 -192
- package/tests/dml/insert.spec.ts +0 -210
- package/tests/dml/update.expected.ts +0 -176
- package/tests/dml/update.spec.ts +0 -222
- package/tests/dml/upsert.expected.ts +0 -215
- package/tests/dml/upsert.spec.ts +0 -190
- package/tests/errors/queryable-errors.spec.ts +0 -126
- package/tests/escape.spec.ts +0 -59
- package/tests/examples/pivot.expected.ts +0 -211
- package/tests/examples/pivot.spec.ts +0 -200
- package/tests/examples/sampling.expected.ts +0 -69
- package/tests/examples/sampling.spec.ts +0 -42
- package/tests/examples/unpivot.expected.ts +0 -120
- package/tests/examples/unpivot.spec.ts +0 -161
- package/tests/exec/search-parser.spec.ts +0 -267
- package/tests/executable/basic.expected.ts +0 -18
- package/tests/executable/basic.spec.ts +0 -54
- package/tests/expr/comparison.expected.ts +0 -282
- package/tests/expr/comparison.spec.ts +0 -334
- package/tests/expr/conditional.expected.ts +0 -134
- package/tests/expr/conditional.spec.ts +0 -249
- package/tests/expr/date.expected.ts +0 -332
- package/tests/expr/date.spec.ts +0 -459
- package/tests/expr/math.expected.ts +0 -62
- package/tests/expr/math.spec.ts +0 -59
- package/tests/expr/string.expected.ts +0 -218
- package/tests/expr/string.spec.ts +0 -300
- package/tests/expr/utility.expected.ts +0 -147
- package/tests/expr/utility.spec.ts +0 -155
- package/tests/select/basic.expected.ts +0 -322
- package/tests/select/basic.spec.ts +0 -433
- package/tests/select/filter.expected.ts +0 -357
- package/tests/select/filter.spec.ts +0 -954
- package/tests/select/group.expected.ts +0 -169
- package/tests/select/group.spec.ts +0 -159
- package/tests/select/join.expected.ts +0 -582
- package/tests/select/join.spec.ts +0 -692
- package/tests/select/order.expected.ts +0 -150
- package/tests/select/order.spec.ts +0 -140
- package/tests/select/recursive-cte.expected.ts +0 -244
- package/tests/select/recursive-cte.spec.ts +0 -514
- package/tests/select/result-meta.spec.ts +0 -270
- package/tests/select/subquery.expected.ts +0 -363
- package/tests/select/subquery.spec.ts +0 -441
- package/tests/select/view.expected.ts +0 -155
- package/tests/select/view.spec.ts +0 -235
- package/tests/select/window.expected.ts +0 -345
- package/tests/select/window.spec.ts +0 -433
- package/tests/setup/MockExecutor.ts +0 -18
- package/tests/setup/TestDbContext.ts +0 -59
- package/tests/setup/models/Company.ts +0 -13
- package/tests/setup/models/Employee.ts +0 -10
- package/tests/setup/models/MonthlySales.ts +0 -11
- package/tests/setup/models/Post.ts +0 -16
- package/tests/setup/models/Sales.ts +0 -10
- package/tests/setup/models/User.ts +0 -19
- package/tests/setup/procedure/GetAllUsers.ts +0 -9
- package/tests/setup/procedure/GetUserById.ts +0 -12
- package/tests/setup/test-utils.ts +0 -72
- package/tests/setup/views/ActiveUsers.ts +0 -8
- package/tests/setup/views/UserSummary.ts +0 -11
- package/tests/types/nullable-queryable-record.spec.ts +0 -97
- package/tests/utils/result-parser-perf.spec.ts +0 -143
- package/tests/utils/result-parser.spec.ts +0 -667
|
@@ -69,22 +69,22 @@ import type { DataType } from "../../types/column";
|
|
|
69
69
|
import { ExprRendererBase } from "../base/expr-renderer-base";
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* MSSQL
|
|
72
|
+
* MSSQL 표현식 렌더러
|
|
73
73
|
*/
|
|
74
74
|
export class MssqlExprRenderer extends ExprRendererBase {
|
|
75
|
-
//#region ==========
|
|
75
|
+
//#region ========== 유틸리티 (public - QueryBuilder에서도 사용) ==========
|
|
76
76
|
|
|
77
|
-
/**
|
|
77
|
+
/** 식별자 감싸기 */
|
|
78
78
|
wrap(name: string): string {
|
|
79
79
|
return `[${name.replace(/]/g, "]]")}]`;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
82
|
+
/** SQL 문자열 리터럴용 이스케이프 (따옴표 제외) */
|
|
83
83
|
escapeString(value: string): string {
|
|
84
84
|
return value.replace(/'/g, "''");
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/**
|
|
87
|
+
/** 값 이스케이프 */
|
|
88
88
|
escapeValue(value: unknown): string {
|
|
89
89
|
if (value == null) {
|
|
90
90
|
return "NULL";
|
|
@@ -113,7 +113,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
|
|
|
113
113
|
if (value instanceof Uint8Array) {
|
|
114
114
|
return `0x${bytes.toHex(value)}`;
|
|
115
115
|
}
|
|
116
|
-
throw new Error(
|
|
116
|
+
throw new Error(`알 수 없는 값 타입: ${typeof value}`);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/** DataType → SQL type */
|
|
@@ -217,13 +217,13 @@ export class MssqlExprRenderer extends ExprRendererBase {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
protected like(expr: ExprLike): string {
|
|
220
|
-
//
|
|
220
|
+
// 항상 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
|
|
226
|
-
throw new Error("MSSQL
|
|
225
|
+
// MSSQL은 REGEXP을 지원하지 않음 - LIKE 패턴 또는 CLR 필요
|
|
226
|
+
throw new Error("MSSQL은 기본적으로 REGEXP을 지원하지 않습니다.");
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
protected in(expr: ExprIn): string {
|
|
@@ -502,7 +502,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
|
|
|
502
502
|
//#region ========== Other ==========
|
|
503
503
|
|
|
504
504
|
protected greatest(expr: ExprGreatest): string {
|
|
505
|
-
if (expr.args.length === 0) throw new Error("greatest
|
|
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
507
|
// MSSQL 2012+: VALUES + MAX approach
|
|
508
508
|
const values = expr.args.map((a) => `(${this.render(a)})`).join(", ");
|
|
@@ -510,7 +510,7 @@ export class MssqlExprRenderer extends ExprRendererBase {
|
|
|
510
510
|
}
|
|
511
511
|
|
|
512
512
|
protected least(expr: ExprLeast): string {
|
|
513
|
-
if (expr.args.length === 0) throw new Error("least
|
|
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
515
|
// MSSQL 2012+: VALUES + MIN approach
|
|
516
516
|
const values = expr.args.map((a) => `(${this.render(a)})`).join(", ");
|
|
@@ -157,7 +157,7 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
|
|
|
157
157
|
const table = this.tableName(def.table);
|
|
158
158
|
|
|
159
159
|
if (def.records.length === 0) {
|
|
160
|
-
throw new Error("INSERT
|
|
160
|
+
throw new Error("INSERT에는 최소 1개의 레코드가 필요합니다.");
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
const columns = Object.keys(def.records[0]);
|
|
@@ -574,13 +574,13 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
|
|
|
574
574
|
//#region ========== Utils ==========
|
|
575
575
|
|
|
576
576
|
protected clearSchema(def: ClearSchemaQueryDef): QueryBuildResult {
|
|
577
|
-
// SQL
|
|
577
|
+
// SQL 인젝션 방지: 식별자 유효성 검사
|
|
578
578
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(def.database)) {
|
|
579
|
-
throw new Error(
|
|
579
|
+
throw new Error(`잘못된 데이터베이스 이름: ${def.database}`);
|
|
580
580
|
}
|
|
581
581
|
const schemaName = def.schema ?? "dbo";
|
|
582
582
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(schemaName)) {
|
|
583
|
-
throw new Error(
|
|
583
|
+
throw new Error(`잘못된 schema 이름: ${schemaName}`);
|
|
584
584
|
}
|
|
585
585
|
|
|
586
586
|
const db = this.expr.wrap(def.database);
|
|
@@ -590,22 +590,22 @@ export class MssqlQueryBuilder extends QueryBuilderBase {
|
|
|
590
590
|
DECLARE @sql NVARCHAR(MAX);
|
|
591
591
|
SET @sql = N'';
|
|
592
592
|
|
|
593
|
-
--
|
|
593
|
+
-- FK 제약조건 삭제
|
|
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
|
-
--
|
|
598
|
+
-- 테이블 삭제
|
|
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
|
-
--
|
|
603
|
+
-- 뷰 삭제
|
|
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
|
-
--
|
|
608
|
+
-- 프로시저 삭제
|
|
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,13 +615,13 @@ EXEC sp_executesql @sql;`,
|
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
protected schemaExists(def: SchemaExistsQueryDef): QueryBuildResult {
|
|
618
|
-
// SQL
|
|
618
|
+
// SQL 인젝션 방지: 식별자 유효성 검사
|
|
619
619
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(def.database)) {
|
|
620
|
-
throw new Error(
|
|
620
|
+
throw new Error(`잘못된 데이터베이스 이름: ${def.database}`);
|
|
621
621
|
}
|
|
622
622
|
const schemaName = def.schema ?? "dbo";
|
|
623
623
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(schemaName)) {
|
|
624
|
-
throw new Error(
|
|
624
|
+
throw new Error(`잘못된 schema 이름: ${schemaName}`);
|
|
625
625
|
}
|
|
626
626
|
|
|
627
627
|
const dbName = this.expr.escapeString(def.database);
|
|
@@ -69,28 +69,28 @@ import type { DataType } from "../../types/column";
|
|
|
69
69
|
import { ExprRendererBase } from "../base/expr-renderer-base";
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* MySQL
|
|
72
|
+
* MySQL 표현식 렌더러
|
|
73
73
|
*/
|
|
74
74
|
export class MysqlExprRenderer extends ExprRendererBase {
|
|
75
|
-
//#region ==========
|
|
75
|
+
//#region ========== 유틸리티 (public - QueryBuilder에서도 사용) ==========
|
|
76
76
|
|
|
77
|
-
/**
|
|
77
|
+
/** 식별자 감싸기 */
|
|
78
78
|
wrap(name: string): string {
|
|
79
79
|
return `\`${name.replace(/`/g, "``")}\``;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
82
|
+
/** SQL 문자열 리터럴용 이스케이프 (따옴표 제외) */
|
|
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, "\\\\") // 백슬래시 (최우선)
|
|
86
|
+
.replace(/'/g, "''") // 작은따옴표
|
|
87
|
+
.replace(/\0/g, "\\0") // NULL 바이트
|
|
88
|
+
.replace(/\n/g, "\\n") // 줄바꿈
|
|
89
|
+
.replace(/\r/g, "\\r") // 캐리지 리턴
|
|
90
|
+
.replace(/\t/g, "\\t"); // 탭
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
/**
|
|
93
|
+
/** 값 이스케이프 */
|
|
94
94
|
escapeValue(value: unknown): string {
|
|
95
95
|
if (value == null) {
|
|
96
96
|
return "NULL";
|
|
@@ -119,7 +119,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
|
|
|
119
119
|
if (value instanceof Uint8Array) {
|
|
120
120
|
return `0x${bytes.toHex(value)}`;
|
|
121
121
|
}
|
|
122
|
-
throw new Error(
|
|
122
|
+
throw new Error(`알 수 없는 값 타입: ${typeof value}`);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/** DataType → SQL type */
|
|
@@ -221,7 +221,7 @@ export class MysqlExprRenderer extends ExprRendererBase {
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
protected like(expr: ExprLike): string {
|
|
224
|
-
//
|
|
224
|
+
// 항상 ESCAPE '\' 추가
|
|
225
225
|
return `${this.render(expr.source)} LIKE ${this.render(expr.pattern)} ESCAPE '\\\\'`;
|
|
226
226
|
}
|
|
227
227
|
|
|
@@ -512,12 +512,12 @@ export class MysqlExprRenderer extends ExprRendererBase {
|
|
|
512
512
|
//#region ========== Other ==========
|
|
513
513
|
|
|
514
514
|
protected greatest(expr: ExprGreatest): string {
|
|
515
|
-
if (expr.args.length === 0) throw new Error("greatest
|
|
515
|
+
if (expr.args.length === 0) throw new Error("greatest에는 최소 1개의 인수가 필요합니다.");
|
|
516
516
|
return `GREATEST(${expr.args.map((a) => this.render(a)).join(", ")})`;
|
|
517
517
|
}
|
|
518
518
|
|
|
519
519
|
protected least(expr: ExprLeast): string {
|
|
520
|
-
if (expr.args.length === 0) throw new Error("least
|
|
520
|
+
if (expr.args.length === 0) throw new Error("least에는 최소 1개의 인수가 필요합니다.");
|
|
521
521
|
return `LEAST(${expr.args.map((a) => this.render(a)).join(", ")})`;
|
|
522
522
|
}
|
|
523
523
|
|
|
@@ -165,7 +165,7 @@ export class MysqlQueryBuilder extends QueryBuilderBase {
|
|
|
165
165
|
const table = this.tableName(def.table);
|
|
166
166
|
|
|
167
167
|
if (def.records.length === 0) {
|
|
168
|
-
throw new Error("INSERT
|
|
168
|
+
throw new Error("INSERT에는 최소 1개의 레코드가 필요합니다.");
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
const columns = Object.keys(def.records[0]);
|
|
@@ -721,9 +721,9 @@ export class MysqlQueryBuilder extends QueryBuilderBase {
|
|
|
721
721
|
protected clearSchema(def: ClearSchemaQueryDef): QueryBuildResult {
|
|
722
722
|
// MySQL: DROP all tables (in MySQL, database and schema are synonymous)
|
|
723
723
|
// Query table list from information_schema then DROP
|
|
724
|
-
// SQL
|
|
724
|
+
// SQL 인젝션 방지: 식별자 유효성 검사
|
|
725
725
|
if (!/^[a-zA-Z0-9_]+$/.test(def.database)) {
|
|
726
|
-
throw new Error(
|
|
726
|
+
throw new Error(`잘못된 데이터베이스 이름: ${def.database}`);
|
|
727
727
|
}
|
|
728
728
|
|
|
729
729
|
const dbName = this.expr.escapeString(def.database);
|
|
@@ -69,22 +69,22 @@ import type { DataType } from "../../types/column";
|
|
|
69
69
|
import { ExprRendererBase } from "../base/expr-renderer-base";
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* PostgreSQL
|
|
72
|
+
* PostgreSQL 표현식 렌더러
|
|
73
73
|
*/
|
|
74
74
|
export class PostgresqlExprRenderer extends ExprRendererBase {
|
|
75
|
-
//#region ==========
|
|
75
|
+
//#region ========== 유틸리티 (public - QueryBuilder에서도 사용) ==========
|
|
76
76
|
|
|
77
|
-
/**
|
|
77
|
+
/** 식별자 감싸기 */
|
|
78
78
|
wrap(name: string): string {
|
|
79
79
|
return `"${name.replace(/"/g, '""')}"`;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
82
|
+
/** SQL 문자열 리터럴용 이스케이프 (따옴표 제외) */
|
|
83
83
|
escapeString(value: string): string {
|
|
84
84
|
return value.replace(/'/g, "''");
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/**
|
|
87
|
+
/** 값 이스케이프 */
|
|
88
88
|
escapeValue(value: unknown): string {
|
|
89
89
|
if (value == null) {
|
|
90
90
|
return "NULL";
|
|
@@ -113,7 +113,7 @@ export class PostgresqlExprRenderer extends ExprRendererBase {
|
|
|
113
113
|
if (value instanceof Uint8Array) {
|
|
114
114
|
return `'\\x${bytes.toHex(value)}'::bytea`;
|
|
115
115
|
}
|
|
116
|
-
throw new Error(
|
|
116
|
+
throw new Error(`알 수 없는 값 타입: ${typeof value}`);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/** DataType → SQL type */
|
|
@@ -217,7 +217,7 @@ export class PostgresqlExprRenderer extends ExprRendererBase {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
protected like(expr: ExprLike): string {
|
|
220
|
-
//
|
|
220
|
+
// 항상 ESCAPE '\' 추가
|
|
221
221
|
return `${this.render(expr.source)} LIKE ${this.render(expr.pattern)} ESCAPE '\\'`;
|
|
222
222
|
}
|
|
223
223
|
|
|
@@ -510,13 +510,13 @@ export class PostgresqlExprRenderer extends ExprRendererBase {
|
|
|
510
510
|
//#region ========== Other ==========
|
|
511
511
|
|
|
512
512
|
protected greatest(expr: ExprGreatest): string {
|
|
513
|
-
if (expr.args.length === 0) throw new Error("greatest
|
|
513
|
+
if (expr.args.length === 0) throw new Error("greatest에는 최소 1개의 인수가 필요합니다.");
|
|
514
514
|
// PostgreSQL: native GREATEST support
|
|
515
515
|
return `GREATEST(${expr.args.map((a) => this.render(a)).join(", ")})`;
|
|
516
516
|
}
|
|
517
517
|
|
|
518
518
|
protected least(expr: ExprLeast): string {
|
|
519
|
-
if (expr.args.length === 0) throw new Error("least
|
|
519
|
+
if (expr.args.length === 0) throw new Error("least에는 최소 1개의 인수가 필요합니다.");
|
|
520
520
|
// PostgreSQL: native LEAST support
|
|
521
521
|
return `LEAST(${expr.args.map((a) => this.render(a)).join(", ")})`;
|
|
522
522
|
}
|
|
@@ -156,7 +156,7 @@ export class PostgresqlQueryBuilder extends QueryBuilderBase {
|
|
|
156
156
|
const table = this.tableName(def.table);
|
|
157
157
|
|
|
158
158
|
if (def.records.length === 0) {
|
|
159
|
-
throw new Error("INSERT
|
|
159
|
+
throw new Error("INSERT에는 최소 1개의 레코드가 필요합니다.");
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
const columns = Object.keys(def.records[0]);
|
|
@@ -625,9 +625,9 @@ export class PostgresqlQueryBuilder extends QueryBuilderBase {
|
|
|
625
625
|
|
|
626
626
|
protected clearSchema(def: ClearSchemaQueryDef): QueryBuildResult {
|
|
627
627
|
const schemaName = def.schema ?? "public";
|
|
628
|
-
// SQL
|
|
628
|
+
// SQL 인젝션 방지: schema 이름 유효성 검사
|
|
629
629
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(schemaName)) {
|
|
630
|
-
throw new Error(
|
|
630
|
+
throw new Error(`잘못된 schema 이름: ${schemaName}`);
|
|
631
631
|
}
|
|
632
632
|
const schema = this.expr.escapeString(schemaName);
|
|
633
633
|
return {
|
|
@@ -636,7 +636,7 @@ DO $$
|
|
|
636
636
|
DECLARE
|
|
637
637
|
r RECORD;
|
|
638
638
|
BEGIN
|
|
639
|
-
--
|
|
639
|
+
-- FK 제약조건 삭제
|
|
640
640
|
FOR r IN (SELECT conname, conrelid::regclass AS tablename
|
|
641
641
|
FROM pg_constraint
|
|
642
642
|
WHERE contype = 'f' AND connamespace = '${schema}'::regnamespace)
|
|
@@ -644,19 +644,19 @@ BEGIN
|
|
|
644
644
|
EXECUTE 'ALTER TABLE ' || r.tablename || ' DROP CONSTRAINT ' || quote_ident(r.conname);
|
|
645
645
|
END LOOP;
|
|
646
646
|
|
|
647
|
-
--
|
|
647
|
+
-- 테이블 삭제
|
|
648
648
|
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = '${schema}')
|
|
649
649
|
LOOP
|
|
650
650
|
EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE';
|
|
651
651
|
END LOOP;
|
|
652
652
|
|
|
653
|
-
--
|
|
653
|
+
-- 뷰 삭제
|
|
654
654
|
FOR r IN (SELECT viewname FROM pg_views WHERE schemaname = '${schema}')
|
|
655
655
|
LOOP
|
|
656
656
|
EXECUTE 'DROP VIEW IF EXISTS ' || quote_ident(r.viewname) || ' CASCADE';
|
|
657
657
|
END LOOP;
|
|
658
658
|
|
|
659
|
-
--
|
|
659
|
+
-- 함수 삭제
|
|
660
660
|
FOR r IN (SELECT proname, pg_get_function_identity_arguments(oid) AS args
|
|
661
661
|
FROM pg_proc WHERE pronamespace = '${schema}'::regnamespace)
|
|
662
662
|
LOOP
|
|
@@ -5,7 +5,7 @@ import { MysqlQueryBuilder } from "./mysql/mysql-query-builder";
|
|
|
5
5
|
import { PostgresqlQueryBuilder } from "./postgresql/postgresql-query-builder";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* 주어진 Dialect에 맞는 QueryBuilder 인스턴스 생성
|
|
9
9
|
*/
|
|
10
10
|
export function createQueryBuilder(dialect: Dialect): QueryBuilderBase {
|
|
11
11
|
switch (dialect) {
|