Safe casting of PgNode values with bindgen tag discovery#2347
Open
cbandy wants to merge 2 commits into
Open
Conversation
Postgres implements various node types to represent parse, plan, and executor trees. It uses tagged polymorphism to emulate object-oriented inheritance in C: every node struct begins with a `NodeTag` field that identifies its type at runtime. Pointers to these nodes are accessed generically as `Node *` and queried using the `IsA` macro. The hierarchy includes intermediate base types, such as `Expr`, which are inherited by concrete types like `Const` and `Var`. This commit introduces type-safe node casting (both upcasting and downcasting) via the `PgNode` trait. Safe casts are statically generated with the trait implementations emitted by `pgrx-bindgen`. Each node type may be cast to any of its "parent" types, all the way up to `Node`. The `PgNode` trait is extended with: - A constant `CAST_TAGS` slice containing the valid tags for the type and all of its descendants. This is efficient because the hierarchy is shallow and wide. The vast majority of types have four or fewer tags in `CAST_TAGS`. - A `try_cast` associated function and a `try_as` method check `CAST_TAGS` before performing an unsafe pointer cast. The lookup is a simple linear scan and relies on compiler and CPU optimizations. - A `node_tag` method that returns the tag and an `is_a` method that behaves like the `IsA` macro. See: pgcentralfoundation#1048
This refactors the evaluation of the Postgres node type hierarchy in `pgrx-bindgen` to reduce build times slightly. It combines the search for type aliases and node tags into one pass over syntax items, and it combines the DFS search for node structs with the calculation of safe cast tags.
Contributor
Author
|
The first commit adds to I had an agent compare all three commits using Benchmark Results: Postgres Node Hierarchy TraversalBelow is the comparative performance benchmark measuring compilation and binding generation for PGRX_PG_SYS_GENERATE_BINDINGS_FOR_RELEASE=1 cargo build -p pgrx-pg-sys --no-default-features -F pg18
¹ Warm Runs Mean ignores the first run to account for filesystem cache warm-up. Key Takeaways
|
cbandy
commented
Jul 3, 2026
Contributor
Author
There was a problem hiding this comment.
- These files generated quite differently on my system, so I only included the changes to PgNode impl here.
- I also don't have PG 19 set up yet, so that is missing from the PR.
Should this PR include these changes, or perhaps are they regenerated only when it is time to tag?
cbandy
commented
Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Postgres implements various node types to represent parse, plan, and executor trees. It uses tagged polymorphism to emulate object-oriented inheritance in C: every node struct begins with a
NodeTagfield that identifies its type at runtime. Pointers to these nodes are accessed generically asNode *and queried using theIsAmacro. The hierarchy includes intermediate base types, such asExpr, which are inherited by concrete types likeConstandVar.This PR introduces type-safe node casting (both upcasting and downcasting) via the
PgNodetrait. Safe casts are statically generated with the trait implementations emitted bypgrx-bindgen. Each node type may be cast to any of its "parent" types, all the way up toNode.The
PgNodetrait is extended with:CAST_TAGSslice containing the valid tags for the type and all of its descendants. This is efficient because the hierarchy is shallow and wide. The vast majority of types have four or fewer tags inCAST_TAGS.try_castassociated function and atry_asmethod checkCAST_TAGSbefore performing an unsafe pointer cast. The lookup is a simple linear scan and relies on compiler and CPU optimizations.node_tagmethod that returns the tag and anis_amethod that behaves like theIsAmacro.See: #1048