2016-05-18 43 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可能不是最合適的。