-
Notifications
You must be signed in to change notification settings - Fork 106
-
分析出当前sql内容中新创建的 catalog名/schema名/表名/视图名/函数名/字段名,用于自动补全
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 22 comments 7 replies
-
Are there any support plans in the near future?
Beta Was this translation helpful? Give feedback.
All reactions
-
I just came across this context completion requirement and would like to know if there is any intention to implement this feature in the near future or the approximate time to do so.
Beta Was this translation helpful? Give feedback.
All reactions
-
Are there any support plans in the near future?
We will implement this feature in December. We haven't started doing this yet because we are still rechecking the sql grammar files, which takes a lot of time.
Beta Was this translation helpful? Give feedback.
All reactions
-
I just came across this context completion requirement and would like to know if there is any intention to implement this feature in the near future or the approximate time to do so.
Which sql language do you need to implement this feature in? If you need implement this feature in one of the languages in flink,hive,spark or trino, we can support it in the near future
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 3
-
Are there any support plans in the near future?
We will implement this feature in December. We haven't started doing this yet because we are still rechecking the sql grammar files, which takes a lot of time.
This is good news. Looking forward to it!
Beta Was this translation helpful? Give feedback.
All reactions
-
I just came across this context completion requirement and would like to know if there is any intention to implement this feature in the near future or the approximate time to do so.
Which sql language do you need to implement this feature in? If you need implement this feature in one of the languages in flink,hive,spark or trino, we can support it in the near future
It's great to hear from you. All of those you mentioned above are in demand, even mysql, and it's really exciting to hear that new features are coming
Beta Was this translation helpful? Give feedback.
All reactions
-
上下文
之前有看到一个结合 Monaco editor 的一个思路:把 cursor(当前光标) 当做一个特殊的 token,用于通过语法匹配并提示当前光标处的补全
ex:
select | from user; // '|' means cursor position
把 cursor 当成一个特殊的 token 通过 AST,此时 AST 正确,cursor token 充当了 select list 的作用
此时就可以提供提示了,提示这里应该输入 field,可通过 from 后面的上下文,来提示这里应该输入 user 这张表的 field
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
请问这一块需要社区支持吗?
我很感兴趣并且最近很有需要
Beta Was this translation helpful? Give feedback.
All reactions
-
需要社区支持,等我们同学规划出版本内容后可以认领任务哈~
Beta Was this translation helpful? Give feedback.
All reactions
-
上下文 之前有看到一个结合 Monaco editor 的一个思路:把 cursor(当前光标) 当做一个特殊的 token,用于通过语法匹配并提示当前光标处的补全
ex: select | from user; // '|' means cursor position 把 cursor 当成一个特殊的 token 通过 AST,此时 AST 正确,cursor token 充当了 select list 的作用 此时就可以提供提示了,提示这里应该输入 field,可通过 from 后面的上下文,来提示这里应该输入 user 这张表的 field
How should monaco get the position of the cursor, please? I didn't find the api for it, but it does have this function
Beta Was this translation helpful? Give feedback.
All reactions
-
How should monaco get the position of the cursor, please? I didn't find the api for it, but it does have this function
// 获取 monaco-editor 实例 var editor = monaco.editor.create(document.getElementById('container'), { value: 'function hello() {\n\talert("Hello, world!");\n}', language: 'javascript' }); // 获取光标位置 var position = editor.getPosition(); console.log(position);
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
把 cursor 当成一个特殊的 token 通过 AST,此时 AST 正确,cursor token 充当了 select list 的作用
此时就可以提供提示了,提示这里应该输入 field
@zhi-zhi-zhi 事实上这个功能已经支持了,目前 dt-sql-parser 的自动补全功能通过引入 antlr4-c3 实现。 你可以通过调用 parser. getSuggestionAtCaretPosition
来体验该功能,详情见自动补全功能文档。或者你可以通过自动补全功能的单元测试来查看如何使用, 自动补全功能的单元测试位于 test/parser/*/suggestion/*.test.ts
.
请问这一块需要社区支持吗? 我很感兴趣并且最近很有需要
@zhi-zhi-zhi 非常欢迎来自社区的 PR。
但是在此之前,我们需要先讨论一下该功能,以保证该功能实现后符合我们的期望。
需要实现什么?
- 提示某个指定位置应该填写什么类型(类型是指如表,字段,函数等,下文中同),例:
SELECT * FROM |
|
代表光标位置,那么在此处进行自动补全时,dt-sql-parser 的自动补全方法应该提示用户,这里应该填一个表名。再次强调,目前此功能已经支持。
- 收集sql 上下文中出现的表名,字段名,函数名等,比如:
CREATE TABLE tb1 (id int); CREATE TABLE tb2 (id int); CREATE TABLE tb3 (id int); SELECT * FROM |
事实上,对于使用自动补全功能的用户来说,除了想要知道指定位置应该填什么类型,还需要知道该类型对应有哪些实体?比如此例中的光标位置,在此位置做自动补全功能时,除了需要知道该位置要填表名,还需要知道当前上下文中有哪些表名。 那么此时我们需要一个功能去收集sql文本上下文中出现的所有表名,在本例中应该收集到 tableNames: [ 'tb1', 'tb2', 'tb3' ]
一般情况下,在实际业务场景中,这些信息应该由数据库/计算引擎的元数据提供,但即使有元数据支持,此功能仍然具有意义,因为当前上下文中的 DDL 语句可能并未执行,也就是说当前上下文中可能包含元数据中不包含的信息。比如建表语句未执行前,元数据中没有该表的信息,但是我们可以在sql上下文中获取。
有哪些类型需要收集?
参照 SyntaxContextType
, 其中包含的所有类型都需要收集。
注意事项
需要考虑多种情况,例如:
- 先创建表,再通过 Alter Table 修改表:
CREATE TABLE tb1 (id int); ALTER TABLE tb1 RENAME TO tb2;
- 字段别名/表别名
CREATE TABLE tb ( sum(age) as col int )
这里无法列举出全部情况,因为不同的sql类型的语法具有差异。
如何实现?
- 考虑通过 Anltr4 Visitor 或者 Antlr4 Listener 实现。
- 应该通过在抽象类
BasicParser
中新增一个独立成员方法,以对外暴露该功能。
Beta Was this translation helpful? Give feedback.
All reactions
-
🚀 2 -
👀 1
-
一般情况下,在实际业务场景中,这些信息应该由数据库/计算引擎的元数据提供
我想通过后端接口获取到db.tb, tb.column补全列表;
如用户输入:
use a_db; SELECT * FROM a_table INNER JOIN b_db.b_table AS b ON a_table.<cursor_1> = b.<cursor_2> WHERE <cursor_3>
在光标处,目前我获取到的补全上下文信息,只有以下信息:
{ "syntaxContextType": "column", "wordRanges": [ { "text": " ", "startIndex": 28, "stopIndex": 28, "line": 3, "startColumn": 6, "stopColumn": 6 } ] }
但是我无法请求后端得到当前应该输入的字段列表,因为后端无法得知需要什么表下面字段列表,也就是上下文信息中缺少更多信息,如果是以下这样,那就可以完成后端获取数据。
上述SQL中包含了,a_db.a_table以及 b_db.b_table,且b_table 有一个别名b.
对于 <cursor_1>,仅需要补全a_table下的字段:
{ "syntaxContextType": "column", "wordRanges": [ { // 其他属性省略 "tables": [{"db": "a_db", "tb": "a_table"}] } ] }
对于 <cursor_2>,仅需要补全b_table(别名b)下的字段:
{ "syntaxContextType": "column", "wordRanges": [ { // 其他属性省略 "tables": [{"db": "b_db", "tb": "b_table"}] } ] }
对于 <cursor_3>,需要补全a_table和b_table下的字段:
{ "syntaxContextType": "column", "wordRanges": [ { // 其他属性省略 "tables": [{"db": "b_db", "tb": "b_table"}, {"db": "b_db", "tb": "b_table"}] } ] }
还有一些更为复杂的场景:
如包含子查询和别名(b_table -> bbb -> b),其中最初的b_table是后端需要的真实的表名,且其来自于b_db。
bbb和b对后端无意义:
use a_db; SELECT * FROM a_table INNER JOIN ( SELECT * FROM b_db.b_table bbb LIMIT 100 ) b ON a_table.<cursor_1> = b.<cursor_2> WHERE <cursor_3>
当然,除此之外,自动感知上下文中的库、表和字段也很重要。
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
@Shu-Ji
emmm,你讲到了自动补全功能的重点也是目前自动补全功能的一大缺陷,我暂且将这些信息统称为上下文信息。
目前自动补全功能提供的上下文信息,是一个wordRange 数组,而在 wordRange 中,并没有提到该 word 是什么类型,这实际上是因为 dt-sql-parser 也无法确认对应的类型。
很多数据库/大数据引擎都具有三层结构,比如 Trino 有 catalog -> database -> table/view
。
表名的自动补全
当输入为:
SELECT * FROM a<cursor pos>
此时光标位置可能的输入类型有:catalog
、database
、table
、view
,那么对于 dt-sq-parser
来说,a 就可能是catalog
、database
、table
或者view
的一部分 。
当输入为
SELECT * FROM a.b<cursor pos>
此时 a 就可能是 catalog
、database
, b 就可能是 database
、table
或者 view
的一部分。
这在理论上也似乎无法确认(最起码对我来说,没想到什么好办法),如果你有什么更好的点子可以告诉我们。
字段的自动补全
事实上,dt-sql-parser 能够获取到 wordRanges 数组是依靠位置,即对于表名的补全来说,所需要的信息在位置上一定与当前补全位置相邻。另外,wordRanges 数组中的 token 与补全位置的 token 一定在同一个 Antlr4.parserRuleContext 下。
字段的自动补全在此基础上变得复杂了起来,由于位置的关系,dt-sql-parser 无法直接感知到关于对应表名的上下文信息。
另外如你所说,这需要考虑很多复杂的情况,比如子查询,表别名,当然也包括 tb1 JOIN tb2 ON tb1.id = tb2.id
的情况。
这个功能相当重要,但是这都需要额外的开发,基于上下文的自动补全功能实际上需要语义分析的支持,Antlr4 本身则只支持词法分析和语法分析。又因为每一种 SQL 的语法各不相同,所以在做语义分析时,需要每一种 SQL 单独支持,即使实现起来并不困难(理论上),但这仍然需要大量的时间。
这个功能目前在我们的计划中,但是并不是最高优先级,最近的开发计划还是集中在支持更多类型的 SQL 上,比如 Oracle
、StarRocks
、Inceptor
。同时开发基于上下文的自动补全功能对目前的维护团队来说是难以承受的负担。因为这个工作量需要与SQL类型数量相乘。
另外,我们非常欢迎来自社区的 PR 或者说是需要社区的帮助。
Beta Was this translation helpful? Give feedback.
All reactions
-
@Shu-Ji
目前,我们计划先支持获取上下文中的库、表和字段,这相比于完整的语义分析来说简单许多,只需要通过 listener 监听特定节点即可。
此功能支持后,可能对字段的补全有所帮助。即你可以一次性获取上下文中出现的所有的表名,从后端接口中获取所有的表的元数据,当当前的 SyntaxContextType
为 column 时,在补全项中插入所有的字段名,这些字段名的自动补全项根据所属的表名出现的位置与当前光标位置的远近来排序。
这显然不是最终的解决方案,但是可能值得一试。
Beta Was this translation helpful? Give feedback.
All reactions
-
👀 1
-
@HaydenOrz 请问这个支持了吗
Beta Was this translation helpful? Give feedback.
All reactions
-
完成了大半,但是目前遇到了一些困难。
以 SELECT FROM TB;
为例, 对于自动补全来功能说,需要先解析出 TB,即表名,才能根据去获取对应字段。但是这个语句中包含语法错误,没有字段,无法正常解析。
我们正在想办法解决这个问题,目前这个问题上已经有了一些进展,但暂时没有完全解决。
Beta Was this translation helpful? Give feedback.
All reactions
-
好的好的,感谢你的同步,期待!👍
Beta Was this translation helpful? Give feedback.
All reactions
-
这是一个好办法期待,期待!
Beta Was this translation helpful? Give feedback.
All reactions
-
@Shu-Ji 目前,我们计划先支持获取上下文中的库、表和字段,这相比于完整的语义分析来说简单许多,只需要通过 listener 监听特定节点即可。
此功能支持后,可能对字段的补全有所帮助。即你可以一次性获取上下文中出现的所有的表名,从后端接口中获取所有的表的元数据,当当前的
SyntaxContextType
为 column 时,在补全项中插入所有的字段名,这些字段名的自动补全项根据所属的表名出现的位置与当前光标位置的远近来排序。这显然不是最终的解决方案,但是可能值得一试。
如果能取到当前文档中所有的库名和表名,那也很好了,因为至少可以补全了,只是补全列表中会出现相对无关的条目,不过问题不大,基本解决问题了。(尝试了一下MySqlParserVisitor模式,在语法不正确的时候,无法visit table等信息,会报语法错误,如何把光标处当成正确的字符,让解析器通过呢?)
有一些阿里同学的参考文章:
还有一些后端的lsp项目中,基本都提供光标处的自动补全功能;如:
https://github.com/lighttiger2505/sqls
@kissycn 这位同学都激动的彪中文了🐮,这个功能真的对于编辑器来说是很刚需
Beta Was this translation helpful? Give feedback.
All reactions
-
如果能取到当前文档中所有的库名和表名,那也很好了,因为至少可以补全了,只是补全列表中会出现相对无关的条目,不过问题不大,基本解决问题了。(尝试了一下MySqlParserVisitor模式,在语法不正确的时候,无法visit table等信息,会报语法错误,如何把光标处当成正确的字符,让解析器通过呢?)
@Shu-Ji 这个问题确实值得思考,你所提到的参考文章是很好的思路。
这位同学都激动的彪中文了🐮,这个功能真的对于编辑器来说是很刚需
@kissycn 关于这个功能的实现时间,我可能需要说声抱歉,之前暂定是 12 月份实现此功能,但是目前看来是没办法做到了,主要是因为年底了,emmm 琐事有点多。
Beta Was this translation helpful? Give feedback.
All reactions
-
理解,理解,年底大家事情都多!
Beta Was this translation helpful? Give feedback.
All reactions
-
尝试了一下MySqlParserVisitor模式,在语法不正确的时候,无法visit table等信息,会报语法错误,如何把光标处当成正确的字符,让解析器通过呢?
Beta Was this translation helpful? Give feedback.
All reactions
-
尝试了一下MySqlParserVisitor模式,在语法不正确的时候,无法visit table等信息,会报语法错误,如何把光标处当成正确的字符,让解析器通过呢?
或许我们可以通过Antlr4 ErrorStrategy (即 Antlr4 提供的自定义错误处理策略接口)来做到这件事,此前我在这个 pr #230 中已经修改过ErrorStrategy ,但是并不涉及错误恢复策略的改动。
对于 Antlr4 ErrorStrategy 我只是略微了解不是太熟悉,目前没有更多进展,这只是一个可能可行的方案。
另外想要在错误处进行恢复也要分多种情况,对于有结构性错误的语句在理论上很难恢复,因为很难预料语句原本是什么样子的。
Beta Was this translation helpful? Give feedback.
All reactions
-
为什么要收集这些信息?
目前主要作用是为自动补全功能提供必要的信息,在实际业务场景中,这些信息应该由数据库/计算引擎的元数据提供。但是当前上下文中可能包含引擎元数据中不包含的信息,比如建表语句未执行前,元数据中没有该表的信息,但是我们可以在sql上下文中获取。
需要收集什么?
从此功能的定位来看,所有可以用 DDL 语句创建的都需要被收集,比如表名、库名等,下文中统一称之为实体(Entity)。
实现思路
上下文中出现的实体都将被平铺在数组中输出,这样更易于查询。可以将输出结果类比为果篮,将实体类比为解析树上的果子,此功能就是在语法树上将果子摘下来放到同一个果篮中。
实体需要包含哪些信息?
对于一个实体,其内部需要包含的信息有
- 实体的名称(文本)
- 实体的类型(表名/字段名...)
- 实体所处的位置(行列号+索引位置)
- 实体所属的语句
- 其他附加信息
关于实体所属的语句,可以理解为解析树上的枝桠,在输出结果中所有的实体都将平铺输出,这样的结构无法表达实体之间的联系,比如 CREATE new_tb ... AS SELECT ... FROM old_tb;
,所以需要通过在实体上附加实体所属的语句信息来简单描述实体间的关联关系。
除了通过所属语句来连接不同的实体以外,还可以直接附加有关联的其他实体到本实体上,比如上例中, new_tb
的附加信息中包含 old_tb
。
另有一些特殊情况,比如建表语句中的字段信息,实际上这些字段信息被单独输出没有太多意义,这些字段通常需要绑定表名一起出现,所以这些字段信息将被作为表实体的附加信息。
interface EntityContext { readonly syntaxContextType: SyntaxContextType; readonly text: string; readonly position: WordPosition; readonly belongStmt: StmtContext; relatedEntities?: EntityContext[]; columns?: EntityContext[]; }
语句需要包含哪些信息?
- 语句类型
- 语句位置
- 语句的父语句
- 语句的根语句
因为语句之间可以有很深的嵌套关系,所以语句的父语句信息在某些情况下是必要的,另外语句的根语句则是一个指向最外层语句的指针,这可以在很多情况下降低查找的难度。
根语句一般指一条独立(当前上下文中不作为其他语句的字句)且完整的语句, 例如 CREATE new_tb ... AS SELECT ... FROM old_tb;
中的根语句是一个建表语句,内部的子查询语句则不能作为根语句,子查询的根语句指向外层的建表语句。
除此之外,在最顶层会有一个通用的语句节点,这是一种兜底策略,主要是为了处理语句类型没有被访问收集,但是语句内部包含实体的情况,此时语句内部实体中附加语句信息会指向这个通用节点,它除了没有明确的语句类型以外,其他信息都与正常的语句相同且有效。
interface StmtContext { readonly stmtContextType: StmtContextType; readonly position: TextPosition; readonly rootStmt?: StmtContext | null; readonly parentStmt?: StmtContext | null; }
具体实现
- 使用 ANTLR4 listener 访问节点(visitor 同)
- 创建一个语句暂存栈,用于暂存当前所处的语句信息
- 创建一个实体暂存栈,用于存储当前语句下所有的实体信息
- 在退出根语句时,聚合根语句内部的所有实体
CREATE TABLE new_tb AS SELECT old_tb1.column1, old_tb2.column2 FROM old_tb1 JOIN old_tb2 ON old_tb1.id = old_tb2.id WHERE old_tb1.column1 = 'value'
对于这段 sql (MySQL),用一个简化的语法树表示:
当使用 listener 访问这棵树上的节点时,语句暂存栈和实体暂存栈的变化如下图所示:
关键的过程节点
- 语句栈为空或者语句栈中只有通用节点的情况下,push 新的语句后将 rootStmt 指针指向新的语句节点。
- 如果语句栈中 rootStmt 指针的指向的语句被弹出 ,则弹出实体栈中所有的节点,聚合/组合当前栈中所有的实体信息,并将聚合/组合后的实体添加至结果列表中。以及此时应该将 rootStmt 指针向栈底移动一位。
这样做是为了以一条独立语句为单位来聚合/组合语句下的所有实体信息,但是这种策略会导致部分实体间的关联关系被忽略,所以实际上这里 是在平衡分析的复杂度和实体间关联关系的丰富度。换句话说, 何时从实体栈中弹出实体,决定了聚合/组合实体的复杂度也决定了实体间关联关系的丰富度。
聚合和组合实体信息
聚合和组合的逻辑应该是最复杂的,聚合和组合实际上是把同一个独立语句中出现的实体联系起来,这需要考虑很多复杂的情况,目前只开发了极为简单的情况下的实体聚合/组合逻辑。
依照上述策略,最后生成的结果如图所示,输出结果中仅包含实体,对应的 stmt 信息可以通过实体的 belongStmt
属性访问。
Beta Was this translation helpful? Give feedback.
All reactions
-
🎉 2 -
🚀 4
-
遗留问题
- 别名问题暂未处理
- 无法区分不同的含义但是相同类型的实体,比如select的字段和where子句中出现的字段
- 有语法错误时,解析准确度
Beta Was this translation helpful? Give feedback.
All reactions
-
我这边根据之前业务需求的逻辑,简单写了一个demo。
https://github.com/Kijin-Seija/dt-sql-parser-analyse-demo
应该可以解决一部分上面提出的问题。
我这边也有业务需求,主要方向是PostgreSQL。如果方便的话,我很乐意参与讨论和共建,或者作为一个plugin支持。
Based on the previous business requirements, I've written a simple demo.
https://github.com/Kijin-Seija/dt-sql-parser-analyse-demo
It should be able to address some of the issues mentioned above.
I also have business requirements, mainly focused on PostgreSQL. If it's convenient, I'd be happy to participate in discussions and collaboration, or to support as a plugin.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 5
-
@Kijin-Seija 抱歉这么晚才回复
目前收集实体功能已经初步完成了,并发布了。
你可以通过安装 4.0.0-beta.4.12 版本来体验。
目前的收集实体逻辑确实存在一些问题,我会尽快查看你提供的 demo,非常感谢你的贡献。
Beta Was this translation helpful? Give feedback.
All reactions
-
不客气,我这边主要还是提供一些点子,希望能有所帮助。后续也有一些基于我这边业务中遇到的情况进行一些优化。主要专注于 pg
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1