調研系列第五篇:antlr以及hive的parse執行入口


關於antlr的使用

Hive使用的是antlr來做詞法、語法的解析工作,最終生成一棵有語義的ast數。

關於antlr  

1、ANTLRANother Tool for Language Recognition的縮寫又一個語言識別工具,讀[ 'æntlə ]。從名字上可以看出在ANTLR出現之前已經存在其它語言識別工具了(如LEX1GCC ,YACC2 )。Antlr通過自己的語法來定義此法規則和語法規則,然后將這些語法規則生成相應的Java/C++代碼,分別是一個EELexer.java(詞法解析器)和EEParser.java(語法解析器),其中EEEE.g文件的文件名,這兩個文件可以直接拿來使用,具體的demo如下 

2、Antlr的詞法規則

詞法是一些正則表達式的東東,可以根據規則將一篇文章切分成一個個的“單詞”,又叫tokensuch  as 

Identifier : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;

STRING : '\'' (~'\'')* '\'';

INT : '0'..'9'+;

WS :   ( ' ' | '\t' | '\r' | '\n' )+ { Skip(); } ;

其中WS也是一個詞法規則,但是會被過濾掉,可以看作是詞之間的分隔符 

詞法規則使用大寫來表示,一般寫在antlr **.g文件的末尾  

3、Antlr的語法規則

語法規則是將一系列詞法規則組合起來用的,語法規則可以嵌套語法規則,但是有個最頂層的語法規則,最終對於輸入要生成這個語法規則,不然會報錯 

語法樹重寫:

insertStatement : insertClause selectClause fromClause whereClause?

         -> ^(INSERT_STATEMENT insertClause selectClause fromClause whereClause?);

 

這種叫做語法重寫,是根據相應的識別相應的語法規則,然后重寫這個規則的語法樹,主要是為了使得結果的語法樹更清晰 。

^(*)表示需要有根節點、葉子節點的語法樹,如果不加這個標志的,則解析后的每個token都是一個葉子節點。

其中的第一個token是這棵樹的根節點 。

4、一個sql  Demo

對於一個StormQ1.g文件  

grammar StormQl;
options { language=Java; output=AST;}
tokens {
  DDL_STATEMEMT;
  INSERT_STATEMENT;
  SELECT_STATEMENT;
  INSERT_TABLE;
  SELECT_LIST;
  TABLE_LIST;
  WHERE_CONDITION;
  FIELD_NAME;
  COMPARE_ITEM;
  CONSTANT_STR;
} 
dmlStatement  :    insertStatement | selectStatement ;
insertStatement : insertClause selectClause fromClause whereClause? 
         -> ^(INSERT_STATEMENT insertClause selectClause fromClause whereClause?);
selectStatement : selectClause fromClause whereClause? 
         -> ^(SELECT_STATEMENT selectClause fromClause whereClause?);
insertClause : 'INSERT''INTO'tableName ->^(INSERT_TABLE tableName);
selectClause : 'SELECT' ('*' -> ^(SELECT_LIST '*')
           | fieldName (',' fieldName)* -> ^(SELECT_LIST fieldName+)
            );
fromClause : 'FROM' tableSource (',' tableSource)* 
       -> ^(TABLE_LIST tableSource+);
whereClause : 'WHERE' searchCondition -> ^(WHERE_CONDITION searchCondition);
searchCondition    : searchItem ('AND' searchItem)*;
searchItem : expression (
                               (p='=' | p='>' | p='<' | p='<>') expression -> ^(COMPARE_ITEM expression $p expression) | 
                               'IS' (null_c='NOT NULL'|null_c='NULL')-> ^(COMPARE_ITEM expression 'IS' ^(CONSTANT_STR $null_c))
                         );
expression : fieldName | STRING | INT;
tableSource : tableName | '(' selectStatement ')' 'AS' tableName
              -> ^(tableName selectStatement);
fieldName : Identifier ('.' Identifier)* 
       -> ^(FIELD_NAME Identifier ('.' Identifier)*);
tableName : Identifier;
Identifier : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
STRING : '\'' (~'\'')* '\'';
INT : '0'..'9'+;
WS :   ( ' ' | '\t' | '\r' | '\n' )+ { Skip(); } ;

 

可以使用antlrworks-1.4.jar打開 

antlrwork中,可以使用Generate—>Genterate Code 來生成相應的Java代碼,具體生成的文件如下:

StormQ1Lexer.java(詞法分析器,將sql解析成一個個的單詞)、StormQ1Parser.java(語法解析器,將前一個生成的詞法Token List轉換成一棵AST樹)

PS:由於antlrwork的一些bug,生成的Java代碼需要改動一些東西  ,修改一個方法的大小寫,然后就可以直接用這兩個類了 

利用Java調用的demo如下 

         String sql="INSERT INTO desttable SELECT   aa , bb ,cc FROM log_alb_sum WHERE cc>1000  AND  bb=dd AND ss IS NULL AND cc IS   NOT NULL";

        InputStream in =new ByteArrayInputStream(sql.getBytes());

        ANTLRInputStream input = new ANTLRInputStream(in);

        Lexer lexer = new StormQlLexer(input);

        CommonTokenStream tokens = new CommonTokenStream(lexer);

        StormQlParser parser = new StormQlParser(tokens);

        StormQlParser.dmlStatement_return r =parser.dmlStatement();

        Token start=r.start;

        Token end=r.stop;

        BaseTree tree=(BaseTree)r.tree;

 

r是解析后返回的一個值,其中最終要的屬性值r.tree ,是解析后的語法樹的根節點,其結構如下:

主要是children子樹  

Token是表示每個詞,其中主要屬性是其文本,開始、結束位置以及type,主要是在語法報錯時候會用到,type的值在生成的StormQl.tokens會有定義,表示某個token對應的值是多少,主要可以用在后面語法解析的時候,用這個值找到自己定義的token(主要是重寫為子樹的根節點),然后實現自己的語法解析,一般這種token是用戶自己的語法關鍵點,語義分析也都是遍歷這些關鍵token點(可以參考hive的語法中的token

 

 

Hive中的antlr以及詞法語法解析

1、HIVEantlr語法文件(位於ql\src\java\org\apache\hadoop\hive\ql\parse目錄下)

IdentifiersParser.g

HiveLexer.g

FromClauseParser.g

SelectClauseParser.g

HiveParser.g

幾個文件位於,里面是antlr的語法,有興趣可以看下。

      

2、Hive中整詞法語法解析(未用antlr的)的步驟

整個sql語句編譯、執行的步驟,執行的入口方法是在Driver.run(String command)方法中,執行的參數也就是一個sql字符串 ,主要的方法是:

int ret = compile(command);   //編譯,主要是將sql字符串翻譯成ast樹,然后翻譯成可執行的task ,然后再優化執行樹

ret = execute();               //執行所有的task  

a)   Hive中調用antlr類的代碼org.apache.hadoop.hive.ql.parse.ParseDriver     返回的HiveParser.statement_return和上面一樣,是棵ast的語法樹 ,具體語法樹的接口可以參見相應的HiveParse.g文件 

 

b)   得到語法樹之后,會根據語法樹根節點的類型來選擇相應的SemanticAnalyzer

