Pio*_*ana 5 java sql parsing apache-calcite
我正在实现一个简单的应用程序,它可以更改 SQL 语句中的列名称(并保留表名称)。该语句作为 a 传递String,修改后的语句也作为 a 返回String,不涉及数据库连接。
为了实现这一点,我使用 Apache Calcite 的 SQL 解析器。我将 SQL 字符串解析为SqlNode,接受SqlVisitor创建重命名的SqlNode,然后将所有内容写回String(使用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();
}
Run Code Online (Sandbox Code Playgroud)
例如
SELECT name, address, age FROM mytablename WHERE age = 23 AND name = 'John'
Run Code Online (Sandbox Code Playgroud)
将被修改为
SELECT `name-test`, `address-test`, `age-test` FROM `mytablename-test` WHERE `age-test` = 23 AND `name-test` = 'John'
Run Code Online (Sandbox Code Playgroud)
我如何判断给定的SqlIdentifier是列还是表?
小智 3
要解析表和列的标识符并确定它们的类型,您将需要使用 Calcite 的验证器 ( SqlValidator)。验证器理解 SQL 名称解析规则(例如 FROM 子句中的别名是否可以在子查询中看到),而我们故意不让解析器及其SqlNode生成的数据结构了解这些事情。
验证器中的两个关键概念是范围(SqlValidatorScope)和命名空间(SqlValidatorNamespace)。
范围是您所处的位置并尝试解析标识符。例如,您可能位于查询的 SELECT 子句中。或者在特定子查询的 WHERE 子句中。您将能够在不同范围内看到不同的表和列集合。即使 GROUP BY 子句和 ORDER BY 子句也有不同的范围。
命名空间看起来像一个表,并且有一个列列表。它可能是一个表,或者 FROM 子句中的子查询。如果您在某个范围内,则可以查找表别名,获取名称空间,然后查看它有哪些列。
出于您的目的,如果有一个变体SqlShuttle能够准确地知道您所处的范围,以及您可以在哪里要求将标识符扩展到表和列引用,那么这将很有用。不幸的是还没有人建造出这样的东西。