曾经遇到这么个需求,大致描述一下就是需要改写原始的sql语句,为其中的某些字段加上udf函数。
比如说:
select name from emps where id = 1;
正常执行结果为:
xiaowang
name属于敏感信息,需要进行脱敏处理,并将其配置了脱敏策略,比如脱敏策略是:
hash_func(col,1,5,*),将前五位设置为*号。
那么执行这条sql语句查询结果为*****ang。
如果是在sql的执行计划中,查找的某个列,再去查找策略,再返回到执行计划。
这样会对原先的代码侵入性很多,而且不同的数据存储如果想接入这套脱敏的都需要自己实现一遍。比如Hive,SparkSql等。
正好因为接触过一点calcite,所以来分析一下,看能不能将这个问题简化,在一开始将sql语句改写,再去数据库中执行改写后的语句。这样能够解耦脱敏服务和数据存储组件。
改写为:
select hash_func(name,1,5,*) name from emps where id = 1;
所以问题就变成了如何查找sql语句中的原始列。
我们知道,在sql执行之前,有词法分析和语法分析。
就是先将sql解析成AST,再去和数据库的元信息进行校验。
比如,输入一条错误的sql语句:
select names from emps;
执行calcite中
org.apache.calcite.test.CsvTest#testSelectSingleProjectGz
@Test public void testSelectSingleProjectGz() throws SQLException {
sql("smart", "select names from EMPS").ok();
}
这条测试用例。将name 改为names。会报以下的错:
java.lang.RuntimeException: java.sql.SQLException: Error while executing SQL "select names from EMPS": From line 1, column 8 to line 1, column 12: Column 'NAMES' not found in any table
at org.apache.calcite.test.CsvTest$Fluent.ok(CsvTest.java:1054)
at org.apache.calcite.test.CsvTest.testSelectSingleProjectGz(CsvTest.java:183)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.s