metal-orm 1.1.3 → 1.1.5
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 +715 -703
- package/dist/index.cjs +655 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +170 -8
- package/dist/index.d.ts +170 -8
- package/dist/index.js +649 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-entities/render.mjs +24 -1
- package/scripts/naming-strategy.mjs +16 -1
- package/src/core/ast/procedure.ts +21 -0
- package/src/core/ast/query.ts +47 -19
- package/src/core/ddl/introspect/utils.ts +56 -56
- package/src/core/dialect/abstract.ts +560 -547
- package/src/core/dialect/base/sql-dialect.ts +43 -29
- package/src/core/dialect/mssql/index.ts +369 -232
- package/src/core/dialect/mysql/index.ts +99 -7
- package/src/core/dialect/postgres/index.ts +121 -60
- package/src/core/dialect/sqlite/index.ts +97 -64
- package/src/core/execution/db-executor.ts +108 -90
- package/src/core/execution/executors/mssql-executor.ts +28 -24
- package/src/core/execution/executors/mysql-executor.ts +62 -27
- package/src/core/execution/executors/sqlite-executor.ts +10 -9
- package/src/index.ts +9 -6
- package/src/orm/execute-procedure.ts +77 -0
- package/src/orm/execute.ts +74 -73
- package/src/orm/interceptor-pipeline.ts +21 -17
- package/src/orm/pooled-executor-factory.ts +41 -20
- package/src/orm/unit-of-work.ts +6 -4
- package/src/query/index.ts +8 -5
- package/src/query-builder/delete.ts +3 -2
- package/src/query-builder/insert-query-state.ts +47 -19
- package/src/query-builder/insert.ts +142 -28
- package/src/query-builder/procedure-call.ts +122 -0
- package/src/query-builder/select/select-operations.ts +5 -2
- package/src/query-builder/select.ts +1146 -1105
- package/src/query-builder/update.ts +3 -2
- package/src/tree/tree-manager.ts +754 -754
package/src/orm/execute.ts
CHANGED
|
@@ -12,19 +12,20 @@ import {
|
|
|
12
12
|
import { EntityContext } from './entity-context.js';
|
|
13
13
|
import { ExecutionContext } from './execution-context.js';
|
|
14
14
|
import { HydrationContext } from './hydration-context.js';
|
|
15
|
-
import { RelationIncludeOptions } from '../query-builder/relation-types.js';
|
|
16
|
-
import { getEntityMeta, RelationKey } from './entity-meta.js';
|
|
17
|
-
import { preloadRelationIncludes } from './relation-preload.js';
|
|
18
|
-
import {
|
|
19
|
-
loadHasManyRelation,
|
|
20
|
-
loadHasOneRelation,
|
|
21
|
-
loadBelongsToRelation,
|
|
22
|
-
loadBelongsToManyRelation
|
|
23
|
-
} from './lazy-batch.js';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
15
|
+
import { RelationIncludeOptions } from '../query-builder/relation-types.js';
|
|
16
|
+
import { getEntityMeta, RelationKey } from './entity-meta.js';
|
|
17
|
+
import { preloadRelationIncludes } from './relation-preload.js';
|
|
18
|
+
import {
|
|
19
|
+
loadHasManyRelation,
|
|
20
|
+
loadHasOneRelation,
|
|
21
|
+
loadBelongsToRelation,
|
|
22
|
+
loadBelongsToManyRelation
|
|
23
|
+
} from './lazy-batch.js';
|
|
24
|
+
import { payloadResultSets } from '../core/execution/db-executor.js';
|
|
25
|
+
|
|
26
|
+
type Row = Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
const flattenResults = (results: { columns: string[]; values: unknown[][] }[]): Row[] => {
|
|
28
29
|
const rows: Row[] = [];
|
|
29
30
|
for (const result of results) {
|
|
30
31
|
const { columns, values } = result;
|
|
@@ -39,41 +40,41 @@ const flattenResults = (results: { columns: string[]; values: unknown[][] }[]):
|
|
|
39
40
|
return rows;
|
|
40
41
|
};
|
|
41
42
|
|
|
42
|
-
const executeWithContexts = async <TTable extends TableDef>(
|
|
43
|
-
execCtx: ExecutionContext,
|
|
44
|
-
entityCtx: EntityContext,
|
|
45
|
-
qb: SelectQueryBuilder<unknown, TTable>
|
|
46
|
-
): Promise<EntityInstance<TTable>[]> => {
|
|
47
|
-
const ast = qb.getAST();
|
|
43
|
+
const executeWithContexts = async <TTable extends TableDef>(
|
|
44
|
+
execCtx: ExecutionContext,
|
|
45
|
+
entityCtx: EntityContext,
|
|
46
|
+
qb: SelectQueryBuilder<unknown, TTable>
|
|
47
|
+
): Promise<EntityInstance<TTable>[]> => {
|
|
48
|
+
const ast = qb.getAST();
|
|
48
49
|
const compiled = execCtx.dialect.compileSelect(ast);
|
|
49
50
|
const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
|
|
50
|
-
const rows = flattenResults(executed);
|
|
51
|
-
const lazyRelations = qb.getLazyRelations() as RelationKey<TTable>[];
|
|
52
|
-
const lazyRelationOptions = qb.getLazyRelationOptions();
|
|
53
|
-
const includeTree = qb.getIncludeTree();
|
|
54
|
-
|
|
55
|
-
if (ast.setOps && ast.setOps.length > 0) {
|
|
56
|
-
const proxies = rows.map(row => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
|
|
57
|
-
await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
|
|
58
|
-
await preloadRelationIncludes(proxies as Record<string, unknown>[], includeTree);
|
|
59
|
-
return proxies;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const hydrated = hydrateRows(rows, qb.getHydrationPlan());
|
|
63
|
-
const entities = hydrated.map(row => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
|
|
64
|
-
await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
|
|
65
|
-
await preloadRelationIncludes(entities as Record<string, unknown>[], includeTree);
|
|
66
|
-
return entities;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const executePlainWithContexts = async <TTable extends TableDef>(
|
|
70
|
-
execCtx: ExecutionContext,
|
|
71
|
-
qb: SelectQueryBuilder<unknown, TTable>
|
|
72
|
-
): Promise<Record<string, unknown>[]> => {
|
|
73
|
-
const ast = qb.getAST();
|
|
51
|
+
const rows = flattenResults(payloadResultSets(executed));
|
|
52
|
+
const lazyRelations = qb.getLazyRelations() as RelationKey<TTable>[];
|
|
53
|
+
const lazyRelationOptions = qb.getLazyRelationOptions();
|
|
54
|
+
const includeTree = qb.getIncludeTree();
|
|
55
|
+
|
|
56
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
57
|
+
const proxies = rows.map(row => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
|
|
58
|
+
await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
|
|
59
|
+
await preloadRelationIncludes(proxies as Record<string, unknown>[], includeTree);
|
|
60
|
+
return proxies;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const hydrated = hydrateRows(rows, qb.getHydrationPlan());
|
|
64
|
+
const entities = hydrated.map(row => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
|
|
65
|
+
await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
|
|
66
|
+
await preloadRelationIncludes(entities as Record<string, unknown>[], includeTree);
|
|
67
|
+
return entities;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const executePlainWithContexts = async <TTable extends TableDef>(
|
|
71
|
+
execCtx: ExecutionContext,
|
|
72
|
+
qb: SelectQueryBuilder<unknown, TTable>
|
|
73
|
+
): Promise<Record<string, unknown>[]> => {
|
|
74
|
+
const ast = qb.getAST();
|
|
74
75
|
const compiled = execCtx.dialect.compileSelect(ast);
|
|
75
76
|
const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
|
|
76
|
-
const rows = flattenResults(executed);
|
|
77
|
+
const rows = flattenResults(payloadResultSets(executed));
|
|
77
78
|
|
|
78
79
|
if (ast.setOps && ast.setOps.length > 0) {
|
|
79
80
|
return rows;
|
|
@@ -89,12 +90,12 @@ const executePlainWithContexts = async <TTable extends TableDef>(
|
|
|
89
90
|
* @param qb - The select query builder
|
|
90
91
|
* @returns Promise resolving to array of entity instances
|
|
91
92
|
*/
|
|
92
|
-
export async function executeHydrated<TTable extends TableDef>(
|
|
93
|
-
session: OrmSession,
|
|
94
|
-
qb: SelectQueryBuilder<unknown, TTable>
|
|
95
|
-
): Promise<EntityInstance<TTable>[]> {
|
|
96
|
-
return executeWithContexts(session.getExecutionContext(), session, qb);
|
|
97
|
-
}
|
|
93
|
+
export async function executeHydrated<TTable extends TableDef>(
|
|
94
|
+
session: OrmSession,
|
|
95
|
+
qb: SelectQueryBuilder<unknown, TTable>
|
|
96
|
+
): Promise<EntityInstance<TTable>[]> {
|
|
97
|
+
return executeWithContexts(session.getExecutionContext(), session, qb);
|
|
98
|
+
}
|
|
98
99
|
|
|
99
100
|
/**
|
|
100
101
|
* Executes a hydrated query and returns plain row objects (no entity proxies).
|
|
@@ -103,12 +104,12 @@ export async function executeHydrated<TTable extends TableDef>(
|
|
|
103
104
|
* @param qb - The select query builder
|
|
104
105
|
* @returns Promise resolving to array of plain row objects
|
|
105
106
|
*/
|
|
106
|
-
export async function executeHydratedPlain<TTable extends TableDef>(
|
|
107
|
-
session: OrmSession,
|
|
108
|
-
qb: SelectQueryBuilder<unknown, TTable>
|
|
109
|
-
): Promise<Record<string, unknown>[]> {
|
|
110
|
-
return executePlainWithContexts(session.getExecutionContext(), qb);
|
|
111
|
-
}
|
|
107
|
+
export async function executeHydratedPlain<TTable extends TableDef>(
|
|
108
|
+
session: OrmSession,
|
|
109
|
+
qb: SelectQueryBuilder<unknown, TTable>
|
|
110
|
+
): Promise<Record<string, unknown>[]> {
|
|
111
|
+
return executePlainWithContexts(session.getExecutionContext(), qb);
|
|
112
|
+
}
|
|
112
113
|
|
|
113
114
|
/**
|
|
114
115
|
* Executes a hydrated query using execution and hydration contexts.
|
|
@@ -118,17 +119,17 @@ export async function executeHydratedPlain<TTable extends TableDef>(
|
|
|
118
119
|
* @param qb - The select query builder
|
|
119
120
|
* @returns Promise resolving to array of entity instances
|
|
120
121
|
*/
|
|
121
|
-
export async function executeHydratedWithContexts<TTable extends TableDef>(
|
|
122
|
-
execCtx: ExecutionContext,
|
|
123
|
-
hydCtx: HydrationContext,
|
|
124
|
-
qb: SelectQueryBuilder<unknown, TTable>
|
|
125
|
-
): Promise<EntityInstance<TTable>[]> {
|
|
126
|
-
const entityCtx = hydCtx.entityContext;
|
|
127
|
-
if (!entityCtx) {
|
|
128
|
-
throw new Error('Hydration context is missing an EntityContext');
|
|
129
|
-
}
|
|
130
|
-
return executeWithContexts(execCtx, entityCtx, qb);
|
|
131
|
-
}
|
|
122
|
+
export async function executeHydratedWithContexts<TTable extends TableDef>(
|
|
123
|
+
execCtx: ExecutionContext,
|
|
124
|
+
hydCtx: HydrationContext,
|
|
125
|
+
qb: SelectQueryBuilder<unknown, TTable>
|
|
126
|
+
): Promise<EntityInstance<TTable>[]> {
|
|
127
|
+
const entityCtx = hydCtx.entityContext;
|
|
128
|
+
if (!entityCtx) {
|
|
129
|
+
throw new Error('Hydration context is missing an EntityContext');
|
|
130
|
+
}
|
|
131
|
+
return executeWithContexts(execCtx, entityCtx, qb);
|
|
132
|
+
}
|
|
132
133
|
|
|
133
134
|
/**
|
|
134
135
|
* Executes a hydrated query using execution context and returns plain row objects.
|
|
@@ -137,12 +138,12 @@ export async function executeHydratedWithContexts<TTable extends TableDef>(
|
|
|
137
138
|
* @param qb - The select query builder
|
|
138
139
|
* @returns Promise resolving to array of plain row objects
|
|
139
140
|
*/
|
|
140
|
-
export async function executeHydratedPlainWithContexts<TTable extends TableDef>(
|
|
141
|
-
execCtx: ExecutionContext,
|
|
142
|
-
qb: SelectQueryBuilder<unknown, TTable>
|
|
143
|
-
): Promise<Record<string, unknown>[]> {
|
|
144
|
-
return executePlainWithContexts(execCtx, qb);
|
|
145
|
-
}
|
|
141
|
+
export async function executeHydratedPlainWithContexts<TTable extends TableDef>(
|
|
142
|
+
execCtx: ExecutionContext,
|
|
143
|
+
qb: SelectQueryBuilder<unknown, TTable>
|
|
144
|
+
): Promise<Record<string, unknown>[]> {
|
|
145
|
+
return executePlainWithContexts(execCtx, qb);
|
|
146
|
+
}
|
|
146
147
|
|
|
147
148
|
const loadLazyRelationsForTable = async <TTable extends TableDef>(
|
|
148
149
|
ctx: EntityContext,
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import type { DbExecutor,
|
|
1
|
+
import type { DbExecutor, ExecutionPayload } from '../core/execution/db-executor.js';
|
|
2
|
+
import { toExecutionPayload } from '../core/execution/db-executor.js';
|
|
2
3
|
|
|
3
|
-
export interface QueryContext {
|
|
4
|
+
export interface QueryContext {
|
|
4
5
|
sql: string;
|
|
5
6
|
params: unknown[];
|
|
6
7
|
// maybe metadata like entity type, operation type, etc.
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type QueryInterceptor = (
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type QueryInterceptor = (
|
|
11
|
+
ctx: QueryContext,
|
|
12
|
+
next: () => Promise<ExecutionPayload>
|
|
13
|
+
) => Promise<ExecutionPayload>;
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* Pipeline for query interceptors.
|
|
@@ -19,15 +23,15 @@ export class InterceptorPipeline {
|
|
|
19
23
|
this.interceptors.push(interceptor);
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
async run(ctx: QueryContext, executor: DbExecutor): Promise<
|
|
23
|
-
let i = 0;
|
|
24
|
-
const dispatch = async (): Promise<
|
|
25
|
-
const interceptor = this.interceptors[i++];
|
|
26
|
-
if (!interceptor) {
|
|
27
|
-
return executor.executeSql(ctx.sql, ctx.params);
|
|
28
|
-
}
|
|
29
|
-
return interceptor(ctx, dispatch);
|
|
30
|
-
};
|
|
31
|
-
return dispatch();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
26
|
+
async run(ctx: QueryContext, executor: DbExecutor): Promise<ExecutionPayload> {
|
|
27
|
+
let i = 0;
|
|
28
|
+
const dispatch = async (): Promise<ExecutionPayload> => {
|
|
29
|
+
const interceptor = this.interceptors[i++];
|
|
30
|
+
if (!interceptor) {
|
|
31
|
+
return toExecutionPayload(await executor.executeSql(ctx.sql, ctx.params));
|
|
32
|
+
}
|
|
33
|
+
return toExecutionPayload(await interceptor(ctx, dispatch));
|
|
34
|
+
};
|
|
35
|
+
return dispatch();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,24 +1,39 @@
|
|
|
1
|
-
import type { DbExecutor, QueryResult } from '../core/execution/db-executor.js';
|
|
2
|
-
import {
|
|
1
|
+
import type { DbExecutor, QueryResult } from '../core/execution/db-executor.js';
|
|
2
|
+
import { toExecutionPayload } from '../core/execution/db-executor.js';
|
|
3
|
+
import { rowsToQueryResult } from '../core/execution/db-executor.js';
|
|
3
4
|
import type { Pool } from '../core/execution/pooling/pool.js';
|
|
4
5
|
import type { DbExecutorFactory } from './orm.js';
|
|
5
6
|
|
|
6
|
-
export interface PooledConnectionAdapter<TConn> {
|
|
7
|
-
query(
|
|
8
|
-
conn: TConn,
|
|
9
|
-
sql: string,
|
|
10
|
-
params?: unknown[]
|
|
11
|
-
): Promise<
|
|
7
|
+
export interface PooledConnectionAdapter<TConn> {
|
|
8
|
+
query(
|
|
9
|
+
conn: TConn,
|
|
10
|
+
sql: string,
|
|
11
|
+
params?: unknown[]
|
|
12
|
+
): Promise<
|
|
13
|
+
| Array<Record<string, unknown>>
|
|
14
|
+
| Array<Array<Record<string, unknown>>>
|
|
15
|
+
| QueryResult[]
|
|
16
|
+
>;
|
|
12
17
|
|
|
13
18
|
beginTransaction(conn: TConn): Promise<void>;
|
|
14
19
|
commitTransaction(conn: TConn): Promise<void>;
|
|
15
20
|
rollbackTransaction(conn: TConn): Promise<void>;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
type PooledExecutorFactoryOptions<TConn> = {
|
|
19
|
-
pool: Pool<TConn>;
|
|
20
|
-
adapter: PooledConnectionAdapter<TConn>;
|
|
21
|
-
};
|
|
23
|
+
type PooledExecutorFactoryOptions<TConn> = {
|
|
24
|
+
pool: Pool<TConn>;
|
|
25
|
+
adapter: PooledConnectionAdapter<TConn>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const isQueryResult = (value: unknown): value is QueryResult =>
|
|
29
|
+
typeof value === 'object' &&
|
|
30
|
+
value !== null &&
|
|
31
|
+
Array.isArray((value as QueryResult).columns) &&
|
|
32
|
+
Array.isArray((value as QueryResult).values);
|
|
33
|
+
|
|
34
|
+
const isRowArray = (value: unknown): value is Array<Record<string, unknown>> =>
|
|
35
|
+
Array.isArray(value) &&
|
|
36
|
+
value.every(item => typeof item === 'object' && item !== null && !Array.isArray(item));
|
|
22
37
|
|
|
23
38
|
/**
|
|
24
39
|
* Creates a first-class DbExecutorFactory backed by MetalORM's Pool.
|
|
@@ -42,14 +57,20 @@ export function createPooledExecutorFactory<TConn>(
|
|
|
42
57
|
return lease;
|
|
43
58
|
};
|
|
44
59
|
|
|
45
|
-
const executeWithConn = async (
|
|
46
|
-
conn: TConn,
|
|
47
|
-
sql: string,
|
|
48
|
-
params?: unknown[]
|
|
49
|
-
)
|
|
50
|
-
const rows = await adapter.query(conn, sql, params);
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
const executeWithConn = async (
|
|
61
|
+
conn: TConn,
|
|
62
|
+
sql: string,
|
|
63
|
+
params?: unknown[]
|
|
64
|
+
) => {
|
|
65
|
+
const rows = await adapter.query(conn, sql, params);
|
|
66
|
+
if (Array.isArray(rows) && rows.length > 0 && rows.every(isQueryResult)) {
|
|
67
|
+
return toExecutionPayload(rows);
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(rows) && rows.length > 0 && rows.every(isRowArray)) {
|
|
70
|
+
return toExecutionPayload(rows.map(set => rowsToQueryResult(set)));
|
|
71
|
+
}
|
|
72
|
+
return toExecutionPayload([rowsToQueryResult(rows as Array<Record<string, unknown>>)]);
|
|
73
|
+
};
|
|
53
74
|
|
|
54
75
|
return {
|
|
55
76
|
capabilities: { transactions: true },
|
package/src/orm/unit-of-work.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { UpdateQueryBuilder } from '../query-builder/update.js';
|
|
|
6
6
|
import { DeleteQueryBuilder } from '../query-builder/delete.js';
|
|
7
7
|
import { findPrimaryKey } from '../query-builder/hydration-planner.js';
|
|
8
8
|
import type { TableDef, TableHooks } from '../schema/table.js';
|
|
9
|
-
import
|
|
9
|
+
import { payloadResultSets } from '../core/execution/db-executor.js';
|
|
10
|
+
import type { DbExecutor, QueryResult } from '../core/execution/db-executor.js';
|
|
10
11
|
import { IdentityMap } from './identity-map.js';
|
|
11
12
|
import { EntityStatus } from './runtime-types.js';
|
|
12
13
|
import type { TrackedEntity } from './runtime-types.js';
|
|
@@ -322,9 +323,10 @@ export class UnitOfWork {
|
|
|
322
323
|
* @param compiled - The compiled query
|
|
323
324
|
* @returns Query results
|
|
324
325
|
*/
|
|
325
|
-
private async executeCompiled(compiled: CompiledQuery): Promise<QueryResult[]> {
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
private async executeCompiled(compiled: CompiledQuery): Promise<QueryResult[]> {
|
|
327
|
+
const payload = await this.executor.executeSql(compiled.sql, compiled.params);
|
|
328
|
+
return payloadResultSets(payload);
|
|
329
|
+
}
|
|
328
330
|
|
|
329
331
|
/**
|
|
330
332
|
* Gets columns for RETURNING clause.
|
package/src/query/index.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { TableDef } from '../schema/table.js';
|
|
|
2
2
|
import { SelectQueryBuilder } from '../query-builder/select.js';
|
|
3
3
|
import { InsertQueryBuilder } from '../query-builder/insert.js';
|
|
4
4
|
import { UpdateQueryBuilder } from '../query-builder/update.js';
|
|
5
|
-
import { DeleteQueryBuilder } from '../query-builder/delete.js';
|
|
5
|
+
import { DeleteQueryBuilder } from '../query-builder/delete.js';
|
|
6
|
+
import { callProcedure } from '../query-builder/procedure-call.js';
|
|
6
7
|
import { QueryTarget, resolveTable } from './target.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -68,7 +69,9 @@ export const update = <TTable extends TableDef>(target: QueryTarget<TTable>): Up
|
|
|
68
69
|
* const query = deleteFrom(UserTable).where(eq(UserTable.id, 1));
|
|
69
70
|
* ```
|
|
70
71
|
*/
|
|
71
|
-
export const deleteFrom = <TTable extends TableDef>(target: QueryTarget<TTable>): DeleteQueryBuilder<unknown> => {
|
|
72
|
-
const table = resolveTable(target);
|
|
73
|
-
return new DeleteQueryBuilder(table);
|
|
74
|
-
};
|
|
72
|
+
export const deleteFrom = <TTable extends TableDef>(target: QueryTarget<TTable>): DeleteQueryBuilder<unknown> => {
|
|
73
|
+
const table = resolveTable(target);
|
|
74
|
+
return new DeleteQueryBuilder(table);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { callProcedure };
|
|
@@ -9,7 +9,7 @@ import { DeleteQueryState } from './delete-query-state.js';
|
|
|
9
9
|
import { createJoinNode } from '../core/ast/join-node.js';
|
|
10
10
|
import { buildColumnNode } from '../core/ast/builders.js';
|
|
11
11
|
import { OrmSession } from '../orm/orm-session.js';
|
|
12
|
-
import { QueryResult } from '../core/execution/db-executor.js';
|
|
12
|
+
import { payloadResultSets, QueryResult } from '../core/execution/db-executor.js';
|
|
13
13
|
|
|
14
14
|
type DeleteDialectInput = Dialect | DialectKey;
|
|
15
15
|
|
|
@@ -130,7 +130,8 @@ export class DeleteQueryBuilder<T> {
|
|
|
130
130
|
async execute(session: OrmSession): Promise<QueryResult[]> {
|
|
131
131
|
const execCtx = session.getExecutionContext();
|
|
132
132
|
const compiled = this.compile(execCtx.dialect);
|
|
133
|
-
|
|
133
|
+
const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
|
|
134
|
+
return payloadResultSets(payload);
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
/**
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table.js';
|
|
2
|
-
import { InsertQueryNode, SelectQueryNode } from '../core/ast/query.js';
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { InsertQueryNode, SelectQueryNode, UpsertClause } from '../core/ast/query.js';
|
|
3
3
|
import {
|
|
4
4
|
ColumnNode,
|
|
5
5
|
OperandNode,
|
|
6
6
|
isValueOperandInput,
|
|
7
|
-
valueToOperand
|
|
8
|
-
} from '../core/ast/expression.js';
|
|
9
|
-
import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
7
|
+
valueToOperand
|
|
8
|
+
} from '../core/ast/expression.js';
|
|
9
|
+
import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
10
10
|
import {
|
|
11
11
|
buildColumnNodes,
|
|
12
12
|
createTableNode
|
|
13
13
|
} from '../core/ast/builders.js';
|
|
14
14
|
|
|
15
|
-
type InsertRows = Record<string, ValueOperandInput>[];
|
|
15
|
+
type InsertRows = Record<string, ValueOperandInput>[];
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Maintains immutable state for building INSERT queries
|
|
@@ -68,7 +68,7 @@ export class InsertQueryState {
|
|
|
68
68
|
* @throws Error if mixing VALUES with SELECT source
|
|
69
69
|
* @throws Error if invalid values are provided
|
|
70
70
|
*/
|
|
71
|
-
withValues(rows: InsertRows): InsertQueryState {
|
|
71
|
+
withValues(rows: InsertRows): InsertQueryState {
|
|
72
72
|
if (!rows.length) return this;
|
|
73
73
|
|
|
74
74
|
if (this.ast.source.type === 'InsertSelect') {
|
|
@@ -81,11 +81,11 @@ export class InsertQueryState {
|
|
|
81
81
|
definedColumns.map(column => {
|
|
82
82
|
const rawValue = row[column.name];
|
|
83
83
|
|
|
84
|
-
if (!isValueOperandInput(rawValue)) {
|
|
85
|
-
throw new Error(
|
|
86
|
-
`Invalid insert value for column "${column.name}" in row ${rowIndex}: only string, number, boolean, Date, Buffer, null, or OperandNodes are allowed`
|
|
87
|
-
);
|
|
88
|
-
}
|
|
84
|
+
if (!isValueOperandInput(rawValue)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Invalid insert value for column "${column.name}" in row ${rowIndex}: only string, number, boolean, Date, Buffer, null, or OperandNodes are allowed`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
89
|
|
|
90
90
|
return valueToOperand(rawValue);
|
|
91
91
|
})
|
|
@@ -153,10 +153,38 @@ export class InsertQueryState {
|
|
|
153
153
|
* @param columns - Columns to return after insertion
|
|
154
154
|
* @returns A new InsertQueryState with the RETURNING clause added
|
|
155
155
|
*/
|
|
156
|
-
withReturning(columns: ColumnNode[]): InsertQueryState {
|
|
157
|
-
return this.clone({
|
|
158
|
-
...this.ast,
|
|
159
|
-
returning: [...columns]
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
156
|
+
withReturning(columns: ColumnNode[]): InsertQueryState {
|
|
157
|
+
return this.clone({
|
|
158
|
+
...this.ast,
|
|
159
|
+
returning: [...columns]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Adds an UPSERT conflict clause to the INSERT query
|
|
165
|
+
* @param clause - Conflict clause and action
|
|
166
|
+
* @returns A new InsertQueryState with conflict handling configured
|
|
167
|
+
*/
|
|
168
|
+
withOnConflict(clause: UpsertClause): InsertQueryState {
|
|
169
|
+
return this.clone({
|
|
170
|
+
...this.ast,
|
|
171
|
+
onConflict: {
|
|
172
|
+
target: {
|
|
173
|
+
columns: [...clause.target.columns],
|
|
174
|
+
constraint: clause.target.constraint
|
|
175
|
+
},
|
|
176
|
+
action:
|
|
177
|
+
clause.action.type === 'DoUpdate'
|
|
178
|
+
? {
|
|
179
|
+
type: 'DoUpdate',
|
|
180
|
+
set: clause.action.set.map(assignment => ({
|
|
181
|
+
column: { ...assignment.column },
|
|
182
|
+
value: assignment.value
|
|
183
|
+
})),
|
|
184
|
+
where: clause.action.where
|
|
185
|
+
}
|
|
186
|
+
: { type: 'DoNothing' }
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|