It seems that your requirement is for an ‘expression parser’ that works in the context of a table, and goes from SQL text via AST and validation to a RexNode.
You can achieve this by generating a query, “select “ + expression + “ from “ + table, and sending it through the parse/validate/SQL-to-rel process, after which you will surely receive a plan that looks like this: Project(rex) TableScan(table) From this plan you can mine ‘rex’ and splice it into other queries. If you do this ahead of time, the RexNode will refer to types in a different type factory (a soluble problem). Julian > On Mar 17, 2025, at 9:35 AM, Mihai Budiu <mbu...@gmail.com> wrote: > > 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