我正在实现一个简单的应用程序,它可以在SQL语句中更改列名(并且只保留表名)。该语句作为String
传递,修改后的语句也作为String
返回,不涉及数据库连接。
为实现这一目标,我正在使用Apache Calcite的SQL解析器。我将SQL字符串解析为SqlNode
,接受创建重命名的SqlVisitor
的{{1}},然后将所有内容写回SqlNode
(使用String
)。< / p>
问题是我不知道在接受SqlNode.toSqlString()
时如何区分解析后的SqlNode
对象中的列和表。两者都表示为SqlVisitor
,具有相同的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'
是列还是表格?
答案 0 :(得分:1)
要解析表和列的标识符并确定其类型,您需要使用Calcite的验证器(SqlValidator
)。验证器理解SQL名称解析规则(例如,是否可以在子查询中看到FROM子句中的别名),而我们故意不使解析器和它生成的SqlNode
数据结构知道这些事情
验证器中的两个关键概念是范围(SqlValidatorScope
)和命名空间(SqlValidatorNamespace
)。
范围是您站立并尝试解析标识符的位置。例如,您可能位于查询的SELECT子句中。或者在特定子查询的WHERE子句中。您将能够在不同的范围内看到不同的表和列集合。甚至GROUP BY子句和ORDER BY子句也有不同的范围。
名称空间看起来像一个表,并且有一个列列表。它可能是一个表,或者说是FROM子句中的子查询。如果您在作用域中,则可以查找表别名,获取命名空间,然后查看它具有的列。
出于您的目的,如果有SqlShuttle
的变体确切地知道您所在的范围,以及您可以将标识符扩展为表格和列引用的位置,那将非常有用。不幸的是,还没有人建造这样的东西。
答案 1 :(得分:0)
我碰巧使用了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";
}
通常,您会在table
声明中获得from
。您将在columns
声明中获得select
。
最后但并非最不重要的是,calcite
专门通过应用大量优化规则来优化查询。根据您的需要(转换列/表名称),calcite
可能不是最合适的。