Skip to content

feat(transform): route object identities by cast type - #343

Merged
pyramation merged 2 commits into
mainfrom
feat/identity-cast-routing
Aug 6, 2026
Merged

feat(transform): route object identities by cast type#343
pyramation merged 2 commits into
mainfrom
feat/identity-cast-routing

Conversation

@pyramation

Copy link
Copy Markdown
Collaborator

Summary

'sch.tbl'::regclass is not opaque text — it is a reference the parser resolves, and the cast says which namespace it resolves in. The transform previously treated it as a plain string constant and rewrote it with transformSchemaRefsInString, i.e. "does this text contain <mappedSchema>.". Two consequences: object-level routes and rebinds never applied to it (only the schema-level map did), and a reference with no dot was unreachable — 'apps_private'::regnamespace cannot be matched at all, which is why downstream tooling still keeps helper-name lists (verify_(function|table|...) regexes) purely to rename bare schema names.

This adds a TypeCast visitor keyed on the cast's target type, so those references route through the same SchemaRouter path as a RangeVar:

TypeCast: (path) => {
  const sval = path.node?.arg?.A_Const?.sval;
  const ns = identityCastNamespace(path.node.typeName);  // regclass -> 'relation', regnamespace -> 'schema', ...
  if (!ns || typeof sval?.sval !== 'string') return;
  if (!claimSite(result, sval, 'sval')) return;
  sval.sval = transformIdentityCastLiteral(sval.sval, ns, router, result);
}

regnamespace means the whole operand is a schema name; regclass/regproc/regprocedure/regtype mean the qualifier before the first dot is, with the object name routed in its own namespace (regprocedure's argument list is preserved verbatim). Both scoped and unqualified operands are handled — an unqualified 'users'::regclass names no schema (search_path resolves it), so it is returned unchanged rather than guessed at. Target names are re-emitted through QuoteUtils.quoteIdentifier, so a hyphenated route yields '"my-schema".users'::regclass.

'my-schema'::regnamespace                -> 'my_schema'::regnamespace   (was: unreachable)
'my-schema.users'::regclass              -> 'my_schema.users'::regclass
'my-schema.fn(uuid, text)'::regprocedure -> 'my_schema.fn(uuid, text)'::regprocedure
'users'::regclass                        -> unchanged

Because the key is the cast and not the calling function's name, nothing needs registering when a new helper appears — the failure mode where verify_security was omitted from the regex list cannot recur.

The generic A_Const visitor now claims its site (claimSite(result, node.sval, 'sval')) so a literal reached by both visitors routes exactly once; without that a cyclic mapping (a→b, b→a) swaps back.

New exports: identityCastNamespace, transformIdentityCastLiteral. transformVerifyCalls is left in place — it can be deleted once its consumers emit identity casts.

Link to Devin session: https://app.devin.ai/sessions/37a60838adfb4fd6ac8c29f09c2e2827
Requested by: @pyramation

@pyramation pyramation self-assigned this Aug 6, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 1a804fa into main Aug 6, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant