Commit 368c036
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: #10481 parent 60cf79c commit 368c036
3 files changed
Lines changed: 409 additions & 153 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
3 | 7 | | |
4 | 8 | | |
| 9 | + | |
0 commit comments