成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

基于ANTLR4的大數(shù)據(jù)SQL編輯器解析引擎實(shí)踐

數(shù)據(jù)庫 其他數(shù)據(jù)庫
通過SQL引擎能力建設(shè)我們?cè)贕alaxy數(shù)據(jù)研發(fā)IDE上支持了個(gè)性化詞法規(guī)則定制能力,包含字段別名支持中文, 表變量等場(chǎng)景, 同時(shí)通過語法解析和監(jiān)聽器能力,支持實(shí)時(shí)識(shí)別各類的語法規(guī)則,包含表,函數(shù),字段等做輔助編程提示和做精準(zhǔn)化的庫,表,字段代碼補(bǔ)全和推薦。

一、背景

二、ANTLR4 簡(jiǎn)介

    1. ANTLR4 特性

    2. ANTLR4 的應(yīng)用場(chǎng)景

    3. ANTLR4入門

三、SparkSQL介紹

四、技術(shù)實(shí)現(xiàn)

    1. 語法設(shè)計(jì)

    2. 語法補(bǔ)全

    3. 語法校驗(yàn)

    4. 性能

    5.編輯器應(yīng)用

五、大模型下的SQL編輯器應(yīng)用

    1. NL2SQL應(yīng)用場(chǎng)景

    2. NL2SQL自動(dòng)補(bǔ)全

六、總結(jié)

一、背景

隨著得物離線業(yè)務(wù)的快速增長,為了脫離全托管服務(wù)的一些限制和享受技術(shù)發(fā)展帶來的成本優(yōu)化,公司提出了大數(shù)據(jù)Galaxy開源演進(jìn)項(xiàng)目,將離線業(yè)務(wù)從全托管且封閉的環(huán)境遷移到一個(gè)開源且自主可控的生態(tài)系統(tǒng)中,而離線開發(fā)治理套件是Galaxy自研體系中一個(gè)核心的項(xiàng)目,在數(shù)據(jù)開發(fā)IDE中最核心的就是SQL編輯器,我們需要一個(gè)SQL解析引擎在SQL編輯提供適配得物自研Spark引擎的語法定義,實(shí)時(shí)語法解析,語法補(bǔ)全,語法校驗(yàn)等能力,結(jié)合業(yè)內(nèi)dataworks和dataphin的實(shí)踐,我們最終選用ANTLR作為SQL解析引擎底座。

二、ANTLR4 簡(jiǎn)介

ANTLR(一種語法解析引擎工具)是一個(gè)功能強(qiáng)大的解析器生成器,用于讀取、處理、執(zhí)行或翻譯結(jié)構(gòu)化文本或二進(jìn)制文件。它廣泛用于構(gòu)建語言、工具和框架。ANTLR可以根據(jù)語法規(guī)則文件生成一個(gè)可以構(gòu)建和遍歷解析樹的解析器。

ANTLR4 特性

ANTLR4 是一個(gè)強(qiáng)大的工具,適合用于語言處理、編譯器構(gòu)建、代碼分析等多種場(chǎng)景。它的易用性、靈活性和強(qiáng)大的特性使得它成為開發(fā)者的熱門選擇。

  1. 強(qiáng)大的文法定義:ANTLR4 允許用戶使用簡(jiǎn)單且易讀的文法語法來定義語言的結(jié)構(gòu)。這使得創(chuàng)建和維護(hù)語言解析器變得更加直觀,同時(shí)在復(fù)雜文法構(gòu)造上支持左遞歸文法、嵌套結(jié)構(gòu)以及其他復(fù)雜的文法構(gòu)造,使得能夠解析更復(fù)雜的語言結(jié)構(gòu)。
  2. 抽象語法樹遍歷:ANTLR4 可以生成抽象語法樹,使得在解析源代碼時(shí)能夠更容易地進(jìn)行分析和變換。AST 是編譯器和解釋器的核心組件。同時(shí)提供了簡(jiǎn)單的 API 來遍歷生成的語法樹,使得實(shí)現(xiàn)代碼分析、轉(zhuǎn)換等操作變得簡(jiǎn)單
  3. 自動(dòng)語法錯(cuò)誤處理:ANTLR4 提供了內(nèi)置的錯(cuò)誤處理機(jī)制,可以在解析過程中自動(dòng)處理語法錯(cuò)誤,并且可以自定義錯(cuò)誤消息和處理邏輯
  4. 可擴(kuò)展性:ANTLR4 允許用戶擴(kuò)展和自定義生成的解析器的行為。例如,您可以自定義解析器的方法、錯(cuò)誤處理以及其他功能。
  5. 工具&社區(qū)生態(tài):ANTLR4 提供了豐富的工具支持,包括命令行工具、集成開發(fā)環(huán)境插件和可視化工具,可以幫助您更輕松地開發(fā)和調(diào)試解析器。同時(shí)擁有活躍的社區(qū),提供了大量的文檔、示例和支持。這使得新用戶能夠快速上手,并得到必要的幫助。

 ANTLR4 的應(yīng)用場(chǎng)景

Apache Spark:  流行的大數(shù)據(jù)處理框架,使用ANTLR作為其SQL解析器的一部分,支持SQL查詢。

Twitter: Twitter 使用ANTLR來解析和分析用戶的查詢語言,這有助于他們的搜索和分析功能。

IBM: IBM使用ANTLR來支持一些其產(chǎn)品和工具中的DSL(領(lǐng)域特定語言)解析需求,例如,在其企業(yè)集成解決方案中。

ANTLR4入門

ANTLR元語言

為了實(shí)現(xiàn)一門計(jì)算機(jī)編程語言,我們需要構(gòu)建一個(gè)程序來讀取輸入語句,對(duì)其中的詞組和符號(hào)進(jìn)行識(shí)別處理,即我們需要語法解釋器或者翻譯器來識(shí)別出一門特定語言的所有詞組,子詞組,語句。我們將語法分析過程拆分為兩個(gè)獨(dú)立的階段則為詞法分析和語法分析。

