2016-05-18 40 views
1

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

为了达到这个目的,我使用了Apache Calcite的SQL解析器。我将SQL字符串解析为SqlNode,接受SqlVisitor创建重命名为SqlNode,然后将所有内容写回到String(使用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' 

我怎么能知道如果给SqlIdentifier是一列或表?

回答

1

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

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

A 范围是你站在并试图解决标识符的地方。例如,你可能在查询的SELECT子句中。或者在特定的子查询的WHERE子句中。您将能够在不同范围内看到不同范围的表和列的集合。即使是GROUP BY子句和ORDER BY子句也有不同的作用域。

A 命名空间是看起来像一个表,并有一个列的列表。它可能是一个表或FROM子句中的一个子查询。如果你在一个范围内,你可以查找一个表别名,得到一个命名空间,然后看看它有什么列。

为了您的目的,如果有一个SqlShuttle的变体,它确切知道您在哪个范围内,以及在哪里可以要求将标识符扩展为表格和列引用,这将非常有用。不幸的是,还没有人建立这样的事情呢。

+0

SqlScopedShuttle呢? –

+0

SqlScopedShuttle听起来很有用,但它不会像你想要的那么多。当你递归到树中时,它只保留一堆AST节点(SqlNode)。它并不知道SQL范围规则的任何内容。 –

0

我碰巧用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专门优化查询通过应用大量的优化规则。取决于你所需要的(改变列/表名),calcite可能不是最合适的。