@simplysm/orm-common 13.0.69 → 13.0.70

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 (204) hide show
  1. package/README.md +54 -1447
  2. package/dist/create-db-context.d.ts +10 -10
  3. package/dist/create-db-context.js +9 -9
  4. package/dist/create-db-context.js.map +1 -1
  5. package/dist/ddl/column-ddl.d.ts +4 -4
  6. package/dist/ddl/initialize.d.ts +17 -17
  7. package/dist/ddl/initialize.js +2 -2
  8. package/dist/ddl/initialize.js.map +1 -1
  9. package/dist/ddl/relation-ddl.d.ts +6 -6
  10. package/dist/ddl/schema-ddl.d.ts +4 -4
  11. package/dist/ddl/table-ddl.d.ts +24 -24
  12. package/dist/ddl/table-ddl.js +4 -4
  13. package/dist/ddl/table-ddl.js.map +1 -1
  14. package/dist/errors/db-transaction-error.d.ts +15 -15
  15. package/dist/errors/db-transaction-error.d.ts.map +1 -1
  16. package/dist/exec/executable.d.ts +23 -23
  17. package/dist/exec/executable.js +3 -3
  18. package/dist/exec/executable.js.map +1 -1
  19. package/dist/exec/queryable.d.ts +160 -160
  20. package/dist/exec/queryable.js +119 -119
  21. package/dist/exec/queryable.js.map +1 -1
  22. package/dist/exec/search-parser.d.ts +37 -37
  23. package/dist/exec/search-parser.d.ts.map +1 -1
  24. package/dist/expr/expr-unit.d.ts +4 -4
  25. package/dist/expr/expr.d.ts +257 -257
  26. package/dist/expr/expr.js +265 -265
  27. package/dist/expr/expr.js.map +1 -1
  28. package/dist/query-builder/base/expr-renderer-base.d.ts +9 -9
  29. package/dist/query-builder/base/expr-renderer-base.js +2 -2
  30. package/dist/query-builder/base/expr-renderer-base.js.map +1 -1
  31. package/dist/query-builder/base/query-builder-base.d.ts +26 -26
  32. package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
  33. package/dist/query-builder/base/query-builder-base.js +22 -22
  34. package/dist/query-builder/base/query-builder-base.js.map +1 -1
  35. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +4 -4
  36. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  37. package/dist/query-builder/mssql/mssql-expr-renderer.js +18 -18
  38. package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
  39. package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
  40. package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
  41. package/dist/query-builder/mssql/mssql-query-builder.js +11 -11
  42. package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
  43. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +4 -4
  44. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  45. package/dist/query-builder/mysql/mysql-expr-renderer.js +17 -17
  46. package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
  47. package/dist/query-builder/mysql/mysql-query-builder.d.ts +8 -8
  48. package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
  49. package/dist/query-builder/mysql/mysql-query-builder.js +5 -5
  50. package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
  51. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +4 -4
  52. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  53. package/dist/query-builder/postgresql/postgresql-expr-renderer.js +17 -17
  54. package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
  55. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +5 -5
  56. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  57. package/dist/query-builder/postgresql/postgresql-query-builder.js +8 -8
  58. package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
  59. package/dist/query-builder/query-builder.d.ts +1 -1
  60. package/dist/schema/factory/column-builder.d.ts +79 -79
  61. package/dist/schema/factory/column-builder.js +42 -42
  62. package/dist/schema/factory/index-builder.d.ts +39 -39
  63. package/dist/schema/factory/index-builder.js +26 -26
  64. package/dist/schema/factory/relation-builder.d.ts +99 -99
  65. package/dist/schema/factory/relation-builder.d.ts.map +1 -1
  66. package/dist/schema/factory/relation-builder.js +38 -38
  67. package/dist/schema/procedure-builder.d.ts +49 -49
  68. package/dist/schema/procedure-builder.d.ts.map +1 -1
  69. package/dist/schema/procedure-builder.js +33 -33
  70. package/dist/schema/table-builder.d.ts +59 -59
  71. package/dist/schema/table-builder.d.ts.map +1 -1
  72. package/dist/schema/table-builder.js +43 -43
  73. package/dist/schema/view-builder.d.ts +49 -49
  74. package/dist/schema/view-builder.d.ts.map +1 -1
  75. package/dist/schema/view-builder.js +32 -32
  76. package/dist/types/column.d.ts +22 -22
  77. package/dist/types/column.js +1 -1
  78. package/dist/types/column.js.map +1 -1
  79. package/dist/types/db.d.ts +40 -40
  80. package/dist/types/expr.d.ts +59 -59
  81. package/dist/types/expr.d.ts.map +1 -1
  82. package/dist/types/query-def.d.ts +44 -44
  83. package/dist/types/query-def.d.ts.map +1 -1
  84. package/dist/utils/result-parser.d.ts +11 -11
  85. package/dist/utils/result-parser.js +3 -3
  86. package/dist/utils/result-parser.js.map +1 -1
  87. package/package.json +5 -5
  88. package/src/create-db-context.ts +20 -20
  89. package/src/ddl/column-ddl.ts +4 -4
  90. package/src/ddl/initialize.ts +259 -259
  91. package/src/ddl/relation-ddl.ts +89 -89
  92. package/src/ddl/schema-ddl.ts +4 -4
  93. package/src/ddl/table-ddl.ts +189 -189
  94. package/src/errors/db-transaction-error.ts +13 -13
  95. package/src/exec/executable.ts +25 -25
  96. package/src/exec/queryable.ts +2033 -2033
  97. package/src/exec/search-parser.ts +57 -57
  98. package/src/expr/expr-unit.ts +4 -4
  99. package/src/expr/expr.ts +2140 -2140
  100. package/src/query-builder/base/expr-renderer-base.ts +237 -237
  101. package/src/query-builder/base/query-builder-base.ts +213 -213
  102. package/src/query-builder/mssql/mssql-expr-renderer.ts +607 -607
  103. package/src/query-builder/mssql/mssql-query-builder.ts +650 -650
  104. package/src/query-builder/mysql/mysql-expr-renderer.ts +613 -613
  105. package/src/query-builder/mysql/mysql-query-builder.ts +759 -759
  106. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +611 -611
  107. package/src/query-builder/postgresql/postgresql-query-builder.ts +686 -686
  108. package/src/query-builder/query-builder.ts +19 -19
  109. package/src/schema/factory/column-builder.ts +423 -423
  110. package/src/schema/factory/index-builder.ts +164 -164
  111. package/src/schema/factory/relation-builder.ts +453 -453
  112. package/src/schema/procedure-builder.ts +232 -232
  113. package/src/schema/table-builder.ts +319 -319
  114. package/src/schema/view-builder.ts +221 -221
  115. package/src/types/column.ts +188 -188
  116. package/src/types/db.ts +208 -208
  117. package/src/types/expr.ts +697 -697
  118. package/src/types/query-def.ts +513 -513
  119. package/src/utils/result-parser.ts +458 -458
  120. package/tests/db-context/create-db-context.spec.ts +224 -0
  121. package/tests/db-context/define-db-context.spec.ts +68 -0
  122. package/tests/ddl/basic.expected.ts +341 -0
  123. package/tests/ddl/basic.spec.ts +714 -0
  124. package/tests/ddl/column-builder.expected.ts +310 -0
  125. package/tests/ddl/column-builder.spec.ts +637 -0
  126. package/tests/ddl/index-builder.expected.ts +38 -0
  127. package/tests/ddl/index-builder.spec.ts +202 -0
  128. package/tests/ddl/procedure-builder.expected.ts +52 -0
  129. package/tests/ddl/procedure-builder.spec.ts +234 -0
  130. package/tests/ddl/relation-builder.expected.ts +36 -0
  131. package/tests/ddl/relation-builder.spec.ts +372 -0
  132. package/tests/ddl/table-builder.expected.ts +113 -0
  133. package/tests/ddl/table-builder.spec.ts +433 -0
  134. package/tests/ddl/view-builder.expected.ts +38 -0
  135. package/tests/ddl/view-builder.spec.ts +176 -0
  136. package/tests/dml/delete.expected.ts +96 -0
  137. package/tests/dml/delete.spec.ts +160 -0
  138. package/tests/dml/insert.expected.ts +192 -0
  139. package/tests/dml/insert.spec.ts +288 -0
  140. package/tests/dml/update.expected.ts +176 -0
  141. package/tests/dml/update.spec.ts +318 -0
  142. package/tests/dml/upsert.expected.ts +215 -0
  143. package/tests/dml/upsert.spec.ts +242 -0
  144. package/tests/errors/queryable-errors.spec.ts +177 -0
  145. package/tests/escape.spec.ts +100 -0
  146. package/tests/examples/pivot.expected.ts +211 -0
  147. package/tests/examples/pivot.spec.ts +533 -0
  148. package/tests/examples/sampling.expected.ts +69 -0
  149. package/tests/examples/sampling.spec.ts +104 -0
  150. package/tests/examples/unpivot.expected.ts +120 -0
  151. package/tests/examples/unpivot.spec.ts +226 -0
  152. package/tests/exec/search-parser.spec.ts +283 -0
  153. package/tests/executable/basic.expected.ts +18 -0
  154. package/tests/executable/basic.spec.ts +54 -0
  155. package/tests/expr/comparison.expected.ts +282 -0
  156. package/tests/expr/comparison.spec.ts +400 -0
  157. package/tests/expr/conditional.expected.ts +134 -0
  158. package/tests/expr/conditional.spec.ts +276 -0
  159. package/tests/expr/date.expected.ts +332 -0
  160. package/tests/expr/date.spec.ts +526 -0
  161. package/tests/expr/math.expected.ts +62 -0
  162. package/tests/expr/math.spec.ts +106 -0
  163. package/tests/expr/string.expected.ts +218 -0
  164. package/tests/expr/string.spec.ts +356 -0
  165. package/tests/expr/utility.expected.ts +147 -0
  166. package/tests/expr/utility.spec.ts +182 -0
  167. package/tests/select/basic.expected.ts +322 -0
  168. package/tests/select/basic.spec.ts +502 -0
  169. package/tests/select/filter.expected.ts +357 -0
  170. package/tests/select/filter.spec.ts +1068 -0
  171. package/tests/select/group.expected.ts +169 -0
  172. package/tests/select/group.spec.ts +244 -0
  173. package/tests/select/join.expected.ts +582 -0
  174. package/tests/select/join.spec.ts +805 -0
  175. package/tests/select/order.expected.ts +150 -0
  176. package/tests/select/order.spec.ts +189 -0
  177. package/tests/select/recursive-cte.expected.ts +244 -0
  178. package/tests/select/recursive-cte.spec.ts +514 -0
  179. package/tests/select/result-meta.spec.ts +270 -0
  180. package/tests/select/subquery.expected.ts +363 -0
  181. package/tests/select/subquery.spec.ts +537 -0
  182. package/tests/select/view.expected.ts +155 -0
  183. package/tests/select/view.spec.ts +235 -0
  184. package/tests/select/window.expected.ts +345 -0
  185. package/tests/select/window.spec.ts +618 -0
  186. package/tests/setup/MockExecutor.ts +18 -0
  187. package/tests/setup/TestDbContext.ts +59 -0
  188. package/tests/setup/models/Company.ts +13 -0
  189. package/tests/setup/models/Employee.ts +10 -0
  190. package/tests/setup/models/MonthlySales.ts +11 -0
  191. package/tests/setup/models/Post.ts +16 -0
  192. package/tests/setup/models/Sales.ts +10 -0
  193. package/tests/setup/models/User.ts +19 -0
  194. package/tests/setup/procedure/GetAllUsers.ts +9 -0
  195. package/tests/setup/procedure/GetUserById.ts +12 -0
  196. package/tests/setup/test-utils.ts +72 -0
  197. package/tests/setup/views/ActiveUsers.ts +8 -0
  198. package/tests/setup/views/UserSummary.ts +11 -0
  199. package/tests/types/nullable-queryable-record.spec.ts +145 -0
  200. package/tests/utils/result-parser-perf.spec.ts +210 -0
  201. package/tests/utils/result-parser.spec.ts +701 -0
  202. package/docs/expressions.md +0 -172
  203. package/docs/queries.md +0 -444
  204. package/docs/schema.md +0 -245