主要是根據根節點的語法樹類型來選擇相應的analyzer,具體的選擇analyzer代碼如下:

 

對於DDL操所,得到的就是DDLSemanticAnalyzer ,對於一般的insert(hive中存select語句會被翻譯成一個insert tmpDirectory的語句)得到的就是SemanticAnalyzer 

c)   然后調用SemanticAnalyzer.analyze(tree,ctx)來將語法樹翻譯成可執行的執行計划

 

可執行的計划存儲在  protected List<Task<? extends Serializable>> rootTasks 屬性中, TaskexecuteTask()方法是可以直接執行的,最終實際的執行也是調用每個taskexecuteTask方法,依賴以及調度是在上層控制的,Task的繼承關系如下:

 

Task是一個樹形結構,每個task有一堆child task ,這些child是在執行順序上依賴於自己的task rootTasks中存儲的就是整個執行計划中需要最開始執行的task list ,一棵倒着的執行依賴樹” 

d)   執行task:Driver.execute()為入口

 

 

將可執行的task放入runnable中,初始為root task list  runnable表示正在運行runningtask 

具體的執行流程如下:

Ø  不斷去遍歷runnable,選出一個執行launchTask(tsk, queryId, noName, running, jobname, jobs, driverCxt) ,在這個方法中,啟動task,其實就是調用taskexecuteTask() 方法 

 

 

這個里面hive是支持並發執行task的,若是需要並發的話每個task被封裝成一個Thread的子類,然后自行啟動。

Ø  找出執行完成的task,然后遍歷該task的子task,選出可執行(pre task已經執行完)task 放入runnable ,然后重復上一個步驟 

 

 

對於一些有多個pre task child  task,會在最后一個pre task執行完后被啟動,所以在這會被在child中過掉。

Ø  待補充

 

e)   待補充

3、關於DDLSemanticAnalyzer:解析ddl語句並生成相應執行DDLTask來執行

 

 

根據相應的ast樹類型,生成一個執行該ddl需要信息的對象DDLWork DDLWork是一個union的數據結構,里面有各種操作信息的引用,但是只有一個有用的 。隨后DDLWork封裝到一個DDLTask中,DDLTask執行具體execute方法的時候,在根據DDLWork中得到的值判斷具體該執行那一種操作(那種操作需要的信息的引用!=null,則執行哪種引用),具體的執行其實是調用Hive對象相應的方法 

 

 

4、SemanticAnalyzer:dmlast樹翻譯成具體的執行計划的analyzer 

 

5、其它幾個LoadSemanticAnalyzerExportSemanticAnalyzerImportSemanticAnalyzer簡單,具體可以關注下相關的代碼。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM