Skip to content

fix(workspace): build the catalog the same way for lint, hover, completions and inlay hints - #974

Open
psteinroe wants to merge 1 commit into
perf/analyser-shared-astsfrom
fix/workspace-shared-statement-walk
Open

psteinroe wants to merge 1 commit into
perf/analyser-shared-astsfrom
fix/workspace-shared-statement-walk

Conversation

@psteinroe

Copy link
Copy Markdown
Collaborator

Stacked on #965.

The analyser and the workspace each had a loop that follows the catalog through the statements of a file: Analyser::analyse for lint and typecheck, and WorkspaceServer::walk_statements for hover, completions and inlay hints. On main there was a third one, snapshot_before, which ignored sections, unparsable statements and identifier parameters; #956 already moved completions and hover onto walk_statements. The two remaining loops still differed.

What was broken

The workspace's loop only switched to a new migration section when it reached a statement in it. Completions walk up to the start of the statement being completed, so in the first statement after a down section they saw the down section's changes, while lint didn't:

-- migrate:up
create table items (id int);
-- migrate:down
alter table items add column down_only int;
-- migrate:up
select | from items;                -- completions offered down_only
select down_only from items;        -- lint: unknownColumn

Statements with identifier parameters were handled differently too:

  • The analyser applied create table :name (...) to the catalog with the placeholder name, so the file then had a table name. It stopped typechecking right after, so this didn't show in lint, but it is wrong for anything else that reads the catalog.
  • The workspace tainted the catalog instead, but also for statements in the bodies of SQL functions, which don't run.

What changed

  • pgls_analyser::walk_statements(WalkParams, visit) in a new walk.rs is the only walk. It holds the section handling (each section has its own session and transaction mode, and a down section's changes are dropped afterwards), unparsable statements tainting the catalog, and function bodies not being applied. visit gets each statement with its AnalysedFileContext. WalkParams::end stops the walk, and the walk returns the catalog that a statement at end sees. The section that contains end decides which catalog that is, not the last statement walked, and that fixes the completions case above.
  • Analyser::analyse runs the rules in visit. WorkspaceServer::walk_statements collects the statements that start before end and calls the same function. Hover and inlay hints skip statements with identifier parameters in their visitor, like before.
  • Identifier parameters: I kept the analyser's behaviour where it is right and the workspace's where the analyser's was wrong. A statement on placeholder names that isn't a plain query taints the catalog and isn't applied, but only if it runs. The analyser still turns typecheck off for the rest of the file after one, since it should only report what Postgres would certainly reject, and a tainted catalog can still have wrong column types. Hover, completions and inlay hints keep working on the tainted catalog. They are best-effort, and nothing they show claims an error.
  • may_change_catalog and is_plain_query moved into walk.rs and are now private to the crate. They had no other users.

Tests

lint_hover_and_completions_see_the_same_catalog (workspace) checks typecheck diagnostics, completions and hover in the same file for three cases: the first statement after a down section, a statement after one that doesn't parse, and a statement after create table :tbl. On #965, the down-section case fails: completions include down_only. The other two pass there and pin the shared behaviour. pgls_analyser (rules specs, Postgres regression), pgls_workspace (including the memory and edit-churn tests) and pgls_lsp pass.

Performance

Release build, 5,000-statement dbmate file. Each iteration changes a table name in the middle of the file, then requests hover on the last query, completions at the end, inlay hints over the last 2 KB, and diagnostics. The numbers are averages over 20 keystrokes. Runs alternated between the two branches on a shared machine, so expect about ±15% noise.

per keystroke #965 this PR
hover 103–158 ms 108–121 ms
completions 172–259 ms 175–199 ms
inlay hints 54–115 ms 50–59 ms
diagnostics 377–700 ms 338–408 ms

Memory tests (release, defaults): memory_bounds one-document peak 25.7 → 26.0 MiB (the workspace walk now collects the statements before walking them), all-open peak 242.4 MiB and retained 220.7 MiB unchanged. edit_churn retains 32.9 / 36.1 MiB on both.

Not covered

Hover and completions still walk every statement before the cursor after each change, about 100 ms at 5,000 statements in release. Caching catalogs at points in the file would fix that, but it is a separate change.

…etions and inlay hints

The analyser and the workspace each had their own loop that follows the catalog through the
statements of a file. The workspace's loop only switched to a new migration section when it
reached a statement in it, so completions in the first statement after a down section saw the
changes of the down section. Both now use `pgls_analyser::walk_statements`, which also returns
the catalog as a statement at the end of the walk sees it.

Statements with identifier parameters like `create table :name` taint the catalog instead of
being applied with their placeholder names, and only statements that run do so, not the bodies
of SQL functions. The analyser still stops typechecking after such a statement.

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant