apache方解块区分表名

时间:2016-05-18 00:12:30

标签: java sql parsing apache-calcite

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

为实现这一目标,我正在使用Apache Calcite的SQL解析器。我将SQL字符串解析为SqlNode,接受创建重命名的SqlVisitor的{​​{1}},然后将所有内容写回SqlNode(使用String)。< / p>

问题是我不知道在接受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();
}

将被修改为

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' 是列还是表格?

2 个答案:

答案 0 :(得分:1)

要解析表和列的标识符并确定其类型,您需要使用Calcite的验证器(SqlValidator)。验证器理解SQL名称解析规则(例如,是否可以在子查询中看到FROM子句中的别名),而我们故意不使解析器和它生成的SqlNode数据结构知道这些事情

验证器中的两个关键概念是范围(SqlValidatorScope)和命名空间(SqlValidatorNamespace)。

范围是您站立并尝试解析标识符的位置。例如,您可能位于查询的SELECT子句中。或者在特定子查询的WHERE子句中。您将能够在不同的范围内看到不同的表和列集合。甚至GROUP BY子句和ORDER BY子句也有不同的范围。

名称空间看起来像一个表,并且有一个列列表。它可能是一个表,或者说是FROM子句中的子查询。如果您在作用域中,则可以查找表别名,获取命名空间,然后查看它具有的列。

出于您的目的,如果有SqlShuttle的变体确切地知道您所在的范围,以及您可以将标识符扩展为表格和列引用的位置,那将非常有用。不幸的是,还没有人建造这样的东西。

答案 1 :(得分:0)

我碰巧使用了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";
  }

通常,您会在table声明中获得from。您将在columns声明中获得select

最后但并非最不重要的是,calcite专门通过应用大量优化规则来优化查询。根据您的需要(转换列/表名称),calcite可能不是最合适的。