Skip to content
  • Sponsor
  • Notifications You must be signed in to change notification settings
  • Fork 582
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move processing of orderBy to runtime #2357

Merged
merged 29 commits into from
Mar 12, 2025
Merged
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5506454
Move @dataplan/pg over to using runtime orderBy values
benjie Jan 29, 2025
42b1502
Don't add orders in aggregate mode
benjie Jan 29, 2025
3a10c0c
Refactor
benjie Jan 29, 2025
fbcd101
Even better refactoring
benjie Jan 29, 2025
a6d6f80
Give display names
benjie Jan 29, 2025
ef9706c
Update plan diagrams
benjie Jan 29, 2025
64a5ddd
Promote and refactor extractEnumExtensionValue
benjie Jan 29, 2025
c4a87ba
Move autogenerated schema over to evaluating orderBy at runtime
benjie Jan 29, 2025
6482d9c
Update makeAddPgTableOrderByPlugin to use runtime query builder
benjie Jan 29, 2025
7184731
Can.
benjie Jan 29, 2025
a0fb6f0
Add missing optional chaining
benjie Jan 29, 2025
8435d79
Update plan diagrams
benjie Jan 29, 2025
fabd4d9
Update snapshots
benjie Feb 4, 2025
c4bce03
Update snapshots
benjie Feb 4, 2025
611e244
Don't apply orders in aggregate mode
benjie Jan 29, 2025
75cc41a
Lint
benjie Jan 29, 2025
7100298
Improve naming of exports
benjie Feb 4, 2025
aa4d3dc
Update snapshots with improved names
benjie Feb 4, 2025
4af7519
Simplify vulns SQL
benjie Feb 4, 2025
8026b98
docs(changeset): Move handling of orderBy to runtime from plantime.
benjie Feb 4, 2025
b8b6998
Lint
benjie Feb 5, 2025
8cbd4a3
Don't depend on graphql directly
benjie Feb 5, 2025
1aeaca8
Move applies back inside grafast
benjie Feb 11, 2025
ecb530b
Update snapshots
benjie Feb 11, 2025
a8b427d
Combine pgSelectApply and pgUnionAllApply into just 'apply'
benjie Feb 11, 2025
772752b
Reduce function allocations
benjie Feb 11, 2025
c238616
Update snapshots
benjie Feb 11, 2025
65226e8
Update snapshots after rebase
benjie Feb 24, 2025
60dfe34
Add more detail to changeset
benjie Mar 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move applies back inside grafast
benjie committed Mar 10, 2025
commit 1aeaca8222e5b8271a389355e6a1789b5f1fc27b
136 changes: 74 additions & 62 deletions grafast/dataplan-pg/src/examples/exampleSchema.ts
Original file line number Diff line number Diff line change
@@ -1986,60 +1986,68 @@ export function makeExampleSchema(
values: {
BODY_ASC: {
extensions: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
qb.orderBy({
codec: TYPES.text,
fragment: sql`${qb}.body`,
direction: "ASC",
});
},
[TYPES, sql],
),
grafast: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
qb.orderBy({
codec: TYPES.text,
fragment: sql`${qb}.body`,
direction: "ASC",
});
},
[TYPES, sql],
),
},
},
},
BODY_DESC: {
extensions: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
qb.orderBy({
codec: TYPES.text,
fragment: sql`${qb}.body`,
direction: "DESC",
});
},
[TYPES, sql],
),
grafast: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
qb.orderBy({
codec: TYPES.text,
fragment: sql`${qb}.body`,
direction: "DESC",
});
},
[TYPES, sql],
),
},
},
},
AUTHOR_USERNAME_ASC: {
extensions: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
const authorAlias = qb.singleRelation("author");
qb.orderBy({
codec: TYPES.text,
fragment: sql`${authorAlias}.username`,
direction: "ASC",
});
},
[TYPES, sql],
),
grafast: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
const authorAlias = qb.singleRelation("author");
qb.orderBy({
codec: TYPES.text,
fragment: sql`${authorAlias}.username`,
direction: "ASC",
});
},
[TYPES, sql],
),
},
},
},
AUTHOR_USERNAME_DESC: {
extensions: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
const authorAlias = qb.singleRelation("author");
qb.orderBy({
codec: TYPES.text,
fragment: sql`${authorAlias}.username`,
direction: "DESC",
});
},
[TYPES, sql],
),
grafast: {
pgSelectApply: EXPORTABLE(
(TYPES, sql) => (qb) => {
const authorAlias = qb.singleRelation("author");
qb.orderBy({
codec: TYPES.text,
fragment: sql`${authorAlias}.username`,
direction: "DESC",
});
},
[TYPES, sql],
),
},
},
},
},
@@ -3680,28 +3688,32 @@ export function makeExampleSchema(
values: {
CVSS_SCORE_ASC: {
extensions: {
pgUnionAllApply: EXPORTABLE(
() => (qb) => {
qb.orderBy({
attribute: "cvss_score",
direction: "ASC",
});
},
[],
),
grafast: {
pgUnionAllApply: EXPORTABLE(
() => (qb) => {
qb.orderBy({
attribute: "cvss_score",
direction: "ASC",
});
},
[],
),
},
},
},
CVSS_SCORE_DESC: {
extensions: {
pgUnionAllApply: EXPORTABLE(
() => (qb) => {
qb.orderBy({
attribute: "cvss_score",
direction: "DESC",
});
},
[],
),
grafast: {
pgUnionAllApply: EXPORTABLE(
() => (qb) => {
qb.orderBy({
attribute: "cvss_score",
direction: "DESC",
});
},
[],
),
},
},
},
},
@@ -3943,7 +3955,7 @@ export function makeExampleSchema(
$messages.apply(
extractEnumExtensionValue<PgSelectQueryBuilderCallback>(
orderByArg!.type,
"pgSelectApply",
["grafast", "pgSelectApply"],
$orderBy,
),
);
@@ -4671,7 +4683,7 @@ export function makeExampleSchema(
$vulnerabilities.apply(
extractEnumExtensionValue<PgUnionAllQueryBuilderCallback>(
orderByArg!.type,
"pgUnionAllApply",
["grafast", "pgUnionAllApply"],
$orderBy,
),
);
15 changes: 9 additions & 6 deletions grafast/dataplan-pg/src/index.ts
Original file line number Diff line number Diff line change
@@ -547,11 +547,14 @@ declare global {
interface PgRefDefinitionExtensions {}
interface PgCodecRelationExtensions {}
}
}

declare module "graphql" {
interface GraphQLEnumValueExtensions {
pgSelectApply?: PgSelectQueryBuilderCallback;
pgUnionAllApply?: PgUnionAllQueryBuilderCallback;
namespace Grafast {
interface EnumValueExtensions {
pgSelectApply?: PgSelectQueryBuilderCallback;
pgUnionAllApply?: PgUnionAllQueryBuilderCallback;
}
interface ArgumentExtensions {
pgSelectApply?: PgSelectQueryBuilderCallback;
pgUnionAllApply?: PgUnionAllQueryBuilderCallback;
}
}
}
26 changes: 17 additions & 9 deletions grafast/dataplan-pg/src/steps/extractEnumExtensionValue.ts
Original file line number Diff line number Diff line change
@@ -20,36 +20,44 @@ declare module "graphql" {
const $$extensionsByValue = Symbol("extensionsByValue");
function getEnumExtensionPropertyValueLookups(
enumType: GraphQLEnumType,
extensionsProperty: string,
path: string[],
) {
const enumValueConfigs = getEnumValueConfigs(enumType);
if (enumType[$$extensionsByValue] === undefined) {
enumType[$$extensionsByValue] = Object.create(null);
}
if (enumType[$$extensionsByValue]![extensionsProperty] === undefined) {
const serializedPath = JSON.stringify(path);
if (enumType[$$extensionsByValue]![serializedPath] === undefined) {
const pathLength = path.length;
const lookup = Object.entries(enumValueConfigs).reduce(
(memo, [value, config]) => {
memo[value] = config?.extensions?.[extensionsProperty];
let valueAtPath: any = config?.extensions;
for (let pathIndex = 0; pathIndex < pathLength; pathIndex++) {
if (valueAtPath == null) break;
valueAtPath = valueAtPath?.[path[pathIndex]];
}
memo[value] = valueAtPath;
return memo;
},
Object.create(null),
);
const lookupValues = <T>(values: any) =>
values?.map((v: any) => lookup[v] as T | undefined);
lookupValues.displayName = `extractList_${extensionsProperty}`;
const functionNameSuffix = path.join("_");
lookupValues.displayName = `extractList_${functionNameSuffix}`;
const lookupValue = <T>(value: any) => lookup[value] as T | undefined;
lookupValue.displayName = `extract_${extensionsProperty}`;
enumType[$$extensionsByValue]![extensionsProperty] = {
lookupValue.displayName = `extract_${functionNameSuffix}`;
enumType[$$extensionsByValue]![serializedPath] = {
lookupValues,
lookupValue,
};
}
return enumType[$$extensionsByValue]![extensionsProperty]!;
return enumType[$$extensionsByValue]![serializedPath]!;
}

export function extractEnumExtensionValue<T>(
type: GraphQLInputType,
extensionsProperty: string,
path: string[],
$step: InputStep,
): ExecutableStep<ReadonlyArrayOrDirect<Maybe<T>>> {
const nullableType = getNullableType(type);
@@ -59,7 +67,7 @@ export function extractEnumExtensionValue<T>(
}
const { lookupValues, lookupValue } = getEnumExtensionPropertyValueLookups(
enumType,
extensionsProperty,
path,
);
if (
// Quicker than but equivalent to isListType(nullableType):
8 changes: 0 additions & 8 deletions grafast/grafast/src/makeGrafastSchema.ts
Original file line number Diff line number Diff line change
@@ -324,14 +324,6 @@ export function makeGrafastSchema(details: {
argSpec.grafast?.inputPlan,
`${typeName}_${fieldName}_${argName}_inputPlan`,
);
exportNameHint(
argSpec.pgSelectApply,
`${typeName}_${fieldName}_${argName}_pgSelectApply`,
);
exportNameHint(
argSpec.pgUnionAllApply,
`${typeName}_${fieldName}_${argName}_pgUnionAllApply`,
);
Object.assign(arg.extensions!, argSpec);
}
}
Original file line number Diff line number Diff line change
@@ -202,7 +202,7 @@ export const PgConnectionArgOrderByPlugin: GraphileConfig.Plugin = {
$select.apply(
extractEnumExtensionValue<PgSelectQueryBuilderCallback>(
orderByArg!.type,
"pgSelectApply",
["grafast", "pgSelectApply"],
$orderBy,
),
);
@@ -221,7 +221,7 @@ export const PgConnectionArgOrderByPlugin: GraphileConfig.Plugin = {
$select.apply(
extractEnumExtensionValue<PgSelectQueryBuilderCallback>(
orderByArg!.type,
"pgSelectApply",
["grafast", "pgSelectApply"],
$orderBy,
),
);
Original file line number Diff line number Diff line change
@@ -212,7 +212,7 @@ export const PgMutationPayloadEdgePlugin: GraphileConfig.Plugin = {
$select.apply(
extractEnumExtensionValue<PgSelectQueryBuilderCallback>(
orderByArg!.type,
"pgSelectApply",
["grafast", "pgSelectApply"],
$orderBy,
),
);
Original file line number Diff line number Diff line change
@@ -122,26 +122,28 @@ export const PgOrderAllAttributesPlugin: GraphileConfig.Plugin = {
{
[ascFieldName]: {
extensions: {
pgSelectApply: EXPORTABLE(
(attributeName, isUnique, pgOrderByNullsLast) =>
((queryBuilder): void => {
queryBuilder.orderBy({
attribute: attributeName,
direction: "ASC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast
? "LAST"
: "FIRST",
}
: null),
});
if (isUnique) {
queryBuilder.setOrderIsUnique();
}
}) as PgSelectQueryBuilderCallback,
[attributeName, isUnique, pgOrderByNullsLast],
),
grafast: {
pgSelectApply: EXPORTABLE(
(attributeName, isUnique, pgOrderByNullsLast) =>
((queryBuilder): void => {
queryBuilder.orderBy({
attribute: attributeName,
direction: "ASC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast
? "LAST"
: "FIRST",
}
: null),
});
if (isUnique) {
queryBuilder.setOrderIsUnique();
}
}) as PgSelectQueryBuilderCallback,
[attributeName, isUnique, pgOrderByNullsLast],
),
},
},
},
},
@@ -159,26 +161,28 @@ export const PgOrderAllAttributesPlugin: GraphileConfig.Plugin = {
{
[descFieldName]: {
extensions: {
pgSelectApply: EXPORTABLE(
(attributeName, isUnique, pgOrderByNullsLast) =>
((queryBuilder): void => {
queryBuilder.orderBy({
attribute: attributeName,
direction: "DESC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast
? "LAST"
: "FIRST",
}
: null),
});
if (isUnique) {
queryBuilder.setOrderIsUnique();
}
}) as PgSelectQueryBuilderCallback,
[attributeName, isUnique, pgOrderByNullsLast],
),
grafast: {
pgSelectApply: EXPORTABLE(
(attributeName, isUnique, pgOrderByNullsLast) =>
((queryBuilder): void => {
queryBuilder.orderBy({
attribute: attributeName,
direction: "DESC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast
? "LAST"
: "FIRST",
}
: null),
});
if (isUnique) {
queryBuilder.setOrderIsUnique();
}
}) as PgSelectQueryBuilderCallback,
[attributeName, isUnique, pgOrderByNullsLast],
),
},
},
},
},
Original file line number Diff line number Diff line change
@@ -67,54 +67,58 @@ export const PgOrderByPrimaryKeyPlugin: GraphileConfig.Plugin = {
{
[inflection.builtin("PRIMARY_KEY_ASC")]: {
extensions: {
pgSelectApply: EXPORTABLE(
(pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql) =>
((queryBuilder) => {
primaryKeyAttributes.forEach((attributeName) => {
const attribute = pgCodec.attributes[attributeName];
queryBuilder.orderBy({
codec: attribute.codec,
fragment: sql`${queryBuilder}.${sql.identifier(
attributeName,
)}`,
direction: "ASC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast ? "LAST" : "FIRST",
}
: null),
grafast: {
pgSelectApply: EXPORTABLE(
(pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql) =>
((queryBuilder) => {
primaryKeyAttributes.forEach((attributeName) => {
const attribute = pgCodec.attributes[attributeName];
queryBuilder.orderBy({
codec: attribute.codec,
fragment: sql`${queryBuilder}.${sql.identifier(
attributeName,
)}`,
direction: "ASC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast ? "LAST" : "FIRST",
}
: null),
});
});
});
queryBuilder.setOrderIsUnique();
}) as PgSelectQueryBuilderCallback,
[pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql],
),
queryBuilder.setOrderIsUnique();
}) as PgSelectQueryBuilderCallback,
[pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql],
),
},
},
},
[inflection.builtin("PRIMARY_KEY_DESC")]: {
extensions: {
pgSelectApply: EXPORTABLE(
(pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql) =>
((queryBuilder) => {
primaryKeyAttributes.forEach((attributeName) => {
const attribute = pgCodec.attributes[attributeName];
queryBuilder.orderBy({
codec: attribute.codec,
fragment: sql`${queryBuilder}.${sql.identifier(
attributeName,
)}`,
direction: "DESC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast ? "LAST" : "FIRST",
}
: null),
grafast: {
pgSelectApply: EXPORTABLE(
(pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql) =>
((queryBuilder) => {
primaryKeyAttributes.forEach((attributeName) => {
const attribute = pgCodec.attributes[attributeName];
queryBuilder.orderBy({
codec: attribute.codec,
fragment: sql`${queryBuilder}.${sql.identifier(
attributeName,
)}`,
direction: "DESC",
...(pgOrderByNullsLast != null
? {
nulls: pgOrderByNullsLast ? "LAST" : "FIRST",
}
: null),
});
});
});
queryBuilder.setOrderIsUnique();
}) as PgSelectQueryBuilderCallback,
[pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql],
),
queryBuilder.setOrderIsUnique();
}) as PgSelectQueryBuilderCallback,
[pgCodec, pgOrderByNullsLast, primaryKeyAttributes, sql],
),
},
},
},
},
Original file line number Diff line number Diff line change
@@ -120,27 +120,29 @@ export const PgOrderCustomFieldsPlugin: GraphileConfig.Plugin = {
{
[valueName]: {
extensions: {
pgSelectApply: EXPORTABLE(
(ascDesc, pgFieldSource, sql) =>
((queryBuilder) => {
if (typeof pgFieldSource.from !== "function") {
throw new Error(
"Invalid computed attribute 'from'",
);
}
const expression = sql`${pgFieldSource.from({
placeholder: queryBuilder.alias,
})}`;
queryBuilder.orderBy({
codec: pgFieldSource.codec,
fragment: expression,
direction: ascDesc.toUpperCase() as
| "ASC"
| "DESC",
});
}) as PgSelectQueryBuilderCallback,
[ascDesc, pgFieldSource, sql],
),
grafast: {
pgSelectApply: EXPORTABLE(
(ascDesc, pgFieldSource, sql) =>
((queryBuilder) => {
if (typeof pgFieldSource.from !== "function") {
throw new Error(
"Invalid computed attribute 'from'",
);
}
const expression = sql`${pgFieldSource.from({
placeholder: queryBuilder.alias,
})}`;
queryBuilder.orderBy({
codec: pgFieldSource.codec,
fragment: expression,
direction: ascDesc.toUpperCase() as
| "ASC"
| "DESC",
});
}) as PgSelectQueryBuilderCallback,
[ascDesc, pgFieldSource, sql],
),
},
},
},
},
15 changes: 8 additions & 7 deletions graphile-build/graphile-utils/src/makeAddPgTableOrderByPlugin.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import type {
PgSelectQueryBuilder,
PgSelectQueryBuilderCallback,
} from "@dataplan/pg";
import type { GraphQLEnumValueConfig } from "graphql";

import { EXPORTABLE } from "./exportable.js";

@@ -12,11 +13,7 @@ type OrderBySpecIdentity =
| ((queryBuilder: PgSelectQueryBuilder) => Omit<PgOrderSpec, "direction">); // Callback, allows for joins/etc

export interface MakeAddPgTableOrderByPluginOrders {
[orderByEnumValue: string]: {
extensions: {
pgSelectApply: PgSelectQueryBuilderCallback;
};
};
[orderByEnumValue: string]: GraphQLEnumValueConfig;
}

const counterByName = new Map<string, number>();
@@ -238,12 +235,16 @@ export function orderByAscDesc(
const orders: MakeAddPgTableOrderByPluginOrders = {
[`${baseName}_ASC`]: {
extensions: {
pgSelectApply: ascendingCb,
grafast: {
pgSelectApply: ascendingCb,
},
},
},
[`${baseName}_DESC`]: {
extensions: {
pgSelectApply: descendingCb,
grafast: {
pgSelectApply: descendingCb,
},
},
},
};