apache 方解石区分列名和 table 名称
apache calcite distinguish column names from table name
我正在实现一个简单的应用程序,它在 SQL 语句中更改列名称(并单独保留 table 名称)。该语句作为 String
传递,修改后的语句也作为 String
返回,不涉及数据库连接。
为此,我使用了 Apache Calcite 的 SQL 解析器。我将 SQL 字符串解析为 SqlNode
,接受创建重命名 SqlNode
的 SqlVisitor
,然后将所有内容写回 String
(使用 SqlNode.toSqlString()
).
问题是在接受 SqlVisitor
时,我不知道如何区分已解析的 SqlNode
对象中的列和 table 之间的区别。两者都表示为SqlIdentifier
,具有相同的SqlKind
。因此,当 SqlVisitor
访问 SqlIdentifier
时,它会重命名它,无论它是列还是 table。
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
是列还是 table?
刚好用过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
语句中得到table
。你会在 select
语句中得到 columns
。
最后但同样重要的是,calcite
擅长通过应用大量优化规则来优化查询。根据您的需要(转换 column/table 名称),calcite
可能不是最合适的。
要将标识符解析为 table 和列,并确定它们的类型,您需要使用 Calcite 的验证器 (SqlValidator
)。验证器理解 SQL 名称解析规则(例如 FROM 子句中的别名是否可以在子查询中看到),而我们有意没有制作解析器,以及它产生的 SqlNode
数据结构,知道这些事情。
验证器中的两个关键概念是作用域 (SqlValidatorScope
) 和命名空间 (SqlValidatorNamespace
)。
A scope 是您所处的位置并试图解析标识符。例如,您可能在查询的 SELECT 子句中。或者在特定子查询的 WHERE 子句中。您将能够看到不同范围内的 table 和列的不同集合。即使是 GROUP BY 子句和 ORDER BY 子句也有不同的范围。
A namespace 看起来像 table,并且有一个列列表。它可能是 table 或 FROM 子句中的子查询。如果你在一个范围内,你可以查找一个 table 别名,获取一个命名空间,然后查看它有哪些列。
为了您的目的,如果 SqlShuttle
有一个变体可以准确知道您在哪个范围内,以及您可以要求将标识符扩展到 table 和列的位置参考。不幸的是,还没有人建造过这样的东西。
我正在实现一个简单的应用程序,它在 SQL 语句中更改列名称(并单独保留 table 名称)。该语句作为 String
传递,修改后的语句也作为 String
返回,不涉及数据库连接。
为此,我使用了 Apache Calcite 的 SQL 解析器。我将 SQL 字符串解析为 SqlNode
,接受创建重命名 SqlNode
的 SqlVisitor
,然后将所有内容写回 String
(使用 SqlNode.toSqlString()
).
问题是在接受 SqlVisitor
时,我不知道如何区分已解析的 SqlNode
对象中的列和 table 之间的区别。两者都表示为SqlIdentifier
,具有相同的SqlKind
。因此,当 SqlVisitor
访问 SqlIdentifier
时,它会重命名它,无论它是列还是 table。
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
是列还是 table?
刚好用过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
语句中得到table
。你会在 select
语句中得到 columns
。
最后但同样重要的是,calcite
擅长通过应用大量优化规则来优化查询。根据您的需要(转换 column/table 名称),calcite
可能不是最合适的。
要将标识符解析为 table 和列,并确定它们的类型,您需要使用 Calcite 的验证器 (SqlValidator
)。验证器理解 SQL 名称解析规则(例如 FROM 子句中的别名是否可以在子查询中看到),而我们有意没有制作解析器,以及它产生的 SqlNode
数据结构,知道这些事情。
验证器中的两个关键概念是作用域 (SqlValidatorScope
) 和命名空间 (SqlValidatorNamespace
)。
A scope 是您所处的位置并试图解析标识符。例如,您可能在查询的 SELECT 子句中。或者在特定子查询的 WHERE 子句中。您将能够看到不同范围内的 table 和列的不同集合。即使是 GROUP BY 子句和 ORDER BY 子句也有不同的范围。
A namespace 看起来像 table,并且有一个列列表。它可能是 table 或 FROM 子句中的子查询。如果你在一个范围内,你可以查找一个 table 别名,获取一个命名空间,然后查看它有哪些列。
为了您的目的,如果 SqlShuttle
有一个变体可以准确知道您在哪个范围内,以及您可以要求将标识符扩展到 table 和列的位置参考。不幸的是,还没有人建造过这样的东西。