Skip to content

Commit 368c036

Browse files
authored
Safe casting of PgNode values with bindgen tag discovery (#2347)
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 PR 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. Postgres 13 and 14 use the `Value` struct to represent multiple possible value types, including integer, float, and string. Each type has its own `NodeTag`, but they can't be automatically associated with the struct using the information in the bindgen bindings. This hard-codes the associations just before emitting all the `PgNode` implementations. Postgres 15 introduced separate structs for each value type. These fit nicely into the automatic struct-to-tag scheme, but we weren't handling the `ValUnion` union Postgres uses to represent these multiple types in a single `Node`. This extends the detection of parent-child types to implement `PgNode` for union nodes. See: #1048
1 parent 60cf79c commit 368c036

3 files changed

Lines changed: 409 additions & 153 deletions

File tree

.gitattributes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# https://git-scm.com/docs/gitattributes
22

3+
# https://docs.gitlab.com/user/project/merge_requests/changes#collapse-generated-files
4+
# https://github.com/github-linguist/linguist/blob/-/docs/overrides.md#generated-code
5+
[attr]generated gitlab-generated linguist-generated
6+
37
# Preserve LF line endings in golden files during checkout.
48
/cargo-pgrx/tests/fixtures/**/*.toml text eol=lf
9+
/pgrx-pg-sys/src/include/*.rs generated

0 commit comments

Comments
 (0)