@simplysm/orm-common 13.0.96 → 13.0.98

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 CHANGED
@@ -1,112 +1,267 @@
1
1
  # @simplysm/orm-common
2
2
 
3
- 플랫폼 중립적인 ORM 핵심 모듈. 스키마 정의, 타입 안전한 쿼리 빌더, DDL 관리, MySQL/PostgreSQL/MSSQL 방언 지원.
3
+ ORM Module (common) -- dialect-independent ORM for MySQL, MSSQL, and PostgreSQL.
4
4
 
5
- ## 설치
5
+ Provides schema definition, type-safe query building, expression construction, and query rendering without any direct database dependency. Actual database execution is handled by `@simplysm/orm-node`.
6
+
7
+ ## Installation
6
8
 
7
9
  ```bash
8
10
  npm install @simplysm/orm-common
9
11
  ```
10
12
 
11
- **의존성:** `@simplysm/core-common`
13
+ ## API Reference
14
+
15
+ ### Core
16
+
17
+ Define and create database contexts with connection/transaction management.
18
+
19
+ | Export | Kind | Description |
20
+ |--------|------|-------------|
21
+ | `defineDbContext` | function | Create a DbContext definition (blueprint) from tables, views, procedures, and migrations |
22
+ | `createDbContext` | function | Create a runtime DbContext instance from a definition and executor |
23
+ | `DbContextBase` | interface | Internal interface used by Queryable/Executable (status, database, schema, executeDefs) |
24
+ | `DbContextDef` | interface | DbContext definition holding schema metadata (tables, views, procedures, migrations) |
25
+ | `DbContextInstance` | type | Full runtime instance with queryable accessors, DDL methods, and connection management |
26
+ | `DbContextStatus` | type | `"ready" \| "connect" \| "transact"` |
27
+ | `DbTransactionError` | class | Standardized transaction error with `DbErrorCode` |
28
+ | `DbErrorCode` | enum | `NO_ACTIVE_TRANSACTION`, `TRANSACTION_ALREADY_STARTED`, `DEADLOCK`, `LOCK_TIMEOUT` |
29
+
30
+ [Detailed documentation](docs/core.md)
31
+
32
+ ### Queryable / Executable
33
+
34
+ Build and execute SELECT, INSERT, UPDATE, DELETE, and UPSERT queries with full type safety.
35
+
36
+ | Export | Kind | Description |
37
+ |--------|------|-------------|
38
+ | `Queryable` | class | Chaining query builder (select, where, join, include, orderBy, limit, groupBy, etc.) |
39
+ | `queryable` | function | Factory that creates a Queryable getter for a table or view |
40
+ | `getMatchedPrimaryKeys` | function | Match FK columns to target table PK columns |
41
+ | `Executable` | class | Stored procedure execution wrapper |
42
+ | `executable` | function | Factory that creates an Executable getter for a procedure |
43
+ | `parseSearchQuery` | function | Parse search text into LIKE patterns (OR / +must / -not) |
44
+ | `ParsedSearchQuery` | interface | Result of `parseSearchQuery` with `or`, `must`, `not` arrays |
45
+
46
+ [Detailed documentation](docs/queryable.md)
47
+
48
+ ### Expression
49
+
50
+ Dialect-independent SQL expression builder producing JSON AST.
51
+
52
+ | Export | Kind | Description |
53
+ |--------|------|-------------|
54
+ | `expr` | const | Expression builder with 70+ methods (comparisons, string, math, date, aggregate, window) |
55
+ | `ExprUnit` | class | Type-safe expression wrapper tracking return type via generics |
56
+ | `WhereExprUnit` | class | WHERE clause expression wrapper |
57
+ | `ExprInput` | type | Accepts `ExprUnit<T>` or literal `T` |
58
+ | `SwitchExprBuilder` | interface | CASE WHEN builder returned by `expr.switch()` |
59
+
60
+ [Detailed documentation](docs/expression.md)
61
+
62
+ ### Schema Builders
63
+
64
+ Define tables, views, procedures, columns, indexes, and relations via fluent API.
65
+
66
+ | Export | Kind | Description |
67
+ |--------|------|-------------|
68
+ | `Table` | function | Create a `TableBuilder` for a table |
69
+ | `TableBuilder` | class | Fluent builder: `.database()`, `.columns()`, `.primaryKey()`, `.indexes()`, `.relations()` |
70
+ | `View` | function | Create a `ViewBuilder` for a view |
71
+ | `ViewBuilder` | class | Fluent builder: `.database()`, `.query()`, `.relations()` |
72
+ | `Procedure` | function | Create a `ProcedureBuilder` for a stored procedure |
73
+ | `ProcedureBuilder` | class | Fluent builder: `.database()`, `.params()`, `.returns()`, `.body()` |
74
+ | `ColumnBuilder` | class | Column definition: `.autoIncrement()`, `.nullable()`, `.default()`, `.description()` |
75
+ | `createColumnFactory` | function | Returns column type methods (int, bigint, varchar, text, datetime, etc.) |
76
+ | `IndexBuilder` | class | Index definition: `.name()`, `.unique()`, `.orderBy()`, `.description()` |
77
+ | `createIndexFactory` | function | Returns `{ index(...columns) }` |
78
+ | `ForeignKeyBuilder` | class | N:1 FK relation (creates DB constraint) |
79
+ | `ForeignKeyTargetBuilder` | class | 1:N FK reverse-reference with `.single()` |
80
+ | `RelationKeyBuilder` | class | N:1 logical relation (no DB constraint) |
81
+ | `RelationKeyTargetBuilder` | class | 1:N logical reverse-reference with `.single()` |
82
+ | `createRelationFactory` | function | Returns FK + RelationKey methods (table gets both, view gets RelationKey only) |
83
+ | `_Migration` | const | Built-in system migration table (`_migration` with `code` column) |
84
+
85
+ [Detailed documentation](docs/schema-builders.md)
86
+
87
+ ### Query Builder
88
+
89
+ Render `QueryDef` JSON AST to dialect-specific SQL strings.
90
+
91
+ | Export | Kind | Description |
92
+ |--------|------|-------------|
93
+ | `createQueryBuilder` | function | Create a dialect-specific QueryBuilder (`"mysql"`, `"mssql"`, `"postgresql"`) |
94
+ | `QueryBuilderBase` | abstract class | Base class with dispatch and common render methods |
95
+ | `ExprRendererBase` | abstract class | Base class for expression-to-SQL rendering |
96
+ | `MysqlQueryBuilder` | class | MySQL implementation |
97
+ | `MysqlExprRenderer` | class | MySQL expression renderer |
98
+ | `MssqlQueryBuilder` | class | MSSQL implementation |
99
+ | `MssqlExprRenderer` | class | MSSQL expression renderer |
100
+ | `PostgresqlQueryBuilder` | class | PostgreSQL implementation |
101
+ | `PostgresqlExprRenderer` | class | PostgreSQL expression renderer |
102
+
103
+ [Detailed documentation](docs/query-builder.md)
104
+
105
+ ### Types
106
+
107
+ TypeScript types for dialects, queries, expressions, columns, and results.
12
108
 
13
- ## 문서
109
+ | Export | Kind | Description |
110
+ |--------|------|-------------|
111
+ | `Dialect` | type | `"mysql" \| "mssql" \| "postgresql"` |
112
+ | `dialects` | const | `["mysql", "mssql", "postgresql"]` |
113
+ | `QueryBuildResult` | interface | `{ sql, resultSetIndex?, resultSetStride? }` |
114
+ | `IsolationLevel` | type | `"READ_UNCOMMITTED" \| "READ_COMMITTED" \| "REPEATABLE_READ" \| "SERIALIZABLE"` |
115
+ | `DataRecord` | type | Recursive record type for query results |
116
+ | `DbContextExecutor` | interface | Executor interface (connect, close, beginTransaction, executeDefs, etc.) |
117
+ | `ResultMeta` | interface | Metadata for result type transformation and JOIN nesting |
118
+ | `Migration` | interface | `{ name, up }` migration definition |
119
+ | `DataType` | type | SQL type union (int, bigint, varchar, decimal, datetime, etc.) |
120
+ | `ColumnPrimitive` | type | All column value types (string, number, boolean, DateTime, DateOnly, Time, Uuid, Bytes, undefined) |
121
+ | `ColumnPrimitiveStr` | type | Type name keys: `"string" \| "number" \| "boolean" \| "DateTime" \| ...` |
122
+ | `ColumnMeta` | interface | Column metadata (type, dataType, autoIncrement, nullable, default, description) |
123
+ | `Expr` | type | Discriminated union of 40+ expression AST node types |
124
+ | `WhereExpr` | type | Subset of Expr for WHERE clauses (comparison + logical) |
125
+ | `QueryDef` | type | Union of all query definition types (DML + DDL + Utils + Meta) |
126
+ | `SelectQueryDef` | interface | SELECT query definition |
127
+ | `InsertQueryDef` | interface | INSERT query definition |
128
+ | `UpdateQueryDef` | interface | UPDATE query definition |
129
+ | `DeleteQueryDef` | interface | DELETE query definition |
130
+ | `UpsertQueryDef` | interface | UPSERT query definition |
131
+ | `QueryDefObjectName` | interface | `{ database?, schema?, name }` |
14
132
 
