@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.
- package/README.md +54 -1447
- package/dist/create-db-context.d.ts +10 -10
- package/dist/create-db-context.js +9 -9
- package/dist/create-db-context.js.map +1 -1
- package/dist/ddl/column-ddl.d.ts +4 -4
- package/dist/ddl/initialize.d.ts +17 -17
- package/dist/ddl/initialize.js +2 -2
- package/dist/ddl/initialize.js.map +1 -1
- package/dist/ddl/relation-ddl.d.ts +6 -6
- package/dist/ddl/schema-ddl.d.ts +4 -4
- package/dist/ddl/table-ddl.d.ts +24 -24
- package/dist/ddl/table-ddl.js +4 -4
- package/dist/ddl/table-ddl.js.map +1 -1
- package/dist/errors/db-transaction-error.d.ts +15 -15
- package/dist/errors/db-transaction-error.d.ts.map +1 -1
- package/dist/exec/executable.d.ts +23 -23
- package/dist/exec/executable.js +3 -3
- package/dist/exec/executable.js.map +1 -1
- package/dist/exec/queryable.d.ts +160 -160
- package/dist/exec/queryable.js +119 -119
- package/dist/exec/queryable.js.map +1 -1
- package/dist/exec/search-parser.d.ts +37 -37
- package/dist/exec/search-parser.d.ts.map +1 -1
- package/dist/expr/expr-unit.d.ts +4 -4
- package/dist/expr/expr.d.ts +257 -257
- package/dist/expr/expr.js +265 -265
- package/dist/expr/expr.js.map +1 -1
- package/dist/query-builder/base/expr-renderer-base.d.ts +9 -9
- package/dist/query-builder/base/expr-renderer-base.js +2 -2
- package/dist/query-builder/base/expr-renderer-base.js.map +1 -1
- package/dist/query-builder/base/query-builder-base.d.ts +26 -26
- package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
- package/dist/query-builder/base/query-builder-base.js +22 -22
- package/dist/query-builder/base/query-builder-base.js.map +1 -1
- 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 +18 -18
- package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
- package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.js +11 -11
- package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
- 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 +17 -17
- package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.d.ts +8 -8
- package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.js +5 -5
- package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
- 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 +17 -17
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +5 -5
- package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.js +8 -8
- package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
- package/dist/query-builder/query-builder.d.ts +1 -1
- package/dist/schema/factory/column-builder.d.ts +79 -79
- package/dist/schema/factory/column-builder.js +42 -42
- package/dist/schema/factory/index-builder.d.ts +39 -39
- package/dist/schema/factory/index-builder.js +26 -26
- package/dist/schema/factory/relation-builder.d.ts +99 -99
- package/dist/schema/factory/relation-builder.d.ts.map +1 -1
- package/dist/schema/factory/relation-builder.js +38 -38
- package/dist/schema/procedure-builder.d.ts +49 -49
- package/dist/schema/procedure-builder.d.ts.map +1 -1
- package/dist/schema/procedure-builder.js +33 -33
- package/dist/schema/table-builder.d.ts +59 -59
- package/dist/schema/table-builder.d.ts.map +1 -1
- package/dist/schema/table-builder.js +43 -43
- package/dist/schema/view-builder.d.ts +49 -49
- package/dist/schema/view-builder.d.ts.map +1 -1
- package/dist/schema/view-builder.js +32 -32
- package/dist/types/column.d.ts +22 -22
- package/dist/types/column.js +1 -1
- package/dist/types/column.js.map +1 -1
- package/dist/types/db.d.ts +40 -40
- package/dist/types/expr.d.ts +59 -59
- package/dist/types/expr.d.ts.map +1 -1
- package/dist/types/query-def.d.ts +44 -44
- package/dist/types/query-def.d.ts.map +1 -1
- package/dist/utils/result-parser.d.ts +11 -11
- package/dist/utils/result-parser.js +3 -3
- package/dist/utils/result-parser.js.map +1 -1
- package/package.json +5 -5
- package/src/create-db-context.ts +20 -20
- package/src/ddl/column-ddl.ts +4 -4
- package/src/ddl/initialize.ts +259 -259
- package/src/ddl/relation-ddl.ts +89 -89
- package/src/ddl/schema-ddl.ts +4 -4
- package/src/ddl/table-ddl.ts +189 -189
- package/src/errors/db-transaction-error.ts +13 -13
- package/src/exec/executable.ts +25 -25
- package/src/exec/queryable.ts +2033 -2033
- package/src/exec/search-parser.ts +57 -57
- package/src/expr/expr-unit.ts +4 -4
- package/src/expr/expr.ts +2140 -2140
- package/src/query-builder/base/expr-renderer-base.ts +237 -237
- package/src/query-builder/base/query-builder-base.ts +213 -213
- package/src/query-builder/mssql/mssql-expr-renderer.ts +607 -607
- package/src/query-builder/mssql/mssql-query-builder.ts +650 -650
- package/src/query-builder/mysql/mysql-expr-renderer.ts +613 -613
- package/src/query-builder/mysql/mysql-query-builder.ts +759 -759
- package/src/query-builder/postgresql/postgresql-expr-renderer.ts +611 -611
- package/src/query-builder/postgresql/postgresql-query-builder.ts +686 -686
- package/src/query-builder/query-builder.ts +19 -19
- package/src/schema/factory/column-builder.ts +423 -423
- package/src/schema/factory/index-builder.ts +164 -164
- package/src/schema/factory/relation-builder.ts +453 -453
- package/src/schema/procedure-builder.ts +232 -232
- package/src/schema/table-builder.ts +319 -319
- package/src/schema/view-builder.ts +221 -221
- package/src/types/column.ts +188 -188
- package/src/types/db.ts +208 -208
- package/src/types/expr.ts +697 -697
- package/src/types/query-def.ts +513 -513
- package/src/utils/result-parser.ts +458 -458
- package/tests/db-context/create-db-context.spec.ts +224 -0
- package/tests/db-context/define-db-context.spec.ts +68 -0
- package/tests/ddl/basic.expected.ts +341 -0
- package/tests/ddl/basic.spec.ts +714 -0
- package/tests/ddl/column-builder.expected.ts +310 -0
- package/tests/ddl/column-builder.spec.ts +637 -0
- package/tests/ddl/index-builder.expected.ts +38 -0
- package/tests/ddl/index-builder.spec.ts +202 -0
- package/tests/ddl/procedure-builder.expected.ts +52 -0
- package/tests/ddl/procedure-builder.spec.ts +234 -0
- package/tests/ddl/relation-builder.expected.ts +36 -0
- package/tests/ddl/relation-builder.spec.ts +372 -0
- package/tests/ddl/table-builder.expected.ts +113 -0
- package/tests/ddl/table-builder.spec.ts +433 -0
- package/tests/ddl/view-builder.expected.ts +38 -0
- package/tests/ddl/view-builder.spec.ts +176 -0
- package/tests/dml/delete.expected.ts +96 -0
- package/tests/dml/delete.spec.ts +160 -0
- package/tests/dml/insert.expected.ts +192 -0
- package/tests/dml/insert.spec.ts +288 -0
- package/tests/dml/update.expected.ts +176 -0
- package/tests/dml/update.spec.ts +318 -0
- package/tests/dml/upsert.expected.ts +215 -0
- package/tests/dml/upsert.spec.ts +242 -0
- package/tests/errors/queryable-errors.spec.ts +177 -0
- package/tests/escape.spec.ts +100 -0
- package/tests/examples/pivot.expected.ts +211 -0
- package/tests/examples/pivot.spec.ts +533 -0
- package/tests/examples/sampling.expected.ts +69 -0
- package/tests/examples/sampling.spec.ts +105 -0
- package/tests/examples/unpivot.expected.ts +120 -0
- package/tests/examples/unpivot.spec.ts +226 -0
- package/tests/exec/search-parser.spec.ts +283 -0
- package/tests/executable/basic.expected.ts +18 -0
- package/tests/executable/basic.spec.ts +54 -0
- package/tests/expr/comparison.expected.ts +282 -0
- package/tests/expr/comparison.spec.ts +400 -0
- package/tests/expr/conditional.expected.ts +134 -0
- package/tests/expr/conditional.spec.ts +276 -0
- package/tests/expr/date.expected.ts +332 -0
- package/tests/expr/date.spec.ts +526 -0
- package/tests/expr/math.expected.ts +62 -0
- package/tests/expr/math.spec.ts +106 -0
- package/tests/expr/string.expected.ts +218 -0
- package/tests/expr/string.spec.ts +356 -0
- package/tests/expr/utility.expected.ts +147 -0
- package/tests/expr/utility.spec.ts +182 -0
- package/tests/select/basic.expected.ts +322 -0
- package/tests/select/basic.spec.ts +502 -0
- package/tests/select/filter.expected.ts +357 -0
- package/tests/select/filter.spec.ts +1068 -0
- package/tests/select/group.expected.ts +169 -0
- package/tests/select/group.spec.ts +244 -0
- package/tests/select/join.expected.ts +582 -0
- package/tests/select/join.spec.ts +805 -0
- package/tests/select/order.expected.ts +150 -0
- package/tests/select/order.spec.ts +189 -0
- package/tests/select/recursive-cte.expected.ts +244 -0
- package/tests/select/recursive-cte.spec.ts +514 -0
- package/tests/select/result-meta.spec.ts +270 -0
- package/tests/select/subquery.expected.ts +363 -0
- package/tests/select/subquery.spec.ts +537 -0
- package/tests/select/view.expected.ts +155 -0
- package/tests/select/view.spec.ts +235 -0
- package/tests/select/window.expected.ts +345 -0
- package/tests/select/window.spec.ts +618 -0
- package/tests/setup/MockExecutor.ts +18 -0
- package/tests/setup/TestDbContext.ts +59 -0
- package/tests/setup/models/Company.ts +13 -0
- package/tests/setup/models/Employee.ts +10 -0
- package/tests/setup/models/MonthlySales.ts +11 -0
- package/tests/setup/models/Post.ts +16 -0
- package/tests/setup/models/Sales.ts +10 -0
- package/tests/setup/models/User.ts +19 -0
- package/tests/setup/procedure/GetAllUsers.ts +9 -0
- package/tests/setup/procedure/GetUserById.ts +12 -0
- package/tests/setup/test-utils.ts +72 -0
- package/tests/setup/views/ActiveUsers.ts +8 -0
- package/tests/setup/views/UserSummary.ts +11 -0
- package/tests/types/nullable-queryable-record.spec.ts +145 -0
- package/tests/utils/result-parser-perf.spec.ts +210 -0
- package/tests/utils/result-parser.spec.ts +701 -0
- package/docs/expressions.md +0 -172
- package/docs/queries.md +0 -444
- package/docs/schema.md +0 -245
package/README.md
CHANGED
|
@@ -1,1472 +1,79 @@
|
|
|
1
1
|
# @simplysm/orm-common
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
It generates JSON AST instead of SQL strings, which are then converted to SQL for each DBMS (MySQL, MSSQL, PostgreSQL).
|
|
3
|
+
Simplysm Package - ORM Module (common)
|
|
5
4
|
|
|
6
5
|
## Installation
|
|
7
6
|
|
|
8
|
-
```bash
|
|
9
|
-
npm install @simplysm/orm-common
|
|
10
|
-
# or
|
|
11
7
|
pnpm add @simplysm/orm-common
|
|
12
|
-
```
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
**Peer Dependencies:** `@simplysm/core-common`
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|-------------|---------|----------|
|
|
18
|
-
| MySQL | `mysql` | 8.0.14+ |
|
|
19
|
-
| SQL Server | `mssql` | 2012+ |
|
|
20
|
-
| PostgreSQL | `postgresql` | 9.0+ |
|
|
21
|
-
|
|
22
|
-
## Main Modules
|
|
11
|
+
## Source Index
|
|
23
12
|
|
|
24
13
|
### Core
|
|
25
14
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const MyDbDef = defineDbContext({
|
|
34
|
-
tables: { user: User, post: Post },
|
|
35
|
-
views: { activeUsers: ActiveUsers },
|
|
36
|
-
procedures: { getUserById: GetUserById },
|
|
37
|
-
migrations: [
|
|
38
|
-
{
|
|
39
|
-
name: "20260101_add_status",
|
|
40
|
-
up: async (db) => {
|
|
41
|
-
const c = createColumnFactory();
|
|
42
|
-
await db.addColumn(
|
|
43
|
-
{ database: "mydb", name: "User" },
|
|
44
|
-
"status",
|
|
45
|
-
c.varchar(20).nullable(),
|
|
46
|
-
);
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
});
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
| Parameter | Type | Description |
|
|
54
|
-
|-----------|------|-------------|
|
|
55
|
-
| `config.tables` | `Record<string, TableBuilder>` | Table builders |
|
|
56
|
-
| `config.views` | `Record<string, ViewBuilder>` | View builders (optional) |
|
|
57
|
-
| `config.procedures` | `Record<string, ProcedureBuilder>` | Procedure builders (optional) |
|
|
58
|
-
| `config.migrations` | `Migration[]` | Migration list (optional) |
|
|
59
|
-
|
|
60
|
-
Returns `DbContextDef<TTables, TViews, TProcedures>`.
|
|
61
|
-
|
|
62
|
-
Note: `defineDbContext` automatically adds an internal `_Migration` system table to `tables` to track applied migrations.
|
|
63
|
-
|
|
64
|
-
#### `createDbContext(def, executor, opt)`
|
|
65
|
-
|
|
66
|
-
Creates a fully functional `DbContextInstance` from a `DbContextDef`.
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
import { createDbContext } from "@simplysm/orm-common";
|
|
70
|
-
|
|
71
|
-
const db = createDbContext(MyDbDef, executor, { database: "mydb" });
|
|
72
|
-
|
|
73
|
-
await db.connect(async () => {
|
|
74
|
-
const users = await db.user().result();
|
|
75
|
-
});
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
| Parameter | Type | Description |
|
|
79
|
-
|-----------|------|-------------|
|
|
80
|
-
| `def` | `DbContextDef` | Schema definition from `defineDbContext()` |
|
|
81
|
-
| `executor` | `DbContextExecutor` | Query executor (from `orm-node` or service client) |
|
|
82
|
-
| `opt.database` | `string` | Default database name |
|
|
83
|
-
| `opt.schema` | `string?` | Default schema name (MSSQL: `dbo`, PostgreSQL: `public`) |
|
|
84
|
-
|
|
85
|
-
Returns `DbContextInstance<TDef>`.
|
|
86
|
-
|
|
87
|
-
#### Type Definitions
|
|
88
|
-
|
|
89
|
-
| Type | Description |
|
|
90
|
-
|------|-------------|
|
|
91
|
-
| `DbContextDef<TTables, TViews, TProcedures>` | DbContext definition (schema blueprint) |
|
|
92
|
-
| `DbContextInstance<TDef>` | Full DbContext instance with queryable accessors and DDL methods |
|
|
93
|
-
| `DbContextBase` | Core interface used internally (status, executeDefs, etc.) |
|
|
94
|
-
| `DbContextConnectionMethods` | Interface for connection/transaction methods (`connect`, `connectWithoutTransaction`, `trans`) |
|
|
95
|
-
| `DbContextDdlMethods` | Interface for all DDL methods and QueryDef generator methods |
|
|
96
|
-
| `DbContextStatus` | `"ready" \| "connect" \| "transact"` — current connection status |
|
|
97
|
-
|
|
98
|
-
#### `DbTransactionError`
|
|
99
|
-
|
|
100
|
-
Database transaction error class. Wraps DBMS-native errors with a standardized `DbErrorCode`.
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import { DbTransactionError, DbErrorCode } from "@simplysm/orm-common";
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
await executor.rollbackTransaction();
|
|
107
|
-
} catch (err) {
|
|
108
|
-
if (err instanceof DbTransactionError) {
|
|
109
|
-
if (err.code === DbErrorCode.NO_ACTIVE_TRANSACTION) {
|
|
110
|
-
return; // Already rolled back, ignore
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
throw err;
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
| `DbErrorCode` value | Description |
|
|
118
|
-
|---------------------|-------------|
|
|
119
|
-
| `NO_ACTIVE_TRANSACTION` | No active transaction (rollback when none exists) |
|
|
120
|
-
| `TRANSACTION_ALREADY_STARTED` | Transaction already started |
|
|
121
|
-
| `DEADLOCK` | Deadlock detected |
|
|
122
|
-
| `LOCK_TIMEOUT` | Lock wait timeout |
|
|
123
|
-
|
|
124
|
-
`DbTransactionError` properties:
|
|
125
|
-
|
|
126
|
-
| Property | Type | Description |
|
|
127
|
-
|----------|------|-------------|
|
|
128
|
-
| `code` | `DbErrorCode` | Standardized error code |
|
|
129
|
-
| `message` | `string` | Error message |
|
|
130
|
-
| `originalError` | `unknown?` | Original DBMS error (for debugging) |
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
### DbContext Instance API
|
|
135
|
-
|
|
136
|
-
The `DbContextInstance` returned by `createDbContext` exposes the following methods:
|
|
137
|
-
|
|
138
|
-
#### Connection Management
|
|
139
|
-
|
|
140
|
-
| Method | Description |
|
|
141
|
-
|--------|-------------|
|
|
142
|
-
| `connect(fn, isolationLevel?)` | Open connection → begin transaction → run callback → commit → close. Rolls back on error. |
|
|
143
|
-
| `connectWithoutTransaction(fn)` | Open connection → run callback → close. No transaction. Use for DDL or read-only operations. |
|
|
144
|
-
| `trans(fn, isolationLevel?)` | Start a transaction within an already-connected context. Use inside `connectWithoutTransaction` when partial transactions are needed. |
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
// Default: with transaction
|
|
148
|
-
await db.connect(async () => {
|
|
149
|
-
await db.user().insert([{ name: "Alice" }]);
|
|
150
|
-
await db.post().insert([{ title: "Hello", userId: 1 }]);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// Without transaction (e.g., for DDL)
|
|
154
|
-
await db.connectWithoutTransaction(async () => {
|
|
155
|
-
await db.createTable(User);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// Nested transaction inside connectWithoutTransaction
|
|
159
|
-
await db.connectWithoutTransaction(async () => {
|
|
160
|
-
const report = await db.report().result();
|
|
161
|
-
await db.trans(async () => {
|
|
162
|
-
await db.log().insert([{ action: "read" }]);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
#### Queryable / Executable Accessors
|
|
168
|
-
|
|
169
|
-
Each table/view/procedure defined in `defineDbContext` becomes an accessor method on the instance:
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
// Tables and views → returns () => Queryable
|
|
173
|
-
db.user() // Queryable<UserData, typeof User>
|
|
174
|
-
db.post() // Queryable<PostData, typeof Post>
|
|
175
|
-
|
|
176
|
-
// Procedures → returns () => Executable
|
|
177
|
-
db.getUserById() // Executable<Params, Returns>
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
#### `initialize(options?)`
|
|
181
|
-
|
|
182
|
-
Applies all pending migrations in order.
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
await db.connectWithoutTransaction(async () => {
|
|
186
|
-
await db.initialize();
|
|
187
|
-
});
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
| Option | Type | Description |
|
|
191
|
-
|--------|------|-------------|
|
|
192
|
-
| `options.dbs` | `string[]?` | Restrict migration to specific databases |
|
|
193
|
-
| `options.force` | `boolean?` | Re-apply all migrations even if already applied |
|
|
194
|
-
|
|
195
|
-
#### DDL Methods
|
|
196
|
-
|
|
197
|
-
All DDL methods are available on the `DbContextInstance`. DDL cannot be called inside a `connect()` (transact) context — use `connectWithoutTransaction` instead.
|
|
198
|
-
|
|
199
|
-
| Method | Description |
|
|
200
|
-
|--------|-------------|
|
|
201
|
-
| `createTable(table)` | Create a table based on `TableBuilder` |
|
|
202
|
-
| `dropTable(table)` | Drop a table |
|
|
203
|
-
| `renameTable(table, newName)` | Rename a table |
|
|
204
|
-
| `createView(view)` | Create a view |
|
|
205
|
-
| `dropView(view)` | Drop a view |
|
|
206
|
-
| `createProc(procedure)` | Create a stored procedure |
|
|
207
|
-
| `dropProc(procedure)` | Drop a stored procedure |
|
|
208
|
-
| `addColumn(table, columnName, column)` | Add a column |
|
|
209
|
-
| `dropColumn(table, column)` | Drop a column |
|
|
210
|
-
| `modifyColumn(table, columnName, column)` | Modify a column definition |
|
|
211
|
-
| `renameColumn(table, column, newName)` | Rename a column |
|
|
212
|
-
| `addPk(table, columns)` | Add a primary key |
|
|
213
|
-
| `dropPk(table)` | Drop the primary key |
|
|
214
|
-
| `addFk(table, relationName, relationDef)` | Add a foreign key |
|
|
215
|
-
| `dropFk(table, relationName)` | Drop a foreign key |
|
|
216
|
-
| `addIdx(table, indexBuilder)` | Add an index |
|
|
217
|
-
| `dropIdx(table, columns)` | Drop an index |
|
|
218
|
-
| `clearSchema(params)` | Drop all objects in a schema |
|
|
219
|
-
| `schemaExists(database, schema?)` | Check if a schema/database exists |
|
|
220
|
-
| `truncate(table)` | Truncate a table |
|
|
221
|
-
| `switchFk(table, "on" \| "off")` | Toggle FK constraint checking |
|
|
222
|
-
|
|
223
|
-
DDL `QueryDef` generator equivalents (return `QueryDef` without executing):
|
|
224
|
-
|
|
225
|
-
| Method | Description |
|
|
226
|
-
|--------|-------------|
|
|
227
|
-
| `getCreateTableQueryDef(table)` | |
|
|
228
|
-
| `getCreateViewQueryDef(view)` | |
|
|
229
|
-
| `getCreateProcQueryDef(procedure)` | |
|
|
230
|
-
| `getCreateObjectQueryDef(builder)` | Unified for table, view, or procedure |
|
|
231
|
-
| `getDropTableQueryDef(table)` | |
|
|
232
|
-
| `getRenameTableQueryDef(table, newName)` | |
|
|
233
|
-
| `getDropViewQueryDef(view)` | |
|
|
234
|
-
| `getDropProcQueryDef(procedure)` | |
|
|
235
|
-
| `getAddColumnQueryDef(table, columnName, column)` | |
|
|
236
|
-
| `getDropColumnQueryDef(table, column)` | |
|
|
237
|
-
| `getModifyColumnQueryDef(table, columnName, column)` | |
|
|
238
|
-
| `getRenameColumnQueryDef(table, column, newName)` | |
|
|
239
|
-
| `getAddPkQueryDef(table, columns)` | |
|
|
240
|
-
| `getDropPkQueryDef(table)` | |
|
|
241
|
-
| `getAddFkQueryDef(table, relationName, relationDef)` | |
|
|
242
|
-
| `getAddIdxQueryDef(table, indexBuilder)` | |
|
|
243
|
-
| `getDropFkQueryDef(table, relationName)` | |
|
|
244
|
-
| `getDropIdxQueryDef(table, columns)` | |
|
|
245
|
-
| `getClearSchemaQueryDef(params)` | |
|
|
246
|
-
| `getSchemaExistsQueryDef(database, schema?)` | |
|
|
247
|
-
| `getTruncateQueryDef(table)` | |
|
|
248
|
-
| `getSwitchFkQueryDef(table, switch_)` | |
|
|
249
|
-
|
|
250
|
-
---
|
|
15
|
+
| Source | Exports | Description | Test |
|
|
16
|
+
|--------|---------|-------------|------|
|
|
17
|
+
| `src/define-db-context.ts` | `defineDbContext` | Define a database context with tables, views, and migrations | `tests/db-context/define-db-context.spec.ts` |
|
|
18
|
+
| `src/create-db-context.ts` | `createDbContext` | Create a database context instance from a definition | `tests/db-context/create-db-context.spec.ts` |
|
|
19
|
+
| `src/types/db-context-def.ts` | `DbContextBase`, `DbContextStatus`, `DbContextDef`, `DbContextInstance`, `DbContextConnectionMethods`, `DbContextDdlMethods` | Database context type definitions and connection interfaces | `-` |
|
|
20
|
+
| `src/errors/db-transaction-error.ts` | `DbErrorCode`, `DbTransactionError` | Transaction error codes and error class for DB operations | `-` |
|
|
251
21
|
|
|
252
22
|
### Queryable / Executable
|
|
253
23
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
import { Queryable } from "@simplysm/orm-common";
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
**Query building methods (return `Queryable`):**
|
|
263
|
-
|
|
264
|
-
| Method | SQL | Notes |
|
|
265
|
-
|--------|-----|-------|
|
|
266
|
-
| `.select(fn)` | SELECT | Map columns to new shape |
|
|
267
|
-
| `.distinct()` | DISTINCT | Remove duplicate rows |
|
|
268
|
-
| `.lock()` | FOR UPDATE | Row-level exclusive lock (must be inside `connect`) |
|
|
269
|
-
| `.top(count)` | TOP / LIMIT | First N rows |
|
|
270
|
-
| `.limit(skip, take)` | OFFSET / FETCH | Pagination — requires prior `orderBy()` |
|
|
271
|
-
| `.orderBy(fn, dir?)` | ORDER BY | Add sort; multiple calls chain in order |
|
|
272
|
-
| `.where(predicate)` | WHERE | Add condition; multiple calls AND together |
|
|
273
|
-
| `.search(fn, searchText)` | WHERE LIKE | Multi-column text search with special syntax |
|
|
274
|
-
| `.groupBy(fn)` | GROUP BY | Group rows |
|
|
275
|
-
| `.having(predicate)` | HAVING | Filter groups |
|
|
276
|
-
| `.join(as, fwd)` | LEFT JOIN | 1:N relationship → result as array property |
|
|
277
|
-
| `.joinSingle(as, fwd)` | LEFT JOIN | N:1 / 1:1 → result as single object property |
|
|
278
|
-
| `.include(fn)` | LEFT JOIN | Auto-join using `TableBuilder` relation definitions |
|
|
279
|
-
| `.wrap()` | Subquery | Wrap current query as subquery |
|
|
280
|
-
| `.recursive(fwd)` | WITH RECURSIVE | Recursive CTE for hierarchical data |
|
|
281
|
-
|
|
282
|
-
**Static method:**
|
|
283
|
-
|
|
284
|
-
| Method | Description |
|
|
285
|
-
|--------|-------------|
|
|
286
|
-
| `Queryable.union(...queries)` | UNION of 2+ queryables (deduplication) |
|
|
287
|
-
|
|
288
|
-
**Execution methods:**
|
|
289
|
-
|
|
290
|
-
| Method | Returns | Description |
|
|
291
|
-
|--------|---------|-------------|
|
|
292
|
-
| `.result()` | `Promise<TData[]>` | Execute SELECT, return all rows |
|
|
293
|
-
| `.single()` | `Promise<TData \| undefined>` | Return one row; throws if more than one |
|
|
294
|
-
| `.first()` | `Promise<TData \| undefined>` | Return first row only |
|
|
295
|
-
| `.count(fwd?)` | `Promise<number>` | Count rows; cannot use after `distinct()`/`groupBy()` without `wrap()` |
|
|
296
|
-
| `.exists()` | `Promise<boolean>` | Check if any rows match |
|
|
297
|
-
| `.insert(records, outputColumns?)` | `Promise<void \| Pick[]>` | INSERT; returns output if columns specified; auto-chunks at 1000 |
|
|
298
|
-
| `.insertIfNotExists(record, outputColumns?)` | `Promise<void \| Pick>` | INSERT if WHERE condition not matched |
|
|
299
|
-
| `.insertInto(targetTable, outputColumns?)` | `Promise<void \| Pick[]>` | INSERT INTO ... SELECT |
|
|
300
|
-
| `.update(recordFwd, outputColumns?)` | `Promise<void \| Pick[]>` | UPDATE |
|
|
301
|
-
| `.delete(outputColumns?)` | `Promise<void \| Pick[]>` | DELETE |
|
|
302
|
-
| `.upsert(updateFwd, insertFwd?, outputColumns?)` | `Promise<void \| Pick[]>` | UPDATE or INSERT |
|
|
303
|
-
| `.switchFk("on" \| "off")` | `Promise<void>` | Toggle FK for this table |
|
|
304
|
-
|
|
305
|
-
**QueryDef generator methods (for custom execution):**
|
|
306
|
-
|
|
307
|
-
| Method | Description |
|
|
308
|
-
|--------|-------------|
|
|
309
|
-
| `.getSelectQueryDef()` | Build SELECT `QueryDef` |
|
|
310
|
-
| `.getInsertQueryDef(records, outputColumns?)` | Build INSERT `QueryDef` |
|
|
311
|
-
| `.getInsertIfNotExistsQueryDef(record, outputColumns?)` | Build INSERT IF NOT EXISTS `QueryDef` |
|
|
312
|
-
| `.getInsertIntoQueryDef(targetTable, outputColumns?)` | Build INSERT INTO SELECT `QueryDef` |
|
|
313
|
-
| `.getUpdateQueryDef(recordFwd, outputColumns?)` | Build UPDATE `QueryDef` |
|
|
314
|
-
| `.getDeleteQueryDef(outputColumns?)` | Build DELETE `QueryDef` |
|
|
315
|
-
| `.getUpsertQueryDef(updateFwd, insertFwd, outputColumns?)` | Build UPSERT `QueryDef` |
|
|
316
|
-
| `.getResultMeta(outputColumns?)` | Build `ResultMeta` for result parsing |
|
|
317
|
-
|
|
318
|
-
**Examples:**
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
// SELECT with WHERE and ORDER BY
|
|
322
|
-
const users = await db.user()
|
|
323
|
-
.where((u) => [expr.eq(u.isActive, true)])
|
|
324
|
-
.orderBy((u) => u.name)
|
|
325
|
-
.result();
|
|
326
|
-
|
|
327
|
-
// JOIN (1:N)
|
|
328
|
-
const usersWithPosts = await db.user()
|
|
329
|
-
.join("posts", (qr, u) =>
|
|
330
|
-
qr.from(Post).where((p) => [expr.eq(p.userId, u.id)])
|
|
331
|
-
)
|
|
332
|
-
.result();
|
|
333
|
-
// Result: { id, name, posts?: Post[] }
|
|
24
|
+
| Source | Exports | Description | Test |
|
|
25
|
+
|--------|---------|-------------|------|
|
|
26
|
+
| `src/exec/queryable.ts` | `Queryable`, `getMatchedPrimaryKeys`, `QueryableRecord`, `QueryableWriteRecord`, `NullableQueryableRecord`, `UnwrapQueryableRecord`, `PathProxy`, `queryable` | Core queryable builder for SELECT, JOIN, WHERE, and aggregation | `tests/select/`, `tests/dml/`, `tests/errors/` |
|
|
27
|
+
| `src/exec/executable.ts` | `Executable`, `executable` | Executable builder for INSERT, UPDATE, DELETE, and DDL operations | `tests/executable/basic.spec.ts` |
|
|
28
|
+
| `src/exec/search-parser.ts` | `ParsedSearchQuery`, `parseSearchQuery` | Parse search query strings into structured filter objects | `tests/exec/search-parser.spec.ts` |
|
|
334
29
|
|
|
335
|
-
|
|
336
|
-
const posts = await db.post()
|
|
337
|
-
.include((p) => p.author)
|
|
338
|
-
.result();
|
|
30
|
+
### Expression
|
|
339
31
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
.result();
|
|
345
|
-
|
|
346
|
-
// INSERT and get returned ID
|
|
347
|
-
const [inserted] = await db.user().insert(
|
|
348
|
-
[{ name: "Alice" }],
|
|
349
|
-
["id"],
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
// UPSERT
|
|
353
|
-
await db.user()
|
|
354
|
-
.where((u) => [expr.eq(u.email, "alice@example.com")])
|
|
355
|
-
.upsert(
|
|
356
|
-
() => ({ loginCount: expr.val("number", 1) }),
|
|
357
|
-
(update) => ({ ...update, email: expr.val("string", "alice@example.com") }),
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
// Recursive CTE (hierarchical query)
|
|
361
|
-
const tree = await db.employee()
|
|
362
|
-
.where((e) => [expr.null(e.managerId)])
|
|
363
|
-
.recursive((cte) =>
|
|
364
|
-
cte.from(Employee)
|
|
365
|
-
.where((e) => [expr.eq(e.managerId, e.self[0].id)])
|
|
366
|
-
)
|
|
367
|
-
.result();
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
#### `Executable<TParams, TReturns>`
|
|
371
|
-
|
|
372
|
-
Wrapper class for stored procedure execution.
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
import { Executable } from "@simplysm/orm-common";
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
| Method | Returns | Description |
|
|
379
|
-
|--------|---------|-------------|
|
|
380
|
-
| `.execute(params)` | `Promise<InferColumnExprs<TReturns>[][]>` | Execute the stored procedure |
|
|
381
|
-
| `.getExecProcQueryDef(params?)` | `QueryDef` | Build EXEC PROC `QueryDef` |
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
const result = await db.getUserById().execute({ userId: 1 });
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
---
|
|
32
|
+
| Source | Exports | Description | Test |
|
|
33
|
+
|--------|---------|-------------|------|
|
|
34
|
+
| `src/expr/expr.ts` | `SwitchExprBuilder`, `expr`, `toExpr` | Expression builder with switch/case and conversion helpers | `tests/expr/` |
|
|
35
|
+
| `src/expr/expr-unit.ts` | `ExprUnit`, `WhereExprUnit`, `ExprInput` | Expression unit types for building type-safe query expressions | `-` |
|
|
388
36
|
|
|
389
37
|
### Schema Builders
|
|
390
38
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
.database("mydb")
|
|
400
|
-
.columns((c) => ({
|
|
401
|
-
id: c.bigint().autoIncrement(),
|
|
402
|
-
name: c.varchar(100),
|
|
403
|
-
email: c.varchar(200).nullable(),
|
|
404
|
-
status: c.varchar(20).default("active"),
|
|
405
|
-
createdAt: c.datetime(),
|
|
406
|
-
}))
|
|
407
|
-
.primaryKey("id")
|
|
408
|
-
.indexes((i) => [
|
|
409
|
-
i.index("email").unique(),
|
|
410
|
-
i.index("name", "createdAt").orderBy("ASC", "DESC"),
|
|
411
|
-
])
|
|
412
|
-
.relations((r) => ({
|
|
413
|
-
posts: r.foreignKeyTarget(() => Post, "author"),
|
|
414
|
-
profile: r.foreignKeyTarget(() => Profile, "user").single(),
|
|
415
|
-
}));
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
`TableBuilder` fluent methods:
|
|
419
|
-
|
|
420
|
-
| Method | Description |
|
|
421
|
-
|--------|-------------|
|
|
422
|
-
| `.database(db)` | Set database name |
|
|
423
|
-
| `.schema(schema)` | Set schema name (MSSQL/PostgreSQL) |
|
|
424
|
-
| `.description(desc)` | Set table description (DDL comment) |
|
|
425
|
-
| `.columns(fn)` | Define columns via column factory |
|
|
426
|
-
| `.primaryKey(...columns)` | Set primary key (supports composite PK) |
|
|
427
|
-
| `.indexes(fn)` | Define indexes via index factory |
|
|
428
|
-
| `.relations(fn)` | Define relationships via relation factory |
|
|
429
|
-
|
|
430
|
-
`TableBuilder` type inference properties:
|
|
431
|
-
|
|
432
|
-
| Property | Description |
|
|
433
|
-
|----------|-------------|
|
|
434
|
-
| `$infer` | Full row type (columns + relations) |
|
|
435
|
-
| `$inferColumns` | Columns-only type |
|
|
436
|
-
| `$inferInsert` | INSERT type (required fields required, autoIncrement/nullable/default optional) |
|
|
437
|
-
| `$inferUpdate` | UPDATE type (all fields optional) |
|
|
438
|
-
| `$columns` | Raw `ColumnBuilderRecord` |
|
|
439
|
-
| `$relations` | Raw `RelationBuilderRecord` |
|
|
440
|
-
|
|
441
|
-
#### `View(name)` / `ViewBuilder`
|
|
442
|
-
|
|
443
|
-
Factory function and builder class for defining database views.
|
|
444
|
-
|
|
445
|
-
```typescript
|
|
446
|
-
import { View } from "@simplysm/orm-common";
|
|
447
|
-
|
|
448
|
-
const ActiveUsers = View("ActiveUsers")
|
|
449
|
-
.database("mydb")
|
|
450
|
-
.query((db: MyDb) =>
|
|
451
|
-
db.user()
|
|
452
|
-
.where((u) => [expr.eq(u.status, "active")])
|
|
453
|
-
.select((u) => ({ id: u.id, name: u.name }))
|
|
454
|
-
);
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
`ViewBuilder` fluent methods:
|
|
458
|
-
|
|
459
|
-
| Method | Description |
|
|
460
|
-
|--------|-------------|
|
|
461
|
-
| `.database(db)` | Set database name |
|
|
462
|
-
| `.schema(schema)` | Set schema name |
|
|
463
|
-
| `.description(desc)` | Set view description |
|
|
464
|
-
| `.query(viewFn)` | Define the view SELECT query; takes a `DbContextBase` and returns `Queryable` |
|
|
465
|
-
| `.relations(fn)` | Define relationships (only `relationKey`/`relationKeyTarget`, not FK) |
|
|
466
|
-
|
|
467
|
-
`ViewBuilder` type inference properties:
|
|
468
|
-
|
|
469
|
-
| Property | Description |
|
|
470
|
-
|----------|-------------|
|
|
471
|
-
| `$infer` | View row type |
|
|
472
|
-
| `$relations` | Raw `RelationBuilderRecord` |
|
|
473
|
-
|
|
474
|
-
#### `Procedure(name)` / `ProcedureBuilder`
|
|
475
|
-
|
|
476
|
-
Factory function and builder class for defining stored procedures.
|
|
477
|
-
|
|
478
|
-
```typescript
|
|
479
|
-
import { Procedure } from "@simplysm/orm-common";
|
|
480
|
-
|
|
481
|
-
const GetUserById = Procedure("GetUserById")
|
|
482
|
-
.database("mydb")
|
|
483
|
-
.params((c) => ({
|
|
484
|
-
userId: c.bigint(),
|
|
485
|
-
}))
|
|
486
|
-
.returns((c) => ({
|
|
487
|
-
id: c.bigint(),
|
|
488
|
-
name: c.varchar(100),
|
|
489
|
-
email: c.varchar(200).nullable(),
|
|
490
|
-
}))
|
|
491
|
-
.body("SELECT id, name, email FROM User WHERE id = userId");
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
`ProcedureBuilder` fluent methods:
|
|
495
|
-
|
|
496
|
-
| Method | Description |
|
|
497
|
-
|--------|-------------|
|
|
498
|
-
| `.database(db)` | Set database name |
|
|
499
|
-
| `.schema(schema)` | Set schema name |
|
|
500
|
-
| `.description(desc)` | Set procedure description |
|
|
501
|
-
| `.params(fn)` | Define input parameters |
|
|
502
|
-
| `.returns(fn)` | Define result columns |
|
|
503
|
-
| `.body(sql)` | Set procedure body SQL (DBMS-specific syntax) |
|
|
504
|
-
|
|
505
|
-
`ProcedureBuilder` type inference properties:
|
|
506
|
-
|
|
507
|
-
| Property | Description |
|
|
508
|
-
|----------|-------------|
|
|
509
|
-
| `$params` | Raw parameter `ColumnBuilderRecord` |
|
|
510
|
-
| `$returns` | Raw returns `ColumnBuilderRecord` |
|
|
511
|
-
|
|
512
|
-
### Column Types (`createColumnFactory`)
|
|
513
|
-
|
|
514
|
-
Used inside `.columns()`, `.params()`, and `.returns()`, and also directly in migrations.
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
import { createColumnFactory } from "@simplysm/orm-common";
|
|
518
|
-
|
|
519
|
-
const c = createColumnFactory();
|
|
520
|
-
// Used in migrations:
|
|
521
|
-
await db.addColumn({ database: "mydb", name: "User" }, "status", c.varchar(20).nullable());
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
Available column type methods:
|
|
525
|
-
|
|
526
|
-
| Method | SQL Type | TypeScript Type |
|
|
527
|
-
|--------|----------|-----------------|
|
|
528
|
-
| `c.int()` | INT | `number` |
|
|
529
|
-
| `c.bigint()` | BIGINT | `number` |
|
|
530
|
-
| `c.float()` | FLOAT | `number` |
|
|
531
|
-
| `c.double()` | DOUBLE | `number` |
|
|
532
|
-
| `c.decimal(precision, scale?)` | DECIMAL | `number` |
|
|
533
|
-
| `c.varchar(length)` | VARCHAR | `string` |
|
|
534
|
-
| `c.char(length)` | CHAR | `string` |
|
|
535
|
-
| `c.text()` | TEXT | `string` |
|
|
536
|
-
| `c.binary()` | LONGBLOB/VARBINARY(MAX)/BYTEA | `Bytes` (Uint8Array) |
|
|
537
|
-
| `c.boolean()` | TINYINT(1)/BIT/BOOLEAN | `boolean` |
|
|
538
|
-
| `c.datetime()` | DATETIME | `DateTime` |
|
|
539
|
-
| `c.date()` | DATE | `DateOnly` |
|
|
540
|
-
| `c.time()` | TIME | `Time` |
|
|
541
|
-
| `c.uuid()` | BINARY(16)/UNIQUEIDENTIFIER/UUID | `Uuid` |
|
|
542
|
-
|
|
543
|
-
`ColumnBuilder` modifier methods:
|
|
544
|
-
|
|
545
|
-
| Method | Description |
|
|
546
|
-
|--------|-------------|
|
|
547
|
-
| `.autoIncrement()` | Auto-increment; excluded from INSERT required fields |
|
|
548
|
-
| `.nullable()` | Allow NULL; adds `undefined` to the TypeScript type |
|
|
549
|
-
| `.default(value)` | Default value; makes the field optional in INSERT |
|
|
550
|
-
| `.description(desc)` | Column description (DDL comment) |
|
|
551
|
-
|
|
552
|
-
### Relation Builders
|
|
553
|
-
|
|
554
|
-
Used inside `.relations()`:
|
|
555
|
-
|
|
556
|
-
#### `r.foreignKey(columns, targetFn)` → `ForeignKeyBuilder`
|
|
557
|
-
|
|
558
|
-
N:1 relationship with DB FK constraint.
|
|
559
|
-
|
|
560
|
-
```typescript
|
|
561
|
-
const Post = Table("Post")
|
|
562
|
-
.columns((c) => ({ id: c.bigint().autoIncrement(), authorId: c.bigint() }))
|
|
563
|
-
.primaryKey("id")
|
|
564
|
-
.relations((r) => ({
|
|
565
|
-
author: r.foreignKey(["authorId"], () => User),
|
|
566
|
-
}));
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
#### `r.foreignKeyTarget(targetTableFn, relationName)` → `ForeignKeyTargetBuilder`
|
|
570
|
-
|
|
571
|
-
1:N reverse reference. Results in an array by default; call `.single()` for 1:1.
|
|
572
|
-
|
|
573
|
-
```typescript
|
|
574
|
-
const User = Table("User")
|
|
575
|
-
.columns((c) => ({ id: c.bigint().autoIncrement() }))
|
|
576
|
-
.primaryKey("id")
|
|
577
|
-
.relations((r) => ({
|
|
578
|
-
posts: r.foreignKeyTarget(() => Post, "author"), // 1:N → Post[]
|
|
579
|
-
profile: r.foreignKeyTarget(() => Profile, "user").single(), // 1:1 → Profile
|
|
580
|
-
}));
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
#### `r.relationKey(columns, targetFn)` → `RelationKeyBuilder`
|
|
584
|
-
|
|
585
|
-
N:1 logical relationship without a DB FK constraint. Available for both tables and views.
|
|
586
|
-
|
|
587
|
-
#### `r.relationKeyTarget(targetTableFn, relationName)` → `RelationKeyTargetBuilder`
|
|
588
|
-
|
|
589
|
-
1:N logical reverse reference without a DB FK constraint. Call `.single()` for 1:1.
|
|
590
|
-
|
|
591
|
-
#### `createRelationFactory(ownerFn)`
|
|
592
|
-
|
|
593
|
-
Creates a relation builder factory for the given owner table or view. Used internally by `TableBuilder.relations()` and `ViewBuilder.relations()`. Available for building custom schema utilities.
|
|
594
|
-
|
|
595
|
-
Tables receive both FK methods (`foreignKey`, `foreignKeyTarget`) and logical relation methods (`relationKey`, `relationKeyTarget`). Views receive only logical relation methods.
|
|
596
|
-
|
|
597
|
-
```typescript
|
|
598
|
-
import { createRelationFactory } from "@simplysm/orm-common";
|
|
599
|
-
|
|
600
|
-
const r = createRelationFactory(() => Post);
|
|
601
|
-
// r.foreignKey(...) — available (Post is a TableBuilder)
|
|
602
|
-
// r.foreignKeyTarget(...) — available
|
|
603
|
-
// r.relationKey(...) — available
|
|
604
|
-
// r.relationKeyTarget(...) — available
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
#### `IndexBuilder` / `createIndexFactory`
|
|
608
|
-
|
|
609
|
-
Index builder, used inside `.indexes()`.
|
|
610
|
-
|
|
611
|
-
```typescript
|
|
612
|
-
Table("User")
|
|
613
|
-
.indexes((i) => [
|
|
614
|
-
i.index("email").unique(),
|
|
615
|
-
i.index("name").name("IX_User_Name"),
|
|
616
|
-
i.index("createdAt").orderBy("DESC"),
|
|
617
|
-
i.index("status", "createdAt").orderBy("ASC", "DESC"),
|
|
618
|
-
]);
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
`IndexBuilder` methods:
|
|
622
|
-
|
|
623
|
-
| Method | Description |
|
|
624
|
-
|--------|-------------|
|
|
625
|
-
| `.unique()` | Create a unique index |
|
|
626
|
-
| `.name(name)` | Set index name |
|
|
627
|
-
| `.orderBy(...dirs)` | Set sort direction per column (`"ASC"` or `"DESC"`) |
|
|
628
|
-
| `.description(desc)` | Index description |
|
|
629
|
-
|
|
630
|
-
---
|
|
631
|
-
|
|
632
|
-
### SQL Expressions (`expr`)
|
|
633
|
-
|
|
634
|
-
The `expr` object provides all SQL expression builders. All methods return `ExprUnit` (SELECT expressions) or `WhereExprUnit` (WHERE conditions).
|
|
635
|
-
|
|
636
|
-
```typescript
|
|
637
|
-
import { expr } from "@simplysm/orm-common";
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
#### Value / Column Creation
|
|
641
|
-
|
|
642
|
-
| Method | Description |
|
|
643
|
-
|--------|-------------|
|
|
644
|
-
| `expr.val(dataType, value)` | Wrap a literal value as `ExprUnit` |
|
|
645
|
-
| `expr.col(dataType, ...path)` | Create a column reference (internal use) |
|
|
646
|
-
| `expr.raw(dataType)\`SQL ${interpolation}\`` | Raw SQL with interpolated `ExprUnit` values |
|
|
647
|
-
|
|
648
|
-
```typescript
|
|
649
|
-
expr.val("string", "active")
|
|
650
|
-
expr.val("number", 100)
|
|
651
|
-
expr.val("DateOnly", DateOnly.today())
|
|
652
|
-
expr.raw("number")`ARRAY_LENGTH(${u.tags}, 1)`
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
#### Comparison Expressions (WHERE)
|
|
656
|
-
|
|
657
|
-
| Method | SQL | Description |
|
|
658
|
-
|--------|-----|-------------|
|
|
659
|
-
| `expr.eq(source, target)` | `<=>` / IS NULL | NULL-safe equality |
|
|
660
|
-
| `expr.gt(source, target)` | `>` | Greater than |
|
|
661
|
-
| `expr.lt(source, target)` | `<` | Less than |
|
|
662
|
-
| `expr.gte(source, target)` | `>=` | Greater than or equal |
|
|
663
|
-
| `expr.lte(source, target)` | `<=` | Less than or equal |
|
|
664
|
-
| `expr.between(source, from?, to?)` | `BETWEEN` | Range; `undefined` means unbounded |
|
|
665
|
-
| `expr.null(source)` | `IS NULL` | Null check |
|
|
666
|
-
| `expr.like(source, pattern)` | `LIKE` | Pattern matching (`%`, `_`) |
|
|
667
|
-
| `expr.regexp(source, pattern)` | `REGEXP` | Regular expression matching |
|
|
668
|
-
| `expr.in(source, values)` | `IN (...)` | Value list match |
|
|
669
|
-
| `expr.inQuery(source, query)` | `IN (SELECT ...)` | Subquery match (single-column SELECT) |
|
|
670
|
-
| `expr.exists(query)` | `EXISTS (...)` | Subquery existence check |
|
|
671
|
-
|
|
672
|
-
```typescript
|
|
673
|
-
db.user()
|
|
674
|
-
.where((u) => [
|
|
675
|
-
expr.eq(u.status, "active"),
|
|
676
|
-
expr.gte(u.age, 18),
|
|
677
|
-
expr.between(u.score, 0, 100),
|
|
678
|
-
expr.null(u.deletedAt),
|
|
679
|
-
expr.in(u.role, ["admin", "manager"]),
|
|
680
|
-
expr.like(u.name, "John%"),
|
|
681
|
-
])
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
#### Logical Expressions (WHERE)
|
|
685
|
-
|
|
686
|
-
| Method | SQL | Description |
|
|
687
|
-
|--------|-----|-------------|
|
|
688
|
-
| `expr.not(arg)` | `NOT (...)` | Negate a condition |
|
|
689
|
-
| `expr.and(conditions)` | `(... AND ...)` | All conditions must be true |
|
|
690
|
-
| `expr.or(conditions)` | `(... OR ...)` | At least one condition must be true |
|
|
691
|
-
|
|
692
|
-
#### String Expressions
|
|
693
|
-
|
|
694
|
-
| Method | SQL | Description |
|
|
695
|
-
|--------|-----|-------------|
|
|
696
|
-
| `expr.concat(...args)` | `CONCAT(...)` | Concatenate strings; NULL treated as empty string |
|
|
697
|
-
| `expr.left(source, length)` | `LEFT(...)` | Extract N characters from left |
|
|
698
|
-
| `expr.right(source, length)` | `RIGHT(...)` | Extract N characters from right |
|
|
699
|
-
| `expr.trim(source)` | `TRIM(...)` | Remove surrounding whitespace |
|
|
700
|
-
| `expr.padStart(source, length, fillString)` | `LPAD(...)` | Left-pad to target length |
|
|
701
|
-
| `expr.replace(source, from, to)` | `REPLACE(...)` | Replace all occurrences |
|
|
702
|
-
| `expr.upper(source)` | `UPPER(...)` | Uppercase |
|
|
703
|
-
| `expr.lower(source)` | `LOWER(...)` | Lowercase |
|
|
704
|
-
| `expr.length(source)` | `CHAR_LENGTH(...)` | Character length |
|
|
705
|
-
| `expr.byteLength(source)` | `OCTET_LENGTH(...)` | Byte length (UTF-8: Korean = 3 bytes) |
|
|
706
|
-
| `expr.substring(source, start, length?)` | `SUBSTRING(...)` | Extract substring (1-based index) |
|
|
707
|
-
| `expr.indexOf(source, search)` | `LOCATE(...)` | Find position (1-based; 0 if not found) |
|
|
708
|
-
|
|
709
|
-
#### Numeric Expressions
|
|
710
|
-
|
|
711
|
-
| Method | SQL | Description |
|
|
712
|
-
|--------|-----|-------------|
|
|
713
|
-
| `expr.abs(source)` | `ABS(...)` | Absolute value |
|
|
714
|
-
| `expr.round(source, digits)` | `ROUND(...)` | Round to N decimal places |
|
|
715
|
-
| `expr.ceil(source)` | `CEILING(...)` | Round up |
|
|
716
|
-
| `expr.floor(source)` | `FLOOR(...)` | Round down |
|
|
717
|
-
|
|
718
|
-
#### Date Expressions
|
|
719
|
-
|
|
720
|
-
| Method | SQL | Description |
|
|
721
|
-
|--------|-----|-------------|
|
|
722
|
-
| `expr.year(source)` | `YEAR(...)` | Extract year (4-digit) |
|
|
723
|
-
| `expr.month(source)` | `MONTH(...)` | Extract month (1–12) |
|
|
724
|
-
| `expr.day(source)` | `DAY(...)` | Extract day (1–31) |
|
|
725
|
-
| `expr.hour(source)` | `HOUR(...)` | Extract hour (0–23) |
|
|
726
|
-
| `expr.minute(source)` | `MINUTE(...)` | Extract minute (0–59) |
|
|
727
|
-
| `expr.second(source)` | `SECOND(...)` | Extract second (0–59) |
|
|
728
|
-
| `expr.isoWeek(source)` | `WEEK(..., 3)` | ISO 8601 week number (1–53, Mon start) |
|
|
729
|
-
| `expr.isoWeekStartDate(source)` | — | Monday of the week containing the date |
|
|
730
|
-
| `expr.isoYearMonth(source)` | — | First day of the month (YYYY-MM-01) |
|
|
731
|
-
| `expr.dateDiff(separator, from, to)` | `DATEDIFF(...)` | Date difference in specified unit |
|
|
732
|
-
| `expr.dateAdd(separator, source, value)` | `DATEADD(...)` | Add an interval to a date |
|
|
733
|
-
| `expr.formatDate(source, format)` | `DATE_FORMAT(...)` | Format date as string (DBMS-specific format strings) |
|
|
734
|
-
|
|
735
|
-
`DateSeparator` values: `"year"`, `"month"`, `"day"`, `"hour"`, `"minute"`, `"second"`
|
|
736
|
-
|
|
737
|
-
#### Conditional Expressions
|
|
738
|
-
|
|
739
|
-
| Method | SQL | Description |
|
|
740
|
-
|--------|-----|-------------|
|
|
741
|
-
| `expr.ifNull(...args)` | `COALESCE(...)` | Return first non-null value |
|
|
742
|
-
| `expr.nullIf(source, value)` | `NULLIF(...)` | Return NULL if source equals value |
|
|
743
|
-
| `expr.is(condition)` | — | Convert WHERE expression to boolean `ExprUnit` |
|
|
744
|
-
| `expr.if(condition, then, else_)` | `IF(...)` / `CASE` | Ternary conditional |
|
|
745
|
-
| `expr.switch<T>().case(...).default(...)` | `CASE WHEN ... END` | Multi-branch conditional |
|
|
746
|
-
|
|
747
|
-
```typescript
|
|
748
|
-
// COALESCE
|
|
749
|
-
expr.ifNull(u.nickname, u.name, "Guest")
|
|
750
|
-
|
|
751
|
-
// Ternary
|
|
752
|
-
expr.if(expr.gte(u.age, 18), "adult", "minor")
|
|
753
|
-
|
|
754
|
-
// CASE WHEN
|
|
755
|
-
expr.switch<string>()
|
|
756
|
-
.case(expr.gte(u.score, 90), "A")
|
|
757
|
-
.case(expr.gte(u.score, 80), "B")
|
|
758
|
-
.default("F")
|
|
759
|
-
|
|
760
|
-
// Boolean from condition
|
|
761
|
-
db.user().select((u) => ({
|
|
762
|
-
isActive: expr.is(expr.eq(u.status, "active")),
|
|
763
|
-
}))
|
|
764
|
-
```
|
|
765
|
-
|
|
766
|
-
#### Aggregate Expressions
|
|
767
|
-
|
|
768
|
-
| Method | SQL | Description |
|
|
769
|
-
|--------|-----|-------------|
|
|
770
|
-
| `expr.count(arg?, distinct?)` | `COUNT(...)` | Row count; omit arg for `COUNT(*)` |
|
|
771
|
-
| `expr.sum(arg)` | `SUM(...)` | Sum; NULL if all values are NULL |
|
|
772
|
-
| `expr.avg(arg)` | `AVG(...)` | Average; NULL if all values are NULL |
|
|
773
|
-
| `expr.max(arg)` | `MAX(...)` | Maximum; NULL if all values are NULL |
|
|
774
|
-
| `expr.min(arg)` | `MIN(...)` | Minimum; NULL if all values are NULL |
|
|
775
|
-
| `expr.greatest(...args)` | `GREATEST(...)` | Largest among multiple values |
|
|
776
|
-
| `expr.least(...args)` | `LEAST(...)` | Smallest among multiple values |
|
|
777
|
-
|
|
778
|
-
#### Window Functions
|
|
779
|
-
|
|
780
|
-
All window functions accept a `WinSpecInput`:
|
|
781
|
-
|
|
782
|
-
```typescript
|
|
783
|
-
interface WinSpecInput {
|
|
784
|
-
partitionBy?: ExprInput<ColumnPrimitive>[];
|
|
785
|
-
orderBy?: [ExprInput<ColumnPrimitive>, ("ASC" | "DESC")?][];
|
|
786
|
-
}
|
|
787
|
-
```
|
|
788
|
-
|
|
789
|
-
| Method | SQL | Description |
|
|
790
|
-
|--------|-----|-------------|
|
|
791
|
-
| `expr.rowNumber(spec)` | `ROW_NUMBER() OVER (...)` | Sequential row number within partition |
|
|
792
|
-
| `expr.rank(spec)` | `RANK() OVER (...)` | Rank; ties get same rank, next skipped |
|
|
793
|
-
| `expr.denseRank(spec)` | `DENSE_RANK() OVER (...)` | Dense rank; ties get same rank, no skipping |
|
|
794
|
-
| `expr.ntile(n, spec)` | `NTILE(n) OVER (...)` | Split partition into N buckets |
|
|
795
|
-
| `expr.lag(column, spec, options?)` | `LAG(...) OVER (...)` | Previous row value |
|
|
796
|
-
| `expr.lead(column, spec, options?)` | `LEAD(...) OVER (...)` | Next row value |
|
|
797
|
-
| `expr.firstValue(column, spec)` | `FIRST_VALUE(...) OVER (...)` | First value in window |
|
|
798
|
-
| `expr.lastValue(column, spec)` | `LAST_VALUE(...) OVER (...)` | Last value in window |
|
|
799
|
-
| `expr.sumOver(column, spec)` | `SUM(...) OVER (...)` | Cumulative/windowed sum |
|
|
800
|
-
| `expr.avgOver(column, spec)` | `AVG(...) OVER (...)` | Windowed average |
|
|
801
|
-
| `expr.countOver(spec, column?)` | `COUNT(*) OVER (...)` | Windowed row count |
|
|
802
|
-
| `expr.minOver(column, spec)` | `MIN(...) OVER (...)` | Windowed minimum |
|
|
803
|
-
| `expr.maxOver(column, spec)` | `MAX(...) OVER (...)` | Windowed maximum |
|
|
804
|
-
|
|
805
|
-
```typescript
|
|
806
|
-
db.order().select((o) => ({
|
|
807
|
-
...o,
|
|
808
|
-
rowNum: expr.rowNumber({
|
|
809
|
-
partitionBy: [o.userId],
|
|
810
|
-
orderBy: [[o.createdAt, "DESC"]],
|
|
811
|
-
}),
|
|
812
|
-
runningTotal: expr.sumOver(o.amount, {
|
|
813
|
-
partitionBy: [o.userId],
|
|
814
|
-
orderBy: [[o.createdAt, "ASC"]],
|
|
815
|
-
}),
|
|
816
|
-
}))
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
#### Other Expressions
|
|
820
|
-
|
|
821
|
-
| Method | SQL | Description |
|
|
822
|
-
|--------|-----|-------------|
|
|
823
|
-
| `expr.cast(source, targetType)` | `CAST(... AS ...)` | Type conversion |
|
|
824
|
-
| `expr.subquery(dataType, queryable)` | `(SELECT ...)` | Scalar subquery in SELECT |
|
|
825
|
-
| `expr.random()` | `RAND()` / `RANDOM()` | Random number 0–1 |
|
|
826
|
-
| `expr.rowNum()` | — | Row number without window spec |
|
|
827
|
-
| `expr.toExpr(value)` | — | Convert `ExprInput` to raw `Expr` AST |
|
|
828
|
-
|
|
829
|
-
```typescript
|
|
830
|
-
// CAST
|
|
831
|
-
expr.cast(o.id, { type: "varchar", length: 20 })
|
|
832
|
-
|
|
833
|
-
// Scalar subquery
|
|
834
|
-
expr.subquery(
|
|
835
|
-
"number",
|
|
836
|
-
db.post().where((p) => [expr.eq(p.userId, u.id)]).select(() => ({ cnt: expr.count() }))
|
|
837
|
-
)
|
|
838
|
-
|
|
839
|
-
// Random sort
|
|
840
|
-
db.user().orderBy(() => expr.random()).limit(0, 10)
|
|
841
|
-
```
|
|
842
|
-
|
|
843
|
-
---
|
|
39
|
+
| Source | Exports | Description | Test |
|
|
40
|
+
|--------|---------|-------------|------|
|
|
41
|
+
| `src/schema/table-builder.ts` | `TableBuilder`, `Table` | Table schema builder with columns, indexes, and relations | `tests/ddl/table-builder.spec.ts` |
|
|
42
|
+
| `src/schema/view-builder.ts` | `ViewBuilder`, `View` | View schema builder for defining SQL views | `tests/ddl/view-builder.spec.ts` |
|
|
43
|
+
| `src/schema/procedure-builder.ts` | `ProcedureBuilder`, `Procedure` | Procedure schema builder for stored procedures | `tests/ddl/procedure-builder.spec.ts` |
|
|
44
|
+
| `src/schema/factory/column-builder.ts` | `ColumnBuilder`, `createColumnFactory`, `ColumnBuilderRecord`, `InferColumns`, `InferColumnExprs`, `RequiredInsertKeys`, `OptionalInsertKeys`, `InferInsertColumns`, `InferUpdateColumns`, `DataToColumnBuilderRecord` | Column definition builder with data types and constraints | `tests/ddl/column-builder.spec.ts` |
|
|
45
|
+
| `src/schema/factory/index-builder.ts` | `IndexBuilder`, `createIndexFactory` | Index definition builder for table indexes | `tests/ddl/index-builder.spec.ts` |
|
|
46
|
+
| `src/schema/factory/relation-builder.ts` | `ForeignKeyBuilder`, `ForeignKeyTargetBuilder`, `RelationKeyBuilder`, `RelationKeyTargetBuilder`, `createRelationFactory`, `RelationBuilderRecord`, `ExtractRelationTarget`, `ExtractRelationTargetResult`, `InferDeepRelations` | Foreign key and relation builder for table relationships | `tests/ddl/relation-builder.spec.ts` |
|
|
844
47
|
|
|
845
48
|
### Models
|
|
846
49
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
| Property | Type | Description |
|
|
875
|
-
|----------|------|-------------|
|
|
876
|
-
| `sql` | `string` | Generated SQL string |
|
|
877
|
-
| `resultSetIndex` | `number?` | Which result set to extract (0-based) |
|
|
878
|
-
| `resultSetStride` | `number?` | For multi-INSERT: extract every N-th result set |
|
|
879
|
-
|
|
880
|
-
Available builders (exposed for extension):
|
|
881
|
-
|
|
882
|
-
| Class | Description |
|
|
883
|
-
|-------|-------------|
|
|
884
|
-
| `QueryBuilderBase` | Abstract base class |
|
|
885
|
-
| `ExprRendererBase` | Abstract expression renderer base |
|
|
886
|
-
| `MysqlQueryBuilder` | MySQL implementation |
|
|
887
|
-
| `MysqlExprRenderer` | MySQL expression renderer |
|
|
888
|
-
| `MssqlQueryBuilder` | MSSQL implementation |
|
|
889
|
-
| `MssqlExprRenderer` | MSSQL expression renderer |
|
|
890
|
-
| `PostgresqlQueryBuilder` | PostgreSQL implementation |
|
|
891
|
-
| `PostgresqlExprRenderer` | PostgreSQL expression renderer |
|
|
892
|
-
|
|
893
|
-
---
|
|
894
|
-
|
|
895
|
-
### Low-Level Utilities
|
|
896
|
-
|
|
897
|
-
#### `queryable(db, tableOrView, as?)`
|
|
898
|
-
|
|
899
|
-
Factory that returns a `() => Queryable` accessor. Used internally by `createDbContext`. Available for building custom db context wrappers.
|
|
900
|
-
|
|
901
|
-
```typescript
|
|
902
|
-
import { queryable } from "@simplysm/orm-common";
|
|
903
|
-
|
|
904
|
-
const getUserQueryable = queryable(db, User);
|
|
905
|
-
const users = await getUserQueryable().where((u) => [expr.eq(u.isActive, true)]).result();
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
#### `executable(db, procedureBuilder)`
|
|
909
|
-
|
|
910
|
-
Factory that returns a `() => Executable` accessor. Used internally by `createDbContext`.
|
|
911
|
-
|
|
912
|
-
```typescript
|
|
913
|
-
import { executable } from "@simplysm/orm-common";
|
|
914
|
-
|
|
915
|
-
const getUser = executable(db, GetUserById);
|
|
916
|
-
const result = await getUser().execute({ userId: 1 });
|
|
917
|
-
```
|
|
918
|
-
|
|
919
|
-
#### `parseQueryResult(rawResults, meta)`
|
|
920
|
-
|
|
921
|
-
Parses raw DB query results into typed TypeScript objects. Handles type coercion (e.g., `"1"` → `number`) and JOIN result grouping/nesting. Returns `undefined` for empty or all-null results.
|
|
922
|
-
|
|
923
|
-
```typescript
|
|
924
|
-
import { parseQueryResult } from "@simplysm/orm-common";
|
|
925
|
-
|
|
926
|
-
const meta = {
|
|
927
|
-
columns: { id: "number", name: "string", createdAt: "DateTime" },
|
|
928
|
-
joins: {},
|
|
929
|
-
};
|
|
930
|
-
const result = await parseQueryResult(rawResults, meta);
|
|
931
|
-
// Returns TRecord[] | undefined
|
|
932
|
-
```
|
|
933
|
-
|
|
934
|
-
#### `parseSearchQuery(searchText)`
|
|
935
|
-
|
|
936
|
-
Parses a search query string into SQL LIKE patterns for use with `.search()`.
|
|
937
|
-
|
|
938
|
-
```typescript
|
|
939
|
-
import { parseSearchQuery } from "@simplysm/orm-common";
|
|
940
|
-
|
|
941
|
-
const parsed = parseSearchQuery('apple "exact phrase" +required -excluded app*');
|
|
942
|
-
// {
|
|
943
|
-
// or: ["%apple%", "app%"],
|
|
944
|
-
// must: ["%exact phrase%", "%required%"],
|
|
945
|
-
// not: ["%excluded%"]
|
|
946
|
-
// }
|
|
947
|
-
```
|
|
948
|
-
|
|
949
|
-
Search syntax:
|
|
950
|
-
|
|
951
|
-
| Syntax | Meaning |
|
|
952
|
-
|--------|---------|
|
|
953
|
-
| `term` | OR: contains term |
|
|
954
|
-
| `+term` | MUST: must contain term (AND) |
|
|
955
|
-
| `-term` | NOT: must not contain term |
|
|
956
|
-
| `"exact phrase"` | MUST: exact phrase match |
|
|
957
|
-
| `term*` | Starts with (wildcard) |
|
|
958
|
-
| `*term` | Ends with |
|
|
959
|
-
| `\*`, `\+`, `\-`, `\%`, `\"`, `\\` | Escape special characters |
|
|
960
|
-
|
|
961
|
-
Returns `ParsedSearchQuery`:
|
|
962
|
-
|
|
963
|
-
| Property | Type | Description |
|
|
964
|
-
|----------|------|-------------|
|
|
965
|
-
| `or` | `string[]` | OR conditions (LIKE patterns) |
|
|
966
|
-
| `must` | `string[]` | Required AND conditions (LIKE patterns) |
|
|
967
|
-
| `not` | `string[]` | NOT conditions (LIKE patterns) |
|
|
968
|
-
|
|
969
|
-
#### `getMatchedPrimaryKeys(fkCols, targetTable)`
|
|
970
|
-
|
|
971
|
-
Matches FK column array against the target table's primary key and returns the PK column name array. Used internally by `include()` and custom JOIN logic.
|
|
972
|
-
|
|
973
|
-
```typescript
|
|
974
|
-
import { getMatchedPrimaryKeys } from "@simplysm/orm-common";
|
|
975
|
-
|
|
976
|
-
const pkCols = getMatchedPrimaryKeys(["userId"], User);
|
|
977
|
-
// Returns: ["id"]
|
|
978
|
-
```
|
|
979
|
-
|
|
980
|
-
---
|
|
981
|
-
|
|
982
|
-
## Expression Types
|
|
983
|
-
|
|
984
|
-
### `ExprUnit<TPrimitive>`
|
|
985
|
-
|
|
986
|
-
Type-safe expression wrapper. All `expr.*` SELECT/value methods return `ExprUnit`.
|
|
987
|
-
|
|
988
|
-
```typescript
|
|
989
|
-
import { ExprUnit } from "@simplysm/orm-common";
|
|
990
|
-
```
|
|
991
|
-
|
|
992
|
-
| Member | Type | Description |
|
|
993
|
-
|--------|------|-------------|
|
|
994
|
-
| `dataType` | `ColumnPrimitiveStr` | Runtime type name |
|
|
995
|
-
| `expr` | `Expr` | Raw JSON AST |
|
|
996
|
-
| `n` | `ExprUnit<NonNullable<TPrimitive>>` | Non-nullable accessor (strips `undefined`) |
|
|
997
|
-
|
|
998
|
-
### `WhereExprUnit`
|
|
999
|
-
|
|
1000
|
-
Wrapper for WHERE condition expressions. All comparison and logical `expr.*` methods return `WhereExprUnit`.
|
|
1001
|
-
|
|
1002
|
-
```typescript
|
|
1003
|
-
import { WhereExprUnit } from "@simplysm/orm-common";
|
|
1004
|
-
```
|
|
1005
|
-
|
|
1006
|
-
### `ExprInput<TPrimitive>`
|
|
1007
|
-
|
|
1008
|
-
Union type that accepts either an `ExprUnit<TPrimitive>` or a plain literal value. Most `expr.*` parameters accept `ExprInput` so you can pass raw values without wrapping in `expr.val()`.
|
|
1009
|
-
|
|
1010
|
-
```typescript
|
|
1011
|
-
type ExprInput<TPrimitive> = ExprUnit<TPrimitive> | TPrimitive;
|
|
1012
|
-
```
|
|
1013
|
-
|
|
1014
|
-
### `SwitchExprBuilder<TPrimitive>`
|
|
1015
|
-
|
|
1016
|
-
Builder returned by `expr.switch()`:
|
|
1017
|
-
|
|
1018
|
-
| Method | Description |
|
|
1019
|
-
|--------|-------------|
|
|
1020
|
-
| `.case(condition, then)` | Add WHEN ... THEN branch |
|
|
1021
|
-
| `.default(value)` | Add ELSE and finalize to `ExprUnit` |
|
|
1022
|
-
|
|
1023
|
-
### `QueryableRecord<TData>`
|
|
1024
|
-
|
|
1025
|
-
Maps a data record type to its expression counterpart. Each primitive field becomes `ExprUnit`, array relations become `QueryableRecord[]`, and nested objects become `QueryableRecord`.
|
|
1026
|
-
|
|
1027
|
-
### `NullableQueryableRecord<TData>`
|
|
1028
|
-
|
|
1029
|
-
Like `QueryableRecord` but all primitive fields have `| undefined` added (representing LEFT JOIN NULL propagation).
|
|
1030
|
-
|
|
1031
|
-
### `QueryableWriteRecord<TData>`
|
|
1032
|
-
|
|
1033
|
-
Write-side record type for `update()`/`upsert()` callbacks. Each field accepts `ExprInput<T>` (either `ExprUnit<T>` or a plain value).
|
|
1034
|
-
|
|
1035
|
-
### `UnwrapQueryableRecord<R>`
|
|
1036
|
-
|
|
1037
|
-
Reverse mapping from a `QueryableRecord` shape back to a plain `DataRecord`. Used to infer the result type of `select()`.
|
|
1038
|
-
|
|
1039
|
-
### `PathProxy<TObject>`
|
|
1040
|
-
|
|
1041
|
-
Type-safe path proxy used by `.include()`. Only non-primitive (relation) fields are accessible.
|
|
1042
|
-
|
|
1043
|
-
```typescript
|
|
1044
|
-
// TypeScript will error if you try to access a primitive column
|
|
1045
|
-
db.post().include((p) => p.author) // OK
|
|
1046
|
-
db.post().include((p) => p.author.company) // OK (chained)
|
|
1047
|
-
// db.post().include((p) => p.title) // Error: title is ColumnPrimitive
|
|
1048
|
-
```
|
|
1049
|
-
|
|
1050
|
-
### `toExpr(value)`
|
|
1051
|
-
|
|
1052
|
-
Converts an `ExprInput` to a raw `Expr` JSON AST. Exposed for custom `QueryBuilder` extensions.
|
|
1053
|
-
|
|
1054
|
-
```typescript
|
|
1055
|
-
import { toExpr } from "@simplysm/orm-common";
|
|
1056
|
-
```
|
|
1057
|
-
|
|
1058
|
-
---
|
|
1059
|
-
|
|
1060
|
-
## Type Reference
|
|
1061
|
-
|
|
1062
|
-
### Column Types
|
|
1063
|
-
|
|
1064
|
-
| Type | Description |
|
|
1065
|
-
|------|-------------|
|
|
1066
|
-
| `ColumnPrimitive` | Union of all column-storable TypeScript types (`string \| number \| boolean \| DateTime \| DateOnly \| Time \| Uuid \| Bytes \| undefined`) |
|
|
1067
|
-
| `ColumnPrimitiveStr` | String key of `ColumnPrimitiveMap` — `"string" \| "number" \| "boolean" \| "DateTime" \| "DateOnly" \| "Time" \| "Uuid" \| "Bytes"` |
|
|
1068
|
-
| `ColumnPrimitiveMap` | Mapping from `ColumnPrimitiveStr` → TypeScript type |
|
|
1069
|
-
| `DataType` | SQL data type discriminated union (`{ type: "int" }`, `{ type: "varchar"; length: number }`, etc.) |
|
|
1070
|
-
| `ColumnMeta` | Column metadata stored inside `ColumnBuilder` |
|
|
1071
|
-
| `InferColumnPrimitiveFromDataType<T>` | TypeScript type from a `DataType` |
|
|
1072
|
-
| `dataTypeStrToColumnPrimitiveStr` | Constant object mapping SQL type name → `ColumnPrimitiveStr` |
|
|
1073
|
-
| `inferColumnPrimitiveStr(value)` | Runtime function: infer `ColumnPrimitiveStr` from a value |
|
|
1074
|
-
| `DateSeparator` | `"year" \| "month" \| "day" \| "hour" \| "minute" \| "second"` — date interval unit for `dateDiff`/`dateAdd` |
|
|
1075
|
-
|
|
1076
|
-
#### SQL Type to TypeScript Type Mapping
|
|
1077
|
-
|
|
1078
|
-
| SQL Type | TypeScript Type | Notes |
|
|
1079
|
-
|----------|----------------|-------|
|
|
1080
|
-
| `int`, `bigint`, `float`, `double`, `decimal` | `number` | `bigint` means SQL BIGINT (8 bytes), not JS `BigInt` |
|
|
1081
|
-
| `varchar`, `char`, `text` | `string` | |
|
|
1082
|
-
| `boolean` | `boolean` | MySQL: TINYINT(1), MSSQL: BIT |
|
|
1083
|
-
| `datetime` | `DateTime` | From `@simplysm/core-common` |
|
|
1084
|
-
| `date` | `DateOnly` | From `@simplysm/core-common` |
|
|
1085
|
-
| `time` | `Time` | From `@simplysm/core-common` |
|
|
1086
|
-
| `uuid` | `Uuid` | From `@simplysm/core-common` |
|
|
1087
|
-
| `binary` | `Bytes` | `Uint8Array` |
|
|
1088
|
-
|
|
1089
|
-
### Database Types
|
|
1090
|
-
|
|
1091
|
-
| Type/Const | Description |
|
|
1092
|
-
|------------|-------------|
|
|
1093
|
-
| `Dialect` | `"mysql" \| "mssql" \| "postgresql"` |
|
|
1094
|
-
| `dialects` | `Dialect[]` constant — all supported dialects |
|
|
1095
|
-
| `IsolationLevel` | `"READ_UNCOMMITTED" \| "READ_COMMITTED" \| "REPEATABLE_READ" \| "SERIALIZABLE"` |
|
|
1096
|
-
| `DataRecord` | Recursive query result type: `{ [key: string]: ColumnPrimitive \| DataRecord \| DataRecord[] }` |
|
|
1097
|
-
| `DbContextExecutor` | Interface that actual DB connectors implement |
|
|
1098
|
-
| `ResultMeta` | Metadata for converting raw query results (`{ columns, joins }`) |
|
|
1099
|
-
| `Migration` | `{ name: string; up: (db) => Promise<void> }` |
|
|
1100
|
-
| `QueryBuildResult` | `{ sql: string; resultSetIndex?: number; resultSetStride?: number }` |
|
|
1101
|
-
|
|
1102
|
-
### Schema Builder Types
|
|
1103
|
-
|
|
1104
|
-
| Type | Description |
|
|
1105
|
-
|------|-------------|
|
|
1106
|
-
| `ColumnBuilderRecord` | `Record<string, ColumnBuilder<...>>` |
|
|
1107
|
-
| `RelationBuilderRecord` | Union record of all relation builder types |
|
|
1108
|
-
| `InferColumns<T>` | Infer column value types from a `ColumnBuilderRecord` |
|
|
1109
|
-
| `InferInsertColumns<T>` | INSERT type (required + optional fields) |
|
|
1110
|
-
| `InferUpdateColumns<T>` | UPDATE type (all optional) |
|
|
1111
|
-
| `InferColumnExprs<T>` | Expression input type from a `ColumnBuilderRecord` |
|
|
1112
|
-
| `InferDeepRelations<T>` | Infer relation types (all optional) |
|
|
1113
|
-
| `QueryableWriteRecord<TData>` | Write-side record type for update/upsert callbacks (accepts `ExprInput<T>`) |
|
|
1114
|
-
| `PathProxy<TObject>` | Type-safe path proxy for `.include()` navigation |
|
|
1115
|
-
| `DataToColumnBuilderRecord<TData>` | Convert `DataRecord` to `ColumnBuilderRecord` (for `insertInto` type checking) |
|
|
1116
|
-
| `RequiredInsertKeys<T>` | Keys that are required in INSERT (no autoIncrement, nullable, or default) |
|
|
1117
|
-
| `OptionalInsertKeys<T>` | Keys that are optional in INSERT |
|
|
1118
|
-
| `ExtractRelationTarget<T>` | Extract the TypeScript type of an FK/RelationKey target |
|
|
1119
|
-
| `ExtractRelationTargetResult<T>` | Extract the TypeScript type of an FKTarget/RelationKeyTarget (array or single) |
|
|
1120
|
-
|
|
1121
|
-
### QueryDef Types
|
|
1122
|
-
|
|
1123
|
-
`QueryDef` is the JSON AST representation of any SQL statement. Every `Queryable` execution method generates a `QueryDef` that is then converted to SQL by `QueryBuilderBase`.
|
|
1124
|
-
|
|
1125
|
-
#### `QueryDef`
|
|
1126
|
-
|
|
1127
|
-
The master union type for all possible query definitions:
|
|
1128
|
-
|
|
1129
|
-
```typescript
|
|
1130
|
-
import { QueryDef } from "@simplysm/orm-common";
|
|
1131
|
-
|
|
1132
|
-
type QueryDef =
|
|
1133
|
-
| SelectQueryDef | InsertQueryDef | InsertIfNotExistsQueryDef | InsertIntoQueryDef
|
|
1134
|
-
| UpdateQueryDef | DeleteQueryDef | UpsertQueryDef
|
|
1135
|
-
| ClearSchemaQueryDef | CreateTableQueryDef | DropTableQueryDef | RenameTableQueryDef
|
|
1136
|
-
| TruncateQueryDef | AddColumnQueryDef | DropColumnQueryDef | ModifyColumnQueryDef
|
|
1137
|
-
| RenameColumnQueryDef | DropPkQueryDef | AddPkQueryDef | AddFkQueryDef | DropFkQueryDef
|
|
1138
|
-
| AddIdxQueryDef | DropIdxQueryDef | CreateViewQueryDef | DropViewQueryDef
|
|
1139
|
-
| CreateProcQueryDef | DropProcQueryDef | ExecProcQueryDef | SwitchFkQueryDef
|
|
1140
|
-
| SchemaExistsQueryDef;
|
|
1141
|
-
```
|
|
1142
|
-
|
|
1143
|
-
#### `QueryDefObjectName`
|
|
1144
|
-
|
|
1145
|
-
Represents a DBMS object identifier (table, view, procedure). DBMS-specific namespace rules:
|
|
1146
|
-
|
|
1147
|
-
- MySQL: `database.name` (schema is ignored)
|
|
1148
|
-
- MSSQL: `database.schema.name` (schema defaults to `dbo`)
|
|
1149
|
-
- PostgreSQL: `schema.name` (database is connection-level)
|
|
1150
|
-
|
|
1151
|
-
```typescript
|
|
1152
|
-
import { QueryDefObjectName } from "@simplysm/orm-common";
|
|
1153
|
-
|
|
1154
|
-
interface QueryDefObjectName {
|
|
1155
|
-
database?: string;
|
|
1156
|
-
schema?: string;
|
|
1157
|
-
name: string;
|
|
1158
|
-
}
|
|
1159
|
-
```
|
|
1160
|
-
|
|
1161
|
-
#### `CudOutputDef`
|
|
1162
|
-
|
|
1163
|
-
Defines the OUTPUT clause for INSERT/UPDATE/DELETE operations that return values.
|
|
1164
|
-
|
|
1165
|
-
```typescript
|
|
1166
|
-
import { CudOutputDef } from "@simplysm/orm-common";
|
|
1167
|
-
|
|
1168
|
-
interface CudOutputDef {
|
|
1169
|
-
columns: string[]; // Column names to return
|
|
1170
|
-
pkColNames: string[]; // Primary key column names
|
|
1171
|
-
aiColName?: string; // Auto-increment column name (if any)
|
|
1172
|
-
}
|
|
1173
|
-
```
|
|
1174
|
-
|
|
1175
|
-
#### DML QueryDef interfaces
|
|
1176
|
-
|
|
1177
|
-
| Interface | `type` | Description |
|
|
1178
|
-
|-----------|--------|-------------|
|
|
1179
|
-
| `SelectQueryDef` | `"select"` | SELECT with optional FROM, WHERE, JOIN, ORDER BY, GROUP BY, HAVING, LIMIT, DISTINCT, recursive CTE |
|
|
1180
|
-
| `SelectQueryDefJoin` | `"select"` | Extends `SelectQueryDef` with `isSingle?: boolean` for 1:1 JOIN distinction |
|
|
1181
|
-
| `InsertQueryDef` | `"insert"` | INSERT with records array; optional `overrideIdentity` and `output` |
|
|
1182
|
-
| `InsertIfNotExistsQueryDef` | `"insertIfNotExists"` | INSERT only if `existsSelectQuery` returns no rows |
|
|
1183
|
-
| `InsertIntoQueryDef` | `"insertInto"` | INSERT INTO ... SELECT from `recordsSelectQuery` |
|
|
1184
|
-
| `UpdateQueryDef` | `"update"` | UPDATE with record map, optional WHERE/JOIN/LIMIT |
|
|
1185
|
-
| `DeleteQueryDef` | `"delete"` | DELETE with optional WHERE/JOIN/LIMIT |
|
|
1186
|
-
| `UpsertQueryDef` | `"upsert"` | UPDATE or INSERT (MERGE pattern): `existsSelectQuery` determines branch |
|
|
1187
|
-
|
|
1188
|
-
#### DDL QueryDef interfaces
|
|
1189
|
-
|
|
1190
|
-
| Interface | `type` | Description |
|
|
1191
|
-
|-----------|--------|-------------|
|
|
1192
|
-
| `ClearSchemaQueryDef` | `"clearSchema"` | Drop all objects in a database/schema |
|
|
1193
|
-
| `CreateTableQueryDef` | `"createTable"` | CREATE TABLE with column definitions and optional primary key |
|
|
1194
|
-
| `DropTableQueryDef` | `"dropTable"` | DROP TABLE |
|
|
1195
|
-
| `RenameTableQueryDef` | `"renameTable"` | RENAME TABLE to `newName` |
|
|
1196
|
-
| `TruncateQueryDef` | `"truncate"` | TRUNCATE TABLE |
|
|
1197
|
-
| `AddColumnQueryDef` | `"addColumn"` | ADD COLUMN with data type and optional constraints |
|
|
1198
|
-
| `DropColumnQueryDef` | `"dropColumn"` | DROP COLUMN |
|
|
1199
|
-
| `ModifyColumnQueryDef` | `"modifyColumn"` | MODIFY/ALTER COLUMN definition |
|
|
1200
|
-
| `RenameColumnQueryDef` | `"renameColumn"` | RENAME COLUMN to `newName` |
|
|
1201
|
-
| `DropPkQueryDef` | `"dropPk"` | DROP PRIMARY KEY |
|
|
1202
|
-
| `AddPkQueryDef` | `"addPk"` | ADD PRIMARY KEY on given `columns` |
|
|
1203
|
-
| `AddFkQueryDef` | `"addFk"` | ADD FOREIGN KEY with name, FK columns, target table and PK columns |
|
|
1204
|
-
| `DropFkQueryDef` | `"dropFk"` | DROP FOREIGN KEY by name |
|
|
1205
|
-
| `AddIdxQueryDef` | `"addIdx"` | CREATE INDEX with name, columns, order direction, and optional unique |
|
|
1206
|
-
| `DropIdxQueryDef` | `"dropIdx"` | DROP INDEX by name |
|
|
1207
|
-
| `CreateViewQueryDef` | `"createView"` | CREATE VIEW with a `SelectQueryDef` body |
|
|
1208
|
-
| `DropViewQueryDef` | `"dropView"` | DROP VIEW |
|
|
1209
|
-
| `CreateProcQueryDef` | `"createProc"` | CREATE PROCEDURE with params, returns columns, and body SQL string |
|
|
1210
|
-
| `DropProcQueryDef` | `"dropProc"` | DROP PROCEDURE |
|
|
1211
|
-
| `ExecProcQueryDef` | `"execProc"` | EXECUTE PROCEDURE with optional params record |
|
|
1212
|
-
|
|
1213
|
-
#### Utility / Meta QueryDef interfaces
|
|
1214
|
-
|
|
1215
|
-
| Interface | `type` | Description |
|
|
1216
|
-
|-----------|--------|-------------|
|
|
1217
|
-
| `SwitchFkQueryDef` | `"switchFk"` | Enable/disable FK constraint checking for a table (`"on"` or `"off"`) |
|
|
1218
|
-
| `SchemaExistsQueryDef` | `"schemaExists"` | Check if a database/schema exists |
|
|
1219
|
-
|
|
1220
|
-
#### `DDL_TYPES` / `DdlType`
|
|
1221
|
-
|
|
1222
|
-
Constant array of all DDL operation type strings. Used internally to block DDL inside transactions and to validate whether a given `QueryDef` is a DDL operation. `SwitchFkQueryDef` is excluded because FK toggle is permitted inside transactions.
|
|
1223
|
-
|
|
1224
|
-
```typescript
|
|
1225
|
-
import { DDL_TYPES, DdlType } from "@simplysm/orm-common";
|
|
1226
|
-
|
|
1227
|
-
// DDL_TYPES: readonly string[] constant
|
|
1228
|
-
// ["clearSchema", "createTable", "dropTable", ..., "dropProc"]
|
|
1229
|
-
|
|
1230
|
-
// DdlType: union of all DDL type strings
|
|
1231
|
-
type DdlType = "clearSchema" | "createTable" | "dropTable" | "renameTable" | "truncate"
|
|
1232
|
-
| "addColumn" | "dropColumn" | "modifyColumn" | "renameColumn"
|
|
1233
|
-
| "dropPk" | "addPk" | "addFk" | "dropFk" | "addIdx" | "dropIdx"
|
|
1234
|
-
| "createView" | "dropView" | "createProc" | "dropProc";
|
|
1235
|
-
|
|
1236
|
-
// Check if a QueryDef is DDL:
|
|
1237
|
-
if (DDL_TYPES.includes(queryDef.type as DdlType)) {
|
|
1238
|
-
// DDL — cannot run inside a transaction
|
|
1239
|
-
}
|
|
1240
|
-
```
|
|
1241
|
-
|
|
1242
|
-
### Expr AST Types
|
|
1243
|
-
|
|
1244
|
-
Low-level JSON AST interfaces that represent individual SQL expressions. These are the types stored in `QueryDef` fields such as `select`, `where`, `orderBy`, etc. Direct use is only necessary when extending `QueryBuilderBase` or `ExprRendererBase`.
|
|
1245
|
-
|
|
1246
|
-
#### `Expr` and `WhereExpr` (union types)
|
|
1247
|
-
|
|
1248
|
-
```typescript
|
|
1249
|
-
import { Expr, WhereExpr } from "@simplysm/orm-common";
|
|
1250
|
-
```
|
|
1251
|
-
|
|
1252
|
-
| Union type | Description |
|
|
1253
|
-
|------------|-------------|
|
|
1254
|
-
| `Expr` | All value-producing expressions: column refs, literals, string/numeric/date/conditional/aggregate/window/subquery |
|
|
1255
|
-
| `WhereExpr` | All boolean-producing expressions: comparison operators + logical operators (AND/OR/NOT) |
|
|
1256
|
-
|
|
1257
|
-
#### Value expressions
|
|
1258
|
-
|
|
1259
|
-
| Interface | `type` | Description |
|
|
1260
|
-
|-----------|--------|-------------|
|
|
1261
|
-
| `ExprColumn` | `"column"` | Column reference: `{ path: string[] }` — table alias + column name |
|
|
1262
|
-
| `ExprValue` | `"value"` | Literal value: `{ value: ColumnPrimitive }` |
|
|
1263
|
-
| `ExprRaw` | `"raw"` | Raw SQL template: `{ sql: string; params: Expr[] }` — params referenced as `{0}`, `{1}` |
|
|
1264
|
-
|
|
1265
|
-
#### Comparison expressions (WHERE)
|
|
1266
|
-
|
|
1267
|
-
| Interface | `type` | SQL |
|
|
1268
|
-
|-----------|--------|-----|
|
|
1269
|
-
| `ExprEq` | `"eq"` | NULL-safe `=` / `IS NULL` |
|
|
1270
|
-
| `ExprGt` | `"gt"` | `>` |
|
|
1271
|
-
| `ExprLt` | `"lt"` | `<` |
|
|
1272
|
-
| `ExprGte` | `"gte"` | `>=` |
|
|
1273
|
-
| `ExprLte` | `"lte"` | `<=` |
|
|
1274
|
-
| `ExprBetween` | `"between"` | `BETWEEN from AND to` |
|
|
1275
|
-
| `ExprIsNull` | `"null"` | `IS NULL` |
|
|
1276
|
-
| `ExprLike` | `"like"` | `LIKE` |
|
|
1277
|
-
| `ExprRegexp` | `"regexp"` | `REGEXP` |
|
|
1278
|
-
| `ExprIn` | `"in"` | `IN (values)` |
|
|
1279
|
-
| `ExprInQuery` | `"inQuery"` | `IN (subquery)` |
|
|
1280
|
-
| `ExprExists` | `"exists"` | `EXISTS (subquery)` |
|
|
1281
|
-
|
|
1282
|
-
#### Logical expressions (WHERE)
|
|
1283
|
-
|
|
1284
|
-
| Interface | `type` | SQL |
|
|
1285
|
-
|-----------|--------|-----|
|
|
1286
|
-
| `ExprNot` | `"not"` | `NOT (...)` |
|
|
1287
|
-
| `ExprAnd` | `"and"` | `(... AND ...)` |
|
|
1288
|
-
| `ExprOr` | `"or"` | `(... OR ...)` |
|
|
1289
|
-
|
|
1290
|
-
#### String expressions
|
|
1291
|
-
|
|
1292
|
-
| Interface | `type` | SQL |
|
|
1293
|
-
|-----------|--------|-----|
|
|
1294
|
-
| `ExprConcat` | `"concat"` | `CONCAT(args...)` |
|
|
1295
|
-
| `ExprLeft` | `"left"` | `LEFT(source, length)` |
|
|
1296
|
-
| `ExprRight` | `"right"` | `RIGHT(source, length)` |
|
|
1297
|
-
| `ExprTrim` | `"trim"` | `TRIM(arg)` |
|
|
1298
|
-
| `ExprPadStart` | `"padStart"` | `LPAD(source, length, fillString)` |
|
|
1299
|
-
| `ExprReplace` | `"replace"` | `REPLACE(source, from, to)` |
|
|
1300
|
-
| `ExprUpper` | `"upper"` | `UPPER(arg)` |
|
|
1301
|
-
| `ExprLower` | `"lower"` | `LOWER(arg)` |
|
|
1302
|
-
| `ExprLength` | `"length"` | `CHAR_LENGTH(arg)` |
|
|
1303
|
-
| `ExprByteLength` | `"byteLength"` | `OCTET_LENGTH(arg)` |
|
|
1304
|
-
| `ExprSubstring` | `"substring"` | `SUBSTRING(source, start, length?)` |
|
|
1305
|
-
| `ExprIndexOf` | `"indexOf"` | `LOCATE(source, search)` |
|
|
1306
|
-
|
|
1307
|
-
#### Numeric expressions
|
|
1308
|
-
|
|
1309
|
-
| Interface | `type` | SQL |
|
|
1310
|
-
|-----------|--------|-----|
|
|
1311
|
-
| `ExprAbs` | `"abs"` | `ABS(arg)` |
|
|
1312
|
-
| `ExprRound` | `"round"` | `ROUND(arg, digits)` |
|
|
1313
|
-
| `ExprCeil` | `"ceil"` | `CEILING(arg)` |
|
|
1314
|
-
| `ExprFloor` | `"floor"` | `FLOOR(arg)` |
|
|
1315
|
-
|
|
1316
|
-
#### Date expressions
|
|
1317
|
-
|
|
1318
|
-
| Interface | `type` | SQL |
|
|
1319
|
-
|-----------|--------|-----|
|
|
1320
|
-
| `ExprYear` | `"year"` | `YEAR(arg)` |
|
|
1321
|
-
| `ExprMonth` | `"month"` | `MONTH(arg)` |
|
|
1322
|
-
| `ExprDay` | `"day"` | `DAY(arg)` |
|
|
1323
|
-
| `ExprHour` | `"hour"` | `HOUR(arg)` |
|
|
1324
|
-
| `ExprMinute` | `"minute"` | `MINUTE(arg)` |
|
|
1325
|
-
| `ExprSecond` | `"second"` | `SECOND(arg)` |
|
|
1326
|
-
| `ExprIsoWeek` | `"isoWeek"` | `WEEK(arg, 3)` |
|
|
1327
|
-
| `ExprIsoWeekStartDate` | `"isoWeekStartDate"` | Monday of the ISO week |
|
|
1328
|
-
| `ExprIsoYearMonth` | `"isoYearMonth"` | First day of month (YYYY-MM-01) |
|
|
1329
|
-
| `ExprDateDiff` | `"dateDiff"` | `DATEDIFF(separator, from, to)` |
|
|
1330
|
-
| `ExprDateAdd` | `"dateAdd"` | `DATEADD(separator, source, value)` |
|
|
1331
|
-
| `ExprFormatDate` | `"formatDate"` | `DATE_FORMAT(source, format)` |
|
|
1332
|
-
|
|
1333
|
-
#### Conditional expressions
|
|
1334
|
-
|
|
1335
|
-
| Interface | `type` | SQL |
|
|
1336
|
-
|-----------|--------|-----|
|
|
1337
|
-
| `ExprIfNull` | `"ifNull"` | `COALESCE(args...)` |
|
|
1338
|
-
| `ExprNullIf` | `"nullIf"` | `NULLIF(source, value)` |
|
|
1339
|
-
| `ExprIs` | `"is"` | Convert boolean condition to 0/1 value |
|
|
1340
|
-
| `ExprSwitch` | `"switch"` | `CASE WHEN ... THEN ... ELSE ... END` |
|
|
1341
|
-
| `ExprIf` | `"if"` | `IF(condition, then, else)` |
|
|
1342
|
-
|
|
1343
|
-
#### Aggregate expressions
|
|
1344
|
-
|
|
1345
|
-
| Interface | `type` | SQL |
|
|
1346
|
-
|-----------|--------|-----|
|
|
1347
|
-
| `ExprCount` | `"count"` | `COUNT(*)` or `COUNT(arg)` or `COUNT(DISTINCT arg)` |
|
|
1348
|
-
| `ExprSum` | `"sum"` | `SUM(arg)` |
|
|
1349
|
-
| `ExprAvg` | `"avg"` | `AVG(arg)` |
|
|
1350
|
-
| `ExprMax` | `"max"` | `MAX(arg)` |
|
|
1351
|
-
| `ExprMin` | `"min"` | `MIN(arg)` |
|
|
1352
|
-
|
|
1353
|
-
#### Other expressions
|
|
1354
|
-
|
|
1355
|
-
| Interface | `type` | SQL |
|
|
1356
|
-
|-----------|--------|-----|
|
|
1357
|
-
| `ExprGreatest` | `"greatest"` | `GREATEST(args...)` |
|
|
1358
|
-
| `ExprLeast` | `"least"` | `LEAST(args...)` |
|
|
1359
|
-
| `ExprRowNum` | `"rowNum"` | Row number (simple, no window spec) |
|
|
1360
|
-
| `ExprRandom` | `"random"` | `RAND()` / `RANDOM()` |
|
|
1361
|
-
| `ExprCast` | `"cast"` | `CAST(source AS targetType)` |
|
|
1362
|
-
| `ExprSubquery` | `"subquery"` | Scalar subquery `(SELECT ...)` |
|
|
1363
|
-
|
|
1364
|
-
#### Window function types
|
|
1365
|
-
|
|
1366
|
-
`ExprWindow` is the AST node for any window function expression. It combines a `WinFn` (the function) with a `WinSpec` (the OVER clause).
|
|
1367
|
-
|
|
1368
|
-
```typescript
|
|
1369
|
-
import { ExprWindow, WinFn, WinSpec } from "@simplysm/orm-common";
|
|
1370
|
-
|
|
1371
|
-
interface ExprWindow {
|
|
1372
|
-
type: "window";
|
|
1373
|
-
fn: WinFn; // The window function
|
|
1374
|
-
spec: WinSpec; // OVER (PARTITION BY ... ORDER BY ...)
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
interface WinSpec {
|
|
1378
|
-
partitionBy?: Expr[];
|
|
1379
|
-
orderBy?: [Expr, ("ASC" | "DESC")?][];
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
type WinFn =
|
|
1383
|
-
| WinFnRowNumber | WinFnRank | WinFnDenseRank | WinFnNtile
|
|
1384
|
-
| WinFnLag | WinFnLead | WinFnFirstValue | WinFnLastValue
|
|
1385
|
-
| WinFnSum | WinFnAvg | WinFnCount | WinFnMin | WinFnMax;
|
|
1386
|
-
```
|
|
1387
|
-
|
|
1388
|
-
Individual `WinFn` interfaces:
|
|
1389
|
-
|
|
1390
|
-
| Interface | `type` | SQL | Notes |
|
|
1391
|
-
|-----------|--------|-----|-------|
|
|
1392
|
-
| `WinFnRowNumber` | `"rowNumber"` | `ROW_NUMBER()` | |
|
|
1393
|
-
| `WinFnRank` | `"rank"` | `RANK()` | |
|
|
1394
|
-
| `WinFnDenseRank` | `"denseRank"` | `DENSE_RANK()` | |
|
|
1395
|
-
| `WinFnNtile` | `"ntile"` | `NTILE(n)` | Has `n: number` property |
|
|
1396
|
-
| `WinFnLag` | `"lag"` | `LAG(column, offset?, default?)` | |
|
|
1397
|
-
| `WinFnLead` | `"lead"` | `LEAD(column, offset?, default?)` | |
|
|
1398
|
-
| `WinFnFirstValue` | `"firstValue"` | `FIRST_VALUE(column)` | |
|
|
1399
|
-
| `WinFnLastValue` | `"lastValue"` | `LAST_VALUE(column)` | |
|
|
1400
|
-
| `WinFnSum` | `"sum"` | `SUM(column) OVER (...)` | |
|
|
1401
|
-
| `WinFnAvg` | `"avg"` | `AVG(column) OVER (...)` | |
|
|
1402
|
-
| `WinFnCount` | `"count"` | `COUNT(*) OVER (...)` | `column` is optional |
|
|
1403
|
-
| `WinFnMin` | `"min"` | `MIN(column) OVER (...)` | |
|
|
1404
|
-
| `WinFnMax` | `"max"` | `MAX(column) OVER (...)` | |
|
|
1405
|
-
|
|
1406
|
-
---
|
|
1407
|
-
|
|
1408
|
-
## Quick Start
|
|
1409
|
-
|
|
1410
|
-
```typescript
|
|
1411
|
-
import { Table, defineDbContext, createDbContext, expr, DateTime } from "@simplysm/orm-common";
|
|
1412
|
-
|
|
1413
|
-
// Define table schema
|
|
1414
|
-
const User = Table("User")
|
|
1415
|
-
.database("mydb")
|
|
1416
|
-
.columns((c) => ({
|
|
1417
|
-
id: c.bigint().autoIncrement(),
|
|
1418
|
-
name: c.varchar(100),
|
|
1419
|
-
email: c.varchar(200).nullable(),
|
|
1420
|
-
createdAt: c.datetime(),
|
|
1421
|
-
}))
|
|
1422
|
-
.primaryKey("id");
|
|
1423
|
-
|
|
1424
|
-
// Define DbContext
|
|
1425
|
-
const MyDbDef = defineDbContext({
|
|
1426
|
-
tables: { user: User },
|
|
1427
|
-
});
|
|
1428
|
-
|
|
1429
|
-
// Create DbContext instance with executor (from orm-node package)
|
|
1430
|
-
const db = createDbContext(MyDbDef, executor, { database: "mydb" });
|
|
1431
|
-
|
|
1432
|
-
// Execute queries
|
|
1433
|
-
await db.connect(async () => {
|
|
1434
|
-
// INSERT
|
|
1435
|
-
await db.user().insert([
|
|
1436
|
-
{ name: "John", createdAt: DateTime.now() }
|
|
1437
|
-
]);
|
|
1438
|
-
|
|
1439
|
-
// SELECT
|
|
1440
|
-
const users = await db.user()
|
|
1441
|
-
.where((u) => [expr.eq(u.email, "john@example.com")])
|
|
1442
|
-
.result();
|
|
1443
|
-
|
|
1444
|
-
// UPDATE
|
|
1445
|
-
await db.user()
|
|
1446
|
-
.where((u) => [expr.eq(u.id, 1)])
|
|
1447
|
-
.update(() => ({ name: "Jane" }));
|
|
1448
|
-
});
|
|
1449
|
-
```
|
|
1450
|
-
|
|
1451
|
-
## Security Notes
|
|
1452
|
-
|
|
1453
|
-
orm-common uses **enhanced string escaping** instead of parameter binding due to its dynamic query nature.
|
|
1454
|
-
Always perform input validation at the application level.
|
|
1455
|
-
|
|
1456
|
-
```typescript
|
|
1457
|
-
// Bad: Direct user input
|
|
1458
|
-
const userInput = req.query.name; // e.g. malicious SQL payload
|
|
1459
|
-
await db.user().where((u) => [expr.eq(u.name, userInput)]).result();
|
|
1460
|
-
|
|
1461
|
-
// Good: Validate before use
|
|
1462
|
-
const userName = validateUserName(req.query.name);
|
|
1463
|
-
await db.user().where((u) => [expr.eq(u.name, userName)]).result();
|
|
1464
|
-
|
|
1465
|
-
// Better: Type coercion
|
|
1466
|
-
const userId = Number(req.query.id);
|
|
1467
|
-
if (Number.isNaN(userId)) throw new Error("Invalid ID");
|
|
1468
|
-
await db.user().where((u) => [expr.eq(u.id, userId)]).result();
|
|
1469
|
-
```
|
|
50
|
+
| Source | Exports | Description | Test |
|
|
51
|
+
|--------|---------|-------------|------|
|
|
52
|
+
| `src/models/system-migration.ts` | `_Migration` | Built-in migration tracking table model | `-` |
|
|
53
|
+
|
|
54
|
+
### Query Builder
|
|
55
|
+
|
|
56
|
+
| Source | Exports | Description | Test |
|
|
57
|
+
|--------|---------|-------------|------|
|
|
58
|
+
| `src/query-builder/query-builder.ts` | `createQueryBuilder` | Factory to create dialect-specific query builders | `-` |
|
|
59
|
+
| `src/query-builder/base/query-builder-base.ts` | `QueryBuilderBase` | Abstract base class for SQL query generation | `-` |
|
|
60
|
+
| `src/query-builder/base/expr-renderer-base.ts` | `ExprRendererBase` | Abstract base class for SQL expression rendering | `-` |
|
|
61
|
+
| `src/query-builder/mysql/mysql-query-builder.ts` | `MysqlQueryBuilder` | MySQL-specific SQL query builder | `-` |
|
|
62
|
+
| `src/query-builder/mysql/mysql-expr-renderer.ts` | `MysqlExprRenderer` | MySQL-specific expression renderer | `-` |
|
|
63
|
+
| `src/query-builder/mssql/mssql-query-builder.ts` | `MssqlQueryBuilder` | MSSQL-specific SQL query builder | `-` |
|
|
64
|
+
| `src/query-builder/mssql/mssql-expr-renderer.ts` | `MssqlExprRenderer` | MSSQL-specific expression renderer | `-` |
|
|
65
|
+
| `src/query-builder/postgresql/postgresql-query-builder.ts` | `PostgresqlQueryBuilder` | PostgreSQL-specific SQL query builder | `-` |
|
|
66
|
+
| `src/query-builder/postgresql/postgresql-expr-renderer.ts` | `PostgresqlExprRenderer` | PostgreSQL-specific expression renderer | `-` |
|
|
67
|
+
|
|
68
|
+
### Types
|
|
69
|
+
|
|
70
|
+
| Source | Exports | Description | Test |
|
|
71
|
+
|--------|---------|-------------|------|
|
|
72
|
+
| `src/types/db.ts` | `Dialect`, `dialects`, `QueryBuildResult`, `IsolationLevel`, `DataRecord`, `DbContextExecutor`, `ResultMeta`, `Migration` | Core database types (Dialect, IsolationLevel, QueryBuildResult, Migration) | `-` |
|
|
73
|
+
| `src/utils/result-parser.ts` | `parseQueryResult` | Parse raw DB query results into typed JavaScript objects | `tests/utils/result-parser.spec.ts` |
|
|
74
|
+
| `src/types/column.ts` | `DataType`, `ColumnPrimitiveMap`, `ColumnPrimitiveStr`, `ColumnPrimitive`, `dataTypeStrToColumnPrimitiveStr`, `InferColumnPrimitiveFromDataType`, `inferColumnPrimitiveStr`, `ColumnMeta` | Column data type definitions and primitive type mapping | `-` |
|
|
75
|
+
| `src/types/expr.ts` | `DateSeparator`, `ExprColumn`, `ExprValue`, `ExprRaw`, `ExprEq`, `ExprGt`, `ExprLt`, `ExprGte`, `ExprLte`, `ExprBetween`, `ExprIsNull`, `ExprLike`, `ExprRegexp`, `ExprIn`, `ExprInQuery`, `ExprExists`, `ExprNot`, `ExprAnd`, `ExprOr`, `ExprConcat`, `ExprLeft`, `ExprRight`, `ExprTrim`, `ExprPadStart`, `ExprReplace`, `ExprUpper`, `ExprLower`, `ExprLength`, `ExprByteLength`, `ExprSubstring`, `ExprIndexOf`, `ExprAbs`, `ExprRound`, `ExprCeil`, `ExprFloor`, `ExprYear`, `ExprMonth`, `ExprDay`, `ExprHour`, `ExprMinute`, `ExprSecond`, `ExprIsoWeek`, `ExprIsoWeekStartDate`, `ExprIsoYearMonth`, `ExprDateDiff`, `ExprDateAdd`, `ExprFormatDate`, `ExprIfNull`, `ExprNullIf`, `ExprIs`, `ExprSwitch`, `ExprIf`, `ExprCount`, `ExprSum`, `ExprAvg`, `ExprMax`, `ExprMin`, `ExprGreatest`, `ExprLeast`, `ExprRowNum`, `ExprRandom`, `ExprCast`, `WinFnRowNumber`, `WinFnRank`, `WinFnDenseRank`, `WinFnNtile`, `WinFnLag`, `WinFnLead`, `WinFnFirstValue`, `WinFnLastValue`, `WinFnSum`, `WinFnAvg`, `WinFnCount`, `WinFnMin`, `WinFnMax`, `WinFn`, `WinSpec`, `ExprWindow`, `ExprSubquery`, `WhereExpr`, `Expr` | All expression AST node types for the query builder | `-` |
|
|
76
|
+
| `src/types/query-def.ts` | `QueryDefObjectName`, `CudOutputDef`, `SelectQueryDef`, `SelectQueryDefJoin`, `InsertQueryDef`, `InsertIfNotExistsQueryDef`, `InsertIntoQueryDef`, `UpdateQueryDef`, `DeleteQueryDef`, `UpsertQueryDef`, `SwitchFkQueryDef`, `ClearSchemaQueryDef`, `CreateTableQueryDef`, `DropTableQueryDef`, `RenameTableQueryDef`, `TruncateQueryDef`, `AddColumnQueryDef`, `DropColumnQueryDef`, `ModifyColumnQueryDef`, `RenameColumnQueryDef`, `DropPkQueryDef`, `AddPkQueryDef`, `AddFkQueryDef`, `DropFkQueryDef`, `AddIdxQueryDef`, `DropIdxQueryDef`, `CreateViewQueryDef`, `DropViewQueryDef`, `CreateProcQueryDef`, `DropProcQueryDef`, `ExecProcQueryDef`, `SchemaExistsQueryDef`, `DDL_TYPES`, `DdlType`, `QueryDef` | Query definition types for all SQL statement kinds (SELECT, INSERT, DDL, etc.) | `-` |
|
|
1470
77
|
|
|
1471
78
|
## License
|
|
1472
79
|
|