From d2450c9fee73c8582351b9165d83496d8f00313c Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 13 May 2026 10:05:24 +0100 Subject: [PATCH 1/6] PgCondition should accept options object --- grafast/dataplan-pg/src/steps/pgCondition.ts | 34 ++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/grafast/dataplan-pg/src/steps/pgCondition.ts b/grafast/dataplan-pg/src/steps/pgCondition.ts index 3a40ea744b..dd7d2a1c0d 100644 --- a/grafast/dataplan-pg/src/steps/pgCondition.ts +++ b/grafast/dataplan-pg/src/steps/pgCondition.ts @@ -53,6 +53,32 @@ export type PgConditionMode = | "NOT" | PgConditionResolvedMode; +interface PgConditionOptions { + /** @defaultValue `false` */ + isHaving?: boolean; + /** @defaultValue `"PASS_THRU"` */ + mode?: PgConditionMode; +} + +function resolveOptions( + isHavingOrOptions: PgConditionOptions | boolean | undefined, + maybeMode: PgConditionMode | undefined, +) { + if (typeof isHavingOrOptions === "boolean") { + return { isHaving: isHavingOrOptions, mode: maybeMode }; + } else if (isHavingOrOptions == null) { + return { mode: maybeMode }; + } else if (maybeMode !== undefined) { + throw new Error( + `Invalid call signature to PgCondition constructor, use \`new PgCondition(parent, options)\``, + ); + } else if (isHavingOrOptions) { + return isHavingOrOptions; + } else { + return {}; + } +} + export class PgCondition< TParent extends PgConditionCapableParent = PgConditionCapableParent, > @@ -73,12 +99,16 @@ export class PgCondition< public readonly resolvedMode: PgConditionResolvedMode; private isHaving: boolean; + constructor(parent: TParent, options: PgConditionOptions); + constructor(parent: TParent, isHaving?: boolean, mode?: PgConditionMode); constructor( parent: TParent, - isHaving = false, - mode: PgConditionMode = "PASS_THRU", + isHavingOrOptions?: PgConditionOptions | boolean, + maybeMode?: PgConditionMode, ) { super(parent); + const options = resolveOptions(isHavingOrOptions, maybeMode); + const { isHaving = false, mode = "PASS_THRU" } = options; this.isHaving = isHaving; if (typeof mode === "string") { this.resolvedMode = { mode }; From 13bb7abc86d32474ba5000b19b1d48af65e24b30 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 13 May 2026 10:44:05 +0100 Subject: [PATCH 2/6] RECORD_EXPRESSION take 1 --- grafast/dataplan-pg/src/steps/pgCondition.ts | 27 ++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/grafast/dataplan-pg/src/steps/pgCondition.ts b/grafast/dataplan-pg/src/steps/pgCondition.ts index dd7d2a1c0d..6d8d41ade2 100644 --- a/grafast/dataplan-pg/src/steps/pgCondition.ts +++ b/grafast/dataplan-pg/src/steps/pgCondition.ts @@ -1,4 +1,4 @@ -import { Modifier } from "grafast"; +import { inspect, Modifier } from "grafast"; import type { SQL } from "pg-sql2"; import { $$toSQL, sql } from "pg-sql2"; @@ -39,12 +39,19 @@ type PgConditionModeExists = { equals?: boolean; }; +type PgConditionModeTableExpression = { + mode: "RECORD_EXPRESSION"; + expression: SQL; + alias?: string; +}; + export type PgConditionResolvedMode = | { mode: "PASS_THRU" } | { mode: "AND" } | { mode: "OR" } | { mode: "NOT" } - | PgConditionModeExists; + | PgConditionModeExists + | PgConditionModeTableExpression; export type PgConditionMode = | "PASS_THRU" @@ -129,6 +136,16 @@ export class PgCondition< ); break; } + case "RECORD_EXPRESSION": { + this.alias = sql.identifier( + Symbol(this.resolvedMode.alias ?? "table_expression"), + ); + break; + } + default: { + const never: never = this.resolvedMode; + throw new Error(`Unexpected mode ${inspect(never)}`); + } } } @@ -220,6 +237,12 @@ where ${sqlCondition}`})`; return sqlExists; } } + case "RECORD_EXPRESSION": { + return sql`(${sql.indent`\ +select ${sqlCondition} +from (select ${this.resolvedMode.expression}.*) as ${this.alias} +`})`; + } default: { const never: never = this.resolvedMode; throw new Error(`Unhandled mode: ${(never as any).mode}`); From 76fc7d55ef2e7901721397a3c3a6d64e42227b7a Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 13 May 2026 10:48:52 +0100 Subject: [PATCH 3/6] Take 2 --- grafast/dataplan-pg/src/steps/pgCondition.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/grafast/dataplan-pg/src/steps/pgCondition.ts b/grafast/dataplan-pg/src/steps/pgCondition.ts index 6d8d41ade2..7d30c2399b 100644 --- a/grafast/dataplan-pg/src/steps/pgCondition.ts +++ b/grafast/dataplan-pg/src/steps/pgCondition.ts @@ -39,10 +39,15 @@ type PgConditionModeExists = { equals?: boolean; }; -type PgConditionModeTableExpression = { +type PgConditionModeRecordExpression = { mode: "RECORD_EXPRESSION"; + /** + * BEWARE: this expression may be _repeated_ multiple times in the generated + * query, so it should be inexpensive and have no side effects. Intended for + * use with columns of a composite type, so expression should typically be + * wrapped in parenthesis, enabling `(my_table.my_column).my_attribute` + */ expression: SQL; - alias?: string; }; export type PgConditionResolvedMode = @@ -51,7 +56,7 @@ export type PgConditionResolvedMode = | { mode: "OR" } | { mode: "NOT" } | PgConditionModeExists - | PgConditionModeTableExpression; + | PgConditionModeRecordExpression; export type PgConditionMode = | "PASS_THRU" @@ -137,9 +142,7 @@ export class PgCondition< break; } case "RECORD_EXPRESSION": { - this.alias = sql.identifier( - Symbol(this.resolvedMode.alias ?? "table_expression"), - ); + this.alias = this.resolvedMode.expression; break; } default: { @@ -238,10 +241,7 @@ where ${sqlCondition}`})`; } } case "RECORD_EXPRESSION": { - return sql`(${sql.indent`\ -select ${sqlCondition} -from (select ${this.resolvedMode.expression}.*) as ${this.alias} -`})`; + return sqlCondition; } default: { const never: never = this.resolvedMode; From e7f9242a5f592c7dca590e44457b500e1f9f5178 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 13 May 2026 10:53:55 +0100 Subject: [PATCH 4/6] docs(changeset): Add new `RECORD_EXPRESSION` mode to PgCondition so postgraphile-plugin-connection-filter doesn't need to rely on a hack to enable filtering on composite columns. --- .changeset/beige-toes-tease.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/beige-toes-tease.md diff --git a/.changeset/beige-toes-tease.md b/.changeset/beige-toes-tease.md new file mode 100644 index 0000000000..e5dc718048 --- /dev/null +++ b/.changeset/beige-toes-tease.md @@ -0,0 +1,7 @@ +--- +"@dataplan/pg": patch +--- + +Add new `RECORD_EXPRESSION` mode to PgCondition so +postgraphile-plugin-connection-filter doesn't need to rely on a hack to enable +filtering on composite columns. From c56601c9efae8945a0f67e713e8028ec20f55e50 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 13 May 2026 11:00:40 +0100 Subject: [PATCH 5/6] Config object note --- .changeset/beige-toes-tease.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/beige-toes-tease.md b/.changeset/beige-toes-tease.md index e5dc718048..f10e87b484 100644 --- a/.changeset/beige-toes-tease.md +++ b/.changeset/beige-toes-tease.md @@ -4,4 +4,5 @@ Add new `RECORD_EXPRESSION` mode to PgCondition so postgraphile-plugin-connection-filter doesn't need to rely on a hack to enable -filtering on composite columns. +filtering on composite columns. Also changes `PgCondition` signature to accept a +configuration object. From 4100c78a085b6227de43a7e20e78e883556246a9 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 13 May 2026 11:02:04 +0100 Subject: [PATCH 6/6] Remove unnecessary check --- grafast/dataplan-pg/src/steps/pgCondition.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/grafast/dataplan-pg/src/steps/pgCondition.ts b/grafast/dataplan-pg/src/steps/pgCondition.ts index 7d30c2399b..7024d76317 100644 --- a/grafast/dataplan-pg/src/steps/pgCondition.ts +++ b/grafast/dataplan-pg/src/steps/pgCondition.ts @@ -75,7 +75,7 @@ interface PgConditionOptions { function resolveOptions( isHavingOrOptions: PgConditionOptions | boolean | undefined, maybeMode: PgConditionMode | undefined, -) { +): PgConditionOptions { if (typeof isHavingOrOptions === "boolean") { return { isHaving: isHavingOrOptions, mode: maybeMode }; } else if (isHavingOrOptions == null) { @@ -84,10 +84,8 @@ function resolveOptions( throw new Error( `Invalid call signature to PgCondition constructor, use \`new PgCondition(parent, options)\``, ); - } else if (isHavingOrOptions) { - return isHavingOrOptions; } else { - return {}; + return isHavingOrOptions; } }