Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何根据SQL语句上下文得知别名与实体的关系 #151

Open
resetsix opened this issue Oct 17, 2024 · 10 comments
Open

如何根据SQL语句上下文得知别名与实体的关系 #151

resetsix opened this issue Oct 17, 2024 · 10 comments
Labels
question Further information is requested

Comments

@resetsix
Copy link
Contributor

Issue 区分

自动补全方式

实现自动补全的方式有两种:

  1. 前端静态补全:如使用 monaco-sql-languages
  2. 后端动态补全:如使用 LSPLanguage Server Protocol,语言服务器协议)。

核心原理

通过预测用户意图并提供可能的输入选项,基于已输入的内容和上下文信息,推断用户可能想要输入的内容。

实现步骤

  1. 词法分析(Lexical Analysis)

    • 操作:将输入的文本分解为标记(tokens)。
    • 目标:识别出关键字、标识符、运算符等。
  2. 语法分析(Syntactic Analysis)

    • 操作:基于词法分析结果,理解代码结构,构建语法树。
    • 目标:确定当前输入在语法树中的位置。
  3. 语义分析(Semantic Analysis)

    • 操作:理解代码的语义,分析变量作用域、类型信息等。
    • 目标:推断出当前光标位置应该提示什么类型的数据。
  4. 上下文感知(Context Awareness)

    • 操作:结合当前光标位置,分析上下文代码结构及之前的输入。
    • 目标:理解当前环境,确保提示项符合上下文。
  5. 候选项生成(Candidate Generation)

    • 操作:根据词法、语法、语义分析生成可能的补全选项。
    • 目标:生成如关键字、变量名、函数名等补全内容。
  6. 排序和过滤(Sorting & Filtering)

    • 操作:根据上下文的相关性和历史使用频率,对候选项进行排序。
    • 目标:过滤掉不符合当前上下文的无关项,并优化排序。

实现方法

  • 静态分析:基于预定义的语言规则和规范生成补全项。
  • 动态分析:考虑运行时信息和项目特定的上下文(如自定义函数、表别名等)。

数据来源

  • 语言规范:关键字、内置函数等基础补全内容。
  • 项目代码:如自定义别名、函数、变量等。
  • 上下文:用户已输入的代码内容,如库、表等。

AST 与动态编辑的局限

AST(抽象语法树)虽然可以帮助解析 SQL 语句的结构,但它只适合静态分析的场景。

在动态编辑环境中,SQL 语句往往不完整或存在语法错误,此时生成 AST 可能会影响性能。因此AST并不适用于实时编辑器的输入场景。

LSP 的优势

LSP更加适合动态编辑环境,它能够持续分析不完整或有语法错误的代码,并提供自动补全、跳转到定义等功能。通过增量更新可以提高性能,适合实时代码编辑和补全需求。

前端实现表别名与表名映射(重点)

虽然使用LSP有颇多好处,但十分依赖后端服务。

如果站在前端角度来看待自动补全别名处理(这也是monaco-sql-languages正在实现的愿景),可能需要如下步骤:

  1. 使用正则表达式识别 AS 语句或类似别名定义的语句。(无法精准匹配,因为 AS 关键字可以省略)
  2. 维护一个表别名与表名的映射表,记录所有别名和其对应的表名。
  3. 实时更新这个映射表,在用户输入 SQL 语句时,结合光标位置和上下文分析表别名。
  4. Select u. from user AS u语句中假设用户正在输入u.,那么通过查询映射表找到对应的表名(如 user),并提供列的自动补全。

别名处理会更加相对复杂,因为除了表可以作为别名还有字段、计算函数、子查询等等。

目前在前端处理别名映射我并没有想到一个完美解决方案和思路,例如AS关键字省略怎么办?

@openai0229
Copy link

lsp不是一个交互协议而已吗?为什么lsp能持续分析不完整或有语法错误的代码?

@resetsix
Copy link
Contributor Author

lsp不是一个交互协议而已吗?为什么lsp能持续分析不完整或有语法错误的代码?

LSP 本身确实只是一个协议,但它实际是作为连接编辑器和语言服务器的桥梁。真正进行代码分析的是语言服务器(Language Server) ,而不是协议本身。

举个具体例子:

前端 TypeScript        LSP协议        后端 Java(例如)
(Monaco Editor) <----------------> (SQL Language Server)
  1. 用户输入:

|代表光标位置。