圖片圖片

ANTLR語法遵循了一種專門用來描述其他語言的語法,我們稱之為ANTLR元語言(ANTLR’s meta-language)。ANTLR元語句是一個(gè)強(qiáng)大的工具,可以用來定義編程語言的語法。通過定義詞法和語法規(guī)則,可以基于antlr生成解析器和詞法分析器。

1.自頂向下

在語言結(jié)構(gòu)中,整體的辨識(shí)都是從最粗的粒度開始,一直進(jìn)行到最詳細(xì)的層次,并把它們編寫成為語法規(guī)則,ANTLR4就是采用自頂向下的,詞法語法分離,上下文無關(guān)的語法框架來描述語言。

// MyGLexer.g4
lexer grammar MyGLexer;


SEMICOLON: ';';
LEFT_PAREN: '(';
RIGHT_PAREN: ')';
COMMA: ',';
DOT: '.';
LEFT_BRACKET: '[';
RIGHT_BRACKET: ']';
LEFT_BRACES: '{';
RIGHT_RACES: '}';
EQ: '=';


FUNCTOM: 'FUNCTION';
LET: 'LET';
CONST: 'CONST';
VAR: 'VAR';
IF: 'IF';
ELSE: 'ELSE';
WHILE: 'WHILE';
FOR: 'FOR';
RETURN: 'RETURN';
// MyGParser.g4
parser grammar MyGParser;


options {
  tokenVocab = MyGLexer;
}


// 入口規(guī)則
program: statement* EOF;


statement:
  variableDeclaration
  | functionDeclaration
  | expressionStatement
  | blockStatement
  | ifStatement
  | whileStatement
  | forStatement
  | returnStatement;
  ......

2.語言模式

計(jì)算機(jī)語言常見4種語言模式:序列(sequence)、選擇(choice)、詞法符號(hào)依賴 (token dependency),以及嵌套結(jié)構(gòu)(nested phrase)。以下是ANTLR對(duì)4種模式的語法規(guī)則描述。

圖片圖片

3.語法歧義

在自頂向下的語法和手工編寫的遞歸下降語法分析器中,處理表達(dá)式都是一件相當(dāng)棘手的事情,這首先是因?yàn)榇蠖鄶?shù)語法都存在歧義,其次是因?yàn)榇蠖鄶?shù)語言的規(guī)范使用了一種特殊的遞歸方式,稱為左遞歸。

expr : expr '*' expr
     | expr '+' expr
     | INT
     ;

我們舉個(gè)運(yùn)算符優(yōu)先級(jí)帶來的語法歧義問題,同樣的規(guī)則可以匹配多個(gè)輸入字符流。

圖片圖片

在其他語法工具中,通常通過指定額外的標(biāo)記來指定運(yùn)算符優(yōu)先級(jí)。而在ANTLR4中通過備選分支的排序來指定優(yōu)先級(jí),越靠前優(yōu)先級(jí)越高。

代碼自動(dòng)生成

ANTLR可以根據(jù)lexer.g4和parser.g4自動(dòng)生成詞法分析器,語法分析器,監(jiān)聽器,訪問器等。