@@ -1,213 +1,213 @@
1
- import type {
2
- QueryDef,
3
- SelectQueryDef,
4
- InsertQueryDef,
5
- InsertIfNotExistsQueryDef,
6
- InsertIntoQueryDef,
7
- UpdateQueryDef,
8
- DeleteQueryDef,
9
- UpsertQueryDef,
10
- CreateTableQueryDef,
11
- DropTableQueryDef,
12
- RenameTableQueryDef,
13
- TruncateQueryDef,
14
- AddColumnQueryDef,
15
- DropColumnQueryDef,
16
- ModifyColumnQueryDef,
17
- RenameColumnQueryDef,
18
- AddPkQueryDef,
19
- DropPkQueryDef,
20
- AddFkQueryDef,
21
- DropFkQueryDef,
22
- AddIdxQueryDef,
23
- DropIdxQueryDef,
24
- CreateViewQueryDef,
25
- DropViewQueryDef,
26
- CreateProcQueryDef,
27
- DropProcQueryDef,
28
- ExecProcQueryDef,
29
- ClearSchemaQueryDef,
30
- SchemaExistsQueryDef,
31
- SwitchFkQueryDef,
32
- SelectQueryDefJoin,
33
- QueryDefObjectName,
34
- } from "../../types/query-def";
35
- import type { Expr, WhereExpr } from "../../types/expr";
36
- import type { QueryBuildResult } from "../../types/db";
37
- import type { ExprRendererBase } from "./expr-renderer-base";
38
-
39
- /**
40
- * QueryDef → SQL 렌더링 추상 기본 클래스
41
- *
42
- * Base 원칙:
43
- * - 100% 모든 dialect가 동일한 로직만 구현 (dispatch)
44
- * - 조금이라도 다르면 전부 abstract
45
- * - 메서드명은 def.type 동일 (동적 dispatch 가능)
46
- */
47
- export abstract class QueryBuilderBase {
48
- protected abstract expr: ExprRendererBase;
49
-
50
- //#region ========== Dispatch (100% 동일) ==========
51
-
52
- build(def: QueryDef): QueryBuildResult {
53
- const method = this[def.type as keyof this];
54
- if (typeof method !== "function") {
55
- throw new Error(`알 수 없는 QueryDef 타입: ${def.type}`);
56
- }
57
- return (method as (d: QueryDef) => QueryBuildResult).call(this, def);
58
- }
59
-
60
- /** SQL QueryBuildResult 래핑하는 헬퍼 */
61
- protected result(sql: string, resultSetIndex?: number): QueryBuildResult {
62
- return resultSetIndex != null ? { sql, resultSetIndex } : { sql };
63
- }
64
-
65
- //#endregion
66
-
67
- //#region ========== 공통 렌더링 메서드 (100% 동일) ==========
68
-
69
- /** 테이블명 렌더링 (dialect별로 다르므로 abstract) */
70
- protected abstract tableName(obj: QueryDefObjectName): string;
71
-
72
- /** WHERE 렌더링 */
73
- protected renderWhere(wheres: WhereExpr[] | undefined): string {
74
- if (wheres == null || wheres.length === 0) return "";
75
- return ` WHERE ${this.expr.renderWhere(wheres)}`;
76
- }
77
-
78
- /** ORDER BY 렌더링 */
79
- protected renderOrderBy(orderBy: [Expr, ("ASC" | "DESC")?][] | undefined): string {
80
- if (orderBy == null || orderBy.length === 0) return "";
81
- const parts = orderBy.map(
82
- ([e, dir]) => `${this.expr.render(e)}${dir != null ? ` ${dir}` : ""}`,
83
- );
84
- return ` ORDER BY ${parts.join(", ")}`;
85
- }
86
-
87
- /** GROUP BY 렌더링 */
88
- protected renderGroupBy(groupBy: Expr[] | undefined): string {
89
- if (groupBy == null || groupBy.length === 0) return "";
90
- return ` GROUP BY ${groupBy.map((g) => this.expr.render(g)).join(", ")}`;
91
- }
92
-
93
- /** HAVING 렌더링 */
94
- protected renderHaving(having: WhereExpr[] | undefined): string {
95
- if (having == null || having.length === 0) return "";
96
- return ` HAVING ${this.expr.renderWhere(having)}`;
97
- }
98
-
99
- /** JOIN 렌더링 */
100
- protected renderJoins(joins: SelectQueryDefJoin[] | undefined): string {
101
- if (joins == null || joins.length === 0) return "";
102
- return joins.map((j) => this.renderJoin(j)).join("");
103
- }
104
-
105
- /** 단일 JOIN 렌더링 (dialect별로 다르므로 abstract) */
106
- protected abstract renderJoin(join: SelectQueryDefJoin): string;
107
-
108
- /**
109
- * JOIN LATERAL/CROSS APPLY가 필요한지 감지
110
- *
111
- * 기본 JOIN 속성(type, from, as, where, isSingle) 있으면 일반 JOIN으로 처리.
112
- * 속성이 있으면 서브쿼리가 필요하므로 LATERAL JOIN 사용:
113
- *
114
- * - select: 컬럼 가공/집계가 필요 (일반 JOIN 테이블 전체를 참조)
115
- * - joins: 중첩 JOIN 서브쿼리 내부에서 처리
116
- * - orderBy, top, limit: 정렬/제한을 서브쿼리 내부에서 적용
117
- * - groupBy, having: 집계를 서브쿼리 내부에서 수행
118
- * - distinct: 중복 제거를 서브쿼리 내부에서 적용
119
- * - from (배열): UNION ALL 패턴
120
- *
121
- * 주의: select joins 중첩 join 자동 생성되므로 basicJoinProps에 포함하지 않음.
122
- * 사용자가 직접 .select() 호출하지 않아도 내부 .joinSingle() 호출로 인해
123
- * select/joins 추가될 있으며, 이 경우에도 서브쿼리가 필요함.
124
- */
125
- protected needsLateral(join: SelectQueryDefJoin): boolean {
126
- // from 배열이면 무조건 LATERAL (UNION ALL 패턴)
127
- if (Array.isArray(join.from)) {
128
- return true;
129
- }
130
-
131
- // join 자체에 기본 JOIN 속성 외의 추가 속성이 있으면 LATERAL 필요
132
- const basicJoinProps = ["type", "from", "as", "where", "isSingle"];
133
- return Object.keys(join).some((key) => !basicJoinProps.includes(key));
134
- }
135
-
136
- /** FROM 소스 렌더링 */
137
- protected renderFrom(from: SelectQueryDef["from"]): string {
138
- if (from == null) {
139
- throw new Error("FROM 필요합니다.");
140
- }
141
- if (typeof from === "string") {
142
- return this.expr.wrap(from);
143
- }
144
- if ("type" in from) {
145
- return `(${this.select(from).sql})`;
146
- }
147
- if (Array.isArray(from)) {
148
- return `(${from.map((f) => this.select(f).sql).join(" UNION ALL ")})`;
149
- }
150
- return this.tableName(from);
151
- }
152
-
153
- //#endregion
154
-
155
- //#region ========== Abstract - DML ==========
156
-
157
- protected abstract select(def: SelectQueryDef): QueryBuildResult;
158
- protected abstract insert(def: InsertQueryDef): QueryBuildResult;
159
- protected abstract insertIfNotExists(def: InsertIfNotExistsQueryDef): QueryBuildResult;
160
- protected abstract insertInto(def: InsertIntoQueryDef): QueryBuildResult;
161
- protected abstract update(def: UpdateQueryDef): QueryBuildResult;
162
- protected abstract delete(def: DeleteQueryDef): QueryBuildResult;
163
- protected abstract upsert(def: UpsertQueryDef): QueryBuildResult;
164
-
165
- //#endregion
166
-
167
- //#region ========== Abstract - DDL Table ==========
168
-
169
- protected abstract createTable(def: CreateTableQueryDef): QueryBuildResult;
170
- protected abstract dropTable(def: DropTableQueryDef): QueryBuildResult;
171
- protected abstract renameTable(def: RenameTableQueryDef): QueryBuildResult;
172
- protected abstract truncate(def: TruncateQueryDef): QueryBuildResult;
173
-
174
- //#endregion
175
-
176
- //#region ========== Abstract - DDL Column ==========
177
-
178
- protected abstract addColumn(def: AddColumnQueryDef): QueryBuildResult;
179
- protected abstract dropColumn(def: DropColumnQueryDef): QueryBuildResult;
180
- protected abstract modifyColumn(def: ModifyColumnQueryDef): QueryBuildResult;
181
- protected abstract renameColumn(def: RenameColumnQueryDef): QueryBuildResult;
182
-
183
- //#endregion
184
-
185
- //#region ========== Abstract - DDL Constraint ==========
186
-
187
- protected abstract addPk(def: AddPkQueryDef): QueryBuildResult;
188
- protected abstract dropPk(def: DropPkQueryDef): QueryBuildResult;
189
- protected abstract addFk(def: AddFkQueryDef): QueryBuildResult;
190
- protected abstract dropFk(def: DropFkQueryDef): QueryBuildResult;
191
- protected abstract addIdx(def: AddIdxQueryDef): QueryBuildResult;
192
- protected abstract dropIdx(def: DropIdxQueryDef): QueryBuildResult;
193
-
194
- //#endregion
195
-
196
- //#region ========== Abstract - DDL View/Procedure ==========
197
-
198
- protected abstract createView(def: CreateViewQueryDef): QueryBuildResult;
199
- protected abstract dropView(def: DropViewQueryDef): QueryBuildResult;
200
- protected abstract createProc(def: CreateProcQueryDef): QueryBuildResult;
201
- protected abstract dropProc(def: DropProcQueryDef): QueryBuildResult;
202
- protected abstract execProc(def: ExecProcQueryDef): QueryBuildResult;
203
-
204
- //#endregion
205
-
206
- //#region ========== Abstract - Utils ==========
207
-
208
- protected abstract clearSchema(def: ClearSchemaQueryDef): QueryBuildResult;
209
- protected abstract schemaExists(def: SchemaExistsQueryDef): QueryBuildResult;
210
- protected abstract switchFk(def: SwitchFkQueryDef): QueryBuildResult;
211
-
212
- //#endregion
213
- }
1
+ import type {
2
+ QueryDef,
3
+ SelectQueryDef,
4
+ InsertQueryDef,
5
+ InsertIfNotExistsQueryDef,
6
+ InsertIntoQueryDef,
7
+ UpdateQueryDef,
8
+ DeleteQueryDef,
9
+ UpsertQueryDef,
10
+ CreateTableQueryDef,
11
+ DropTableQueryDef,
12
+ RenameTableQueryDef,
13
+ TruncateQueryDef,
14
+ AddColumnQueryDef,
15
+ DropColumnQueryDef,
16
+ ModifyColumnQueryDef,
17
+ RenameColumnQueryDef,
18
+ AddPkQueryDef,
19
+ DropPkQueryDef,
20
+ AddFkQueryDef,
21
+ DropFkQueryDef,
22
+ AddIdxQueryDef,
23
+ DropIdxQueryDef,
24
+ CreateViewQueryDef,
25
+ DropViewQueryDef,
26
+ CreateProcQueryDef,
27
+ DropProcQueryDef,
28
+ ExecProcQueryDef,
29
+ ClearSchemaQueryDef,
30
+ SchemaExistsQueryDef,
31
+ SwitchFkQueryDef,
32
+ SelectQueryDefJoin,
33
+ QueryDefObjectName,
34
+ } from "../../types/query-def";
35
+ import type { Expr, WhereExpr } from "../../types/expr";
36
+ import type { QueryBuildResult } from "../../types/db";
37
+ import type { ExprRendererBase } from "./expr-renderer-base";
38
+
39
+ /**
40
+ * QueryDef → SQL render abstract base class
41
+ *
42
+ * Base principles:
43
+ * - Implement only 100% identical logic across all dialects (dispatch)
44
+ * - If different at all, make it abstract
45
+ * - Method name identical to def.type (enables dynamic dispatch)
46
+ */
47
+ export abstract class QueryBuilderBase {
48
+ protected abstract expr: ExprRendererBase;
49
+
50
+ //#region ========== Dispatch (100% identical) ==========
51
+
52
+ build(def: QueryDef): QueryBuildResult {
53
+ const method = this[def.type as keyof this];
54
+ if (typeof method !== "function") {
55
+ throw new Error(`Unknown QueryDef type: ${def.type}`);
56
+ }
57
+ return (method as (d: QueryDef) => QueryBuildResult).call(this, def);
58
+ }
59
+
60
+ /** Helper to wrap SQL in QueryBuildResult */
61
+ protected result(sql: string, resultSetIndex?: number): QueryBuildResult {
62
+ return resultSetIndex != null ? { sql, resultSetIndex } : { sql };
63
+ }
64
+
65
+ //#endregion
66
+
67
+ //#region ========== Common render method (100% identical) ==========
68
+
69
+ /** Table name render (different per dialect, so abstract) */
70
+ protected abstract tableName(obj: QueryDefObjectName): string;
71
+
72
+ /** WHERE clause render */
73
+ protected renderWhere(wheres: WhereExpr[] | undefined): string {
74
+ if (wheres == null || wheres.length === 0) return "";
75
+ return ` WHERE ${this.expr.renderWhere(wheres)}`;
76
+ }
77
+
78
+ /** ORDER BY clause render */
79
+ protected renderOrderBy(orderBy: [Expr, ("ASC" | "DESC")?][] | undefined): string {
80
+ if (orderBy == null || orderBy.length === 0) return "";
81
+ const parts = orderBy.map(
82
+ ([e, dir]) => `${this.expr.render(e)}${dir != null ? ` ${dir}` : ""}`,
83
+ );
84
+ return ` ORDER BY ${parts.join(", ")}`;
85
+ }
86
+
87
+ /** GROUP BY clause render */
88
+ protected renderGroupBy(groupBy: Expr[] | undefined): string {
89
+ if (groupBy == null || groupBy.length === 0) return "";
90
+ return ` GROUP BY ${groupBy.map((g) => this.expr.render(g)).join(", ")}`;
91
+ }
92
+
93
+ /** HAVING clause render */
94
+ protected renderHaving(having: WhereExpr[] | undefined): string {
95
+ if (having == null || having.length === 0) return "";
96
+ return ` HAVING ${this.expr.renderWhere(having)}`;
97
+ }
98
+
99
+ /** JOIN clause render */
100
+ protected renderJoins(joins: SelectQueryDefJoin[] | undefined): string {
101
+ if (joins == null || joins.length === 0) return "";
102
+ return joins.map((j) => this.renderJoin(j)).join("");
103
+ }
104
+
105
+ /** Single JOIN render (different per dialect, so abstract) */
106
+ protected abstract renderJoin(join: SelectQueryDefJoin): string;
107
+
108
+ /**
109
+ * Detect if JOIN needs LATERAL/CROSS APPLY
110
+ *
111
+ * If JOIN has only basic properties (type, from, as, where, isSingle), treat as normal JOIN.
112
+ * Otherwise, subquery is needed, so use LATERAL JOIN:
113
+ *
114
+ * - select: column transformation/aggregation needed (normal JOIN references entire table)
115
+ * - joins: nested JOIN handled inside subquery
116
+ * - orderBy, top, limit: sorting/limit applied inside subquery
117
+ * - groupBy, having: aggregation performed inside subquery
118
+ * - distinct: deduplication applied inside subquery
119
+ * - from (array): UNION ALL pattern
120
+ *
121
+ * Note: select and joins are auto-generated during nested joins, so not in basicJoinProps.
122
+ * Even if user doesn't call .select() directly, internal .joinSingle() may add
123
+ * select/joins, which also requires subquery.
124
+ */
125
+ protected needsLateral(join: SelectQueryDefJoin): boolean {
126
+ // If from is array, always LATERAL (UNION ALL pattern)
127
+ if (Array.isArray(join.from)) {
128
+ return true;
129
+ }
130
+
131
+ // LATERAL needed if join has additional properties beyond basic JOIN properties
132
+ const basicJoinProps = ["type", "from", "as", "where", "isSingle"];
133
+ return Object.keys(join).some((key) => !basicJoinProps.includes(key));
134
+ }
135
+
136
+ /** FROM clause source render */
137
+ protected renderFrom(from: SelectQueryDef["from"]): string {
138
+ if (from == null) {
139
+ throw new Error("FROM clause is required.");
140
+ }
141
+ if (typeof from === "string") {
142
+ return this.expr.wrap(from);
143
+ }
144
+ if ("type" in from) {
145
+ return `(${this.select(from).sql})`;
146
+ }
147
+ if (Array.isArray(from)) {
148
+ return `(${from.map((f) => this.select(f).sql).join(" UNION ALL ")})`;
149
+ }
150
+ return this.tableName(from);
151
+ }
152
+
153
+ //#endregion
154
+
155
+ //#region ========== Abstract - DML ==========
156
+
157
+ protected abstract select(def: SelectQueryDef): QueryBuildResult;
158
+ protected abstract insert(def: InsertQueryDef): QueryBuildResult;
159
+ protected abstract insertIfNotExists(def: InsertIfNotExistsQueryDef): QueryBuildResult;
160
+ protected abstract insertInto(def: InsertIntoQueryDef): QueryBuildResult;
161
+ protected abstract update(def: UpdateQueryDef): QueryBuildResult;
162
+ protected abstract delete(def: DeleteQueryDef): QueryBuildResult;
163
+ protected abstract upsert(def: UpsertQueryDef): QueryBuildResult;
164
+
165
+ //#endregion
166
+
167
+ //#region ========== Abstract - DDL Table ==========
168
+
169
+ protected abstract createTable(def: CreateTableQueryDef): QueryBuildResult;
170
+ protected abstract dropTable(def: DropTableQueryDef): QueryBuildResult;
171
+ protected abstract renameTable(def: RenameTableQueryDef): QueryBuildResult;
172
+ protected abstract truncate(def: TruncateQueryDef): QueryBuildResult;
173
+
174
+ //#endregion
175
+
176
+ //#region ========== Abstract - DDL Column ==========
177
+
178
+ protected abstract addColumn(def: AddColumnQueryDef): QueryBuildResult;
179
+ protected abstract dropColumn(def: DropColumnQueryDef): QueryBuildResult;
180
+ protected abstract modifyColumn(def: ModifyColumnQueryDef): QueryBuildResult;
181
+ protected abstract renameColumn(def: RenameColumnQueryDef): QueryBuildResult;
182
+
183
+ //#endregion
184
+
185
+ //#region ========== Abstract - DDL Constraint ==========
186
+
187
+ protected abstract addPk(def: AddPkQueryDef): QueryBuildResult;
188
+ protected abstract dropPk(def: DropPkQueryDef): QueryBuildResult;
189
+ protected abstract addFk(def: AddFkQueryDef): QueryBuildResult;
190
+ protected abstract dropFk(def: DropFkQueryDef): QueryBuildResult;
191
+ protected abstract addIdx(def: AddIdxQueryDef): QueryBuildResult;
192
+ protected abstract dropIdx(def: DropIdxQueryDef): QueryBuildResult;
193
+
194
+ //#endregion
195
+
196
+ //#region ========== Abstract - DDL View/Procedure ==========
197
+
198
+ protected abstract createView(def: CreateViewQueryDef): QueryBuildResult;
199
+ protected abstract dropView(def: DropViewQueryDef): QueryBuildResult;
200
+ protected abstract createProc(def: CreateProcQueryDef): QueryBuildResult;
201
+ protected abstract dropProc(def: DropProcQueryDef): QueryBuildResult;
202
+ protected abstract execProc(def: ExecProcQueryDef): QueryBuildResult;
203
+
204
+ //#endregion
205
+
206
+ //#region ========== Abstract - Utils ==========
207
+
208
+ protected abstract clearSchema(def: ClearSchemaQueryDef): QueryBuildResult;
209
+ protected abstract schemaExists(def: SchemaExistsQueryDef): QueryBuildResult;
210
+ protected abstract switchFk(def: SwitchFkQueryDef): QueryBuildResult;
211
+
212
+ //#endregion
213
+ }