I don't think the planner is the right place; in general planner transformations preserve semantics, whereas your transformation won't necessarily. Planner rules are also designed for optimization, and here there is nothing to optimize.
The visitor-based APIs are probably the main tool you need. https://www.feldera.com/blog/calcite-irs#9099c1483ca6 Mihai ________________________________ From: Kristian Lein-Mathisen <kristianl...@gmail.com> Sent: Monday, March 17, 2025 7:57 AM To: dev@calcite.apache.org <dev@calcite.apache.org> Subject: Parsing expression inside RelShuttle Hi! I'm new to Calcite and I'd like to use it to transform incoming SQL queries to conform to some generic policy rules, like wrapping all table-scans with a row-level filter. I hope this is the right place to ask questions like this. Here's an example of what I'm trying to acheive: -- admin says: ALTER TABLE orders SET TBLPROPERTIES (policy.filter = 'country IN user_countries') SELECT * FROM orders -- <-- from user SELECT * FROM (SELECT * FROM orders WHERE country IN user_countries()) -- <-- to backend This is fairly simple to acheive when you have a static filter expression. Here's what I've got working: public static RelNode policyFilter(RelNode root, RelBuilder builder) { return root.accept(new RelShuttleImpl() { @Override public RelNode visit(TableScan scan) { RelNode n = super.visit(scan); RexNode x = builder.in(builder.field("country"), builder.literal("se"), builder.literal("nl"), builder.literal("dk")); return builder .push(n) .filter(x) .build(); } }); } This works, provided all my tables have a "country" column. What I'd like to do, however, is allow user-defined filter expressions: make "RexNode x" from a per-table String. I'd like this to be evaluated in the context of the current table (so you can reference column names) and session (so current_user(), for example, is available). I've looked at the Planner, but it doesn't seem it's exposing enough of its internals (I don't have access to its validator.validateExpression, for example). Note that I'm new to Calcite, so I could be going around this completely wrong. Looking at other uses of Calcite, I'm getting the impression that I should be extending RelNodes and adding some Rules. I've also assumed that I should be doing this with RelNodes instead of SqlNodes. For the record, Kyuubi does something very similar using Spark here: https://github.com/apache/kyuubi/blob/master/extensions/spark/kyuubi-spark-authz/src/main/scala/org/apache/kyuubi/plugin/spark/authz/rule/rowfilter/RuleApplyRowFilter.scala#L42 Thanks for your feedback, - Kris