Repository navigation
Conversation
pgls_query_ext::function_body finds the statements of '...', E'...' and $$ bodies by the token after AS, BEGIN ATOMIC statements, and RETURN expressions, with an OffsetMap from each statement's text to its source. The document caches them per statement and hands out DocumentStatements that carry the map, so the analyser and the editor features place spans through it. The test harnesses use the same extractor.
This was referenced Oct 26, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #989.
This is the first PR of a rebuild of #918, #929, #930 and the PRs stacked on them. Those built two cache systems and three walks over the statements of a document, side by side. The rebuild replaces them with one design, in a smaller stack from
main. The design comes first, then what this PR does.The design
1. One extractor for embedded statements.
pgls_query_ext::function_bodyis the only code that finds the statements of a SQL function body:'...',E'...'and$$...$$strings,BEGIN ATOMIC ... END, andRETURN expr. Each is anEmbeddedStatement { sql, range, offsets }: the text that is parsed, where it is in itsCREATE FUNCTION, and anOffsetMapbetween the two. The workspace, the analyser's test harnesses and the Postgres regression harness use it.2. The document owns its statements. The document's iterator yields
DocumentStatement { id, range, offsets }for top-level and embedded statements. The embedded statements of a top-level statement are computed once per statement text, next to its parse tree. Features map spans withdocument_range/statement_offseton the statement and never addrange.start()by hand.3. One walk.
pgls_analyser::walk_statementsis the only loop that follows the catalog and the session through the statements of a file, with the migration sections, unparsable statements, function bodies and identifier parameters handled once. Lint runs its rules in it; hover, completions, inlay hints, semantic tokens and code actions resolve in it. It can resume from aWalkStatetaken at a top-level statement.4. One cache per document, in one module.
server/document_cacheowns everything a document caches, and its types are private to it:CacheKey(the settings version and the identity of the catalog base, which is built from the database snapshot): the analysis, a few walk checkpoints, the resolved references of the statements that requests looked at, and the snapshot a statement sees.An edit keeps every entry whose top-level statement starts before the change and whose preceding statements didn't move, and drops the rest. That is one rule for all key-dependent entries, so there is no hashing of the text before a statement on every request.
5. One memory budget. A
CacheBudgetacross documents evicts the caches of the least recently used documents when all of them together are over the limit. It counts the entries of all the caches above, and the memory and edit-churn tests from #951/#953/#956/#957 check it.6. Requests off the runtime. Documents are shared as
Arc<Document>; editor requests run on the blocking pool on a snapshot of the document, stop when it changes or the request is cancelled, and entries computed on an outdated version are not stored.The stack
server.rsdocument_cachemodule, moves only (4)main)inlayHints.parameterNamesThis PR
Statements in
BEGIN ATOMICbodies,RETURNbodies, and'...'orE'...'bodies with escapes got no semantic tokens, inlay hints, hover, or lint diagnostics, because the body was found withcontent.find(&body)on the unescaped body. That also matched an earlier occurrence of the same text, like a parameter default'select 1'beforeas 'select 1'.What changed
pgls_query_ext::function_body(replacesutils::sql_function_bodyandSqlFunctionBody::statements):AS '...',AS E'...',AS $tag$...$tag$: the string constant right after anAStoken whose value is the body. The value of'...'andE'...'is read like Postgres' lexer reads it (scan.lat REL_18_6):'',\b \f \n \r \t \v, octal,\x,\u/\U, and\before any other character. The body is split into statements like before (fix(workspace): analyse every statement of a SQL function body #947), and each statement gets the part of the offset map that covers it.BEGIN ATOMIC: one statement per;, tracking parentheses andCASE ... END, up to theENDthat starts a statement. If the count doesn't match the parsed body, there are none.RETURN expr: resolved asselect expr, which has the same length. The map marksselectas standing forreturn, so the text counts as rewritten.OffsetMap::is_verbatimtells whether a statement's text is its source. Fixes are only kept on verbatim statements.function_body_locationlocates the string body of a function in any language. The PL/pgSQL syntax error uses it, instead ofcontent.find, and covers the whole statement if the body can't be found instead of being skipped.pgls_analyser::AnalysableStatementcarries theOffsetMap, withdocument_range,document_offsetandstatement_offset. The analyser places diagnostics with it.AnalysableStatement::embeddedbuilds one from anEmbeddedStatement, which is all the test harnesses still do themselves.PgQueryStore::get_or_cache_embeddedkeeps the embedded statements of a statement next to its parse tree.ParseIteratoryields them asDocumentStatements, and mappers and filters take aDocumentStatement. Hover, completions (CompletionsResult::in_document), inlay hints and semantic tokens map through it. The expand-*action is not offered in a statement whose text isn't verbatim, since its edit would need escaping.U&'...'and PL/pgSQL bodies.Tests
function_body: splitting string bodies, a default with the same text as the body,$body$quoting,''mapped through two statements of one body,E'...'escapes, the unescaping rules and what isn't matched,BEGIN ATOMICwithCASE ... END,RETURN, and the offset map in both directions.pg_query: PL/pgSQL syntax error spans for a body with'', and for a body whose text also appears earlier, in a parameter default.test_sql_function_bodies(tokens, hover and anunknownColumndiagnostic inBEGIN ATOMIC,RETURN,''andE'...'bodies), and formatting of a''body (now formatted like other bodies, into$$) and an idempotentBEGIN ATOMICfunction.create_function_sql.sql:203and two inwindow.sql), so the test passes.pgls_analyser,pgls_query_ext,pgls_workspaceandpgls_lsppass, and clippy on the whole workspace.Cost
Each top-level statement is scanned once per statement text to find its body, only if it is a
CREATE FUNCTIONof a SQL function, and the result is cached with its parse tree. Before, every walk over the document searched each SQL function's text for its body again.Not covered
U&'...'bodies, and editor features in PL/pgSQL bodies (only the syntax check).