15
- | 카테고리 | 설명 |
16
- |---------|------|
17
- | [스키마 정의](docs/schema.md) | Table, View, Procedure, Column, Relation, Index 빌더 |
18
- | [쿼리 & 표현식](docs/query.md) | Queryable, Executable, expr 표현식 빌더 |
19
- | [DDL & 초기화](docs/ddl.md) | DDL 메서드, 스키마 초기화, 마이그레이션 |
133
+ [Detailed documentation](docs/types.md)
20
134
 
21
- ## 빠른 시작
135
+ ### Utilities
22
136
 
23
- ### DbContext 정의
137
+ Result parsing helpers.
138
+
139
+ | Export | Kind | Description |
140
+ |--------|------|-------------|
141
+ | `parseQueryResult` | function | Transform flat DB results to typed nested objects via `ResultMeta` |
142
+
143
+ [Detailed documentation](docs/utilities.md)
144
+
145
+ ## Usage Examples
146
+
147
+ ### 1. Define Schema and DbContext
24
148
 
25
149
  ```typescript
26
- import { defineDbContext, createDbContext, Table, expr } from "@simplysm/orm-common";
150
+ import { Table, defineDbContext, createDbContext, expr } from "@simplysm/orm-common";
27
151
 
28
- // 테이블 정의
29
- const User = Table("user")
152
+ const User = Table("User")
153
+ .database("mydb")
30
154
  .columns((c) => ({
31
- id: c.int().autoIncrement(),
155
+ id: c.bigint().autoIncrement(),
32
156
  name: c.varchar(100),
33
157
  email: c.varchar(200).nullable(),
158
+ status: c.varchar(20).default("active"),
34
159
  createdAt: c.datetime(),
35
160
  }))
36
161
  .primaryKey("id")
37
162
  .indexes((i) => [i.index("email").unique()]);
38
163
 
39
- const Order = Table("order")
164
+ const Post = Table("Post")
165
+ .database("mydb")
40
166
  .columns((c) => ({
41
- id: c.int().autoIncrement(),
42
- userId: c.int(),
43
- amount: c.decimal(10, 2),
167
+ id: c.bigint().autoIncrement(),
168
+ authorId: c.bigint(),
169
+ title: c.varchar(200),
170
+ content: c.text(),
44
171
  }))
45
172
  .primaryKey("id")
46
173
  .relations((r) => ({
47
- user: r.foreignKey(["userId"], () => User),
174
+ author: r.foreignKey(["authorId"], () => User),
48
175
  }));
49
176
 
50
- // DbContext 정의 (스키마 블루프린트)
51
177
  const MyDb = defineDbContext({
52
- tables: { user: User, order: Order },
178
+ tables: { user: User, post: Post },
53
179
  });
54
180
 
55
- // DbContext 인스턴스 생성 (런타임)
181
+ // createDbContext requires an executor (from @simplysm/orm-node or service-client)
56
182
  const db = createDbContext(MyDb, executor, { database: "mydb" });
57
183
  ```
58
184
 
59
- ### 쿼리 실행
185
+ ### 2. Query with Filters, Joins, and Aggregation
60
186
 
61
187
  ```typescript
62
188
  await db.connect(async () => {
63
- // SELECT
64
- const users = await db.user()
65
- .where((c) => [expr.eq(c.name, "Alice")])
66
- .orderBy((c) => c.createdAt, "DESC")
189
+ // Basic query with WHERE
190
+ const activeUsers = await db.user()
191
+ .where((u) => [expr.eq(u.status, "active")])
192
+ .orderBy((u) => u.name)
67
193
  .execute();
68
194
 
69
- // JOIN (관계 기반)
70
- const orders = await db.order()
71
- .include((c) => c.user)
72
- .where((c) => [expr.gt(c.amount, 100)])
195
+ // JOIN with include
196
+ const postsWithAuthor = await db.post()
197
+ .include((p) => p.author)
73
198
  .execute();
74
199
 
75
- // 집계
76
- const stats = await db.order()
77
- .select((c) => ({
78
- userId: c.userId,
79
- total: expr.sum(c.amount),
80
- count: expr.count(),
200
+ // Aggregation
201
+ const stats = await db.post()
202
+ .select((p) => ({
203
+ authorId: p.authorId,
204
+ postCount: expr.count(p.id),
81
205
  }))
82
- .groupBy((c) => [c.userId])
206
+ .groupBy((p) => [p.authorId])
83
207
  .execute();
208
+ });
209
+ ```
84
210
 
85
- // INSERT
86
- await db.user().insert([{ name: "Bob", email: "bob@example.com", createdAt: new DateTime() }]);
211
+ ### 3. Insert, Update, Delete
87
212
 
88
- // INSERT 후 ID 반환
213
+ ```typescript
214
+ await db.connect(async () => {
215
+ // Insert with output
89
216
  const [inserted] = await db.user().insert(
90
- [{ name: "Charlie", createdAt: new DateTime() }],
217
+ [{ name: "Alice", createdAt: DateTime.now() }],
91
218
  ["id"],
92
219
  );
93
220
 
94
- // UPDATE
221
+ // Update
95
222
  await db.user()
96
- .where((c) => [expr.eq(c.id, 1)])
97
- .update((c) => ({ name: expr.val("string", "Alice2") }));
223
+ .where((u) => [expr.eq(u.id, inserted.id)])
224
+ .update((u) => ({
225
+ status: expr.val("string", "verified"),
226
+ }));
98
227
 
99
- // DELETE
228
+ // Delete
100
229
  await db.user()
101
- .where((c) => [expr.eq(c.id, 1)])
230
+ .where((u) => [expr.eq(u.status, "deleted")])
102
231
  .delete();
232
+
233
+ // Upsert
234
+ await db.user()
235
+ .where((u) => [expr.eq(u.email, "alice@test.com")])
236
+ .upsert(() => ({
237
+ name: expr.val("string", "Alice"),
238
+ email: expr.val("string", "alice@test.com"),
239
+ createdAt: expr.val("DateTime", DateTime.now()),
240
+ }));
241
+ });
242
+ ```
243
+
244
+ ### 4. Text Search
245
+
246
+ ```typescript
247
+ await db.connect(async () => {
248
+ // Search with OR, +must, -exclude syntax
249
+ const results = await db.user()
250
+ .search((u) => [u.name, u.email], "alice +active -deleted")
251
+ .execute();
103
252
  });
104
253
  ```
105
254
 
106
- ### 지원 방언
255
+ ### 5. Recursive CTE
107
256
 
