import type { QueryCatalog, QueryColumn, QueryForeignKey, QueryFunction } from '../supabase/functions/_backend/private/console_query_sql' import { PgDialect } from 'drizzle-orm/pg-core' import { describe, expect, it } from 'vitest' import { drizzleFrom, finishConsoleQuery, readSqlError, statusForSqlState } from '../supabase/functions/_backend/private/console_query_run' import { compileConsoleQuery, functionFromCatalogRow, shapeConsoleQueryResult } from '../supabase/functions/_backend/private/console_query_sql' function column(name: string, udt: string): QueryColumn { return { name, udt } } function key(name: string, sourceTable: string, sourceColumn: string, targetTable: string, targetColumn: string, unique = false): QueryForeignKey { return { name, sourceTable, sourceColumn, targetTable, targetColumn, unique } } function fn(name: string, argNames: string[], argTypes: string[], returnsSet = false, returnsComposite = false, defaultCount = 0): QueryFunction { return { name, argNames, argTypes, returnsSet, returnsComposite, defaultCount } } const catalog: QueryCatalog = { columns: new Map([ ['users', [column('id', 'uuid'), column('email', 'text'), column('onboarding', 'jsonb')]], ['orgs', [column('id', 'uuid'), column('name', 'text'), column('customer_id', 'text'), column('created_at', 'timestamptz')]], ['stripe_info', [column('customer_id', 'text'), column('paid_at', 'timestamptz'), column('trial_at', 'timestamptz')]], ['org_users', [column('user_id', 'uuid'), column('org_id', 'uuid')]], ['deploy_history', [column('id', 'int8'), column('app_id', 'text'), column('channel_id', 'int8'), column('version_id', 'int8'), column('created_at', 'timestamptz')]], ['app_versions', [column('id', 'int8'), column('name', 'text'), column('app_id', 'text'), column('created_at', 'timestamptz'), column('deleted', 'bool'), column('native_packages', 'jsonb')]], ['channels', [column('id', 'int8'), column('name', 'text'), column('app_id', 'text'), column('version', 'int8')]], ['audit_logs', [column('org_id', 'uuid'), column('operation', 'text'), column('table_name', 'text'), column('changed_fields', '_text'), column('old_record', 'jsonb'), column('created_at', 'timestamptz'), column('record_id', 'text')]], ['role_bindings', [column('principal_id', 'text'), column('role_id', 'uuid'), column('org_id', 'uuid')]], ['roles', [column('id', 'uuid'), column('name', 'text')]], ['apps', [column('id', 'uuid'), column('app_id', 'text'), column('owner_org', 'uuid'), column('created_by', 'uuid'), column('other_user', 'uuid')]], ['group_members', [column('group_id', 'uuid'), column('user_id', 'uuid')]], ]), foreignKeys: [ key('orgs_customer_id_fkey', 'orgs', 'customer_id', 'stripe_info', 'customer_id'), key('org_users_user_id_fkey', 'org_users', 'user_id', 'users', 'id'), key('deploy_history_version_id_fkey', 'deploy_history', 'version_id', 'app_versions', 'id'), key('deploy_history_channel_id_fkey', 'deploy_history', 'channel_id', 'channels', 'id'), key('channels_version_fkey', 'channels', 'version', 'app_versions', 'id'), key('role_bindings_role_id_fkey', 'role_bindings', 'role_id', 'roles', 'id'), key('apps_owner_org_fkey', 'apps', 'owner_org', 'orgs', 'id'), key('apps_created_by_fkey', 'apps', 'created_by', 'users', 'id'), key('apps_other_user_fkey', 'apps', 'other_user', 'users', 'id'), ], functions: [ fn('is_platform_admin', [], []), fn('is_platform_admin', ['userid'], ['uuid']), fn('is_not_deleted', ['email_check'], ['varchar']), fn('get_org_members', ['guild_id'], ['uuid'], true, true), fn('get_org_members', ['guild_id', 'user_id'], ['uuid', 'uuid'], true, true), ], } function flat(text: string) { return text.replace(/\s+/g, ' ').trim() } function compile(request: Parameters[1]) { const compiled = compileConsoleQuery(catalog, request) return { ...compiled, text: flat(compiled.text) } } describe('console query sql', () => { it('keeps an apostrophe literal in an or filter', () => { const compiled = compile({ kind: 'table', name: 'channels', args: [], operations: [ { method: 'select', args: ['id, name'] }, { method: 'or', args: ['name.ilike.%O\'Reilly%,id.in.(42)'] }, ], }) expect(compiled.text).toContain('t."name" ILIKE $1') expect(compiled.text).toContain('OR t."id" = ANY($2::bigint[])') expect(compiled.values).toEqual(['%O\'Reilly%', ['42']]) }) it('compiles an equality filter and keeps the value out of the SQL text', () => { const compiled = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'select', args: ['id, email'] }, { method: 'eq', args: ['id', 'user-1'] }, { method: 'single', args: [] }], }) expect(compiled.text).toBe('SELECT jsonb_build_object(\'id\', t."id", \'email\', t."email") AS row FROM "public"."users" AS t WHERE t."id" = $1::uuid LIMIT 2') expect(compiled.values).toEqual(['user-1']) expect(compiled.text).not.toContain('user-1') expect(compiled.cardinality).toBe('one') }) it('treats eq and neq null as null checks', () => { const missing = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'select', args: ['*'] }, { method: 'eq', args: ['email', null] }], }) expect(missing.text).toContain('WHERE t."email" IS NULL') const present = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'not', args: ['email', 'is', null] }], }) expect(present.text).toContain('WHERE NOT (t."email" IS NULL)') }) it('compiles in, jsonb equality, and a json path as text', () => { const listed = compile({ kind: 'table', name: 'role_bindings', args: [], operations: [{ method: 'select', args: ['principal_id'] }, { method: 'in', args: ['principal_id', ['a', 'b']] }], }) expect(listed.text).toContain('t."principal_id" = ANY($1::text[])') expect(listed.values).toEqual([['a', 'b']]) const dialect = new PgDialect() const bound = dialect.sqlToQuery(drizzleFrom(listed.text, listed.values)) expect(bound.sql).toContain('= ANY($1::text[])') expect(bound.params).toEqual([['a', 'b']]) const orgs = compile({ kind: 'table', name: 'apps', args: [], operations: [{ method: 'select', args: ['app_id'] }, { method: 'in', args: ['owner_org', ['11111111-1111-4111-8111-111111111111']] }], }) const oneOrg = dialect.sqlToQuery(drizzleFrom(orgs.text, orgs.values)) expect(oneOrg.sql).toContain('= ANY($1::uuid[])') expect(oneOrg.params).toEqual([['11111111-1111-4111-8111-111111111111']]) const onboarding = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'filter', args: ['onboarding', 'eq', '{"step":1}'] }], }) expect(onboarding.text).toContain('t."onboarding" = $1::jsonb') expect(onboarding.values).toEqual(['{"step":1}']) const path = compile({ kind: 'table', name: 'audit_logs', args: [], operations: [{ method: 'eq', args: ['old_record->>version', '1.2.3'] }], }) expect(path.text).toContain('(t."old_record"->>\'version\') = $1::text') expect(path.values).toEqual(['1.2.3']) }) it('compiles or, and, and not.cd array literals', () => { const compiled = compile({ kind: 'table', name: 'audit_logs', args: [], operations: [{ method: 'or', args: ['operation.eq.INSERT,and(operation.eq.UPDATE,changed_fields.not.cd.{updated_at})'] }], }) expect(compiled.text).toContain('WHERE (t."operation" = $1::text OR (t."operation" = $2::text AND NOT (t."changed_fields" <@ $3::text[])))') expect(compiled.values).toEqual(['INSERT', 'UPDATE', '{updated_at}']) }) it('embeds a foreign key column, an inner target table, and stripe_info', () => { const history = compile({ kind: 'table', name: 'deploy_history', args: [], operations: [ { method: 'select', args: ['*, version:version_id (id, name)'] }, { method: 'eq', args: ['app_id', 'com.demo.app'] }, { method: 'like', args: ['version.name', '%demo%'] }, { method: 'order', args: ['created_at', { ascending: false, nullsFirst: true }] }, { method: 'range', args: [10, 19] }, ], }) expect(history.text).toContain('to_jsonb(t) || jsonb_build_object(\'version\', (SELECT jsonb_build_object(\'id\', e1."id", \'name\', e1."name") FROM "public"."app_versions" AS e1 WHERE e1."id" = t."version_id"))') expect(history.text).toContain('EXISTS (SELECT 1 FROM "public"."app_versions" AS ef WHERE ef."id" = t."version_id" AND ef."name" LIKE $2::text)') expect(history.text).toContain('ORDER BY t."created_at" DESC NULLS FIRST LIMIT 10 OFFSET 10') expect(history.values).toEqual(['com.demo.app', '%demo%']) const members = compile({ kind: 'table', name: 'org_users', args: [], operations: [{ method: 'select', args: ['user_id, users!inner(email)'] }, { method: 'eq', args: ['org_id', 'org-1'] }], }) expect(members.text).toContain('\'users\', (SELECT jsonb_build_object(\'email\', e1."email") FROM "public"."users" AS e1 WHERE e1."id" = t."user_id")') expect(members.text).toContain('EXISTS (SELECT 1 FROM "public"."users" AS ei WHERE ei."id" = t."user_id")') const billing = compile({ kind: 'table', name: 'orgs', args: [], operations: [{ method: 'select', args: ['created_at, stripe_info(paid_at, trial_at)'] }, { method: 'maybeSingle', args: [] }], }) expect(billing.text).toContain('\'stripe_info\', (SELECT jsonb_build_object(\'paid_at\', e1."paid_at", \'trial_at\', e1."trial_at") FROM "public"."stripe_info" AS e1 WHERE e1."customer_id" = t."customer_id")') expect(billing.cardinality).toBe('maybe') const channel = compile({ kind: 'table', name: 'channels', args: [], operations: [{ method: 'select', args: ['version:app_versions!channels_version_fkey(native_packages)'] }], }) expect(channel.text).toContain('FROM "public"."app_versions" AS e1 WHERE e1."id" = t."version"') }) it('keeps an exact count when the page is empty and caps the page size', () => { const compiled = compile({ kind: 'table', name: 'audit_logs', args: [], operations: [{ method: 'select', args: ['*', { count: 'estimated', head: false }] }, { method: 'limit', args: [5000] }], }) expect(compiled.count).toBe(true) expect(compiled.text).toContain('SELECT count(*)::bigint AS total_count FROM "public"."audit_logs" AS t') expect(compiled.text).toContain('LEFT JOIN LATERAL') expect(compiled.text).toContain('LIMIT 1000') expect(compiled.text).not.toContain('LIMIT 5000') const head = compile({ kind: 'table', name: 'apps', args: [], operations: [{ method: 'select', args: ['id', { count: 'exact', head: true }] }], }) expect(head.head).toBe(true) expect(head.text).toBe('SELECT count(*)::bigint AS total_count FROM "public"."apps" AS t') }) it('compiles upsert, ignore-duplicates, and a filtered jsonb update', () => { const upsert = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'upsert', args: [{ id: 'user-1', email: 'person@example.com' }, { onConflict: 'id' }] }, { method: 'select', args: [] }, { method: 'single', args: [] }], }) expect(upsert.text).toContain('INSERT INTO "public"."users" ("id", "email") VALUES ($1::uuid, $2::text) ON CONFLICT ("id") DO UPDATE SET "email" = EXCLUDED."email" RETURNING *') expect(upsert.values).toEqual(['user-1', 'person@example.com']) const ignored = compile({ kind: 'table', name: 'group_members', args: [], operations: [{ method: 'upsert', args: [[{ group_id: 'g', user_id: 'u' }], { onConflict: 'group_id,user_id', ignoreDuplicates: true }] }], }) expect(ignored.text).toContain('ON CONFLICT ("group_id", "user_id") DO NOTHING') expect(ignored.text).not.toContain('RETURNING') expect(ignored.minimal).toBe(true) expect(shapeConsoleQueryResult(ignored, [{ row: { group_id: 'g' } }])).toEqual({ data: null, error: null, count: null, status: 200 }) const update = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'update', args: [{ onboarding: { step: 1 } }] }, { method: 'eq', args: ['id', 'user-1'] }, { method: 'filter', args: ['onboarding', 'eq', '{"step":1}'] }], }) expect(update.text).toContain('SET "onboarding" = $3::jsonb') expect(update.text).toContain('t."id" = $1::uuid') expect(update.text).toContain('t."onboarding" = $2::jsonb') expect(update.text).not.toContain('RETURNING') expect(update.values).toEqual(['user-1', '{"step":1}', '{"step":1}']) }) it('rejects identifiers and ambiguous relationships', () => { expect(() => compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'eq', args: ['id;drop', 'x'] }], })).toThrow(/Invalid identifier/) expect(() => compile({ kind: 'table', name: 'apps', args: [], operations: [{ method: 'select', args: ['users(id)'] }], })).toThrow(/more than one relationship/) expect(() => compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'order', args: ['missing'] }], })).toThrow(/does not exist/) }) it('picks the RPC overload from the provided argument names', () => { const admin = compile({ kind: 'rpc', name: 'is_platform_admin', args: [], operations: [] }) expect(admin.text).toBe('SELECT to_jsonb(public."is_platform_admin"()) AS data') const probe = compile({ kind: 'rpc', name: 'is_not_deleted', args: [{ email_check: 'nobody@example.com' }], operations: [] }) expect(probe.text).toBe('SELECT to_jsonb(public."is_not_deleted"("email_check" => $1::varchar)) AS data') expect(probe.values).toEqual(['nobody@example.com']) const members = compile({ kind: 'rpc', name: 'get_org_members', args: [{ guild_id: 'org-1' }], operations: [] }) expect(members.text).toContain('jsonb_agg(to_jsonb(q)) FROM (SELECT * FROM public."get_org_members"("guild_id" => $1::uuid) AS q LIMIT 1000) AS q') expect(members.returnsSet).toBe(true) }) it('shapes pages, cardinality, and scalar RPC results', () => { const many = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'select', args: ['id'] }] }) expect(shapeConsoleQueryResult(many, [{ row: { id: 'a' } }, { row: { id: 'b' } }]).data).toEqual([{ id: 'a' }, { id: 'b' }]) const single = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'single', args: [] }] }) expect(shapeConsoleQueryResult(single, []).error?.code).toBe('PGRST116') expect(shapeConsoleQueryResult(single, []).status).toBe(406) expect(shapeConsoleQueryResult(single, [{ row: { id: 'a' } }, { row: { id: 'b' } }]).error?.code).toBe('PGRST116') const maybe = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'maybeSingle', args: [] }] }) expect(shapeConsoleQueryResult(maybe, []).data).toBeNull() expect(shapeConsoleQueryResult(maybe, []).error).toBeNull() const counted = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'select', args: ['id', { count: 'exact' }] }], }) const page = shapeConsoleQueryResult(counted, [{ row: null, total_count: '4' }]) expect(page.data).toEqual([]) expect(page.count).toBe(4) const head = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'select', args: ['id', { count: 'exact', head: true }] }], }) expect(shapeConsoleQueryResult(head, [{ total_count: '8' }])).toEqual({ data: null, error: null, count: 8, status: 200 }) const rpc = compile({ kind: 'rpc', name: 'is_platform_admin', args: [], operations: [] }) expect(shapeConsoleQueryResult(rpc, [{ data: false }]).data).toBe(false) expect(shapeConsoleQueryResult(rpc, [{ data: '{"step":1}' }]).data).toBe('{"step":1}') expect(shapeConsoleQueryResult(rpc, [{ data: { step: 1 } }]).data).toEqual({ step: 1 }) }) it('reads function signatures from the array text node-pg leaves unparsed', () => { const zero = functionFromCatalogRow({ name: 'is_platform_admin', returns_set: false, arg_count: 0, arg_names: null, arg_modes: null, return_kind: 'b', arg_types: '{}', }) const named = functionFromCatalogRow({ name: 'is_not_deleted', returns_set: false, arg_count: 1, arg_names: ['email_check'], arg_modes: '{i}', return_kind: 'b', arg_types: '{varchar}', }) const withOut = functionFromCatalogRow({ name: 'example', returns_set: false, arg_count: 1, arg_names: '{email_check,result}', arg_modes: '{i,o}', return_kind: 'b', arg_types: '{varchar}', }) expect(zero?.argNames).toEqual([]) expect(named?.argNames).toEqual(['email_check']) expect(named?.argTypes).toEqual(['varchar']) expect(withOut?.argNames).toEqual(['email_check']) const parsed: QueryCatalog = { columns: new Map(), foreignKeys: [], functions: [zero, named].filter(item => item != null), } expect(compileConsoleQuery(parsed, { kind: 'rpc', name: 'is_platform_admin', args: [], operations: [] }).text).toContain('public."is_platform_admin"()') const deleted = compileConsoleQuery(parsed, { kind: 'rpc', name: 'is_not_deleted', args: [{ email_check: 'nobody@example.com' }], operations: [] }) expect(deleted.text).toContain('public."is_not_deleted"("email_check" => $1::varchar)') expect(deleted.values).toEqual(['nobody@example.com']) }) it('returns every column of a table function and omits defaulted arguments', () => { const orgs = functionFromCatalogRow({ name: 'get_orgs_v7', returns_set: true, arg_count: 0, arg_names: '{gid,name}', arg_modes: '{t,t}', return_kind: 'p', arg_types: '{}', default_count: 0, }) const orgsForUser = functionFromCatalogRow({ name: 'get_orgs_v7', returns_set: true, arg_count: 1, arg_names: '{userid,gid,name}', arg_modes: '{i,t,t}', return_kind: 'p', arg_types: '{uuid}', default_count: 0, }) const scalarSet = functionFromCatalogRow({ name: 'list_ids', returns_set: true, arg_count: 0, arg_names: null, arg_modes: null, return_kind: 'b', arg_types: '{}', }) const permission = functionFromCatalogRow({ name: 'rbac_check_permission', returns_set: false, arg_count: 4, arg_names: '{p_permission_key,p_org_id,p_app_id,p_channel_id}', arg_modes: null, return_kind: 'b', arg_types: '{text,uuid,varchar,int8}', default_count: '3', }) expect(orgs?.argNames).toEqual([]) expect(orgs?.returnsComposite).toBe(true) expect(orgsForUser?.argNames).toEqual(['userid']) expect(scalarSet?.returnsComposite).toBe(false) expect(permission?.defaultCount).toBe(3) const parsed: QueryCatalog = { columns: new Map(), foreignKeys: [], functions: [orgs, orgsForUser, scalarSet, permission].filter(item => item != null), } const listed = compileConsoleQuery(parsed, { kind: 'rpc', name: 'get_orgs_v7', args: [], operations: [] }) expect(listed.text).toContain('jsonb_agg(to_jsonb(q)) FROM (SELECT * FROM public."get_orgs_v7"() AS q LIMIT 1000) AS q') expect(listed.text).not.toContain('q(val)') const ids = compileConsoleQuery(parsed, { kind: 'rpc', name: 'list_ids', args: [], operations: [] }) expect(ids.text).toContain('jsonb_agg(to_jsonb(q.val)) FROM (SELECT val FROM public."list_ids"() AS q(val) LIMIT 1000) AS q') const check = compileConsoleQuery(parsed, { kind: 'rpc', name: 'rbac_check_permission', args: [{ p_permission_key: 'org.read_billing', p_org_id: 'org-1' }], operations: [], }) expect(check.text).toContain('public."rbac_check_permission"("p_permission_key" => $1::text, "p_org_id" => $2::uuid)') expect(check.text).not.toContain('p_app_id') expect(check.text).not.toContain('p_channel_id') expect(check.values).toEqual(['org.read_billing', 'org-1']) expect(() => compileConsoleQuery(parsed, { kind: 'rpc', name: 'rbac_check_permission', args: [{ p_org_id: 'org-1' }], operations: [], })).toThrow(/Could not find the function/) expect(() => compileConsoleQuery({ columns: new Map(), foreignKeys: [], functions: [ fn('ambiguous', ['a'], ['text']), fn('ambiguous', ['a', 'b'], ['text', 'text'], false, false, 1), ], }, { kind: 'rpc', name: 'ambiguous', args: [{ a: 'x' }], operations: [] })).toThrow(/Could not find the function/) }) it('preserves RPC argument types and pages set-returning functions', () => { const apps = fn( 'get_org_apps_with_last_upload', ['p_org_id', 'p_search', 'p_sort_by', 'p_sort_desc', 'p_limit', 'p_offset'], ['uuid', 'text', 'text', 'bool', 'int4', 'int4'], true, true, 5, ) const uploads = fn( 'get_bundle_upload_daily_counts', ['p_app_ids', 'p_start', 'p_end'], ['_varchar', 'timestamptz', 'timestamptz'], true, true, ) const parsed: QueryCatalog = { columns: new Map(), foreignKeys: [], functions: [apps, uploads], } const listed = compileConsoleQuery(parsed, { kind: 'rpc', name: 'get_org_apps_with_last_upload', args: [{ p_org_id: 'org-1', p_limit: 10, p_offset: 20 }], operations: [], }) expect(listed.text).toContain('"p_limit" => $2::integer') expect(listed.text).toContain('"p_offset" => $3::integer') expect(listed.text).not.toContain('bigint') expect(listed.values).toEqual(['org-1', 10, 20]) const page = compileConsoleQuery(parsed, { kind: 'rpc', name: 'get_bundle_upload_daily_counts', args: [{ p_app_ids: ['com.demo.app'], p_start: '2026-01-01T00:00:00.000Z', p_end: '2026-01-02T00:00:00.000Z' }], operations: [{ method: 'range', args: [0, 999] }], }) expect(flat(page.text)).toContain('jsonb_agg(to_jsonb(q)) FROM (SELECT * FROM public."get_bundle_upload_daily_counts"("p_app_ids" => $1::varchar[], "p_start" => $2::timestamptz, "p_end" => $3::timestamptz) AS q LIMIT 1000) AS q') expect(page.text).not.toContain('text[]') expect(page.text).not.toContain('OFFSET') expect(page.values).toEqual([['com.demo.app'], '2026-01-01T00:00:00.000Z', '2026-01-02T00:00:00.000Z']) const next = compileConsoleQuery(parsed, { kind: 'rpc', name: 'get_bundle_upload_daily_counts', args: [{ p_app_ids: ['com.demo.app'], p_start: '2026-01-01T00:00:00.000Z', p_end: '2026-01-02T00:00:00.000Z' }], operations: [{ method: 'range', args: [1000, 2999] }], }) expect(next.text).toContain('LIMIT 1000 OFFSET 1000') const channel = compile({ kind: 'table', name: 'channels', args: [], operations: [{ method: 'eq', args: ['id', 1] }], }) expect(channel.text).toContain('t."id" = $1::bigint') }) it('keeps a json string on writes and parses filter in lists', () => { const update = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'update', args: [{ onboarding: '1.0' }] }, { method: 'eq', args: ['id', 'user-1'] }, { method: 'select', args: [] }], }) expect(update.text).toContain('RETURNING') expect(update.values).toEqual(['user-1', '"1.0"']) const filter = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'filter', args: ['onboarding', 'eq', '1.0'] }], }) expect(filter.values).toEqual(['1.0']) const listed = compile({ kind: 'table', name: 'channels', args: [], operations: [{ method: 'filter', args: ['id', 'in', '(5,6,7)'] }], }) expect(listed.text).toContain('t."id" = ANY($1::bigint[])') expect(listed.values).toEqual([['5', '6', '7']]) const excluded = compile({ kind: 'table', name: 'channels', args: [], operations: [{ method: 'not', args: ['id', 'in', '(5,6)'] }], }) expect(excluded.text).toContain('NOT (t."id" = ANY($1::bigint[]))') expect(excluded.values).toEqual([['5', '6']]) const noted = compileConsoleQuery({ ...catalog, functions: [...catalog.functions, fn('save_note', ['payload'], ['jsonb'])], }, { kind: 'rpc', name: 'save_note', args: [{ payload: '1.0' }], operations: [], }) expect(noted.values).toEqual(['"1.0"']) const counted = compile({ kind: 'table', name: 'users', args: [], operations: [{ method: 'delete', args: [undefined, { count: 'exact', head: true }] }, { method: 'eq', args: ['id', 'user-1'] }], }) expect(counted.text).toContain('DELETE FROM "public"."users" AS t WHERE t."id" = $1::uuid RETURNING *') expect(counted.head).toBe(true) const ordered = compile({ kind: 'table', name: 'users', args: [], operations: [ { method: 'update', args: [{ email: 'person@example.com' }] }, { method: 'eq', args: ['id', 'user-1'] }, { method: 'select', args: [] }, { method: 'order', args: ['email', { ascending: true }] }, ], }) expect(ordered.text).toContain('ORDER BY w."email" ASC') expect(ordered.text).not.toContain('ORDER BY t.') }) it('does not treat a quoted placeholder as a bind', () => { const dialect = new PgDialect() const quoted = dialect.sqlToQuery(drizzleFrom('select \'$1\', "$1", $1::uuid', ['real-id'])) expect(quoted.sql).toContain('\'$1\'') expect(quoted.sql).toContain('"$1"') expect(quoted.params).toEqual(['real-id']) const repeated = dialect.sqlToQuery(drizzleFrom('select $1::text, $1::text', ['a'])) expect(repeated.params).toEqual(['a', 'a']) const dollar = dialect.sqlToQuery(drizzleFrom('select $body$ $1 $body$, $1::text', ['kept'])) expect(dollar.sql).toContain('$body$ $1 $body$') expect(dollar.params).toEqual(['kept']) }) it('maps permission failures by session and rethrows connection failures', () => { expect(statusForSqlState('42501', 'authenticated')).toBe(403) expect(statusForSqlState('42501', 'anon')).toBe(401) expect(statusForSqlState('23505', 'authenticated')).toBe(409) expect(statusForSqlState('42P01', 'authenticated')).toBe(400) const denied = new Error('Failed query: update users params: person@example.com') Object.assign(denied, { cause: Object.assign(new Error('permission denied for table users'), { name: 'DatabaseError', code: '42501', severity: 'ERROR', }), }) expect(readSqlError(denied)).toEqual({ code: '42501', message: 'permission denied for table users' }) expect(readSqlError(Object.assign(new Error('write EPIPE'), { code: 'EPIPE' }))).toBeNull() for (const code of ['08006', '53300', '57P01']) { expect(readSqlError(Object.assign(new Error('connection failed'), { name: 'DatabaseError', code, severity: 'FATAL' }))).toBeNull() } expect(readSqlError(Object.assign(new Error('canceling statement due to statement timeout'), { name: 'DatabaseError', code: '57014', severity: 'ERROR' }))).toEqual({ code: '57014', message: 'canceling statement due to statement timeout', }) }) it('throws a rollback signal when singular cardinality fails', () => { const ok = { data: [{ id: 'a' }], error: null, count: null, status: 200 } expect(finishConsoleQuery(ok)).toBe(ok) const failed = { data: null, error: { code: 'PGRST116', message: 'JSON object requested, multiple (or no) rows returned', details: 'The result contains 2 rows', }, count: null, status: 406, } expect(() => finishConsoleQuery(failed)).toThrowError(expect.objectContaining({ name: 'ConsoleQueryRollback', result: failed, })) }) })