@simplysm/orm-common 13.0.69 → 13.0.71

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 +105 -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,172 +0,0 @@
1
- # Expressions (expr)
2
-
3
- The `expr` object generates Dialect-independent SQL expressions. It creates JSON AST instead of SQL strings, which the `QueryBuilder` converts for each DBMS.
4
-
5
- ## Comparison Expressions (WHERE)
6
-
7
- | Method | SQL | Description |
8
- |--------|-----|------|
9
- | `expr.eq(a, b)` | `a = b` (NULL-safe) | Equality comparison |
10
- | `expr.gt(a, b)` | `a > b` | Greater than |
11
- | `expr.lt(a, b)` | `a < b` | Less than |
12
- | `expr.gte(a, b)` | `a >= b` | Greater than or equal |
13
- | `expr.lte(a, b)` | `a <= b` | Less than or equal |
14
- | `expr.between(a, from, to)` | `a BETWEEN from AND to` | Range (unbounded in direction if undefined) |
15
- | `expr.null(a)` | `a IS NULL` | NULL check |
16
- | `expr.like(a, pattern)` | `a LIKE pattern` | Pattern matching |
17
- | `expr.regexp(a, pattern)` | `a REGEXP pattern` | Regex matching |
18
- | `expr.in(a, values)` | `a IN (v1, v2, ...)` | Value list comparison |
19
- | `expr.inQuery(a, query)` | `a IN (SELECT ...)` | Subquery comparison |
20
- | `expr.exists(query)` | `EXISTS (SELECT ...)` | Subquery existence |
21
-
22
- ## Logical Expressions (WHERE)
23
-
24
- | Method | SQL | Description |
25
- |--------|-----|------|
26
- | `expr.and(conditions)` | `(c1 AND c2 AND ...)` | All conditions met |
27
- | `expr.or(conditions)` | `(c1 OR c2 OR ...)` | At least one condition met |
28
- | `expr.not(condition)` | `NOT (condition)` | Negate condition |
29
-
30
- ## String Expressions
31
-
32
- | Method | SQL | Description |
33
- |--------|-----|------|
34
- | `expr.concat(...args)` | `CONCAT(a, b, ...)` | String concatenation |
35
- | `expr.left(s, n)` | `LEFT(s, n)` | Extract n chars from left |
36
- | `expr.right(s, n)` | `RIGHT(s, n)` | Extract n chars from right |
37
- | `expr.trim(s)` | `TRIM(s)` | Trim whitespace from both sides |
38
- | `expr.padStart(s, n, fill)` | `LPAD(s, n, fill)` | Left padding |
39
- | `expr.replace(s, from, to)` | `REPLACE(s, from, to)` | String replacement |
40
- | `expr.upper(s)` | `UPPER(s)` | Convert to uppercase |
41
- | `expr.lower(s)` | `LOWER(s)` | Convert to lowercase |
42
- | `expr.length(s)` | `CHAR_LENGTH(s)` | Character count |
43
- | `expr.byteLength(s)` | `OCTET_LENGTH(s)` | Byte count |
44
- | `expr.substring(s, start, len)` | `SUBSTRING(s, start, len)` | Substring extraction (1-based) |
45
- | `expr.indexOf(s, search)` | `LOCATE(search, s)` | Find position (1-based, 0 if not found) |
46
-
47
- ## Numeric Expressions
48
-
49
- | Method | SQL | Description |
50
- |--------|-----|------|
51
- | `expr.abs(n)` | `ABS(n)` | Absolute value |
52
- | `expr.round(n, digits)` | `ROUND(n, digits)` | Round |
53
- | `expr.ceil(n)` | `CEILING(n)` | Ceiling |
54
- | `expr.floor(n)` | `FLOOR(n)` | Floor |
55
-
56
- ## Date Expressions
57
-
58
- | Method | SQL | Description |
59
- |--------|-----|------|
60
- | `expr.year(d)` | `YEAR(d)` | Extract year |
61
- | `expr.month(d)` | `MONTH(d)` | Extract month (1~12) |
62
- | `expr.day(d)` | `DAY(d)` | Extract day (1~31) |
63
- | `expr.hour(d)` | `HOUR(d)` | Extract hour (0~23) |
64
- | `expr.minute(d)` | `MINUTE(d)` | Extract minute (0~59) |
65
- | `expr.second(d)` | `SECOND(d)` | Extract second (0~59) |
66
- | `expr.isoWeek(d)` | `WEEK(d, 3)` | ISO week (1~53) |
67
- | `expr.isoWeekStartDate(d)` | - | ISO week start date (Monday) |
68
- | `expr.isoYearMonth(d)` | - | First day of the month |
69
- | `expr.dateDiff(sep, from, to)` | `DATEDIFF(sep, from, to)` | Date difference |
70
- | `expr.dateAdd(sep, source, value)` | `DATEADD(sep, value, source)` | Add to date |
71
- | `expr.formatDate(d, format)` | `DATE_FORMAT(d, format)` | Date formatting |
72
-
73
- `DateSeparator`: `"year"`, `"month"`, `"day"`, `"hour"`, `"minute"`, `"second"`
74
-
75
- ## Conditional Expressions
76
-
77
- | Method | SQL | Description |
78
- |--------|-----|------|
79
- | `expr.ifNull(a, b, ...)` | `COALESCE(a, b, ...)` | Return first non-null value |
80
- | `expr.nullIf(a, b)` | `NULLIF(a, b)` | NULL if `a === b` |
81
- | `expr.is(condition)` | `(condition)` | Convert WHERE to boolean |
82
- | `expr.if(cond, then, else)` | `IF(cond, then, else)` | Ternary condition |
83
- | `expr.switch()` | `CASE WHEN ... END` | Multiple condition branching |
84
-
85
- ```typescript
86
- // CASE WHEN usage example
87
- db.user().select((u) => ({
88
- grade: expr.switch<string>()
89
- .case(expr.gte(u.score, 90), "A")
90
- .case(expr.gte(u.score, 80), "B")
91
- .case(expr.gte(u.score, 70), "C")
92
- .default("F"),
93
- }))
94
- ```
95
-
96
- ## Aggregate Expressions
97
-
98
- | Method | SQL | Description |
99
- |--------|-----|------|
100
- | `expr.count(col?, distinct?)` | `COUNT(*)` / `COUNT(DISTINCT col)` | Row count |
101
- | `expr.sum(col)` | `SUM(col)` | Sum |
102
- | `expr.avg(col)` | `AVG(col)` | Average |
103
- | `expr.max(col)` | `MAX(col)` | Maximum |
104
- | `expr.min(col)` | `MIN(col)` | Minimum |
105
- | `expr.greatest(...args)` | `GREATEST(a, b, ...)` | Greatest among multiple values |
106
- | `expr.least(...args)` | `LEAST(a, b, ...)` | Least among multiple values |
107
-
108
- ## Window Functions
109
-
110
- | Method | SQL | Description |
111
- |--------|-----|------|
112
- | `expr.rowNumber(spec)` | `ROW_NUMBER() OVER (...)` | Row number |
113
- | `expr.rank(spec)` | `RANK() OVER (...)` | Rank (gaps on ties) |
114
- | `expr.denseRank(spec)` | `DENSE_RANK() OVER (...)` | Dense rank (consecutive) |
115
- | `expr.ntile(n, spec)` | `NTILE(n) OVER (...)` | Split into n groups |
116
- | `expr.lag(col, spec, opts?)` | `LAG(col, offset) OVER (...)` | Previous row value |
117
- | `expr.lead(col, spec, opts?)` | `LEAD(col, offset) OVER (...)` | Next row value |
118
- | `expr.firstValue(col, spec)` | `FIRST_VALUE(col) OVER (...)` | First value |
119
- | `expr.lastValue(col, spec)` | `LAST_VALUE(col) OVER (...)` | Last value |
120
- | `expr.sumOver(col, spec)` | `SUM(col) OVER (...)` | Window sum |
121
- | `expr.avgOver(col, spec)` | `AVG(col) OVER (...)` | Window average |
122
- | `expr.countOver(spec, col?)` | `COUNT(*) OVER (...)` | Window count |
123
- | `expr.minOver(col, spec)` | `MIN(col) OVER (...)` | Window minimum |
124
- | `expr.maxOver(col, spec)` | `MAX(col) OVER (...)` | Window maximum |
125
-
126
- `WinSpec`: `{ partitionBy?: [...], orderBy?: [[col, "ASC"|"DESC"], ...] }`
127
-
128
- ```typescript
129
- // Window function usage example
130
- db.order().select((o) => ({
131
- ...o,
132
- rowNum: expr.rowNumber({
133
- partitionBy: [o.userId],
134
- orderBy: [[o.createdAt, "DESC"]],
135
- }),
136
- runningTotal: expr.sumOver(o.amount, {
137
- partitionBy: [o.userId],
138
- orderBy: [[o.createdAt, "ASC"]],
139
- }),
140
- }))
141
- ```
142
-
143
- ## Other Expressions
144
-
145
- | Method | SQL | Description |
146
- |--------|-----|------|
147
- | `expr.val(dataType, value)` | Literal | Wrap typed value |
148
- | `expr.col(dataType, ...path)` | Column reference | Create column reference (internal) |
149
- | `expr.raw(dataType)\`sql\`` | Raw SQL | Escape hatch for DBMS-specific functions |
150
- | `expr.rowNum()` | - | Total row number |
151
- | `expr.random()` | `RAND()` / `RANDOM()` | Random number 0~1 |
152
- | `expr.cast(source, type)` | `CAST(source AS type)` | Type conversion |
153
- | `expr.subquery(dataType, qr)` | `(SELECT ...)` | Scalar subquery |
154
-
155
- ```typescript
156
- // Raw SQL (using DBMS-specific functions)
157
- db.user().select((u) => ({
158
- name: u.name,
159
- data: expr.raw("string")`JSON_EXTRACT(${u.metadata}, '$.email')`,
160
- }))
161
-
162
- // Scalar subquery
163
- db.user().select((u) => ({
164
- id: u.id,
165
- postCount: expr.subquery(
166
- "number",
167
- db.post()
168
- .where((p) => [expr.eq(p.userId, u.id)])
169
- .select(() => ({ cnt: expr.count() }))
170
- ),
171
- }))
172
- ```
package/docs/queries.md DELETED
@@ -1,444 +0,0 @@
1
- # Query Operations
2
-
3
- ## Connection and Transactions
4
-
5
- ```typescript
6
- import { defineDbContext, createDbContext } from "@simplysm/orm-common";
7
-
8
- const MyDbDef = defineDbContext({ tables: { user: User } });
9
- const db = createDbContext(MyDbDef, executor, { database: "mydb" });
10
-
11
- // Execute within transaction (auto commit/rollback)
12
- const users = await db.connect(async () => {
13
- const result = await db.user().result();
14
- await db.user().insert([{ name: "John Doe", createdAt: DateTime.now() }]);
15
- return result;
16
- });
17
-
18
- // Connect without transaction (for DDL operations or read-only queries)
19
- await db.connectWithoutTransaction(async () => {
20
- await db.initialize(); // Code First initialization
21
- });
22
-
23
- // Partial transaction within connectWithoutTransaction
24
- await db.connectWithoutTransaction(async () => {
25
- const report = await db.report().result(); // read without transaction
26
- await db.trans(async () => {
27
- await db.log().insert([{ message: "accessed" }]); // write with transaction
28
- });
29
- });
30
-
31
- // Specify isolation level
32
- await db.connect(async () => {
33
- // ...
34
- }, "SERIALIZABLE");
35
- ```
36
-
37
- ### Connection Methods
38
-
39
- | Method | Description |
40
- |--------|-------------|
41
- | `db.connect(fn, isolationLevel?)` | Open connection, begin transaction, run fn, commit. Auto-rollback on error. |
42
- | `db.connectWithoutTransaction(fn)` | Open connection without transaction, run fn, close. |
43
- | `db.trans(fn, isolationLevel?)` | Begin transaction on an already-connected db. Must be called inside `connectWithoutTransaction`. |
44
-
45
- ## SELECT Queries
46
-
47
- ```typescript
48
- // Basic query
49
- const users = await db.user()
50
- .where((u) => [expr.eq(u.isActive, true)])
51
- .orderBy((u) => u.name)
52
- .result();
53
-
54
- // Column selection
55
- const names = await db.user()
56
- .select((u) => ({
57
- userName: u.name,
58
- userEmail: u.email,
59
- }))
60
- .result();
61
-
62
- // Distinct rows
63
- const uniqueNames = await db.user()
64
- .select((u) => ({ name: u.name }))
65
- .distinct()
66
- .result();
67
-
68
- // Single result (error if 2 or more)
69
- const user = await db.user()
70
- .where((u) => [expr.eq(u.id, 1)])
71
- .single();
72
-
73
- // First result only
74
- const latest = await db.user()
75
- .orderBy((u) => u.createdAt, "DESC")
76
- .first();
77
-
78
- // Row count
79
- const count = await db.user()
80
- .where((u) => [expr.eq(u.isActive, true)])
81
- .count();
82
-
83
- // Existence check
84
- const hasAdmin = await db.user()
85
- .where((u) => [expr.eq(u.role, "admin")])
86
- .exists();
87
- ```
88
-
89
- ### Queryable Method Reference
90
-
91
- | Method | Description |
92
- |--------|-------------|
93
- | `.select(fn)` | Map columns to a new shape |
94
- | `.distinct()` | Remove duplicate rows |
95
- | `.where(fn)` | Add WHERE condition (multiple calls = AND) |
96
- | `.orderBy(fn, dir?)` | Add ORDER BY (`"ASC"` or `"DESC"`, default `"ASC"`) |
97
- | `.top(n)` | Return at most n rows |
98
- | `.limit(skip, take)` | Paginate (requires `orderBy` first) |
99
- | `.result()` | Execute and return all rows |
100
- | `.single()` | Execute, return first row, error if > 1 row |
101
- | `.first()` | Execute, return first row only |
102
- | `.count(fn?)` | Execute COUNT query |
103
- | `.exists()` | Return true if any row matches |
104
-
105
- ## JOIN Queries
106
-
107
- ```typescript
108
- // Manual JOIN (1:N - array)
109
- const usersWithPosts = await db.user()
110
- .join("posts", (qr, u) =>
111
- qr.from(Post).where((p) => [expr.eq(p.authorId, u.id)])
112
- )
113
- .result();
114
- // Result: { id, name, posts: [{ id, title }, ...] }
115
-
116
- // Manual JOIN (N:1 - single object)
117
- const postsWithUser = await db.post()
118
- .joinSingle("author", (qr, p) =>
119
- qr.from(User).where((u) => [expr.eq(u.id, p.authorId)])
120
- )
121
- .result();
122
- // Result: { id, title, author: { id, name } | undefined }
123
-
124
- // include (auto JOIN based on relationship definition)
125
- const postWithAuthor = await db.post()
126
- .include((p) => p.author)
127
- .single();
128
-
129
- // Nested include
130
- const postWithAuthorCompany = await db.post()
131
- .include((p) => p.author.company)
132
- .result();
133
-
134
- // Multiple includes
135
- const userWithAll = await db.user()
136
- .include((u) => u.posts)
137
- .include((u) => u.profile)
138
- .result();
139
- ```
140
-
141
- ## Grouping and Aggregation
142
-
143
- ```typescript
144
- const stats = await db.order()
145
- .select((o) => ({
146
- userId: o.userId,
147
- totalAmount: expr.sum(o.amount),
148
- orderCount: expr.count(),
149
- }))
150
- .groupBy((o) => [o.userId])
151
- .having((o) => [expr.gte(o.totalAmount, 10000)])
152
- .result();
153
- ```
154
-
155
- ## Pagination
156
-
157
- ```typescript
158
- // TOP (no ORDER BY required)
159
- const topUsers = await db.user().top(10).result();
160
-
161
- // LIMIT/OFFSET (ORDER BY required)
162
- const page = await db.user()
163
- .orderBy((u) => u.createdAt, "DESC")
164
- .limit(0, 20) // skip 0, take 20
165
- .result();
166
- ```
167
-
168
- ## Text Search
169
-
170
- The `search()` method supports structured search syntax.
171
-
172
- ```typescript
173
- const users = await db.user()
174
- .search((u) => [u.name, u.email], "John -deleted")
175
- .result();
176
- ```
177
-
178
- ### Search Syntax
179
-
180
- | Syntax | Description | Example |
181
- |------|------|------|
182
- | Space | OR combination | `apple banana` |
183
- | `""` | Phrase search (required) | `"delicious apple"` |
184
- | `+` | Required (AND) | `+apple +banana` |
185
- | `-` | Exclude (NOT) | `apple -banana` |
186
- | `*` | Wildcard | `app*` |
187
- | `\*` | Escape | `app\*` (literal `*`) |
188
-
189
- ## UNION
190
-
191
- ```typescript
192
- const allItems = await Queryable.union(
193
- db.user()
194
- .where((u) => [expr.eq(u.type, "admin")])
195
- .select((u) => ({ name: u.name })),
196
- db.user()
197
- .where((u) => [expr.eq(u.type, "manager")])
198
- .select((u) => ({ name: u.name })),
199
- ).result();
200
- ```
201
-
202
- ## Subquery Wrapping (wrap)
203
-
204
- To use `count()` after `distinct()` or `groupBy()`, wrap the query with `wrap()`.
205
-
206
- ```typescript
207
- const count = await db.user()
208
- .select((u) => ({ name: u.name }))
209
- .distinct()
210
- .wrap()
211
- .count();
212
- ```
213
-
214
- ## Recursive CTE (recursive)
215
-
216
- Use for querying hierarchical data (org charts, category trees, etc.).
217
-
218
- ```typescript
219
- const employees = await db.employee()
220
- .where((e) => [expr.null(e.managerId)]) // Root node
221
- .recursive((cte) =>
222
- cte.from(Employee)
223
- .where((e) => [expr.eq(e.managerId, e.self[0].id)])
224
- )
225
- .result();
226
- ```
227
-
228
- ## INSERT
229
-
230
- ```typescript
231
- // Simple insert
232
- await db.user().insert([
233
- { name: "John Doe", createdAt: DateTime.now() },
234
- { name: "Jane Smith", createdAt: DateTime.now() },
235
- ]);
236
-
237
- // Insert with ID return (outputColumns)
238
- const [inserted] = await db.user().insert(
239
- [{ name: "John Doe", createdAt: DateTime.now() }],
240
- ["id"],
241
- );
242
- // inserted.id is available
243
-
244
- // Conditional insert (insert if not exists)
245
- await db.user()
246
- .where((u) => [expr.eq(u.email, "test@test.com")])
247
- .insertIfNotExists({ name: "Test", email: "test@test.com", createdAt: DateTime.now() });
248
-
249
- // INSERT INTO ... SELECT
250
- await db.user()
251
- .select((u) => ({ name: u.name, createdAt: u.createdAt }))
252
- .where((u) => [expr.eq(u.isArchived, false)])
253
- .insertInto(ArchivedUser);
254
- ```
255
-
256
- ## UPDATE
257
-
258
- ```typescript
259
- // Plain values (recommended)
260
- await db.user()
261
- .where((u) => [expr.eq(u.id, 1)])
262
- .update(() => ({
263
- name: "새이름",
264
- updatedAt: DateTime.now(),
265
- }));
266
-
267
- // Column reference (use ExprUnit from callback parameter)
268
- await db.product()
269
- .update((p) => ({
270
- price: expr.mul(p.price, 1.1),
271
- }));
272
-
273
- // Mixed: plain values + expressions
274
- await db.user()
275
- .where((u) => [expr.eq(u.id, 1)])
276
- .update((u) => ({
277
- name: "새이름",
278
- loginCount: expr.raw("number")`${u.loginCount} + 1`,
279
- }));
280
- ```
281
-
282
- ## DELETE
283
-
284
- ```typescript
285
- // Simple delete
286
- await db.user()
287
- .where((u) => [expr.eq(u.id, 1)])
288
- .delete();
289
-
290
- // Return deleted data
291
- const deleted = await db.user()
292
- .where((u) => [expr.eq(u.isExpired, true)])
293
- .delete(["id", "name"]);
294
- ```
295
-
296
- ## UPSERT
297
-
298
- ```typescript
299
- // Same data for UPDATE/INSERT
300
- await db.user()
301
- .where((u) => [expr.eq(u.email, "test@test.com")])
302
- .upsert(() => ({
303
- name: "Test",
304
- email: "test@test.com",
305
- }));
306
-
307
- // Different data for UPDATE/INSERT
308
- await db.user()
309
- .where((u) => [expr.eq(u.email, "test@test.com")])
310
- .upsert(
311
- () => ({ loginCount: 1 }),
312
- (update) => ({ ...update, email: "test@test.com" }),
313
- );
314
- ```
315
-
316
- ## Row Locking (FOR UPDATE)
317
-
318
- ```typescript
319
- await db.connect(async () => {
320
- const user = await db.user()
321
- .where((u) => [expr.eq(u.id, 1)])
322
- .lock()
323
- .single();
324
- });
325
- ```
326
-
327
- ## DDL Operations
328
-
329
- `DbContextInstance` supports Code First DDL operations.
330
-
331
- ```typescript
332
- await db.connectWithoutTransaction(async () => {
333
- // Initialize database (create tables/views/procedures/FKs/indexes)
334
- await db.initialize();
335
-
336
- // Force initialize (drop and recreate existing data)
337
- await db.initialize({ force: true });
338
-
339
- // Individual DDL operations
340
- const c = createColumnFactory();
341
- await db.addColumn({ database: "mydb", name: "User" }, "status", c.varchar(20).nullable());
342
- await db.modifyColumn({ database: "mydb", name: "User" }, "status", c.varchar(50).nullable());
343
- await db.renameColumn({ database: "mydb", name: "User" }, "status", "userStatus");
344
- await db.dropColumn({ database: "mydb", name: "User" }, "userStatus");
345
-
346
- await db.renameTable({ database: "mydb", name: "User" }, "Member");
347
- await db.truncate({ database: "mydb", name: "User" });
348
-
349
- // Table existence / schema check
350
- const exists = await db.schemaExists("mydb");
351
-
352
- // FK management
353
- await db.switchFk({ database: "mydb", name: "User" }, "off");
354
- await db.switchFk({ database: "mydb", name: "User" }, "on");
355
- });
356
- ```
357
-
358
- ### DDL Method Reference
359
-
360
- | Method | Description |
361
- |--------|-------------|
362
- | `db.initialize(opts?)` | Create all tables, views, procedures, FKs, indexes per schema. `opts.force` drops before recreating. |
363
- | `db.createTable(table)` | CREATE TABLE |
364
- | `db.dropTable(name)` | DROP TABLE |
365
- | `db.renameTable(name, newName)` | Rename table |
366
- | `db.createView(view)` | CREATE VIEW |
367
- | `db.dropView(name)` | DROP VIEW |
368
- | `db.createProc(proc)` | CREATE PROCEDURE |
369
- | `db.dropProc(name)` | DROP PROCEDURE |
370
- | `db.addColumn(table, col, builder)` | ALTER TABLE ADD COLUMN |
371
- | `db.dropColumn(table, col)` | ALTER TABLE DROP COLUMN |
372
- | `db.modifyColumn(table, col, builder)` | ALTER TABLE MODIFY COLUMN |
373
- | `db.renameColumn(table, col, newName)` | Rename column |
374
- | `db.addPk(table, cols)` | Add primary key constraint |
375
- | `db.dropPk(table)` | Drop primary key constraint |
376
- | `db.addFk(table, relName, fkBuilder)` | Add foreign key constraint |
377
- | `db.dropFk(table, relName)` | Drop foreign key constraint |
378
- | `db.addIdx(table, idxBuilder)` | Add index |
379
- | `db.dropIdx(table, cols)` | Drop index |
380
- | `db.clearSchema(params)` | Drop all objects in schema |
381
- | `db.schemaExists(database, schema?)` | Check if schema exists |
382
- | `db.truncate(table)` | TRUNCATE TABLE |
383
- | `db.switchFk(table, "on"\|"off")` | Enable/disable FK constraints |
384
-
385
- ## Query Builder (SQL Generation)
386
-
387
- Converts `QueryDef` JSON AST to DBMS-specific SQL strings.
388
-
389
- ```typescript
390
- import { createQueryBuilder } from "@simplysm/orm-common";
391
-
392
- const mysqlBuilder = createQueryBuilder("mysql");
393
- const mssqlBuilder = createQueryBuilder("mssql");
394
- const postgresqlBuilder = createQueryBuilder("postgresql");
395
-
396
- // Convert QueryDef to SQL
397
- const queryDef = db.user()
398
- .where((u) => [expr.eq(u.isActive, true)])
399
- .getSelectQueryDef();
400
-
401
- const { sql } = mysqlBuilder.build(queryDef);
402
- ```
403
-
404
- `QueryBuilderBase.build(def)` accepts any `QueryDef` and returns `QueryBuildResult`:
405
-
406
- ```typescript
407
- interface QueryBuildResult {
408
- sql: string;
409
- resultSetIndex?: number; // which result set index to use (for multi-result queries)
410
- resultSetStride?: number; // stride for multi-result queries (MySQL INSERT with OUTPUT)
411
- }
412
- ```
413
-
414
- ## Error Handling
415
-
416
- ```typescript
417
- import { DbTransactionError, DbErrorCode } from "@simplysm/orm-common";
418
-
419
- try {
420
- await db.connect(async () => {
421
- // ...
422
- });
423
- } catch (err) {
424
- if (err instanceof DbTransactionError) {
425
- switch (err.code) {
426
- case DbErrorCode.DEADLOCK:
427
- // Deadlock retry logic
428
- break;
429
- case DbErrorCode.LOCK_TIMEOUT:
430
- // Timeout handling
431
- break;
432
- }
433
- }
434
- }
435
- ```
436
-
437
- ### DbErrorCode
438
-
439
- | Code | Description |
440
- |------|------|
441
- | `NO_ACTIVE_TRANSACTION` | No active transaction (e.g. rollback with no transaction open) |
442
- | `TRANSACTION_ALREADY_STARTED` | Transaction already started |
443
- | `DEADLOCK` | Deadlock occurred |
444
- | `LOCK_TIMEOUT` | Lock timeout |