Skip to content

Commit 1b1ebb4

Browse files
committed
fix: disallow distinct for sqlite
1 parent 31e3361 commit 1b1ebb4

File tree

7 files changed

+160
-194
lines changed

7 files changed

+160
-194
lines changed

packages/language/src/validators/datamodel-validator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ export default class DataModelValidator implements AstValidator<DataModel> {
443443
invariant(model.baseModel.ref, 'baseModel must be resolved');
444444

445445
// check if the base model is a delegate model
446-
if (!isDelegateModel(model.baseModel.ref)) {
446+
if (!isDelegateModel(model.baseModel.ref!)) {
447447
accept('error', `Model ${model.baseModel.$refText} cannot be extended because it's not a delegate model`, {
448448
node: model,
449449
property: 'baseModel',

packages/runtime/src/client/crud-types.ts

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -437,14 +437,6 @@ export type SelectIncludeOmit<Schema extends SchemaDef, Model extends GetModels<
437437
omit?: OmitInput<Schema, Model>;
438438
};
439439

440-
type Distinct<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
441-
distinct?: OrArray<NonRelationFields<Schema, Model>>;
442-
};
443-
444-
type Cursor<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
445-
cursor?: WhereUniqueInput<Schema, Model>;
446-
};
447-
448440
export type SelectInput<
449441
Schema extends SchemaDef,
450442
Model extends GetModels<Schema>,
@@ -621,25 +613,34 @@ type OppositeRelationAndFK<
621613

622614
//#region Find args
623615

616+
type FilterArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
617+
where?: WhereInput<Schema, Model>;
618+
};
619+
620+
type SortAndTakeArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
621+
skip?: number;
622+
take?: number;
623+
orderBy?: OrArray<OrderBy<Schema, Model, true, false>>;
624+
cursor?: WhereUniqueInput<Schema, Model>;
625+
};
626+
624627
export type FindArgs<
625628
Schema extends SchemaDef,
626629
Model extends GetModels<Schema>,
627630
Collection extends boolean,
628631
AllowFilter extends boolean = true,
629-
> = (Collection extends true
630-
? {
631-
skip?: number;
632-
take?: number;
633-
orderBy?: OrArray<OrderBy<Schema, Model, true, false>>;
634-
} & Distinct<Schema, Model> &
635-
Cursor<Schema, Model>
636-
: {}) &
637-
(AllowFilter extends true
638-
? {
639-
where?: WhereInput<Schema, Model>;
640-
}
641-
: {}) &
642-
SelectIncludeOmit<Schema, Model, Collection>;
632+
> =
633+
ProviderSupportsDistinct<Schema> extends true
634+
? (Collection extends true
635+
? SortAndTakeArgs<Schema, Model> & {
636+
distinct?: OrArray<NonRelationFields<Schema, Model>>;
637+
}
638+
: {}) &
639+
(AllowFilter extends true ? FilterArgs<Schema, Model> : {}) &
640+
SelectIncludeOmit<Schema, Model, Collection>
641+
: (Collection extends true ? SortAndTakeArgs<Schema, Model> : {}) &
642+
(AllowFilter extends true ? FilterArgs<Schema, Model> : {}) &
643+
SelectIncludeOmit<Schema, Model, Collection>;
643644

644645
export type FindManyArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = FindArgs<Schema, Model, true>;
645646
export type FindFirstArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = FindArgs<Schema, Model, false>;
@@ -1259,6 +1260,12 @@ type HasToManyRelations<Schema extends SchemaDef, Model extends GetModels<Schema
12591260
? false
12601261
: true;
12611262

1262-
type ProviderSupportsCaseSensitivity<Schema extends SchemaDef> = Schema['provider'] extends 'postgresql' ? true : false;
1263+
type ProviderSupportsCaseSensitivity<Schema extends SchemaDef> = Schema['provider']['type'] extends 'postgresql'
1264+
? true
1265+
: false;
1266+
1267+
type ProviderSupportsDistinct<Schema extends SchemaDef> = Schema['provider']['type'] extends 'postgresql'
1268+
? true
1269+
: false;
12631270

12641271
// #endregion

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

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
aggregate,
2222
buildFieldRef,
2323
buildJoinPairs,
24+
ensureArray,
2425
flattenCompoundUniqueFilters,
2526
getDelegateDescendantModels,
2627
getIdFields,
@@ -58,6 +59,54 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
5859
return result;
5960
}
6061

62+
buildFilterSortTake(
63+
model: GetModels<Schema>,
64+
args: FindArgs<Schema, GetModels<Schema>, true>,
65+
query: SelectQueryBuilder<any, any, {}>,
66+
) {
67+
let result = query;
68+
69+
// where
70+
if (args.where) {
71+
result = result.where((eb) => this.buildFilter(eb, model, model, args?.where));
72+
}
73+
74+
// skip && take
75+
let negateOrderBy = false;
76+
const skip = args.skip;
77+
let take = args.take;
78+
if (take !== undefined && take < 0) {
79+
negateOrderBy = true;
80+
take = -take;
81+
}
82+
result = this.buildSkipTake(result, skip, take);
83+
84+
// orderBy
85+
result = this.buildOrderBy(
86+
result,
87+
model,
88+
model,
89+
args.orderBy,
90+
skip !== undefined || take !== undefined,
91+
negateOrderBy,
92+
);
93+
94+
// distinct
95+
if ('distinct' in args && (args as any).distinct) {
96+
const distinct = ensureArray((args as any).distinct) as string[];
97+
if (this.supportsDistinctOn) {
98+
result = result.distinctOn(distinct.map((f) => sql.ref(`${model}.${f}`)));
99+
} else {
100+
throw new QueryError(`"distinct" is not supported by "${this.schema.provider.type}" provider`);
101+
}
102+
}
103+
104+
if (args.cursor) {
105+
result = this.buildCursorFilter(model, result, args.cursor, args.orderBy, negateOrderBy);
106+
}
107+
return result;
108+
}
109+
61110
buildFilter(
62111
eb: ExpressionBuilder<any, any>,
63112
model: string,
@@ -117,6 +166,47 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
117166
return result;
118167
}
119168

