Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/plenty-badgers-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"postgraphile": minor
"@dataplan/pg": minor
---

Use `unnest()` rather than `json_array_elements()` to feed pgSettings to
Postgres.
28 changes: 18 additions & 10 deletions grafast/dataplan-pg/src/adaptors/pg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,6 @@ async function makeNodePostgresWithPgClient_inner<T>(
alreadyInTransaction: boolean,
) {
/** Transaction level; 0 = no transaction; 1 = begin; 2,... = savepoint */
const pgSettingsEntries: Array<[string, string]> = [];
if (pgSettings != null) {
for (const [key, value] of Object.entries(pgSettings)) {
if (value == null) continue;
pgSettingsEntries.push([key, "" + value]);
}
}

// PERF: under what situations is this actually required? We added it to
// force test queries that were sharing the same client to run in series
Expand All @@ -288,14 +281,29 @@ async function makeNodePostgresWithPgClient_inner<T>(
return (pgClient[$$queue] = (async () => {
try {
// If there's pgSettings; create a transaction and set them, otherwise no transaction needed
if (pgSettingsEntries.length > 0) {
let keys: [string, ...string[]] | null = null;
let values: [string, ...string[]] | null = null;
if (pgSettings != null) {
for (const [key, value] of Object.entries(pgSettings)) {
if (value != null) {
if (keys == null) {
keys = [key];
values = [value];
} else {
keys.push(key);
values!.push("" + value);
}
}
}
}
if (keys != null) {
await pgClient.query({
text: alreadyInTransaction ? "savepoint tx" : "begin",
});
try {
await pgClient.query({
text: "select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el",
values: [JSON.stringify(pgSettingsEntries)],
text: "select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)",
values: [keys, values],
});
const client = newNodePostgresPgClient(
pgClient,
Expand Down
7 changes: 5 additions & 2 deletions postgraphile/postgraphile/__tests__/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ export async function runTestQuery(
cleanupSql?: string;
extends?: string | string[];
pgIdentifiers?: "qualified" | "unqualified";
pgSettings?: Record<string, string | number | boolean | null | undefined>;
search_path?: string;
muteWarnings?: boolean;
dontLogErrors?: boolean;
Expand Down Expand Up @@ -293,6 +294,7 @@ export async function runTestQuery(
setupSql,
cleanupSql,
pgIdentifiers,
pgSettings,
search_path,
muteWarnings = true,
dontLogErrors = false,
Expand Down Expand Up @@ -347,8 +349,9 @@ export async function runTestQuery(
role: "postgraphile_test_authenticator",
}
: null,
pgSettings:
config.ignoreRBAC === false
pgSettings: pgSettings
? () => pgSettings
: config.ignoreRBAC === false
? () => ({
role: "postgraphile_test_visitor",
"jwt.claims.user_id": "3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ grant usage on schema a,b,c to postgraphile_test_visitor;

grant usage, select on all sequences in schema a,b,c to postgraphile_test_visitor;
grant execute on function c.current_user_id() to postgraphile_test_visitor;
grant execute on function c.read_pg_settings() to postgraphile_test_visitor;
grant execute on function c.left_arm_identity(left_arm c.left_arm) to postgraphile_test_visitor;

grant select on c.person to postgraphile_test_visitor;
Expand Down
17 changes: 17 additions & 0 deletions postgraphile/postgraphile/__tests__/kitchen-sink-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,23 @@ end if;
end;
$_$ language plpgsql;

create function c.read_pg_settings() returns json as $$
select json_build_object(
'statement_timeout', current_setting('statement_timeout', true),
'role', current_setting('role', true),
'jwt.claims.string', current_setting('jwt.claims.string', true),
'jwt.claims.number', current_setting('jwt.claims.number', true),
'jwt.claims.boolean_true', current_setting('jwt.claims.boolean_true', true),
'jwt.claims.other_string', current_setting('jwt.claims.other_string', true),
'jwt.claims.other_number', current_setting('jwt.claims.other_number', true),
'jwt.claims.boolean_false', current_setting('jwt.claims.boolean_false', true),
'jwt.claims.empty_string', current_setting('jwt.claims.empty_string', true),
'jwt.claims.escaped_string', current_setting('jwt.claims.escaped_string', true),
'jwt.claims.null', current_setting('jwt.claims.null', true),
'jwt.claims.undefined', current_setting('jwt.claims.undefined', true)
);
$$ language sql stable;

-- This is to test that "one-to-one" relationships work on primary keys
create table c.person_secret (
person_id int not null primary key references c.person on delete cascade,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

insert into "measurements" as __measurements__ ("timestamp", "key", "value", "user_id") values ($1::"timestamptz", $2::"text", $3::"float8", $4::"int4") returning
to_char(__measurements__."timestamp", 'YYYY-MM-DD"T"HH24:MI:SS.USTZH:TZM'::text) as "0",
Expand All @@ -12,7 +12,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__users__."id"::text as "0",
Expand All @@ -26,7 +26,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

update "measurements" as __measurements__ set "value" = $1::"float8" where ((__measurements__."timestamp" = $2::"timestamptz") and (__measurements__."key" = $3::"text")) returning
to_char(__measurements__."timestamp", 'YYYY-MM-DD"T"HH24:MI:SS.USTZH:TZM'::text) as "0",
Expand All @@ -38,7 +38,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__users__."id"::text as "0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

insert into "c"."left_arm" as __left_arm__ ("length_in_metres") values ($1::"float8") returning
__left_arm__."id"::text as "0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

insert into "c"."person" as __person__ ("person_full_name", "aliases", "about", "email", "site") values ($1::"varchar", $2::"text"[], $3::"text", $4::"b"."email", $5::"b"."wrapped_url") returning
__person__."id"::text as "0",
Expand All @@ -16,7 +16,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__frmcdc_wrapped_url__."url" as "0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

delete from "c"."person" as __person__ where (__person__."id" = $1::"int4") returning
__person__."id"::text as "0";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__left_arm_identity__."id"::text as "0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

update "c"."left_arm" as __left_arm__ set "mood" = $1::"text" where (__left_arm__."id" = $2::"int4") returning
__left_arm__."id"::text as "0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

update "c"."person" as __person__ set "person_full_name" = $1::"varchar", "aliases" = $2::"text"[], "about" = $3::"text", "email" = $4::"b"."email", "site" = $5::"b"."wrapped_url" where (__person__."id" = $6::"int4") returning
__person__."id"::text as "0",
Expand All @@ -16,7 +16,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__frmcdc_wrapped_url__."url" as "0"
Expand Down
46 changes: 46 additions & 0 deletions postgraphile/postgraphile/__tests__/pgSettings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { runTestQuery } from "./helpers.ts";

test("applies pgSettings to PostgreSQL", async () => {
const { data, errors } = await runTestQuery(
/* GraphQL */ `
query {
cReadPgSettings
}
`,
{
pgSettings: {
statement_timeout: 12345,
role: "postgraphile_test_visitor",
"jwt.claims.string": "a string value",
"jwt.claims.number": 42,
"jwt.claims.boolean_true": true,
"jwt.claims.other_string": "another string value",
"jwt.claims.other_number": -7,
"jwt.claims.boolean_false": false,
"jwt.claims.empty_string": "",
"jwt.claims.escaped_string": 'a "quoted" value',
"jwt.claims.null": null,
"jwt.claims.undefined": undefined,
},
},
{ path: __filename },
);

expect(errors).toBeFalsy();
expect(data).toEqual({
cReadPgSettings: {
statement_timeout: "12345ms",
role: "postgraphile_test_visitor",
"jwt.claims.string": "a string value",
"jwt.claims.number": "42",
"jwt.claims.boolean_true": "true",
"jwt.claims.other_string": "another string value",
"jwt.claims.other_number": "-7",
"jwt.claims.boolean_false": "false",
"jwt.claims.empty_string": "",
"jwt.claims.escaped_string": 'a "quoted" value',
"jwt.claims.null": null,
"jwt.claims.undefined": null,
},
});
});
22 changes: 11 additions & 11 deletions postgraphile/postgraphile/__tests__/queries/v4/rbac.basic.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__person_secret__."person_id"::text as "0",
Expand All @@ -14,7 +14,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__person_secret__."person_id"::text as "0",
Expand All @@ -26,7 +26,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__person__."id"::text as "0",
Expand All @@ -46,7 +46,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__person__."id"::text as "0",
Expand All @@ -66,7 +66,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__left_arm__."id"::text as "0",
Expand All @@ -82,7 +82,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__left_arm__."id"::text as "0",
Expand All @@ -96,7 +96,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__person__."id"::text as "0",
Expand All @@ -118,7 +118,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__post__."id"::text as "0",
Expand All @@ -134,7 +134,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__post__."id"::text as "0",
Expand All @@ -148,7 +148,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__person__."id"::text as "0",
Expand All @@ -174,7 +174,7 @@ commit; /*fake*/

begin; /*fake*/

select set_config(el->>0, el->>1, true) from json_array_elements($1::json) el
select set_config(key, value, true) from unnest($1::text[], $2::text[]) as settings(key, value)

select
__return_table_without_grants__."person_id_1"::text as "0",
Expand Down
Loading
Loading