我正在实现一个简单的应用程序,它可以更改 SQL 语句中的列名称(并保留表名称)。该语句作为 String
传递,修改后的语句也作为 String
返回,不涉及数据库连接。
为了实现这一点,我使用 Apache Calcite 的 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 的 validator (SqlValidator
)。 validator 理解 SQL 名称解析规则(例如,FROM 子句中的别名是否可以在子查询中看到),而我们故意不让解析器及其生成的 SqlNode
数据结构了解诸如此类的事情。
validator 中的两个关键概念是范围 (SqlValidatorScope
) 和命名空间 (SqlValidatorNamespace
)。
范围是您所处的位置并尝试解析标识符。例如,您可能位于查询的 SELECT 子句中。或者在特定子查询的 WHERE 子句中。您将能够在不同范围内看到不同的表和列集合。即使 GROUP BY 子句和 ORDER BY 子句也有不同的范围。
命名空间看起来像一个表,并且有一个列列表。它可能是一个表,或者 FROM 子句中的子查询。如果您在某个范围内,则可以查找表别名,获取命名空间,然后查看它有哪些列。
出于您的目的,如果有一个 SqlShuttle
的变体能够准确地知道您所在的范围,以及您可以在哪里要求将标识符扩展到表和列引用,那么将会很有用。不幸的是还没有人 build 出这样的东西。
关于java - apache calcite 区分列名和表名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37288184/