169+
private buildCursorFilter(
170+
model: string,
171+
query: SelectQueryBuilder<any, any, any>,
172+
cursor: FindArgs<Schema, GetModels<Schema>, true>['cursor'],
173+
orderBy: FindArgs<Schema, GetModels<Schema>, true>['orderBy'],
174+
negateOrderBy: boolean,
175+
) {
176+
const _orderBy = orderBy ?? makeDefaultOrderBy(this.schema, model);
177+
178+
const orderByItems = ensureArray(_orderBy).flatMap((obj) => Object.entries<SortOrder>(obj));
179+
180+
const eb = expressionBuilder<any, any>();
181+
const cursorFilter = this.buildFilter(eb, model, model, cursor);
182+
183+
let result = query;
184+
const filters: ExpressionWrapper<any, any, any>[] = [];
185+
186+
for (let i = orderByItems.length - 1; i >= 0; i--) {
187+
const andFilters: ExpressionWrapper<any, any, any>[] = [];
188+
189+
for (let j = 0; j <= i; j++) {
190+
const [field, order] = orderByItems[j]!;
191+
const _order = negateOrderBy ? (order === 'asc' ? 'desc' : 'asc') : order;
192+
const op = j === i ? (_order === 'asc' ? '>=' : '<=') : '=';
193+
andFilters.push(
194+
eb(
195+
eb.ref(`${model}.${field}`),
196+
op,
197+
eb.selectFrom(model).select(`${model}.${field}`).where(cursorFilter),
198+
),
199+
);
200+
}
201+
202+
filters.push(eb.and(andFilters));
203+
}
204+
205+
result = result.where((eb) => eb.or(filters));
206+
207+
return result;
208+
}
209+
120210
private isLogicalCombinator(key: string): key is (typeof LOGICAL_COMBINATORS)[number] {
121211
return LOGICAL_COMBINATORS.includes(key as any);
122212
}
@@ -722,7 +812,7 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
722812
// aggregations
723813
if (['_count', '_avg', '_sum', '_min', '_max'].includes(field)) {
724814
invariant(value && typeof value === 'object', `invalid orderBy value for field "${field}"`);
725-
for (const [k, v] of Object.entries<string>(value)) {
815+
for (const [k, v] of Object.entries<SortOrder>(value)) {
726816
invariant(v === 'asc' || v === 'desc', `invalid orderBy value for field "${field}"`);
727817
result = result.orderBy(
728818
(eb) =>

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

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -91,31 +91,7 @@ export class PostgresCrudDialect<Schema extends SchemaDef> extends BaseCrudDiale
9191
);
9292

9393
if (payload && typeof payload === 'object') {
94-
if (payload.where) {
95-
subQuery = subQuery.where((eb) =>
96-
this.buildFilter(eb, relationModel, relationModel, payload.where),
97-
);
98-
}
99-
100-
// skip & take
101-
const skip = payload.skip;
102-
let take = payload.take;
103-
let negateOrderBy = false;
104-
if (take !== undefined && take < 0) {
105-
negateOrderBy = true;
106-
take = -take;
107-
}
108-
subQuery = this.buildSkipTake(subQuery, skip, take);
109-
110-
// orderBy
111-
subQuery = this.buildOrderBy(
112-
subQuery,
113-
relationModel,
114-
relationModel,
115-
payload.orderBy,
116-
skip !== undefined || take !== undefined,
117-
negateOrderBy,
118-
);
94+
subQuery = this.buildFilterSortTake(relationModel, payload, subQuery);
11995
}
12096

12197
// add join conditions

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

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -85,31 +85,8 @@ export class SqliteCrudDialect<Schema extends SchemaDef> extends BaseCrudDialect
8585
);
8686

8787
if (payload && typeof payload === 'object') {
88-
if (payload.where) {
89-
subQuery = subQuery.where((eb) =>
90-
this.buildFilter(eb, relationModel, relationModel, payload.where),
91-
);
92-
}
93-
94-
// skip & take
95-
const skip = payload.skip;
96-
let take = payload.take;
97-
let negateOrderBy = false;
98-
if (take !== undefined && take < 0) {
99-
negateOrderBy = true;
100-
take = -take;
101-
}
102-
subQuery = this.buildSkipTake(subQuery, skip, take);
103-
104-
// orderBy
105-
subQuery = this.buildOrderBy(
106-
subQuery,
107-
relationModel,
108-
relationModel,
109-
payload.orderBy,
110-
skip !== undefined || take !== undefined,
111-
negateOrderBy,
112-
);
88+
// take care of where, orderBy, skip, take, cursor, and distinct
89+
subQuery = this.buildFilterSortTake(relationModel, payload, subQuery);
11390
}
11491

11592
// join conditions

0 commit comments

Comments
 (0)