antlr4ng -Dlanguage=TypeScript -visitor -listener -Xexact-output-dir -o ./src/lib ./src/grammar/*.g

圖片圖片

語法解析與業(yè)務(wù)邏輯解耦

在ANTLR4中語法解析和業(yè)務(wù)邏輯的高度解耦是一個(gè)重要的設(shè)計(jì)理念,優(yōu)點(diǎn)就是同一個(gè) AST 結(jié)構(gòu)能夠在不同的業(yè)務(wù)邏輯實(shí)現(xiàn)之間實(shí)現(xiàn)復(fù)用。不同的業(yè)務(wù)邏輯(如執(zhí)行、轉(zhuǎn)換、優(yōu)化等)可以對(duì)同一個(gè) AST 進(jìn)行不同的處理,而不需要關(guān)心解析過程。核心幾個(gè)設(shè)計(jì)方案如下:

  • 訪問者模式:ANTLR4通過訪問者模式支持業(yè)務(wù)代碼可訪問特定“詞法”或“語法”節(jié)點(diǎn)執(zhí)行自定義的操作,通過這個(gè)方式完全解耦A(yù)ST(抽象語法樹)生成和業(yè)務(wù)邏輯,詞法分析器和解釋器專注于AST生成,而業(yè)務(wù)可以通過訪問器的擴(kuò)展支持業(yè)務(wù)定制化訴求。
  • 語法和語義的獨(dú)立性:ANTLR4中可以獨(dú)立進(jìn)行語法解析和語義分析,可以在 AST 中進(jìn)行語義檢查和業(yè)務(wù)邏輯處理。這種分離使得開發(fā)者可以更靈活地處理輸入的語法和語義。
  • AST生成:ANRL4通過語法解析器生成結(jié)構(gòu)化AST(抽象語法樹),不同業(yè)務(wù)邏輯可以不斷復(fù)用同一個(gè)AST。
  • 上下文模式:解析器在處理輸入數(shù)據(jù)時(shí),上下文會(huì)在解析樹中傳遞信息。每當(dāng)進(jìn)入一個(gè)新的語法規(guī)則時(shí),都會(huì)創(chuàng)建一個(gè)新的上下文實(shí)例上下文可以存儲(chǔ)解析過程中需要的臨時(shí)信息,例如變量的值、數(shù)據(jù)類型等。上下文信息主要結(jié)合訪問器模式進(jìn)行使用,同時(shí)也解決了在解析復(fù)雜語句如多層嵌套結(jié)構(gòu)的層級(jí)調(diào)用問題。

三、SparkSQL介紹

Spark SQL 是 Apache Spark 的一個(gè)模塊,專門用于處理結(jié)構(gòu)化數(shù)據(jù),Spark SQL 的特點(diǎn)包括:

  1. 高效的查詢執(zhí)行:通過 Catalyst 優(yōu)化器和 Tungsten 執(zhí)行引擎,Spark SQL 能夠優(yōu)化查詢執(zhí)行計(jì)劃,提升查詢性能。
  2. 與 Hive 的兼容性:Spark SQL 支持 HiveQL 語法,使得用戶可以輕松遷移現(xiàn)有的 Hive 查詢。
  3. 支持多種數(shù)據(jù)源:Spark SQL 可以從多種數(shù)據(jù)源讀取數(shù)據(jù),包括 HDFS、Parquet、ORC、JDBC 等。

四、技術(shù)實(shí)現(xiàn)

語法設(shè)計(jì)

在Aparch Spark源碼中就是使用ANTLR4來解析和處理SQL語句,以下為Apach Spark中基于ANTLR元語言定義的詞法分析器和語法分析器,在語法定義上我們只需要基于這套標(biāo)準(zhǔn)的SparkSQL語法去適配得物自研引擎的能力,做能力對(duì)齊。

Lexer.g4


https://github.com/apache/spark/blob/master/sql/api/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBaseLexer.g4


Parser.g4


https://github.com/apache/spark/blob/master/sql/api/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBaseParser.g4

語法補(bǔ)全

以下我們以字段補(bǔ)全場(chǎng)景為例解析從語法定義,語法解析,語法補(bǔ)全,上下文信息采集各個(gè)流程節(jié)點(diǎn)剖析最后完成的表字段信息精準(zhǔn)推薦。在下列語法場(chǎng)景中,存在多層Select語法嵌套,同時(shí)表du_emr_test.empsalary tableB和表du_emr_test.hujh_type_tk AS tableB設(shè)置了同一別名,  如圖在父子查詢中都使用了同一個(gè)表別名(tableB),當(dāng)用戶在父子查詢中分別輸入tableB.時(shí),這時(shí)候需要結(jié)合當(dāng)前上下文語境,對(duì)tableB別名推薦不同表的字段。

SELECT 
    tableB.c1
 FROM
    (
       SELECT
            tableB.empno,
            tableC.department
        FROM
                du_emr_test.empsalary as tableB
        LEFT JOIN du_emr_test.employees AS tableC
        WHERE tableC.department = tableB.depname


    ) AS tableA
LEFT JOIN du_emr_test.hujh_type_tk AS tableB
WHERE tableB.c1 = tableA.dename

圖片圖片

圖片圖片

圖片圖片

圖片圖片

在子查詢中我們期望推薦tableB來自du_emr_test.empsalary tableB的字段信息,而在最外層中我們期望的是du_emr_test.hujh_type_tk的字段,如上圖。

基于以上場(chǎng)景我們核心要解決2個(gè)問題:

問題1:當(dāng)前光標(biāo)應(yīng)該提示哪些推薦語法類型

目前,開源方案ANTLR-C3引擎就能完美解決我們問題,用戶在編輯器實(shí)時(shí)輸入時(shí),獲取當(dāng)前光標(biāo)位置,實(shí)時(shí)做語法解析,然后基于開源的ANTLR-C3引擎能力結(jié)合ANTLR 生成的AST即可獲取當(dāng)前光標(biāo)位置所需要的語法規(guī)則。

問題2:  獲取當(dāng)前上下文信息以實(shí)現(xiàn)精準(zhǔn)推薦

根據(jù)不同業(yè)務(wù)場(chǎng)景需要采集的上下文信息不同,基于字段推薦的場(chǎng)景,我們需要獲取當(dāng)前光標(biāo)位置處可以推薦的表信息,表別名信息,結(jié)合編輯器能力實(shí)時(shí)獲取表對(duì)應(yīng)的字段信息進(jìn)行字段推薦補(bǔ)全,而上下文信息的采集,我們可以通過ANTLR生成的監(jiān)聽器來實(shí)現(xiàn)。

語法定義

以下我們用ANTLR元語言實(shí)現(xiàn)一段簡(jiǎn)化版的SQL查詢場(chǎng)景的語法規(guī)則(QueryStatment),方便我們理解。

lexer grammar SqlLexer;


// 基礎(chǔ)詞法
COMMA: ',';
LEFT_PAREN: '(';
RIGHT_PAREN: ')';
IDENTIFY: (LETTER | DIGIT | '_' | '.')+;
fragment DIGIT: [0-9];
fragment LETTER: [A-Z];
SEMICOLON: ';';


parser grammar SqlParser;


program: statment* EOF;


statment: queryStatment SEMICOLON?;
// 查詢語句
queryStatment:
  SELECT columnNames FROM (
    tableName
    | (LEFT_PAREN queryStatment LEFT_PAREN)
  ) whereExpression? relationsExpresssion? SEMICOLON?;


// 字段
columnNames: columnName (COMMA columnName)*;


tableName: IDENTIFY AS? tableAlis;


tableAlis: IDENTIFY;


columnName: IDENTIFY AS? columnAlis;


columnAlis: IDENTIFY;


whereExpression: WHERE booleanExpression;


booleanExpression: (NOT | BANG) booleanExpression           # logicalBinary
  | left = booleanExpression operator = AND right = booleanExpression # logicalBinary
  | left = booleanExpression operator = OR right = booleanExpression  # logicalBinary;


relationsExpresssion:
  LEFT JOIN tableName whereExpression?
  | RIGHT JOIN tableName whereExpression?;

代碼生成

圖片圖片

圖片圖片

以下是部分生成代碼:

1.詞法分析器

// SqlLexer.ts
    
    public static readonly COMMA = 1;
    public static readonly LEFT_PAREN = 2;
    public static readonly RIGHT_PAREN = 3;
    public static readonly IDENTIFY = 4;
    public static readonly SEMICOLON = 5;


    // 詞法分析器可以使用的通道
    public static readonly channelNames = [
        "DEFAULT_TOKEN_CHANNEL", "HIDDEN"
    ];
    // 包含了所有字面量記號(hào)的名稱
    public static readonly literalNames = [
        null, "','", "'('", "')'", null, "';'"
    ];
    // 包含為每個(gè)記號(hào)分配的符號(hào)名,這些符號(hào)在生成解析器時(shí)用于標(biāo)識(shí)記號(hào)
    public static readonly symbolicNames = [
        null, "COMMA", "LEFT_PAREN", "RIGHT_PAREN", "IDENTIFY", "SEMICOLON"
    ];
    
    //  ANTLR 生成的類中的一個(gè)字段,列出了所有定義的規(guī)則
    public static readonly ruleNames = [
        "COMMA", "LEFT_PAREN", "RIGHT_PAREN", "IDENTIFY", "DIGIT", "LETTER", 
        "SEMICOLON",
    ];

2.語法分析器

ANTLR自動(dòng)為每個(gè)規(guī)則生成了一個(gè)解析方法,以下是tableName的 ANTLR 中的解析器方法,具備了處理標(biāo)識(shí)符、可選的別名和錯(cuò)誤處理的能力。

// SQLParse.ts
// ANTLR自動(dòng)生成了一個(gè)解析 SQL 表名的 ANTLR 中的解析器方法,具備了處理標(biāo)識(shí)符、可選的別名和錯(cuò)誤處理的能力
public tableName(): TableNameContext {
        let localContext = new TableNameContext(this.context, this.state);
        this.enterRule(localContext, 8, SqlParser.RULE_tableName);
        let _la: number;
        try {
            this.enterOuterAlt(localContext, 1);
            {
            this.state = 60;
            this.match(SqlParser.IDENTIFY);
            this.state = 62;
            this.errorHandler.sync(this);
            _la = this.tokenStream.LA(1);
            if (_la === 8) {
                {
                this.state = 61;
                this.match(SqlParser.AS);
                }
            }


            this.state = 64;
            this.tableAlis();
            }
        }
        catch (re) {
            if (re instanceof antlr.RecognitionException) {
                this.errorHandler.reportError(this, re);
                this.errorHandler.recover(this, re);
            } else {
                throw re;
            }
        }
        finally {
            this.exitRule();
        }
        return localContext;
    }

自動(dòng)補(bǔ)全

ANTLR4代碼補(bǔ)全核心(antlr4-c3) 是一個(gè)開創(chuàng)性的工具,它為ANTLR4生成的解析器提供了一個(gè)通用的代碼補(bǔ)全解決方案。無論你的項(xiàng)目是處理哪種編程語言或領(lǐng)域特定語言(DSL),只要是基于ANTLR就能夠利用這個(gè)庫實(shí)現(xiàn)精準(zhǔn)的代碼建議和自動(dòng)補(bǔ)全,極大地增強(qiáng)開發(fā)體驗(yàn)。通過antlr4-c3 能力我們通過手動(dòng)配置需要收集的語法規(guī)則,獲取在當(dāng)前光標(biāo)處需要推薦的語法規(guī)則類型。

1.語法規(guī)則

通過ANTLR4工具我們可以自動(dòng)生成Sqllexer.ts詞法解析器,SqlParser.ts語法解析器,SqlParserLister.ts訪問器,SqlParseVisitor.ts監(jiān)聽器,在SqlParser 語法解析器自動(dòng)生成了我們?cè)谡Z法定義中的語法規(guī)則。

preferredRules = new Set([
        SqlParser.RULE_tableName,
        SqlParser.RULE_columnName,
]);

2.代碼補(bǔ)全

以下我們實(shí)現(xiàn)一套簡(jiǎn)化版的代碼補(bǔ)全能力。

當(dāng)用戶在編輯器實(shí)時(shí)輸入時(shí),調(diào)用getSuggestionAtCaretPosition獲取當(dāng)前語境中需要推薦的信息,包含語法規(guī)則,關(guān)鍵詞,上下文信息,在結(jié)合業(yè)務(wù)層數(shù)據(jù)做自動(dòng)補(bǔ)全,其中包含5個(gè)核心步驟:

  • 獲取當(dāng)前語法解析器實(shí)例。
  • 獲取當(dāng)前光標(biāo)位置對(duì)應(yīng)的Token。
  • 生成AST。
  • 獲取當(dāng)前語境上下文信息。
  • 通過ANTLR-C3獲取當(dāng)前位置候選語法規(guī)則。
public getSuggestionAtCaretPosition(
        sqlContent: string,
        caretPosition: CaretPosition
        preferredRules: Set
    ): Suggestions | null {
        
        // 1、 使用SqlParse解析器獲取
        const sqlParserIns = new SqlParse(sqlContent)
        
        // 2、獲取當(dāng)前光標(biāo)處token
        const charStreams = CharStreams.fromString(sqlContent);
        const lexer = new SqlLexer(charStreams);
        const tokenStream = new CommonTokenStream(lexer);
        tokenStream.fill()
        const allTokens = tokenStream.getTokens(); 
        let caretTokenIndex = findCaretToken(caretPosition, allTokens); 


        // 3、獲取AST抽象語法樹
        const parseTree = sqlParserIns.program()
        
        // 4、通過監(jiān)聽器采集上下文表信息(下面上下文分析部分闡述細(xì)節(jié))
        const tableEntity = getTableEntitys()
        
         // 異常場(chǎng)景兼容存在多條sql, 獲取有效最小SQL范圍給到antlr4-c3做推薦。
        const statementCount = splitListener.statementsContext?.length;
        const statementsContext = splitListener.statementsContext; 


        // 5、antlr4-c3接入獲取推薦語法規(guī)則
        let tokenIndexOffset: number = 0;
        const core = new CodeCompletionCore(sqlParserIns);
        // 推薦規(guī)則 來自SQLparse解析器的規(guī)則(元語言定義)
        core.preferredRules = preferredRules; 
        // 通過AST和當(dāng)前光標(biāo)Token獲取推薦類型
        const candidates = core.collectCandidates(caretTokenIndex, parseTree); 
        
        // ruleType -> preferredRules 
        // const [rules, tokens] = candidate;
        const rules = [];
        const keywords = [
                
        for (let candidate of candidates.rules) {
        const [ruleType] = candidate;
        let synContextType;
        switch (ruleType) {
            case SqlParser.RULE_tableName: {
                syntaxContextType = 'table';
                break;
            }
            case SqlParser.RULE_columnName: {
                syntaxContextType = 'column';
                break;
            }
            default:
                break;
        }
        if (synContextType) {
            rules.push(syntaxContextType)
        }
    }


    // 獲取對(duì)應(yīng)keywords
    for (let candidate of candidates.tokens) {
        const displayName = sqlParserIns.vocabulary.getDisplayName(candidate[0]);
        const keyword = displayName.startsWith("'") && displayName.endsWith("'")
                ? displayName.slice(1, -1)
                : displayName
        keywords.push(keyword);
    }


    return {
        rules,
        keywords,
        tableEntity
    };
  }

在這里我們簡(jiǎn)化了流程,忽略了很多異常case的處理,自動(dòng)補(bǔ)全的前提是在當(dāng)前語法規(guī)則正確,而在多級(jí)子查詢嵌套場(chǎng)景我們需要考慮到過濾異常QueryStatment,  在當(dāng)前光標(biāo)出最小范圍有效的QueryStatment做補(bǔ)全。這時(shí)候需要配合監(jiān)聽器去做上下文采集做容錯(cuò)性更高的自動(dòng)補(bǔ)全。

上下文分析

圖片圖片

如圖:每個(gè)table都?xì)w屬于一個(gè)QueryStatment表達(dá)式,  查詢中又存在子層級(jí)查詢的嵌套。我們需要通過上下文收集以下信息:

  • 每個(gè)查詢語句的信息,包含Position位置信息,記錄當(dāng)前的查詢開始行,結(jié)束行,開始列,結(jié)束列。
  • 查詢語句的關(guān)聯(lián)關(guān)系,即記錄當(dāng)前查詢語句父級(jí)查詢語句對(duì)象。
  • 表實(shí)體信息包含表名,表位置信息,表別名信息,當(dāng)前表歸屬于那個(gè)查詢語句。

則我們需要監(jiān)聽3個(gè)語法規(guī)則包含QueryStatment, TableName,TableAlias, 采集QueryStatment信息,Table信息同時(shí)將table與當(dāng)前歸屬的QueryStatment做關(guān)聯(lián), 還有與別名信息作配對(duì)關(guān)聯(lián)。這就要求在不同監(jiān)聽器之間的信息需要做共享,上下文信息需要做傳遞和保留。ANTLR常用的3種信息共享方案包含:

  • 使用訪問器方法來返回值,
  • 使用類成員在事件方法之間共享數(shù)據(jù),
  • 在語法定義中使用樹標(biāo)記來存儲(chǔ)信息。

在這里我們使用第二種(在這里我們簡(jiǎn)化了SQL的語法定義,在實(shí)際場(chǎng)景中語法層級(jí)深度和復(fù)雜度遠(yuǎn)比當(dāng)前高,這也使得方案1和3實(shí)際操作起來更麻煩,規(guī)則嵌套層級(jí)深使得方案一和方案三開發(fā)成本和維護(hù)成本更高)

1.監(jiān)聽器(SqlParserLister)

通過ANTLR4工具我們可以自動(dòng)生成SqlParserLister.ts監(jiān)聽器進(jìn)行自定義擴(kuò)展。

// SqlParserListener.ts
export class QueryStatmentContext extends antlr.ParserRuleContext {
   public override enterRule(listener: SqlParserListener): void {
        if(listener.enterQueryStatment) {
             listener.enterQueryStatment(this);
        }
    }
    public override exitRule(listener: SqlParserListener): void {
        if(listener.exitQueryStatment) {
             listener.exitQueryStatment(this);
        }
    }
 }
 
 export class TableNameContext extends antlr.ParserRuleContext {
     public override enterRule(listener: SparkSqlParserListener): void {
        if(listener.enterTableName) {
             listener.enterTableName(this);
        }
    }
    public override exitRule(listener: SparkSqlParserListener): void {
        if(listener.exitTableName) {
             listener.exitTableName(this);
        }
    }
 }
// ....


export class TableAliasContext extends antlr.ParserRuleContext {
    public KW_AS(): antlr.TerminalNode | null {
        return this.getToken(SparkSqlParser.KW_AS, 0);
    }
    public override enterRule(listener: SparkSqlParserListener): void {
        if(listener.enterTableAlias) {
             listener.enterTableAlias(this);
        }
    }
    public override exitRule(listener: SparkSqlParserListener): void {
        if(listener.exitTableAlias) {
             listener.exitTableAlias(this);
        }
    }
}

2.自定義監(jiān)聽器擴(kuò)展

通過SqlParserListener我們可以自定義采集上下文信息。在

  • 監(jiān)聽進(jìn)入QueryStatment表達(dá)式采集當(dāng)前表達(dá)式信息到_queryStmtsStack。
  • 監(jiān)聽退出TableNameToken時(shí)采集當(dāng)前Table信息,并關(guān)聯(lián)當(dāng)前QueryStatment。
  • 監(jiān)聽退出TableAliasToken時(shí)采集信息,并關(guān)聯(lián)到Table實(shí)體。
  • 監(jiān)聽退出QueryStatment表達(dá)式推出_queryStmtsStack
// tableEntityCollect
 export class SqlEntityCollector implements SqlParserListener {
     super() {
         this._tableEntitiesSet = new Set();
         this._queryStmtsStack = [];
         this._tableAliasStack = [];
         this._currentTable = '';
     }
     
     enterQueryStatment(ctx: QueryStatmentContext) {
        this.pushQueryStmt(ctx);
    }


    exitQueryStatment(ctx: QueryStatmentContext) {
        this.popQueryStmt(); 
    }


     exitTableName(ctx: TableNameContext) {
        this.pushTableEntity(ctx);
        this.setCurrentTable(ctx);
     }
     
     exitTableAlias(ctx: TableAliasContext) {
        this.pushTableEntity(ctx);
     }
     
     pushQueryStmt() {}   // 采集QueryStmt信息
     
     popQueryStmt() {}    // 推出當(dāng)前QueryStmt,進(jìn)入下個(gè)同級(jí)Stmt
     
     pushTableEntity() {} // 采集當(dāng)前表信息,關(guān)聯(lián)當(dāng)前Stmt
     
     pushTableEntity() {} // 采集關(guān)聯(lián)表
     
     enterProgram() {}    // 清空重置
     
     getTableEntity() {
         return this.TableEntity(ctx)
     }
    
 }

在這里我們簡(jiǎn)化了語法定義的規(guī)則便于講解,但在實(shí)際中語法規(guī)則的整體嵌套層級(jí)是很深的,從以下的SparkSql語法定義中我們可以看到右側(cè)聚合的表達(dá)式高達(dá)200+個(gè),單個(gè)表達(dá)式的備選分支最多高達(dá)140+,這也加大了上下文分析采集的復(fù)雜度,即我們無法簡(jiǎn)單的從QueryStmt當(dāng)前QueryStatmentContext中獲取全量信息。

圖片圖片

3.觸發(fā)監(jiān)聽器采集上下文信息
getTableEntitys() {
    const collectListener = new SqlEntityCollector(sqlContent, caretTokenIndex);
    const parse = new SqlParse(sqlContent);
    const parseTree= sqlParserIns.program();
    ParseTreeWalker.DEFAULT.walk(collectListener, parseTree); 
    return collectListener.getTableEntity()
}

語法校驗(yàn)

ANRLR在生成語法分析器中內(nèi)置了自動(dòng)錯(cuò)誤報(bào)告和恢復(fù)策略,能夠在遇到句法錯(cuò)誤時(shí)自動(dòng)產(chǎn)生錯(cuò)誤消息,為每個(gè)句法錯(cuò)誤產(chǎn)生一條錯(cuò)誤消息。

詞法錯(cuò)誤

常見的詞法錯(cuò)誤包含字符遺漏,詞法錯(cuò)誤。舉個(gè)例子,在spark標(biāo)準(zhǔn)語法定義中 tableName規(guī)則不支持表變量場(chǎng)景(${variable}),如果要兼容這里詞法,就需要在語法定義中變更tableName的語法規(guī)則定義。

圖片

以下是語法定義變更:

  • 新增詞法規(guī)則$, {, }。
  • 新增語法規(guī)則identifyVar支持變量模式。
SqlLexer.g4
// 新增詞法
LEFT_BRACE    : '{';
RIGHT_BRACE   : '}';


VARIABLE    : '$';


SqlParse.g4
// before tableName: IDENTIFY AS? tableAlis; 
tableName: identifyVar AS? tableAlis; 


identifyVar
    : IDENTIFY // odps_table_a
    | IDENTIFY? VARIABLE LEFT_BRACE IDENTIFY RIGHT_BRACE IDENTIFY? // odps_table_a_${variable} odps_table_a_${prefix_variable}_abs

自動(dòng)恢復(fù)機(jī)制

語法分析器不應(yīng)該在遇到非法的成員定義時(shí)結(jié)束,而是應(yīng)盡最大可能匹配到一個(gè)合法的類定義,ANRTL4自動(dòng)錯(cuò)誤恢復(fù)機(jī)制能在語法分析器在發(fā)現(xiàn)語法錯(cuò)誤后還能繼續(xù)進(jìn)行嘗試語法解析和自動(dòng)恢復(fù)。

1.異常捕獲

ANRLT自動(dòng)生成的語法解析器中自動(dòng)為每個(gè)規(guī)則包裹異常捕獲能力,并在catch中嘗試錯(cuò)誤恢復(fù)。

圖片圖片

2.恢復(fù)策略

一般情況下,語法分析器在遇到無法匹配的錯(cuò)誤時(shí)會(huì)嘗試最簡(jiǎn)單的符號(hào)補(bǔ)全和移除來嘗試解析,都不管用時(shí),這時(shí)候就會(huì)用更高階的策略來進(jìn)行恢復(fù)。包括掃描后續(xù)詞法符號(hào)來恢復(fù),從不匹配的詞法符號(hào)中恢復(fù),從子規(guī)則的錯(cuò)誤中恢復(fù),捕獲失敗的語義判定。

雖然ANTLR提供了很多策略來進(jìn)行錯(cuò)誤恢復(fù),但在實(shí)際業(yè)務(wù)場(chǎng)景中,需要結(jié)合考慮語法、語境的復(fù)雜度去權(quán)衡性能與更友好的錯(cuò)誤提示之間的抉擇。在復(fù)雜場(chǎng)景中ANTLR表現(xiàn)并不理想,在一些復(fù)雜語法和語境的情況下解析器在檢測(cè)錯(cuò)誤時(shí)難以做出合理的決策,例如:遞歸和嵌套結(jié)構(gòu)中會(huì)使得錯(cuò)誤恢復(fù)變得很復(fù)雜,導(dǎo)致解析器無法做出合理決策。還有在上下文敏感的語境中,錯(cuò)誤恢復(fù)機(jī)制基本無法提供有效恢復(fù)。

性能

在 ANTLR 4 中,語法復(fù)雜度、語法歧義、語法規(guī)則嵌套深度與預(yù)測(cè)算法的選擇都會(huì)顯著影響解析器的性能和準(zhǔn)確性。Spark SQL語法規(guī)則達(dá)200+,備選分支最高達(dá)140, 嵌套深度達(dá)20+,同時(shí)又存在負(fù)責(zé)循環(huán)嵌套場(chǎng)景, 這也意味著在整個(gè)語法解析,語法錯(cuò)誤的處理過程是很復(fù)雜的,當(dāng)遇到復(fù)雜大SQL量和一片狼籍的語法錯(cuò)誤SQL,會(huì)導(dǎo)致語法解析過程變得緩慢引發(fā)性能問題。目前在性能優(yōu)化上,有以下幾個(gè)方向。

緩存優(yōu)化

在antlr4中詞法解析和語法解析能力和業(yè)務(wù)是完全解耦的,這也意味著底層基于同個(gè)SQL內(nèi)容解析出來的tokens和parserTree都是可以在不同業(yè)務(wù)邏輯應(yīng)用里復(fù)用。我們可以通過緩存tokens,parseTree減少詞法解析和語法解析的損耗。

語法優(yōu)化

通過減少語法樹的層級(jí)和優(yōu)化表達(dá)式減少解析過程中“二義性”的次數(shù),可以加速語法解析的速度,優(yōu)化AST生成性能。合理使用語法定義中用法,例如樹標(biāo)記(用于上下文通信數(shù)據(jù)共享),在語法解析過程中會(huì)為每個(gè)標(biāo)記生成上下文,這也意味著每個(gè)局部結(jié)果都會(huì)保留,會(huì)有更大的內(nèi)存消耗。

預(yù)測(cè)模型選擇

在語法解析中不同預(yù)測(cè)模型的選擇對(duì)解析性能有顯著影響,針對(duì)不同的場(chǎng)景需要評(píng)估時(shí)效性與正確性之間的衡量。

ANTLR4預(yù)測(cè)模型:

https://www.antlr.org/api/Java/org/antlr/v4/runtime/atn/PredictionMode.html

圖片

我們可以選擇性價(jià)比更高的SLL預(yù)測(cè)模型作為語法分析策略,結(jié)合定制化的錯(cuò)誤監(jiān)聽器做錯(cuò)誤糾正。

編輯器應(yīng)用

編輯器集成

與MonacoEditor集成流程可查看此文章 https://blog.shizhuang-inc.com/article/MTUzNzY?fromType=personal_blog

輔助編程

1.信息項(xiàng)提示(表,函數(shù),字段)

圖片圖片

圖片圖片

圖片圖片

2.自動(dòng)補(bǔ)全(庫,表,字段,語法)

圖片圖片

圖片圖片

圖片圖片

五、大模型下的SQL編輯器應(yīng)用

隨著大模型的蓬勃發(fā)展,在數(shù)據(jù)產(chǎn)品中的應(yīng)用也逐步得到了驗(yàn)證和落地,目前,Galaxy還沒有接入Copilot, 內(nèi)部暫時(shí)還沒有基于SQL的Copilot。業(yè)界較成熟的是阿里云的Dataworks, DataWorks于2023年推出了Copilot 產(chǎn)品, 核心2個(gè)方向,一個(gè)方向是智能 SQL 編程助手,輔助 SQL 編程,支持 NL2SQL 及 SQL 代碼補(bǔ)全;另一個(gè)方向是 AI Agent,提供 LUI(自然語言用戶界面),以提升產(chǎn)品功能操作的便捷性和用戶體驗(yàn)。

NL2SQL應(yīng)用場(chǎng)景

基于SQL的Copilot一般在以下幾個(gè)應(yīng)用場(chǎng)景比較深入和廣泛的落地效果:簡(jiǎn)單數(shù)據(jù)查詢,SQL 優(yōu)化與轉(zhuǎn)換,SQL 語法查詢與講解, 函數(shù)查詢,功能咨詢,注釋生成,SQL 解釋,SQL 一鍵糾錯(cuò)。

NL2SQL自動(dòng)補(bǔ)全

代碼補(bǔ)全是編程類 Copilot 的主要場(chǎng)景和能力,單市場(chǎng)上主流的編程類 Copilot 對(duì) SQL 支持的好的并不多見。眾所周知,SQL 代碼補(bǔ)全比其他高級(jí)語言的代碼補(bǔ)全更具挑戰(zhàn)性,主要原因有以下幾個(gè)方面:

  • 上下文和環(huán)境的依賴性:SQL 代碼不是獨(dú)立存在的,而是依賴于數(shù)據(jù)表的元數(shù)據(jù)信息以及表與表之間的關(guān)聯(lián)關(guān)系。
  • SQL 語義多樣性:實(shí)現(xiàn)同一種查詢結(jié)果,可以有多種 SQL 寫法,如何實(shí)現(xiàn)“最佳”寫法存在挑戰(zhàn)。
  • 語法簡(jiǎn)潔但高度專業(yè)化:SQL 語法簡(jiǎn)潔但每一個(gè)關(guān)鍵字、函數(shù)或語法都有特定的含義,大模型要準(zhǔn)確理解這些得通過針對(duì)性的訓(xùn)練學(xué)習(xí)。
  • 執(zhí)行計(jì)劃和性能考量: 這跟數(shù)據(jù)庫底層的執(zhí)行計(jì)劃有關(guān),需要考慮如何書寫才能使 SQL 的性能最優(yōu)。
  • 數(shù)據(jù)庫特異性:市面上不同的數(shù)據(jù)庫往往存在不同的 SQL 方言,存在差異,針對(duì)這種差異性我們要投入大量時(shí)間做 SQL 數(shù)據(jù)集準(zhǔn)備、數(shù)據(jù)標(biāo)注、模型微調(diào)。
  • 高度業(yè)務(wù)相關(guān)性:SQL 語句通常與特定業(yè)務(wù)高度相關(guān),比如一個(gè)指標(biāo)存在特定的計(jì)算口徑,這是與公司業(yè)務(wù)相關(guān),通用的大模型也無法提前學(xué)習(xí)。

目前較成熟的代碼補(bǔ)全核心場(chǎng)景主要在有規(guī)律的代碼連續(xù)推薦場(chǎng)景(例如:字段、字段別名推薦,注釋推薦、分區(qū)字段推薦、Group by 字段推薦,上下文自動(dòng)聯(lián)想推薦等)。

六、總結(jié)

通過SQL引擎能力建設(shè)我們?cè)贕alaxy數(shù)據(jù)研發(fā)IDE上支持了個(gè)性化詞法規(guī)則定制能力,包含字段別名支持中文, 表變量等場(chǎng)景, 同時(shí)通過語法解析和監(jiān)聽器能力,支持實(shí)時(shí)識(shí)別各類的語法規(guī)則,包含表,函數(shù),字段等做輔助編程提示和做精準(zhǔn)化的庫,表,字段代碼補(bǔ)全和推薦。

后續(xù)我們?nèi)悦媾R很大的挑戰(zhàn),在非專業(yè)的數(shù)據(jù)開發(fā)背景、復(fù)雜的業(yè)務(wù)定制需求、語言定義的復(fù)雜性和嵌套深度等因素共同導(dǎo)致了解析器的開發(fā)難度。目前,在語法校驗(yàn)自動(dòng)糾錯(cuò)提示上,雖然ANTLR的提供了自動(dòng)錯(cuò)誤恢復(fù)機(jī)制但整體表現(xiàn)并不理想,后續(xù)2個(gè)方向,第一,接入大模型的能力。第二,從基礎(chǔ)語法定義上進(jìn)行重構(gòu),減少語法歧義和層級(jí)優(yōu)化。為了應(yīng)對(duì)這些挑戰(zhàn),我們需要加強(qiáng)對(duì) ANTLR 和 Spark SQL語言,數(shù)據(jù)處理的理解,以便順利使用和擴(kuò)展解析器。

參考資料

  • ANTLR
  • ANTLR4-C3
  • DataWorks Copilot:大模型時(shí)代數(shù)據(jù)開發(fā)的新范式
  • ANTLR4權(quán)威指南 - [美] 特恩斯·帕爾 著
責(zé)任編輯:武曉燕 來源: 得物技術(shù)
相關(guān)推薦

2016-09-23 20:30:54

Javascriptuiwebview富文本編輯器

2021-09-23 19:31:00

AI

2022-10-27 10:06:16

Presto SQLAntlr大數(shù)據(jù)

2013-06-18 01:22:46

CocoStudio工Cocos2d-x

2011-01-10 16:17:49

2021-09-23 19:30:02

AI

2010-02-23 15:29:43

Python 編輯器

2010-03-24 09:20:07

CentOS vi編輯

2023-08-31 11:32:57

圖形編輯器contain

2011-03-22 13:54:57

UbuntuPHP編輯器

2015-04-22 13:20:21

企業(yè)網(wǎng)D1Net

2024-11-27 09:02:01

文本編輯canvas圖形編輯器

2011-01-19 13:40:33

htmlcsswindows

2019-07-10 13:17:07

大數(shù)據(jù)搜索代碼

2017-03-09 11:45:16

LinuxVim編輯器

2025-02-05 12:01:35

屬性編輯器Web

2022-12-23 09:29:52

大數(shù)據(jù)

2017-04-21 11:24:13

數(shù)據(jù)庫Azure T-SQL編輯器

2018-09-25 09:25:11

Vim編輯器命令

2024-01-31 09:55:53

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美日韩精品一区二区三区视频 | 国产成人一区 | 欧美一区二区三区 | 久久久精品视频一区二区三区 | 国产一区二区视频在线观看 | 亚洲一区电影 | 欧美a在线看 | 色爱综合网 | 一本一道久久a久久精品蜜桃 | 日韩第一页 | 免费人成激情视频在线观看冫 | 青青久久久 | 国产中文 | 一级做a爰片性色毛片视频停止 | 国产美女在线观看 | 中文字幕av亚洲精品一部二部 | 成人亚洲精品 | 国产精品一区二区三区免费观看 | 美女久久久久久久 | а_天堂中文最新版地址 | 一级黄色毛片免费 | 欧美视频在线播放 | 91视频在线观看 | 成人国产精品久久 | 亚洲区一区二 | 在线观看欧美日韩视频 | 男女网站免费观看 | 成人午夜| 欧美bondage紧缚视频 | 久久国产激情视频 | 欧美精品一二三区 | 中文字幕免费在线 | 欧美一级片在线播放 | 成人aaa视频 | 高清成人av | 九九久久免费视频 | 亚洲欧美日韩系列 | 一区二区在线 | 欧美福利一区 | 国家一级黄色片 | 中文字幕亚洲精品 |