apache 方解石区分列名和 table 名称

apache calcite distinguish column names from table name

我正在实现一个简单的应用程序,它在 SQL 语句中更改列名称(并单独保留 table 名称)。该语句作为 String 传递,修改后的语句也作为 String 返回,不涉及数据库连接。

为此,我使用了 Apache Calcite 的 SQL 解析器。我将 SQL 字符串解析为 SqlNode,接受创建重命名 SqlNodeSqlVisitor,然后将所有内容写回 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?

刚好用过calcitesqlParser一点。下面发布的一些片段。

  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 和列的位置参考。不幸的是,还没有人建造过这样的东西。