我正在实现一个简单的应用程序,它可以在SQL语句中更改列名(而不更改表名)。该语句作为String
传递,修改后的语句也作为String
返回,不涉及数据库连接。
为了实现这一点,我使用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
是列还是表?
3条答案
按热度按时间33qvvth11#
要将标识符解析为表和列,并确定它们的类型,您需要使用Calcite的验证器(
SqlValidator
)。验证器理解SQL名称解析规则(例如FROM子句中的别名是否可以在子查询中看到),而我们故意不使解析器及其产生的SqlNode
数据结构知道这些事情。验证器中的两个关键概念是作用域(
SqlValidatorScope
)和名称空间(SqlValidatorNamespace
)。一个 scope 是你所站的位置,并试图解析一个标识符。例如,您可能在查询的SELECT子句中。或者在特定子查询的WHERE子句中。您将能够在不同的范围中看到不同的表和列集合。甚至GROUP BY子句和ORDER BY子句也有不同的作用域。
一个 namespace 看起来像一个表,有一个列列表。它可能是一个表,也可能是FROM子句中的一个子查询。如果在作用域中,可以查找表别名,获得名称空间,然后查看它有哪些列。
对于您的目的,如果有一个
SqlShuttle
的变体,它确切地知道您在哪个范围内,以及您可以在哪里请求将标识符扩展为表和列引用,这将是非常有用的。不幸的是,还没有人建造这样的东西。0md85ypi2#
我碰巧使用了
calcite
sqlParser
一点。下面贴出的一些片段。通常,您将在
from
语句中获得table
。你会在select
语句中得到columns
。最后但并非最不重要的是,
calcite
专门通过应用大量优化规则来优化查询。根据您的需要(转换列/表名),calcite
可能不是最佳选择。ht4b089n3#
我现在在访问者的旅行中使用布尔值
isInFrom
来告诉我这个节点是否在子句from [xxxx]
中。下面是我的Visitor实现的一个简化示例。
P.S.
isInFrom
的名字并不准确,但我没有更好的名字。