SELECT * FROM u|
  1. 编辑器通过LSP发送请求:
textDocument/didChange: {
    "content": "SELECT * FROM u"
}
  1. Java实现的语言服务器接收并分析:
  • 识别这是一个不完整的 SQL 查询
  • 通过语法解析器(如 ANTLR)分析
  • 检查数据库表结构,发现可能的表名(user, users 等)
  1. 通过LSP返回结果:
  • 诊断信息(SQL语句未完成)
  • 可能的表名补全建议:
    • user
    • users
    • user_roles
    • ...

@openai0229
Copy link

要识别这种不完整的情况也很棘手,再加上在编辑器中那么多sql,又要准确获取到当前正在写的这段sql的上下文也麻烦

@JackWang032
Copy link
Collaborator

dt-sql-parser 现在已经支持了对别名、Comment等额外实体的收集,这些别名实体会作为额外信息标注在普通实体内,
select t1. from table1 as t1 可以拿到实体信息为
image
可以通过isContainCaret 判断这个实体是不是在当前光标输入所处的statement中,当然如果 SQL 未输入完全导致语法解析树存在错误,可能会有收集准确度的问题。

@resetsix
Copy link
Contributor Author

要识别这种不完整的情况也很棘手

常规步骤都是:获取所有文本,转换为抽象语法树,随后进行词法分析(Lexer)、语法分析(Parser)、遍历 AST 节点。接着才是实现高级功能如: SQL 校验、 自动补全、收集表名字段名。

再加上在编辑器中那么多sql,又要准确获取到当前正在写的这段sql的上下文也麻烦

当前位置的上下文 就需要在前面语法树的基础上获取用户光标位置,然后根据;等关键信息来标识。

目前我是这么实现,通过;来判断当前光标所在语句是否是独立语句。当然也可以根据Select等关键字来继续精准判断。

@liuxy0551
Copy link
Collaborator

要识别这种不完整的情况也很棘手

常规步骤都是:获取所有文本,转换为抽象语法树,随后进行词法分析(Lexer)、语法分析(Parser)、遍历 AST 节点。接着才是实现高级功能如: SQL 校验、 自动补全、收集表名字段名。

再加上在编辑器中那么多sql,又要准确获取到当前正在写的这段sql的上下文也麻烦

当前位置的上下文 就需要在前面语法树的基础上获取用户光标位置,然后根据;等关键信息来标识。

目前我是这么实现,通过;来判断当前光标所在语句是否是独立语句。当然也可以根据Select等关键字来继续精准判断。

; 会更准确一点,以 SELECT 等可以作为语句开头关键词判断容易出现子语句切分异常的问题,DTStack/dt-sql-parser#334 (comment) ,我们在开发中遇到过,所以建议以 ; 判断,不过这要求用户在语句结尾书写 ;

@resetsix
Copy link
Contributor Author

dt-sql-parser 现在已经支持了对别名、Comment等额外实体的收集,这些别名实体会作为额外信息标注在普通实体内

太赞了👍🏻!别名这个特性真的太常用了。

另外我有几个疑惑:

  1. 目前monaco-sql-languages也集成了收集别名新特性吗。
  2. as关键字省略的时候也可以收集到别名吗。
  3. 列别名,表别名和派生表别名都能够收集到吗。

@resetsix
Copy link
Contributor Author

; 会更准确一点,以 SELECT 等可以作为语句开头关键词判断容易出现子语句切分异常的问题,DTStack/dt-sql-parser#334 (comment) ,我们在开发中遇到过,所以建议以 ; 判断,不过这要求用户在语句结尾书写 ;

没毛病。

@JackWang032
Copy link
Collaborator

dt-sql-parser 现在已经支持了对别名、Comment等额外实体的收集,这些别名实体会作为额外信息标注在普通实体内

太赞了👍🏻!别名这个特性真的太常用了。

另外我有几个疑惑:

  1. 目前monaco-sql-languages也集成了收集别名新特性吗。
  2. as关键字省略的时候也可以收集到别名吗。
  3. 列别名,表别名和派生表别名都能够收集到吗。
  1. 4.x已经集成,通过completionService中第5个参数获取实体列表
  2. 可以收集到, sql-parser中并不是以 as 作为标识来识别别名的
  3. 目前只收集到了表别名和定义表、列的Comment, 派生别名目前还未收集

@openai0229
Copy link

有没有交流群呢?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants