Skip to content

Commit c7f02ea

Browse files
authored
fix(delegate): filter fixes (#112)
1 parent 04959ad commit c7f02ea

File tree

7 files changed

+359
-125
lines changed

7 files changed

+359
-125
lines changed

packages/runtime/src/client/crud/dialects/base.ts

Lines changed: 77 additions & 92 deletions
Large diffs are not rendered by default.

packages/runtime/src/client/crud/dialects/postgresql.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,14 @@ export class PostgresCrudDialect<Schema extends SchemaDef> extends BaseCrudDiale
8181
// simple select by default
8282
let result = eb.selectFrom(`${relationModel} as ${joinTableName}`);
8383

84-
const joinBases: string[] = [];
85-
8684
// however if there're filter/orderBy/take/skip,
8785
// we need to build a subquery to handle them before aggregation
8886
result = eb.selectFrom(() => {
89-
let subQuery = eb.selectFrom(relationModel);
87+
let subQuery = this.buildSelectModel(eb, relationModel);
9088
subQuery = this.buildSelectAllFields(
9189
relationModel,
9290
subQuery,
9391
typeof payload === 'object' ? payload?.omit : undefined,
94-
joinBases,
9592
);
9693

9794
if (payload && typeof payload === 'object') {

packages/runtime/src/client/crud/dialects/sqlite.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,12 @@ export class SqliteCrudDialect<Schema extends SchemaDef> extends BaseCrudDialect
7777
const subQueryName = `${parentName}$${relationField}`;
7878

7979
let tbl = eb.selectFrom(() => {
80-
let subQuery = eb.selectFrom(relationModel);
80+
let subQuery = this.buildSelectModel(eb, relationModel);
8181

82-
const joinBases: string[] = [];
8382
subQuery = this.buildSelectAllFields(
8483
relationModel,
8584
subQuery,
8685
typeof payload === 'object' ? payload?.omit : undefined,
87-
joinBases,
8886
);
8987

9088
if (payload && typeof payload === 'object') {

packages/runtime/src/client/crud/operations/base.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
143143
args: FindArgs<Schema, GetModels<Schema>, true> | undefined,
144144
): Promise<any[]> {
145145
// table
146-
let query = kysely.selectFrom(model);
146+
let query = this.dialect.buildSelectModel(expressionBuilder(), model);
147147

148148
// where
149149
if (args?.where) {
@@ -182,22 +182,19 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
182182
}
183183
}
184184

185-
// for deduplicating base joins
186-
const joinedBases: string[] = [];
187-
188185
// select
189186
if (args && 'select' in args && args.select) {
190187
// select is mutually exclusive with omit
191-
query = this.buildFieldSelection(model, query, args.select, model, joinedBases);
188+
query = this.buildFieldSelection(model, query, args.select, model);
192189
} else {
193190
// include all scalar fields except those in omit
194-
query = this.dialect.buildSelectAllFields(model, query, (args as any)?.omit, joinedBases);
191+
query = this.dialect.buildSelectAllFields(model, query, (args as any)?.omit);
195192
}
196193

197194
// include
198195
if (args && 'include' in args && args.include) {
199196
// note that 'omit' is handled above already
200-
query = this.buildFieldSelection(model, query, args.include, model, joinedBases);
197+
query = this.buildFieldSelection(model, query, args.include, model);
201198
}
202199

203200
if (args?.cursor) {
@@ -207,13 +204,15 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
207204
query = query.modifyEnd(this.makeContextComment({ model, operation: 'read' }));
208205

209206
let result: any[] = [];
207+
const queryId = { queryId: `zenstack-${createId()}` };
208+
const compiled = kysely.getExecutor().compileQuery(query.toOperationNode(), queryId);
210209
try {
211-
result = await query.execute();
210+
const r = await kysely.getExecutor().executeQuery(compiled, queryId);
211+
result = r.rows;
212212
} catch (err) {
213-
const { sql, parameters } = query.compile();
214-
let message = `Failed to execute query: ${err}, sql: ${sql}`;
213+
let message = `Failed to execute query: ${err}, sql: ${compiled.sql}`;
215214
if (this.options.debug) {
216-
message += `, parameters: \n${parameters.map((p) => inspect(p)).join('\n')}`;
215+
message += `, parameters: \n${compiled.parameters.map((p) => inspect(p)).join('\n')}`;
217216
}
218217
throw new QueryError(message, err);
219218
}
@@ -248,7 +247,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
248247
query: SelectQueryBuilder<any, any, any>,
249248
selectOrInclude: Record<string, any>,
250249
parentAlias: string,
251-
joinedBases: string[],
252250
) {
253251
let result = query;
254252

@@ -265,17 +263,12 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
265263
const fieldDef = this.requireField(model, field);
266264
if (!fieldDef.relation) {
267265
// scalar field
268-
result = this.dialect.buildSelectField(result, model, parentAlias, field, joinedBases);
266+
result = this.dialect.buildSelectField(result, model, parentAlias, field);
269267
} else {
270268
if (!fieldDef.array && !fieldDef.optional && payload.where) {
271269
throw new QueryError(`Field "${field}" doesn't support filtering`);
272270
}
273271
if (fieldDef.originModel) {
274-
// relation is inherited from a delegate base model, need to build a join
275-
if (!joinedBases.includes(fieldDef.originModel)) {
276-
joinedBases.push(fieldDef.originModel);
277-
result = this.dialect.buildDelegateJoin(parentAlias, fieldDef.originModel, result);
278-
}
279272
result = this.dialect.buildRelationSelection(
280273
result,
281274
fieldDef.originModel,
@@ -399,8 +392,15 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
399392
model: GetModels<Schema>,
400393
data: any,
401394
fromRelation?: FromRelationContext<Schema>,
395+
creatingForDelegate = false,
402396
): Promise<unknown> {
403397
const modelDef = this.requireModel(model);
398+
399+
// additional validations
400+
if (modelDef.isDelegate && !creatingForDelegate) {
401+
throw new QueryError(`Model "${this.model}" is a delegate and cannot be created directly.`);
402+
}
403+
404404
let createFields: any = {};
405405
let parentUpdateTask: ((entity: any) => Promise<unknown>) | undefined = undefined;
406406

@@ -573,7 +573,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
573573
thisCreateFields[discriminatorField] = forModel;
574574

575575
// create base model entity
576-
const createResult = await this.create(kysely, model as GetModels<Schema>, thisCreateFields);
576+
const createResult = await this.create(kysely, model as GetModels<Schema>, thisCreateFields, undefined, true);
577577

578578
// copy over id fields from base model
579579
const idValues = extractIdFields(createResult, this.schema, model);

packages/runtime/src/client/crud/operations/create.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,9 @@ import type { GetModels, SchemaDef } from '../../../schema';
44
import type { CreateArgs, CreateManyAndReturnArgs, CreateManyArgs, WhereInput } from '../../crud-types';
55
import { getIdValues } from '../../query-utils';
66
import { BaseOperationHandler } from './base';
7-
import { QueryError } from '../../errors';
87

98
export class CreateOperationHandler<Schema extends SchemaDef> extends BaseOperationHandler<Schema> {
109
async handle(operation: 'create' | 'createMany' | 'createManyAndReturn', args: unknown | undefined) {
11-
const modelDef = this.requireModel(this.model);
12-
if (modelDef.isDelegate) {
13-
throw new QueryError(`Model "${this.model}" is a delegate and cannot be created directly.`);
14-
}
15-
1610
// normalize args to strip `undefined` fields
1711
const normalizedArgs = this.normalizeArgs(args);
1812

0 commit comments

Comments
 (0)