Skip to content

feat(sqlserver): complete table-valued functions in FROM/JOIN context - #13394

Merged
t8y2 merged 4 commits into
t8y2:mainfrom
yiqiui:feat/13131-sqlserver-tvf-completion
Nov 11, 2026
Merged

t8y2 merged 4 commits into
t8y2:mainfrom
yiqiui:feat/13131-sqlserver-tvf-completion

Conversation

@yiqiui

@yiqiui yiqiui commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #13131.

The completion metadata for SQL Server already returns table-valued functions (FN/IF/TF/FS/FT mapped to FUNCTION in sqlserver_list_objects_sql), but the completion engine only surfaced them in routine contexts. A TVF is a relation source — SELECT * FROM dbo.Split(@csv) — so after FROM/JOIN the candidate set (tables + views) never included it and there was nothing to complete.

Change — on sqlserver, the table-suggestion branch now also converts function-typed completion objects into table candidates (type: "function", signature as detail). Scalar functions keep working everywhere they did (routine context, SELECT list); procedures stay out of the FROM set; every other engine is untouched.

Behavior

Context Before After
SELECT * FROM dbo. tables/views only + TVFs (schema-filtered like tables)
SELECT * FROM tables/views only + TVFs from all schemas
SELECT GetCustomerName( routine completion unchanged
procedure after FROM not offered still not offered
other engines unchanged unchanged

Test plan

  • New sqlCompletion.sqlserverTvf.spec.ts: TVFs offered after FROM/qualified FROM; procedures excluded; no leakage into other engines — 3/3
  • Neighboring completion suites green (context/dolt/cockroachdb): 290/290
  • vue-tsc typecheck clean; oxfmt clean

TVFs are relation sources used directly in FROM/JOIN, so the function
objects from the completion metadata now join the table candidate set on
sqlserver; procedures and other engines are unaffected (t8y2#13131).
@yiqiui yiqiui closed this Oct 24, 2026
@yiqiui yiqiui reopened this Oct 24, 2026
@yiqiui

yiqiui commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

CI 状态说明:本轮 frontend-checks 的失败文件是 apps/desktop/src/lib/table/tableEditing.ts —— 本 PR 的 diff 不包含该文件(只改了 sqlCompletion.ts、新增 spec),格式问题来自 merge 时引入的上游侧代码(上游正在连续修复 CI,fix(ci): repair #12917 系列提交)。已用当前 main 的同名文件本地验证格式是干净的,待上游修复随 merge 重跑即可转绿;如仍红我再跟进。

@yiqiui yiqiui closed this Oct 24, 2026
@yiqiui yiqiui reopened this Oct 24, 2026
zipg
zipg previously requested changes Oct 28, 2026

@zipg zipg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 当前实现把所有 object.type === "function" 都加入 FROM/JOIN,并未区分表值函数与标量函数。现有 SQL Server 元数据把 FN/FS(标量)和 IF/TF/FT(表值)都映射为 FUNCTION,所以 GetCustomerName 这类标量函数也会被当成关系来源推荐;SELECT * FROM dbo.GetCustomerName(...) 随后执行失败。PR 注释和说明中的“Scalar functions stay in the routine context only”与代码行为不一致。

支持 TVF 的需求合理,但应在元数据/补全契约中保留返回表类型的区别,再仅将真正的 IF/TF/FT 放入 FROM/JOIN;未知类别应保守处理。请补充同一 schema 同时存在标量函数和 TVF 的用例,断言 TVF 出现、标量函数和过程不出现,同时保留 SELECT 表达式里的标量函数补全,并验证带参数函数的插入结果。类型依据:Microsoft sys.objects 文档。

这需要补齐上下游类型信息并重新验证,暂不代改或合并。涉及补全候选界面,修改后建议补充 FROM/JOIN 候选和 SELECT 表达式候选的截图,展示 TVF/标量函数的区分。

…letion

The completion assistant reports the return kind through dataType:
IF/TF/FT functions come back as "table" while scalar FN/FS functions
carry their return type name. Filter on that kind so scalar functions
and unknown categories stay in the routine context only (t8y2#13394
review), and insert the parameterized call for TVFs with a known
signature.
@yiqiui

yiqiui commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

已按 review 意见重做(69d50d42a):

只把真正的表值函数放进 FROM/JOIN。 补全助手链路里本来就有返回类型信息:SQL Server 侧 sqlserver_completion_assistant_sql 的 data_type 表达式对 IF/TF/FT 返回 'table',标量函数 FN/FS 返回 parameter_id = 0 的实际返回类型名,前端 SqlCompletionObject.dataType 也一直保留着它。所以候选过滤改为:

object.type === "function" && object.dataType?.trim().toLowerCase() === "table"
  • IF/TF/FT(表值)→ 进入 FROM/JOIN 候选;
  • FN/FS(标量)→ 不再进入 FROM/JOIN,仍只在 SELECT 表达式/routine 上下文里补全;
  • 没有 dataType 的未知类别 → 保守排除(不会因为"可能是函数"就当成关系来源);
  • 有 signature 时插入带参数的调用形式(SplitCsv(${1:@csv NVARCHAR(MAX)})),因为 TVF 必须以调用形式出现在 FROM 中;无签名时保持纯名称。

用例(sqlCompletion.sqlserverTvf.spec.ts,5 个全部通过)覆盖了 review 要求的矩阵:同一 schema 同时存在 TVF(SplitCsv 带签名、TotalSales 无签名)、标量函数(GetCustomerName,dataType=nvarchar)、无类型信息的函数(UnknownKind)和过程(ArchiveOldOrders)——断言 TVF 出现、标量函数/未知类别/过程都不出现;dbo. 限定符下同样只出 TVF;SELECT GetCust 表达式上下文里标量函数仍被补全;带参数函数的插入结果断言为 SplitCsv(${1:@csv NVARCHAR(MAX)})。

全量 apps/desktop/src/lib/__tests__/sql/ 90 个文件 2861 个用例通过,oxfmt --check 与 vue-tsc --noEmit 干净。

关于截图:这个改动只影响补全候选列表,界面上的差异就是 FROM/JOIN 候选里不再出现标量函数。本机没有 SQL Server 实例可截图;如果你需要,我可以在有实例的环境里补 FROM/JOIN 与 SELECT 表达式两个候选列表的截图。

@yiqiui

yiqiui commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Addressed on 69d50d42a — the earlier path admitted every object.type === "function" into FROM/JOIN, which is exactly the scalar/table mix-up you flagged.

The completion assistant already carries the return kind in object.dataType: IF/TF/FT come back as the literal "table", while scalar FN/FS carry their actual return type name (e.g. nvarchar). The FROM/JOIN admission now filters to only object.type === "function" && object.dataType?.trim().toLowerCase() === "table":

  • IF/TF/FT (table-valued) → enter the table candidate set (and, since a TVF must be invoked as a call, insert the parameterized form SplitCsv(${1:@csv NVARCHAR(MAX)}) when a signature is present).
  • FN/FS (scalar, e.g. GetCustomerName) → no longer suggested as a relation source; they remain in the SELECT-expression / routine context only.
  • Unknown category (no dataType) → conservatively excluded.

sqlCompletion.sqlserverTvf.spec.ts covers the requested matrix: same schema with a TVF (SplitCsv, with signature; TotalSales, without), a scalar fn (GetCustomerName, dataType=nvarchar), a no-type fn (UnknownKind), and a procedure (ArchiveOldOrders) — asserts the TVFs appear, scalars/unknown/procedure do not; dbo.-qualified variants show only TVFs; SELECT GetCust… still completes the scalar in expression context; parameterized insert form is asserted. All 5 pass, and the frontend suite is green.

zipg
zipg previously requested changes Nov 2, 2026

@zipg zipg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复核更新,dataType === "table" 已解决之前混入标量函数的问题,但当前 HEAD 还有两处会生成错误 SQL 的缺口,需要作者继续调整。

  1. [P2] 零参数/无签名 TVF 仍插入裸名称。if (!signature) return item 让 TotalSales 的 apply 为 TotalSales,而 FROM 中应为 TotalSales()。即使函数没有参数也必须是调用形式。请覆盖空签名和缺失签名的插入结果。
  2. [P2] 显式 schema 未限制候选。新分支把所有 input.objects 的 TVF 直接传给 buildTableItems,未按 context 的 schema 筛选。在 SELECT * FROM dbo. 下,同时提供 dbo.DboRows 和 archive.ArchiveOnly,仍会推荐 ArchiveOnly;插入后成为错误的 dbo.ArchiveOnly。也请覆盖不同 schema 同名 TVF、各自不同参数签名的情况,避免仅按 name 建立的 signaturesByLabel 串用签名。

我在 exact HEAD 上补了临时评审用例实测:原有 5 项通过,新增两项分别得到 TotalSales(期望 TotalSales())及候选 [DboRows, ArchiveOnly](期望排除 ArchiveOnly),两项均失败。未修改作者分支;这些契约需要一起修正再验证。

修改后建议补充 FROM/JOIN 带 schema 的候选和 SELECT 表达式候选截图,展示 TVF/标量函数及 schema 范围。

@zipg zipg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已 push 7a450ef6 到 feat/13131-sqlserver-tvf-completion,处理上一轮的第 1 点:

  • apply 现在无条件走 buildRoutineApply,无签名/零参数的 TVF 也会插成 TotalSales(),不会再被插成 FROM TotalSales 这种裸名字。
  • 新增用例 inserts a call for zero-argument and signature-less table-valued functions 固定这个行为。

第 2 点(SELECT * FROM dbo. 时未按 context schema 过滤候选)我没有改动:buildTableItems 自己已经吃 context 和 currentSchema,要确认到底是 buildTableItems 的过滤没覆盖这个入口,还是这条新分支绕过了它。麻烦你在补用例时把 dbo.DboRows + archive.ArchiveOnly 的场景加进去;如果 buildTableItems 本身已经过滤,那只是缺测试,不用再改实现。

本地 apps/desktop/src/lib/__tests__/sql 全量 2862 项通过。

yiqiui added a commit to yiqiui/dbx that referenced this pull request Nov 11, 2026
…calls

Per the review on t8y2#13394: the raw object list leaked TVFs from other
schemas into a schema-qualified FROM ( offered
archive.ArchiveOnly), because buildTableItems qualifies labels but does
not filter its input — the regular tables arrive pre-scoped by their
provider, and the TVF branch bypassed that. The candidates are now
narrowed to the typed qualifier's schema, falling back to the current
schema, which also keeps the by-name signature lookup from crossing
schemas when the same function name exists twice.

And a table-valued function is a relation source only in call form: the
insert now always goes through buildRoutineApply, so zero-argument and
signature-less TVFs insert  instead of the bare name.
…calls

Per the review on t8y2#13394: the raw object list leaked TVFs from other
schemas into a schema-qualified FROM ("FROM dbo." offered
archive.ArchiveOnly), because buildTableItems qualifies labels but does
not filter its input - the regular tables arrive pre-scoped by their
provider, and the TVF branch bypassed that. The candidates are now
narrowed to the typed qualifier's schema, falling back to the current
schema, which also keeps the by-name signature lookup from crossing
schemas when the same function name exists twice.

And a table-valued function is a relation source only in call form: the
insert now always goes through buildRoutineApply, so zero-argument and
signature-less TVFs insert "TotalSales()" instead of the bare name.
@yiqiui
yiqiui force-pushed the feat/13131-sqlserver-tvf-completion branch from 0b00cc4 to e144b39 Compare November 11, 2026 15:37
@yiqiui

yiqiui commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

两点都已在分支上处理(注意:review 里提到的 7a450ef6 在 yiqiui/dbx 与 t8y2/dbx 都查不到,可能已被后续操作覆盖,因此两点都重新实现了一遍):

  1. 零参/无签名 TVF 一律插入调用形式:apply 无条件走 buildRoutineApply,TotalSales 这类无签名函数现在插入 TotalSales(),不再出现裸名称;有签名时仍插入带 snippet 参数的形式。
  2. 候选按 schema 收窄:常规表候选由提供方预先按 schema 圈定,而 TVF 分支此前直接喂了全量 objects 绕过了这层——现在按输入的限定符 schema(无限定符则 currentSchema)过滤,FROM dbo. 只会给出 dbo 的 TVF;这也顺带消除了同名函数跨 schema 串用签名的可能。

用例(sqlCompletion.sqlserverTvf.spec.ts,8/8 通过;__tests__/sql 全量 2864 项通过):

  • 零参/无签名 → 插入 TotalSales();
  • FROM dbo. 下同时提供 dbo.DboRows(@top INT) 与 archive.ArchiveOnly(@from DATE):只出现 DboRows,插入 DboRows(${1:@top INT}),ArchiveOnly 不出现;
  • 同名 SplitCsv 两 schema 不同签名:限定 dbo 后插入的是 dbo 的签名。

关于截图:完成候选拦截在 headless 契约测试里断言(候选集合与插入文本),本环境无法出 UI 截图;如需要我可以再看仓库有没有 headless gallery 之类的现成通道。

@t8y2 t8y2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both review rounds are resolved at e144b39: the FROM/JOIN branch now filters TVF candidates by qualifier schema (falling back to currentSchema) and builds signatures from the scoped set, with cross-schema coverage added. Zero-argument call-form insertion is retained. Verified locally on the exact head: the new spec passes 8/8 and the SQL completion suite shows no regressions.

@t8y2
t8y2 dismissed stale reviews from zipg and zipg November 11, 2026 16:14

Outdated changes-requested (commit 4f2f0f3); both rounds are addressed at e144b39 and re-verified.

@t8y2
t8y2 merged commit b2c6bf7 into t8y2:main Nov 11, 2026
26 checks passed
@t8y2

t8y2 commented Nov 11, 2026

Copy link
Copy Markdown
Owner

Thanks for the contribution! Merged in b2c6bf7, will be released in the next version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] sqlserver 的代码提示补全表值函数没有提示

3 participants