Skip to content

Commit 3bd0471

Browse files
data-douserCopilot
andauthored
Fixes for extension .lock database contention and tool improvements to avoid LLM use of grep (#119)
* Resolve database lock contention w/ vscode-codeql Resolves #117 Fixes a known compatibility issue for databases added, and therefore locked, via the GitHub.vscode-codeql extension. The vscode-codeql query server creates .lock files in the cache directory of every registered CodeQL database, preventing the ql-mcp server from running CLI commands (codeql_query_run, codeql_database_analyze) against those same databases. Add a DatabaseCopier that syncs databases from vscode-codeql storage into a managed directory under the `vscode-codeql-development-mcp-server` extension's globalStorage, stripping .lock files from the copy. The EnvironmentBuilder now sets CODEQL_DATABASES_BASE_DIRS to this managed directory by default (configurable via codeql-mcp.copyDatabases). - New DatabaseCopier class with incremental sync (skips unchanged databases) - StoragePaths.getManagedDatabaseStoragePath() for the managed databases/ dir - EnvironmentBuilder accepts injectable DatabaseCopierFactory for testability - codeql-mcp.copyDatabases setting (default: true) - 11 unit tests for DatabaseCopier (real filesystem operations) - 15 unit tests for EnvironmentBuilder (updated for copy mode + fallback) - 3 bridge integration tests (managed dir structure, no .lock files) - 4 E2E integration tests: inject .lock → copy → codeql_query_run + codeql_database_analyze succeed against the lock-free copy * Address PR review comments * Address more PR review comments * Add search_ql_code and codeql_resolve_files tools Add search_ql_code and codeql_resolve_files tools in order to eliminate grep/CLI dependencies. - New tools: search_ql_code (QL text/regex search) and codeql_resolve_files (file discovery by extension/glob) so LLMs never need shell access - Rewrite profile_codeql_query_from_logs with two-tier design: compact inline JSON + line-indexed detail file for targeted read_file access; parser now captures RA operations and pipeline-stage tuple progressions - Fix codeql_resolve_database to probe child directories for databases - Remove all grep/CLI references from prompts and resources - Cross-platform: normalize \r\n line endings in parser and search tool * Add "after" files for query evaluation integration tests * address Code Scanning TOCTOU race and PR review feedback - Eliminate filesystem race condition in search-ql-code.ts (read-then-check instead of stat-then-read) - Add symlink cycle detection using lstatSync and visited-path tracking - Fix tool description field names in profile-codeql-query-from-logs.ts ({startLine,endLine} → detailLines: {start,end}) - Fix monitoring-state.json fixtures to use standard sessions format - Rename find_qll_files → find_ql_files to match actual .ql extension * Stream large files instead of loading into memory - addresses latest review feedback for PR #119 - search-ql-code: check file size via lstatSync before reading; stream large files (>5 MB) line-by-line instead of skipping them - evaluator-log-parser: replace readFileSync with streaming async generator (createReadStream + readline) for brace-depth JSON parsing; parseEvaluatorLog now reads the file once instead of twice - profile-codeql-query: convert local parser to streaming with Map-based lookups instead of O(n) events.find() - database-copier: use lstat in removeLockFiles to skip symlinks; throw on fatal mkdir failures for proper fallback in EnvironmentBuilder - Validate contextLines/maxResults with schema bounds and clamping - Add environment-builder test for syncAll-throws fallback * Fix tool issues found during explain-codeql-query workflow testing - search_ql_code: add missing await in tool handler; skip .codeql, node_modules, and .git directories to avoid duplicate results from compiled pack caches - cli-tool-registry: extract resolveDatabasePath helper for multi-language DB root auto-resolution; apply to codeql_query_run, codeql_database_analyze, and codeql_resolve_database - environment-builder: route CODEQL_MCP_TMP_DIR to workspace-local .codeql/ql-mcp scratch directory (configurable via scratchDir setting); add CODEQL_MCP_WORKSPACE_FOLDERS env var - query-file-finder: add contextual hints array for missing tests, documentation, and expected results * [UPDATE PRIMITIVE] Fix transient HTTP 503 failures in install-packs.sh via exponential backoff retry (#121) * Initial plan * fix: add retry logic with exponential backoff to install-packs.sh The GitHub Actions integration test was failing on windows-latest with HTTP 503 "Egress is over the account limit" when downloading CodeQL packs from GHCR.io. Add a run_with_retry() helper function that retries a command up to 3 times with exponential backoff (10s, 20s, 40s). Both codeql pack install calls in install_packs() now use run_with_retry to handle transient network errors gracefully. Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> * deterministic profiler output and search efficiency - addresses latest feedback for PR #119 ; - profile-codeql-query-from-logs: remove non-deterministic `Generated:` timestamp from detail file header to ensure reproducible output for integration test fixtures ; - search-ql-code: early-exit file processing once maxResults matches are collected; subsequent files are scanned cheaply for totalMatches count only, avoiding large array allocations and context extraction ; * Fix TOCTOU bug for search_ql_code tool * Stream-count large files & detect ambiguous DB paths - search-ql-code: use streaming (readline) for totalMatches counting on large files in the early-exit path; eliminates TOCTOU race from prior lstatSync check - cli-tool-registry: resolveDatabasePath now collects all candidate children and throws on ambiguity instead of silently picking the first - Add tests for cross-file totalMatches accuracy under truncation, single- child DB auto-resolve, and multi-child DB ambiguity error * Address latest PR review comments * Use fstatSync(fd) to avoid OOM w/ searchFile --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent bb2cc42 commit 3bd0471

File tree

51 files changed

+4530
-954
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+4530
-954
lines changed

.github/instructions/server_src_ts.instructions.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,9 @@ This file contains instructions for working with TypeScript source code files in
2424
- PREFER the import of functionality from `@modelcontextprotocol/sdk` over direct implementation, unless absolutely necessary.
2525
- PREFER to implement each MCP server primitive in its own file named after the primitive, e.g., `server/src/<lib-example>/<primitive-example>.ts`.
2626
- PREFER many simple MCP server primitives that each do one thing well over fewer complex MCP server primitives that do many things.
27-
- PREFER copying and/or adapting existing `*.prompt.md` files matching one of the following patterns:
28-
- `ql/.github/prompts/*.prompt.md`
29-
- `ql/languages/*/tools/dev/*.prompt.md`
30-
- `ql/resources/{codeql,qlt}/*.prompt.md`
3127

3228
## CONTRAINTS
3329

3430
- NEVER leave any trailing whitespace on any line.
35-
- NEVER guess at what a `codeql` or `qlt` CLI subcommand does; ALWAYS verify against the official `codeql <subcommand> -h -vv` or `qlt <subcommand> -h` documentation, respectively.
31+
- NEVER guess at what a `codeql` CLI subcommand does; ALWAYS verify against the official `codeql <subcommand> -h -vv` documentation.
32+
- **NEVER use stat/lstat followed by a separate read/open on the same path** — this is a TOCTOU (Time-of-Check-Time-of-Use) race condition (CWE-367). Instead, attempt the operation directly (e.g., `readFileSync`) within a try/catch block. If you need to know the file size before reading, read first and then check the buffer size — do NOT stat then read. For directory traversal, `lstatSync` is acceptable since it is the operation itself (checking entry type), not a precursor to a separate operation.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Integration Test: codeql_resolve_files/find_ql_files
2+
3+
## Purpose
4+
5+
Tests the `codeql_resolve_files` tool to ensure it can find QL query files by extension within a CodeQL pack directory.
6+
7+
## Test Scenario
8+
9+
This test validates that the `codeql_resolve_files` tool can:
10+
11+
1. Accept a directory path and file extension filter
12+
2. Use the `codeql resolve files` CLI command to find matching files
13+
3. Return a list of file paths matching the specified criteria
14+
15+
## Test Parameters
16+
17+
- `dir`: "server/ql/javascript/examples/src"
18+
- `include-extension`: [".ql"]
19+
20+
## Expected Behavior
21+
22+
The tool should:
23+
24+
1. Invoke `codeql resolve files` with the specified directory and extension filter
25+
2. Return file paths for all `.ql` files found in the directory tree
26+
3. Return a successful result with the file listing
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"sessions": [
3+
{
4+
"id": "integration_test_session",
5+
"calls": [
6+
{
7+
"tool": "codeql_resolve_files",
8+
"timestamp": "2025-09-25T16:06:00.000Z",
9+
"status": "success"
10+
}
11+
]
12+
}
13+
]
14+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"sessions": [],
3+
"parameters": {
4+
"dir": "server/ql/javascript/examples/src",
5+
"include-extension": [".ql"]
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# CodeQL Evaluator Profile — Predicate Detail
2+
# Log format: raw
3+
# CodeQL version: 2.23.1
4+
# Use read_file with line ranges from the JSON response to access individual predicates.
5+
6+
== Query: QueryA.ql ==
7+
Total: 200.00ms | Predicates evaluated: 2 | Cache hits: 1
8+
9+
--- QueryA::source/0#abc123 ---
10+
Eval order: 1 of 2
11+
Duration: 65.00 ms
12+
Result: 5 tuples
13+
Strategy: SIMPLE_INTENSIONAL
14+
Position: /workspace/QueryA.ql:10,1-12,5
15+
Pipeline stages (1):
16+
[1] 50.00ms -> 5 tuples (counts=[5])
17+
18+
--- QueryA::sink/0#def456 ---
19+
Eval order: 2 of 2
20+
Duration: 45.00 ms
21+
Result: 3 tuples
22+
Strategy: SIMPLE_INTENSIONAL
23+
Position: /workspace/QueryA.ql:14,1-16,5
24+
Dependencies (1):
25+
- QueryA::source/0#abc123
26+
Pipeline stages (1):
27+
[1] 30.00ms -> 3 tuples (counts=[3])
28+
29+
== Query: QueryB.ql ==
30+
Total: 300.00ms | Predicates evaluated: 3 | Cache hits: 0
31+
32+
--- QueryB::entryPoint/0#jkl012 ---
33+
Eval order: 1 of 3
34+
Duration: 95.00 ms
35+
Result: 8 tuples
36+
Strategy: EXTENSIONAL
37+
Position: /workspace/QueryB.ql:8,1-10,3
38+
Pipeline stages (1):
39+
[1] 80.00ms -> 8 tuples (counts=[8])
40+
41+
--- QueryB::flowStep/2#mno345 ---
42+
Eval order: 2 of 3
43+
Duration: 95.00 ms
44+
Result: 12 tuples
45+
Strategy: SIMPLE_INTENSIONAL
46+
Position: /workspace/QueryB.ql:12,1-18,5
47+
Dependencies (1):
48+
- QueryB::entryPoint/0#jkl012
49+
Pipeline stages (1):
50+
[1] 80.00ms -> 12 tuples (counts=[12])
51+
52+
--- QueryB::result/3#pqr678 ---
53+
Eval order: 3 of 3
54+
Duration: 45.00 ms
55+
Result: 2 tuples
56+
Strategy: SIMPLE_INTENSIONAL
57+
Position: /workspace/QueryB.ql:20,1-22,5
58+
Dependencies (2):
59+
- QueryB::flowStep/2#mno345
60+
- QueryB::entryPoint/0#jkl012
61+
Pipeline stages (1):
62+
[1] 30.00ms -> 2 tuples (counts=[2])

client/integration-tests/primitives/tools/profile_codeql_query_from_logs/multi_query_raw_log/after/query-evaluation-profile.json

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,19 @@
1414
"resultSize": 5,
1515
"pipelineCount": 1,
1616
"evaluationStrategy": "SIMPLE_INTENSIONAL",
17-
"dependencies": []
17+
"dependencies": [],
18+
"pipelineStages": [
19+
{
20+
"durationMs": 50,
21+
"resultSize": 5,
22+
"counts": [
23+
5
24+
],
25+
"duplicationPercentages": [
26+
0
27+
]
28+
}
29+
]
1830
},
1931
{
2032
"predicateName": "QueryA::sink/0#def456",
@@ -25,6 +37,18 @@
2537
"evaluationStrategy": "SIMPLE_INTENSIONAL",
2638
"dependencies": [
2739
"QueryA::source/0#abc123"
40+
],
41+
"pipelineStages": [
42+
{
43+
"durationMs": 30,
44+
"resultSize": 3,
45+
"counts": [
46+
3
47+
],
48+
"duplicationPercentages": [
49+
0
50+
]
51+
}
2852
]
2953
}
3054
],
@@ -42,7 +66,19 @@
4266
"resultSize": 8,
4367
"pipelineCount": 1,
4468
"evaluationStrategy": "EXTENSIONAL",
45-
"dependencies": []
69+
"dependencies": [],
70+
"pipelineStages": [
71+
{
72+
"durationMs": 80,
73+
"resultSize": 8,
74+
"counts": [
75+
8
76+
],
77+
"duplicationPercentages": [
78+
0
79+
]
80+
}
81+
]
4682
},
4783
{
4884
"predicateName": "QueryB::flowStep/2#mno345",
@@ -53,6 +89,18 @@
5389
"evaluationStrategy": "SIMPLE_INTENSIONAL",
5490
"dependencies": [
5591
"QueryB::entryPoint/0#jkl012"
92+
],
93+
"pipelineStages": [
94+
{
95+
"durationMs": 80,
96+
"resultSize": 12,
97+
"counts": [
98+
12
99+
],
100+
"duplicationPercentages": [
101+
0
102+
]
103+
}
56104
]
57105
},
58106
{
@@ -65,6 +113,18 @@
65113
"dependencies": [
66114
"QueryB::flowStep/2#mno345",
67115
"QueryB::entryPoint/0#jkl012"
116+
],
117+
"pipelineStages": [
118+
{
119+
"durationMs": 30,
120+
"resultSize": 2,
121+
"counts": [
122+
2
123+
],
124+
"duplicationPercentages": [
125+
0
126+
]
127+
}
68128
]
69129
}
70130
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# CodeQL Evaluator Profile — Predicate Detail
2+
# Log format: raw
3+
# CodeQL version: 2.23.1
4+
# Use read_file with line ranges from the JSON response to access individual predicates.
5+
6+
== Query: ExampleQuery1.ql ==
7+
Total: 203.81ms | Predicates evaluated: 9 | Cache hits: 1
8+
9+
--- Files::Container.splitAbsolutePath/2#dispred#43b82b7b_120#join_rhs ---
10+
Eval order: 2 of 9
11+
Duration: 58.52 ms
12+
Result: 27 tuples
13+
Strategy: SIMPLE_INTENSIONAL
14+
Position: /home/runner/.codeql/packages/codeql/util/2.0.18/codeql/util/FileSystem.qll:220,5-221,90
15+
Dependencies (1):
16+
- cached_Files::Container.splitAbsolutePath/2#dispred#43b82b7b
17+
RA operations (2 steps):
18+
{3} r1 = SCAN `cached_Files::Container.splitAbsolutePath/2#dispred#43b82b7b` OUTPUT In.1, In.2, In.0
19+
return r1
20+
Pipeline stages (1):
21+
[1] 27.98ms -> 27 tuples (counts=[27])
22+
23+
--- files ---
24+
Eval order: 1 of 9
25+
Duration: 20.94 ms
26+
Result: 1 tuples
27+
Strategy: EXTENSIONAL
28+
Position: /home/runner/.codeql/packages/codeql/javascript-all/2.6.11/semmlecode.javascript.dbscheme:21,1-24,3
29+
30+
--- Files::Impl::File.getURL/0#dispred#3f789d74#bf ---
31+
Eval order: 6 of 9
32+
Duration: 11.28 ms
33+
Result: 1 tuples
34+
Strategy: SIMPLE_INTENSIONAL
35+
Position: /home/runner/.codeql/packages/codeql/util/2.0.18/codeql/util/FileSystem.qll:220,5-221,90
36+
Dependencies (2):
37+
- m#Files::Impl::File.getURL/0#dispred#3f789d74#bf
38+
- Files::Container.getAbsolutePath/0#dispred#051b95e1#bf
39+
RA operations (3 steps):
40+
{4} r1 = JOIN `m#Files::Impl::File.getURL/0#dispred#3f789d74#bf` WITH `Files::Container.getAbsolutePath/0#dispred#051b95e1#bf` ON FIRST 1 OUTPUT Lhs.0, _, Rhs.1, _
41+
{2} | REWRITE WITH Tmp.1 := "file://", Tmp.3 := ":0:0:0:0", Out.1 := (Tmp.1 ++ In.2 ++ Tmp.3) KEEPING 2
42+
return r1
43+
Pipeline stages (1):
44+
[1] 6.74ms -> 1 tuples (counts=[1, 1])
45+
46+
--- m#Files::Impl::File.getURL/0#dispred#3f789d74#bf ---
47+
Eval order: 3 of 9
48+
Duration: 9.62 ms
49+
Result: 1 tuples
50+
Strategy: SIMPLE_INTENSIONAL
51+
Position: /home/runner/.codeql/packages/codeql/util/2.0.18/codeql/util/FileSystem.qll:220,5-221,90
52+
Dependencies (2):
53+
- Files::Container.splitAbsolutePath/2#dispred#43b82b7b_120#join_rhs
54+
- files
55+
RA operations (4 steps):
56+
{2} r1 = CONSTANT(unique string, unique int)["ExampleQuery1.js",1]
57+
{1} | JOIN WITH `Files::Container.splitAbsolutePath/2#dispred#43b82b7b_120#join_rhs` ON FIRST 2 OUTPUT Rhs.2
58+
{1} | JOIN WITH files ON FIRST 1 OUTPUT Lhs.0
59+
return r1
60+
Pipeline stages (1):
61+
[1] 3.57ms -> 1 tuples (counts=[1, 1, 1])
62+
63+
--- #select#query ---
64+
Eval order: 9 of 9
65+
Duration: 9.01 ms
66+
Result: 1 tuples
67+
Strategy: SIMPLE_INTENSIONAL
68+
Position: /home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/server/ql/javascript/examples/src/ExampleQuery1/ExampleQuery1.ql:13,1-15,80
69+
Dependencies (5):
70+
- m#Files::Impl::File.getURL/0#dispred#3f789d74#bf
71+
- Files::Container.getAbsolutePath/0#dispred#051b95e1#bf
72+
- Files::Container.getAbsolutePath/0#dispred#051b95e1#bf_0#antijoin_rhs
73+
- Files::Impl::File.getURL/0#dispred#3f789d74#bf
74+
- Files::Impl::File.getURL/0#dispred#3f789d74#bf_0#antijoin_rhs
75+
RA operations (21 steps):
76+
{2} r1 = JOIN `m#Files::Impl::File.getURL/0#dispred#3f789d74#bf` WITH `Files::Container.getAbsolutePath/0#dispred#051b95e1#bf` ON FIRST 1 OUTPUT Lhs.0, Rhs.1
77+
78+
{1} r2 = `m#Files::Impl::File.getURL/0#dispred#3f789d74#bf` AND NOT `Files::Container.getAbsolutePath/0#dispred#051b95e1#bf_0#antijoin_rhs`(FIRST 1)
79+
{2} | SCAN OUTPUT In.0, _
80+
{2} | REWRITE WITH Out.1 := "(no string representation)"
81+
82+
{2} r3 = r1 UNION r2
83+
{4} | JOIN WITH `Files::Impl::File.getURL/0#dispred#3f789d74#bf` ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Rhs.1, _
84+
{4} | REWRITE WITH Out.3 := "Example test code file found for codeql_test_extract example query."
85+
86+
{1} r4 = `m#Files::Impl::File.getURL/0#dispred#3f789d74#bf` AND NOT `Files::Container.getAbsolutePath/0#dispred#051b95e1#bf_0#antijoin_rhs`(FIRST 1)
87+
{2} | SCAN OUTPUT In.0, _
88+
{2} | REWRITE WITH Out.1 := "(no string representation)"
89+
90+
{2} r5 = r1 UNION r4
91+
{2} | AND NOT `Files::Impl::File.getURL/0#dispred#3f789d74#bf_0#antijoin_rhs`(FIRST 1)
92+
{4} | SCAN OUTPUT In.0, In.1, _, _
93+
{4} | REWRITE WITH Out.2 := "", Out.3 := "Example test code file found for codeql_test_extract example query."
94+
95+
{4} r6 = r3 UNION r5
96+
return r6
97+
Pipeline stages (1):
98+
[1] 4.21ms -> 1 tuples (counts=[1, -1, -1, 0, 0, -1, 1, 1, 1, -1, -1, 0, 0, -1, 1, -1, 0, 0, -1, 1])
99+
100+
--- Files::Container.getAbsolutePath/0#dispred#051b95e1#bf ---
101+
Eval order: 5 of 9
102+
Duration: 4.14 ms
103+
Result: 1 tuples
104+
Strategy: SIMPLE_INTENSIONAL
105+
Position: /home/runner/.codeql/packages/codeql/util/2.0.18/codeql/util/FileSystem.qll:63,5-87,66
106+
Dependencies (2):
107+
- m#Files::Container.getAbsolutePath/0#dispred#051b95e1#bf
108+
- files
109+
RA operations (2 steps):
110+
{2} r1 = JOIN `m#Files::Container.getAbsolutePath/0#dispred#051b95e1#bf` WITH files ON FIRST 1 OUTPUT Lhs.0, Rhs.1
111+
return r1
112+
Pipeline stages (1):
113+
[1] 0.47ms -> 1 tuples (counts=[1])
114+
115+
--- Files::Container.getAbsolutePath/0#dispred#051b95e1#bf_0#antijoin_rhs ---
116+
Eval order: 8 of 9
117+
Duration: 3.71 ms
118+
Result: 1 tuples
119+
Strategy: SIMPLE_INTENSIONAL
120+
Position: /home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/server/ql/javascript/examples/src/ExampleQuery1/ExampleQuery1.ql:13,1-15,80
121+
Dependencies (1):
122+
- Files::Container.getAbsolutePath/0#dispred#051b95e1#bf
123+
RA operations (2 steps):
124+
{1} r1 = SCAN `Files::Container.getAbsolutePath/0#dispred#051b95e1#bf` OUTPUT In.0
125+
return r1
126+
Pipeline stages (1):
127+
[1] 0.30ms -> 1 tuples (counts=[1])
128+
129+
--- m#Files::Container.getAbsolutePath/0#dispred#051b95e1#bf ---
130+
Eval order: 4 of 9
131+
Duration: 2.81 ms
132+
Result: 1 tuples
133+
Strategy: SIMPLE_INTENSIONAL
134+
Position: /home/runner/.codeql/packages/codeql/util/2.0.18/codeql/util/FileSystem.qll:63,5-87,66
135+
Dependencies (1):
136+
- m#Files::Impl::File.getURL/0#dispred#3f789d74#bf
137+
RA operations (1 steps):
138+
return `m#Files::Impl::File.getURL/0#dispred#3f789d74#bf`
139+
Pipeline stages (1):
140+
[1] 2.43ms -> 1 tuples (counts=[])
141+
142+
--- Files::Impl::File.getURL/0#dispred#3f789d74#bf_0#antijoin_rhs ---
143+
Eval order: 7 of 9
144+
Duration: 0.72 ms
145+
Result: 1 tuples
146+
Strategy: SIMPLE_INTENSIONAL
147+
Position: /home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/server/ql/javascript/examples/src/ExampleQuery1/ExampleQuery1.ql:13,1-15,80
148+
Dependencies (1):
149+
- Files::Impl::File.getURL/0#dispred#3f789d74#bf
150+
RA operations (2 steps):
151+
{1} r1 = SCAN `Files::Impl::File.getURL/0#dispred#3f789d74#bf` OUTPUT In.0
152+
return r1
153+
Pipeline stages (1):
154+
[1] 0.35ms -> 1 tuples (counts=[1])

0 commit comments

Comments
 (0)