Java apache方解石可区分列名和表名
我正在实现一个简单的应用程序,它可以更改SQL语句中的列名(并且不使用表名)。语句作为Java apache方解石可区分列名和表名,java,sql,parsing,apache-calcite,Java,Sql,Parsing,Apache Calcite,我正在实现一个简单的应用程序,它可以更改SQL语句中的列名(并且不使用表名)。语句作为字符串传递,修改后的语句也作为字符串返回,不涉及数据库连接 为了实现这一点,我使用ApacheCalcite的SQL解析器。我将SQL字符串解析为SqlNode,接受创建重命名的SqlNode的SqlVisitor,然后将所有内容写回string(使用SqlNode.toSqlString()) 问题是,在接受SqlVisitor时,我不知道如何区分解析的SqlNode对象中的列和表之间的差异。两者都表示为Sq
字符串
传递,修改后的语句也作为字符串
返回,不涉及数据库连接
为了实现这一点,我使用ApacheCalcite的SQL解析器。我将SQL字符串解析为SqlNode
,接受创建重命名的SqlNode
的SqlVisitor
,然后将所有内容写回string
(使用SqlNode.toSqlString()
)
问题是,在接受SqlVisitor
时,我不知道如何区分解析的SqlNode
对象中的列和表之间的差异。两者都表示为SqlIdentifier
,具有相同的SqlKind
。因此,当SqlVisitor
访问SqlIdentifier
时,它将重命名它,无论它是列还是表
private String changeNames(String str) throws SqlParseException {
SqlShuttle visitor = new SqlShuttle() {
private String rename(String str) {
return str + "-test";
}
@Override
public SqlNode visit(SqlIdentifier identifier) {
SqlIdentifier output = new SqlIdentifier(rename(identifier.getSimple()), identifier.getCollation(), identifier.getParserPosition());
return output;
}
};
SqlParser.ConfigBuilder configBuilder = SqlParser.configBuilder();
configBuilder.setLex(Lex.MYSQL);
SqlParser.Config config = configBuilder.build();
SqlParser parser = SqlParser.create(str, config);
SqlNode parsedStatement = parser.parseQuery(str);
SqlNode outputNode = parsedStatement.accept(visitor);
return outputNode.toSqlString(SqlDialect.DUMMY).getSql();
}
比如说
SELECT name, address, age FROM mytablename WHERE age = 23 AND name = 'John'
将修改为
SELECT `name-test`, `address-test`, `age-test` FROM `mytablename-test` WHERE `age-test` = 23 AND `name-test` = 'John'
如何判断给定的
SqlIdentifier
是列还是表?我碰巧使用了calcite
sqlParser
。下面发布的一些片段
public void convertSelect(SqlSelect root) {
convertFrom(root.getFrom());
convertWhere(root.getWhere());
}
public void convertFrom(SqlNode from) {
if (from instanceof SqlJoin) {
convertFromOfJoinExpression((SqlJoin)from);
}
}
public String extractTableFromJoinNode(SqlNode jnn) {
if (jnn instanceof SqlBasicCall) {
SqlBasicCall asExp = (SqlBasicCall)jnn;
if (asExp.getKind().equals(SqlKind.AS)) {
extractTableFromJoinNodeAsExpression(asExp);
}
}
return "SomeTableAlias";
}
通常,您将在from
语句中获得表。您将在select
语句中获得列
最后但并非最不重要的是,calcite
专门通过应用大量优化规则来优化查询。根据需要(转换列/表名),calcite
可能不是最佳选择 要将标识符解析为表和列,并找出它们的类型,您需要使用Calcite的验证器(SqlValidator
)。验证器理解SQL名称解析规则(例如,在子查询中是否可以看到FROM子句中的别名),而我们故意不让解析器及其生成的SqlNode
数据结构知道这些事情
验证器中的两个关键概念是作用域(SqlValidatorScope
)和名称空间(SqlValidatorNamespace
)
作用域是您所处位置并尝试解析标识符的位置。例如,您可能在查询的SELECT子句中。或者在特定子查询的WHERE子句中。您将能够在不同的范围内看到不同的表和列集合。即使是GROUPBY子句和ORDERBY子句也有不同的作用域
名称空间看起来像一个表,并且有一个列列表。它可能是一个表,也可能是FROM子句中的一个子查询。如果您在作用域中,可以查找表别名,获取名称空间,然后查看它有哪些列
出于您的目的,如果有一个SqlShuttle
的变体,它确切地知道您在哪个范围内,并且您可以在哪里要求将标识符扩展到表和列引用中,这将非常有用。不幸的是,还没有人构建这样的东西。SQLScopedShattle呢?SQLScopedShattle听起来很有用,但它的作用并没有你想要的那么多。当您递归到树中时,它只保留一堆AST节点(SqlNode)。它对SQL作用域规则一无所知。@PiotrŚmietana如果你最终得到了一些工作代码,你能分享一下吗?@Arvidaa我想我使用了一些断断续续的解决方法。我不记得它到底是什么,也没有访问此代码的权限,所以我无法提供任何详细信息。