大家久违了。最近我的生活发生了很多变化。与此同时,我病得很重。我希望一切都会很快好起来。今天就给大家分享一下Spark,聊一聊如何修改SparkSQL分析,让它更符合你的业务逻辑。好了,开始吧……理论基础ANTLRAntlr4是一个开源的解析器生成工具,可以根据语法规则文件生成对应的解析器。现在很多流行的应用和开源项目都在使用,比如Hadoop、Hive和Spark等都在使用ANTLR进行语法分析。ANTLR语法识别一般分为两个阶段:1.词法分析阶段对应的分析程序称为词法分析器,负责将token分组为token类或token类型。2.解析阶段是根据词法,构造一个解析树(parsetree)或称为语法树(syntaxtree)ANTLR的语法文件,这很像电路图。从入口到出口,每一个Token就像一个电阻,连接线就是一个短路点。上述语法文件(*.g4)截图对应的语法文件片段定义了两部分语法,一部分是显示表达式和赋值,另一部分是运算和表达式定义。stat:exprNEWLINE#printExpr|ID'='exprNEWLINE#assign|NEWLINE#blank;expr:exprop=('*'|'/')expr#MulDiv|exprop=('+'|'-')expr#AddSub|INT#int|ID#id|'('expr')'#parens;接下来,添加定义词法部分,形成一个完整的语法文件。完整语法文件:grammarLabeledExpr;//renametodistinguishfromExpr.g4prog:stat+;stat:exprNEWLINE#printExpr|ID'='exprNEWLINE#assign|NEWLINE#blank;expr:exprop=('*'|'/')expr#MulDiv|exprop=('+'|'-')expr#AddSub|INT#int|ID#id|'('expr')'#parens;MUL:'*';//assignstokennameto'*'usedaboveingrammarDIV:'/';ADD:'+';SUB:'-';ID:[a-zA-Z]+;//matchidentifiersINT:[0-9]+;//matchintegersNEWLINE:'\r'?'\n';//returnnewlinestopparser(isend-statementsignal)WS:[\t]+->skip;//抛出whitespaceSqlBase.g4Spark语法文件,在sql下的catalyst模块中,如下图:Extendedsyntax定义了一个普通的SQL,比如Selectt.id,t.namefromt,现在我们给它加上一个JACKY表达式,让它出现在Select之后,形成一条语句Selectt.id,t.nameJACKY(2)fromt先来看看正常的语法规则:现在我们添加一个jackyExpressionjackExpression本身的规则是JACKY加上一个用括号括起来的数字。添加JACKY作为标记并修改语法文件如下:jackyExpression:JACKY'('number')'//expression;namedExpression:expression(AS?(identifier|identifierList))?;namedExpressionSeq:namedExpression(','namedExpression|jackyExpression)*;扩展逻辑方案修改后,可以测试一下语法规则,是否符合预期,下面是解析树,可以看jackyExpression已经能够正常解析Spark执行过程。这里引用一张经典的SparkSQL架构图。我们输入的SQL语句首先被解析成UnresolvedLogicalPan,对应逻辑计划中增加了一个遍历方法:.getText)this.jacky=ctx.number().getText.toIntctx.number().getText}在处理namedExpression时,添加jackyExpression处理//Expressions.valexpressions=Option(namedExpressionSeq).toSeq.flatMap(_.namedExpression.asScala).map(typedVisit[Expression])//jackyExpression处理if(namedExpressionSeq().jackyExpression()!=null&&namedExpressionSeq().jackyExpression().size()>0){visitJackyExpression(namedExpressionSeq().jackyExpression().get(0))}好了,到这里我们就完成了逻辑计划的处理。有了逻辑计划,我们就可以在后续的物理计划中加上相应的处理逻辑就可以了(我还没研究过。。。orz)。测试用例publicclassCase4{publicstaticvoidmain(String[]args){CharStreamca=CharStreams.fromString("SELECT`b`.`id`,`b`.`class`JACKY(2)FROM`b`LIMIT10");SqlBaseLexerlexer=newSqlBaseLexer(ca);SqlBaseParsersqlBaseParser=newSqlBaseParser(newCommonTokenStream(lexer));ParseTreeparseTree=sqlBaseParser.singleStatement();AstBuilderastBuilder=newAstBuilder();astBuilder.visit(parseTree);System.out.println(parseTree.toStringTree(sqlBaseParser));System.out.println(astBuilder.jacky());}}执行结果本文转载请联系七思妙想公众号。
