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

Reply via email to