108
- | 방언 | 값 | 최소 버전 |
109
- |------|-----|----------|
110
- | MySQL | `"mysql"` | 8.0.14+ |
111
- | MSSQL | `"mssql"` | 2012+ |
112
- | PostgreSQL | `"postgresql"` | 9.0+ |
257
+ ```typescript
258
+ await db.connect(async () => {
259
+ const hierarchy = await db.employee()
260
+ .where((e) => [expr.null(e.managerId)])
261
+ .recursive((cte) =>
262
+ cte.from(Employee)
263
+ .where((e) => [expr.eq(e.managerId, e.self![0].id)])
264
+ )
265
+ .execute();
266
+ });
267
+ ```
package/docs/core.md ADDED
@@ -0,0 +1,225 @@
1
+ # Core
2
+
3
+ Database context definition, creation, and lifecycle management.
4
+
5
+ Source: `src/define-db-context.ts`, `src/create-db-context.ts`, `src/types/db-context-def.ts`, `src/errors/db-transaction-error.ts`
6
+
7
+ ## defineDbContext
8
+
9
+ Create a `DbContextDef` blueprint from tables, views, procedures, and migrations. Automatically includes the `_migration` system table.
10
+
11
+ ```typescript
12
+ function defineDbContext<
13
+ TTables extends Record<string, TableBuilder<any, any>>,
14
+ TViews extends Record<string, ViewBuilder<any, any, any>>,
15
+ TProcedures extends Record<string, ProcedureBuilder<any, any>>,
16
+ >(config: {
17
+ tables?: TTables;
18
+ views?: TViews;
19
+ procedures?: TProcedures;
20
+ migrations?: Migration[];
21
+ }): DbContextDef<TTables & { _migration: typeof _Migration }, TViews, TProcedures>;
22
+ ```
23
+
24
+ **Example:**
25
+
26
+ ```typescript
27
+ const MyDb = defineDbContext({
28
+ tables: { user: User, post: Post },
29
+ views: { userSummary: UserSummary },
30
+ procedures: { getUserById: GetUserById },
31
+ migrations: [
32
+ { name: "20260101_001_init", up: async (db) => { await db.createTable(User); } },
33
+ ],
34
+ });
35
+ ```
36
+
37
+ ## createDbContext
38
+
39
+ Create a runtime `DbContextInstance` from a definition and executor. This is the main entry point for database operations.
40
+
41
+ ```typescript
42
+ /**
43
+ * @param def - Definition object created by defineDbContext()
44
+ * @param executor - Query executor (NodeDbContextExecutor, ServiceDbContextExecutor, etc.)
45
+ * @param opt - Database options
46
+ * @param opt.database - Database name
47
+ * @param opt.schema - Schema name (MSSQL: dbo, PostgreSQL: public)
48
+ * @returns A complete DbContext instance
49
+ */
50
+ function createDbContext<TDef extends DbContextDef<any, any, any>>(
51
+ def: TDef,
52
+ executor: DbContextExecutor,
53
+ opt: { database: string; schema?: string },
54
+ ): DbContextInstance<TDef>;
55
+ ```
56
+
57
+ The returned instance provides:
58
+
59
+ - **Queryable accessors** -- one per table/view (e.g., `db.user()` returns a `Queryable`)
60
+ - **Executable accessors** -- one per procedure (e.g., `db.getUserById()` returns an `Executable`)
61
+ - **Connection management** -- `connect()`, `connectWithoutTransaction()`, `transaction()`
62
+ - **DDL methods** -- `createTable()`, `dropTable()`, `addColumn()`, `addIndex()`, etc.
63
+ - **Initialization** -- `initialize()` to run migrations
64
+
65
+ **Example:**
66
+
67
+ ```typescript
68
+ const db = createDbContext(MyDb, executor, { database: "mydb" });
69
+
70
+ await db.connect(async () => {
71
+ const users = await db.user().execute();
72
+ });
73
+ ```
74
+
75
+ ## DbContextBase
76
+
77
+ Internal interface satisfied by the DbContext instance. Used by `Queryable`, `Executable`, and `ViewBuilder`.
78
+
79
+ ```typescript
80
+ interface DbContextBase {
81
+ status: DbContextStatus;
82
+ readonly database: string | undefined;
83
+ readonly schema: string | undefined;
84
+ getNextAlias(): string;
85
+ resetAliasCounter(): void;
86
+ executeDefs<T = DataRecord>(
87
+ defs: QueryDef[],
88
+ resultMetas?: (ResultMeta | undefined)[],
89
+ ): Promise<T[][]>;
90
+ getQueryDefObjectName(
91
+ tableOrView: TableBuilder<any, any> | ViewBuilder<any, any, any>,
92
+ ): QueryDefObjectName;
93
+ switchFk(table: QueryDefObjectName, enabled: boolean): Promise<void>;
94
+ }
95
+ ```
96
+
97
+ ## DbContextDef
98
+
99
+ Definition (blueprint) created by `defineDbContext()`. Contains schema metadata but no runtime state.
100
+
101
+ ```typescript
102
+ interface DbContextDef<
103
+ TTables extends Record<string, TableBuilder<any, any>>,
104
+ TViews extends Record<string, ViewBuilder<any, any, any>>,
105
+ TProcedures extends Record<string, ProcedureBuilder<any, any>>,
106
+ > {
107
+ readonly meta: {
108
+ readonly tables: TTables;
109
+ readonly views: TViews;
110
+ readonly procedures: TProcedures;
111
+ readonly migrations: Migration[];
112
+ };
113
+ }
114
+ ```
115
+
116
+ ## DbContextInstance
117
+
118
+ Full runtime type created by `createDbContext`. Combines `DbContextBase`, connection methods, DDL methods, and auto-mapped queryable/executable accessors.
119
+
120
+ ```typescript
121
+ type DbContextInstance<TDef extends DbContextDef<any, any, any>> = DbContextBase &
122
+ DbContextConnectionMethods &
123
+ DbContextDdlMethods & {
124
+ [K in keyof TDef["meta"]["tables"]]: () => Queryable<...>;
125
+ } & {
126
+ [K in keyof TDef["meta"]["views"]]: () => Queryable<...>;
127
+ } & {
128
+ [K in keyof TDef["meta"]["procedures"]]: () => Executable<...>;
129
+ } & {
130
+ _migration: () => Queryable<{ code: string }, any>;
131
+ initialize(options?: { dbs?: string[]; force?: boolean }): Promise<void>;
132
+ };
133
+ ```
134
+
135
+ ### Connection Methods
136
+
137
+ ```typescript
138
+ interface DbContextConnectionMethods {
139
+ /** Execute within a transaction (auto commit/rollback) */
140
+ connect<TResult>(fn: () => Promise<TResult>, isolationLevel?: IsolationLevel): Promise<TResult>;
141
+
142
+ /** Connect without transaction (for DDL or read-only operations) */
143
+ connectWithoutTransaction<TResult>(callback: () => Promise<TResult>): Promise<TResult>;
144
+
145
+ /** Start a transaction within an already-connected state */
146
+ transaction<TResult>(fn: () => Promise<TResult>, isolationLevel?: IsolationLevel): Promise<TResult>;
147
+ }
148
+ ```
149
+
150
+ ### DDL Methods
151
+
152
+ The instance exposes DDL execution methods and their corresponding QueryDef generators:
153
+
154
+ | Method | Description |
155
+ |--------|-------------|
156
+ | `createTable(table)` | CREATE TABLE |
157
+ | `dropTable(table)` | DROP TABLE |
158
+ | `renameTable(table, newName)` | RENAME TABLE |
159
+ | `createView(view)` | CREATE VIEW |
160
+ | `dropView(view)` | DROP VIEW |
161
+ | `createProc(procedure)` | CREATE PROCEDURE |
162
+ | `dropProc(procedure)` | DROP PROCEDURE |
163
+ | `addColumn(table, name, column)` | ADD COLUMN |
164
+ | `dropColumn(table, column)` | DROP COLUMN |
165
+ | `modifyColumn(table, name, column)` | MODIFY COLUMN |
166
+ | `renameColumn(table, column, newName)` | RENAME COLUMN |
167
+ | `addPrimaryKey(table, columns)` | ADD PRIMARY KEY |
168
+ | `dropPrimaryKey(table)` | DROP PRIMARY KEY |
169
+ | `addForeignKey(table, name, def)` | ADD FOREIGN KEY |
170
+ | `dropForeignKey(table, name)` | DROP FOREIGN KEY |
171
+ | `addIndex(table, indexBuilder)` | CREATE INDEX |
172
+ | `dropIndex(table, columns)` | DROP INDEX |
173
+ | `clearSchema(params)` | Clear all objects in schema |
174
+ | `schemaExists(database, schema?)` | Check schema existence |
175
+ | `truncate(table)` | TRUNCATE TABLE |
176
+ | `switchFk(table, enabled)` | Enable/disable FK constraints |
177
+
178
+ ## DbContextStatus
179
+
180
+ ```typescript
181
+ type DbContextStatus = "ready" | "connect" | "transact";
182
+ ```
183
+
184
+ Lifecycle: `ready` -> `connect` -> `transact` -> `connect` -> `ready`
185
+
186
+ ## DbTransactionError
187
+
188
+ Standardized transaction error wrapping DBMS-specific native errors.
189
+
190
+ ```typescript
191
+ class DbTransactionError extends Error {
192
+ readonly name = "DbTransactionError";
193
+ constructor(
194
+ public readonly code: DbErrorCode,
195
+ message: string,
196
+ public readonly originalError?: unknown,
197
+ );
198
+ }
199
+ ```
200
+
201
+ ## DbErrorCode
202
+
203
+ ```typescript
204
+ enum DbErrorCode {
205
+ NO_ACTIVE_TRANSACTION = "NO_ACTIVE_TRANSACTION",
206
+ TRANSACTION_ALREADY_STARTED = "TRANSACTION_ALREADY_STARTED",
207
+ DEADLOCK = "DEADLOCK",
208
+ LOCK_TIMEOUT = "LOCK_TIMEOUT",
209
+ }
210
+ ```
211
+
212
+ **Example:**
213
+
214
+ ```typescript
215
+ try {
216
+ await executor.rollbackTransaction();
217
+ } catch (err) {
218
+ if (err instanceof DbTransactionError) {
219
+ if (err.code === DbErrorCode.NO_ACTIVE_TRANSACTION) {
220
+ return; // Already rolled back
221
+ }
222
+ }
223
+ throw err;
224
+ }
225
+ ```