Skip to content
Merged
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
14 changes: 10 additions & 4 deletions packages/function-resolution/__tests__/request-lane-reads.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,20 @@ describe('request-lane resolution reads', () => {
VALUES ($1, $2, $3) RETURNING id`,
[database, schema.id, table]
)).id;
const routingPrivate = await pg.one(
`INSERT INTO metaschema_public.schema (database_id, name, schema_name)
VALUES ($1, $2, $3) RETURNING id`,
[database, `${name}_routing_private`, `${name}_routing_private`]
);
await pg.query(
`INSERT INTO metaschema_modules_public.route_module
(database_id, scope, schema_id, private_schema_id, resolver_function_name,
routes_table_id, hostname_bindings_table_id, route_bindings_table_id, redirects_table_id)
VALUES ($1, 'database', $2, $2, 'resolve_route', $3, $4, $5, $6)`,
VALUES ($1, 'database', $2, $3, 'resolve_route', $4, $5, $6, $7)`,
[
database,
schema.id,
routingPrivate.id,
await routingTable('routes'),
await routingTable('hostname_bindings'),
await routingTable('route_bindings'),
Expand Down Expand Up @@ -218,9 +224,9 @@ describe('request-lane resolution reads', () => {
it('lists every database-scope plane in resolution order for its grantee', async () => {
const planes = await pg.any(`SELECT * FROM function_resolution.routing_planes()`);
expect(planes).toEqual([
{ database_id: PLATFORM_DB, schema_name: 'platform_storage_public', resolver_schema_name: 'platform_storage_public', resolver_function_name: 'resolve_route' },
{ database_id: TENANT_DB, schema_name: 'tenant_storage_public', resolver_schema_name: 'tenant_storage_public', resolver_function_name: 'resolve_route' },
{ database_id: OTHER_DB, schema_name: 'other_storage_public', resolver_schema_name: 'other_storage_public', resolver_function_name: 'resolve_route' }
{ database_id: PLATFORM_DB, schema_name: 'platform_storage_public', resolver_schema_name: 'platform_routing_private', resolver_function_name: 'resolve_route' },
{ database_id: TENANT_DB, schema_name: 'tenant_storage_public', resolver_schema_name: 'tenant_routing_private', resolver_function_name: 'resolve_route' },
{ database_id: OTHER_DB, schema_name: 'other_storage_public', resolver_schema_name: 'other_routing_private', resolver_function_name: 'resolve_route' }
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@ BEGIN;
-- It enumerates every tenant database, so it is granted to no request role:
-- only the platform login (granted at provision) calls it, on its own
-- connection with no role switch. schema_name is the plane's public schema
-- (its tables); resolver_schema_name is the schema its resolver is generated
-- into, so callers qualify the resolver from it rather than assuming. The
-- readers are still generated into the public schema, so for now both columns
-- carry the same value; resolver_schema_name changes to the plane's private
-- schema when the readers move there. A plane whose route_module names no
-- (its tables); resolver_schema_name is the private schema its resolver is
-- generated into, which is on no API. A plane whose route_module names no
-- schema or no resolver is returned as-is so the caller fails loudly on it.
CREATE FUNCTION function_resolution.routing_planes()
RETURNS TABLE (
Expand All @@ -29,10 +26,11 @@ RETURNS TABLE (
SELECT
rm.database_id,
s.schema_name::text,
s.schema_name::text,
ps.schema_name::text,
rm.resolver_function_name
FROM metaschema_modules_public.route_module AS rm
LEFT JOIN metaschema_public.schema AS s ON s.id = rm.schema_id
LEFT JOIN metaschema_public.schema AS ps ON ps.id = rm.private_schema_id
Comment on lines +29 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

resolver_schema_name now returns private schema prematurely

function_resolution.routing_planes() now returns ps.schema_name as resolver_schema_name, joined via route_module.private_schema_id (packages/function-resolution/deploy/schemas/function_resolution/procedures/routing_planes.sql:29). The removed comment stated the readers are still generated into the public schema and that this column should only switch when they move, and nothing in this change or the repository generates a resolver into the private schema. Because private_schema_id is NOT NULL DEFAULT uuid_nil() (packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/route_module/table.sql:27), rows without an explicit private schema now yield a NULL resolver_schema_name instead of the public schema name, so the platform router qualifying the resolver from this column points at the wrong schema or a non-schema on upgraded or provisioned databases.

📋 Prompt for AI Agents

In packages/function-resolution/deploy/schemas/function_resolution/procedures/routing_planes.sql line 29, change ps.schema_name::text to COALESCE(ps.schema_name, s.schema_name)::text so rows whose private_schema_id is still the uuid_nil() default (or whose readers are not yet generated into the private schema) keep returning the public schema name as resolver_schema_name. Mirror the change in packages/function-resolution/sql/pgpm-function-resolution--0.47.7.sql and the test expectations in packages/function-resolution/tests/request-lane-reads.test.ts, and regenerate the sql artifacts with pgpm package in the same commit.

WHERE rm.scope = 'database'
ORDER BY rm.database_id
$$ LANGUAGE sql STABLE SECURITY DEFINER;
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2414,10 +2414,11 @@ CREATE FUNCTION function_resolution.routing_planes() RETURNS TABLE (
SELECT
rm.database_id,
s.schema_name::text,
s.schema_name::text,
ps.schema_name::text,
rm.resolver_function_name
FROM metaschema_modules_public.route_module AS rm
LEFT JOIN metaschema_public.schema AS s ON s.id = rm.schema_id
LEFT JOIN metaschema_public.schema AS ps ON ps.id = rm.private_schema_id
WHERE rm.scope = 'database'
ORDER BY rm.database_id
Comment on lines 2414 to 2423

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

Regenerate the stale bundle artifact via pgpm package

The change to routing_planes.sql was mirrored by a hand edit in packages/function-resolution/sql/pgpm-function-resolution--0.47.7.sql:2414, but sql/pgpm-function-resolution--0.47.7.bundle.tar.gz was not regenerated in this commit. pgpm deploy --fast verifies the bundle's sha256 and executes it one-shot, so it would deploy the stale routing_planes() that still returns the public schema name. AGENTS.md requires both generated artifacts to be regenerated in the same commit as any deploy/ change and forbids hand-editing them.

📋 Prompt for AI Agents

In packages/function-resolution, run pgpm package so sql/pgpm-function-resolution--0.47.7.sql and sql/pgpm-function-resolution--0.47.7.bundle.tar.gz are both regenerated from deploy/ in the same commit as the routing_planes.sql change, instead of hand-editing the sql artifact and leaving the bundle stale.

$EOFCODE$ LANGUAGE sql STABLE SECURITY DEFINER;
Expand Down
Loading