From 3664394f100906d624c08f11d8fe2c582d2e35a8 Mon Sep 17 00:00:00 2001 From: rezimeshvelashvili <49002882+rezimeshvelashvili@users.noreply.github.com> Date: Mon, 17 Aug 2026 23:36:30 +0400 Subject: [PATCH] fix(policies): fall back to public when roles is an empty array `policies.update()` built the role list with `roles.map(ident).join(',')`, so an empty array produced `ALTER POLICY ... TO ;` and Postgres rejected it with `syntax error at or near ";"`. An empty array is the natural way to express "all roles", and it is what `policies.create()` already defaults to. Studio sidesteps the bug by substituting `['public']` client-side before calling the API, so it only surfaces for direct REST and library consumers. Fixes #361 --- src/lib/PostgresMetaPolicies.ts | 6 +++++- test/lib/policies.ts | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/lib/PostgresMetaPolicies.ts b/src/lib/PostgresMetaPolicies.ts index 72d3157b9..6632fb01c 100644 --- a/src/lib/PostgresMetaPolicies.ts +++ b/src/lib/PostgresMetaPolicies.ts @@ -142,7 +142,11 @@ CREATE POLICY ${ident(name)} ON ${ident(schema)}.${ident(table)} const nameSql = name === undefined ? '' : `${alter} RENAME TO ${ident(name)};` const definitionSql = definition === undefined ? '' : `${alter} USING (${definition});` const checkSql = check === undefined ? '' : `${alter} WITH CHECK (${check});` - const rolesSql = roles === undefined ? '' : `${alter} TO ${roles.map(ident).join(',')};` + // An empty array means "all roles", which is the default `create` applies. + const rolesSql = + roles === undefined + ? '' + : `${alter} TO ${(roles.length === 0 ? ['public'] : roles).map(ident).join(',')};` // nameSql must be last const sql = `BEGIN; ${definitionSql} ${checkSql} ${rolesSql} ${nameSql} COMMIT;` diff --git a/test/lib/policies.ts b/test/lib/policies.ts index ef7eccbc6..257b6929d 100644 --- a/test/lib/policies.ts +++ b/test/lib/policies.ts @@ -188,3 +188,24 @@ test('retrieve, create, update, delete', async () => { }, }) }) + +test('update roles to an empty array falls back to public', async () => { + let res = await pgMeta.policies.create({ + name: 'test empty roles policy', + schema: 'public', + table: 'memes', + roles: ['postgres'], + }) + const policyId = res.data!.id + expect(res.data!.roles).toStrictEqual(['postgres']) + + // An empty array means "all roles" — the same default `create` applies. + // `name` is typed as required even though `update` treats it as optional, and + // renaming a policy to its own name errors, so it has to be omitted here. + res = await pgMeta.policies.update(policyId, { roles: [] } as any) + + expect(res.error).toBeNull() + expect(res.data!.roles).toStrictEqual(['public']) + + await pgMeta.policies.remove(policyId) +})