Skip to content

Feature/plsql support#1033

Open
ouzsrcm wants to merge 51 commits into
DeusData:mainfrom
ouzsrcm:feature/plsql-support
Open

Feature/plsql support#1033
ouzsrcm wants to merge 51 commits into
DeusData:mainfrom
ouzsrcm:feature/plsql-support

Conversation

@ouzsrcm

@ouzsrcm ouzsrcm commented Jul 11, 2026

Copy link
Copy Markdown

What does this PR do?

Checklist

  • Every commit is signed off (git commit -s) — required, CI rejects
    unsigned commits (DCO, see CONTRIBUTING.md)
  • Tests pass locally (make -f Makefile.cbm test)
  • Lint passes (make -f Makefile.cbm lint-ci)
  • New behavior is covered by a test (reproduce-first for bug fixes)

ouzsrcm and others added 6 commits July 11, 2026 17:40
Vendors AndreasMaierDe/tree-sitter-plsql @ 28aebef (MIT, ABI 14, no
external scanner) and adds the grammar_plsql.c compile shim. Updates
MANIFEST.md and THIRD_PARTY.md grammar counts. Local patch: quoted
tree_sitter/parser.h include so the per-grammar header resolves.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
Register Oracle PL/SQL in lang_specs (packages/types/triggers as Class,
procedures/functions as Function, ref_call/branches/assign/raise) and map
.pks/.pkb/.pck/.pls/.plb/.plsql/.fnc/.trg/.bdy/.tps/.tpb. Leave .sql and
.prc unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
Resolve fnc_name/prc_name for procedures and functions, package_name/
type_name/trigger_name for Class nodes, and package-qualified ref_call
callees (PKG.FNC) for CALLS edges.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
Add extension/extraction/grammar regression+label tests for PL/SQL, plus
emp_pkg/util_pkg fixtures verifying Class/Function and CALLS. Document
CREATE TYPE ... AS OBJECT as a known grammar ERROR-node limitation.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
@ouzsrcm ouzsrcm requested a review from DeusData as a code owner July 11, 2026 15:31
@DeusData DeusData added the enhancement New feature or request label Jul 14, 2026
@DeusData DeusData added this to the 0.9.2-rc milestone Jul 14, 2026
@DeusData DeusData added parsing/quality Graph extraction bugs, false positives, missing edges language-request Request for new language support priority/backlog Valuable contribution, lower scheduling urgency; review when maintainer capacity opens. labels Jul 14, 2026
@DeusData

Copy link
Copy Markdown
Owner

Thanks for the PL/SQL contribution. I verified the vendored parser against AndreasMaierDe/tree-sitter-plsql at commit 28aebef209be: it is MIT licensed and byte-identical across 353,744 lines except for the documented local include-path change. This is a 0.9.2-rc language enhancement; remaining review focuses on full build and sanitizer coverage, grammar limits, extraction quality, and representative PL/SQL fixtures.

DeusData added 16 commits July 15, 2026 22:48
Three of DeusData#797's four findings fixed (the fourth — repeated-variable
unification — already holds on main and is now pinned by the test):

- Walk padding: the BFS recursive CTE deduped (node, hop) PAIRS, so a
  single self-loop minted every hop level for every node it could
  reach — *k..k matched fabricated walks of arbitrary length (a
  100-hop 'longest chain' artifact) and the row set exploded to
  nodes x depth (the reported crash). The traversal now returns each
  node once at its MINIMAL hop (MIN/GROUP BY): variable-length matches
  nodes whose shortest path lies in [min,max]. This is deliberately
  shortest-path semantics, not openCypher trail enumeration — right
  for reachability/depth audits on call graphs and linear instead of
  exponential; trace_path shares the traversal and now also reports
  each node once on cyclic graphs.
- Silent 100-hop cap: a clamped hop range now surfaces as
  result->warning ('clamped ... an empty result may mean clamped, not
  no such path'), emitted by query_graph in both TOON and JSON forms,
  on top of the existing log WARN.

Reproduce-first: cypher_exec_varlength_path_semantics_issue797 —
self-loop + real 2-chain fixture; RED on the walk semantics (*2..2
returned 3 endpoints instead of 1), GREEN now, incl. variable
unification, zero rows for nonexistent path lengths, and the clamp
warning assertion.

Closes DeusData#797

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The compat.h include added for CBM_TLS pulled windows.h into cypher.c,
whose legacy 'far' macro (windef.h defines it empty) broke the
existing EXISTS-pattern local of that name on every Windows leg. Use
C11 _Thread_local for the depth-clamp flag instead; cypher.c stays
free of windows.h.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…solution

const foo = require('./foo') emitted a Variable definition for the
binding, so calls to foo() resolved to that same-module Variable (an
import alias, not a definition) instead of following the recorded
import to the exported function - which stayed at zero inbound CALLS
and surfaced as a disconnected Variable node. ESM import bindings never
materialize as Variables; the identifier-bound require form now behaves
identically: extract_js_vars skips the Variable def exactly when the
declarator value is a require() call with a string-literal path (the
form extract_imports records with local_name = the identifier), letting
resolution fall through to the import map. Destructured requires keep
their Variable defs since the import row only records the module.

Closes DeusData#871

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
cbm_store_find_node_by_id heap-allocates the node's string fields;
the DeusData#871 helper left them unfreed, tripping LSan on the Linux legs.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
A persistently dirty or untracked worktree re-triggered a full reindex
(and its DB/artifact rewrite) on every poll cycle - git_is_dirty()
answered 'is the tree dirty' where the watcher needs 'did the tree
CHANGE'. Idle repos with one uncommitted file produced sustained disk
write amplification (reported at 1 TB/day across several repos).

The watcher now tracks a dirty-state signature: FNV-1a over the
entries of git status --porcelain -uall -z plus each listed path's
(size, mtime_ns). Same signature -> no reindex; editing a dirty file,
adding/removing one, or reverting the tree to clean each produce a new
signature and trigger exactly one reindex. Submodule status is folded
in on POSIX, mirroring the old submodule dirty probe. 0 is reserved
for a clean tree; a dirty-at-baseline tree still reindexes once (the
watcher cannot know whether that state reached the DB, e.g. after a
server restart with a stale artifact).

Baselines (HEAD and signature) are now committed only after a
SUCCESSFUL reindex: check_changes stages observations in pending_*
fields, and busy-skips (index_fn > 0, e.g. pipeline lock held) or
failed runs leave the baselines untouched so the change is retried on
the next poll instead of being recorded as seen and silently lost -
the old code stored the new HEAD at check time, dropping any commit
that arrived while the pipeline was busy.

Closes DeusData#937

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The DeusData#599 receiver-aware narrowing (87091ed) shipped without coverage
for the delegation/store bucket reported in DeusData#876: same-named calls
whose receiver is a call result (_get_store().get inside get), a
module singleton (_default.check inside check), or a loop-local cursor
(cur.execute inside execute). All three are clean on current main;
this guard pins the bucket so it cannot regress. Refs DeusData#876

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…ions

A function whose body braces are split across #ifdef/#else branches
(one open brace per branch, a single shared close) parses with an
ERROR region on the raw source - both branches are present at once, so
the braces are unbalanced - and the defs walk silently dropped it
while its callers stayed as unresolved names (cbm_path_within_root,
handle_process_kill).

The preprocessed second pass (simplecpp) already exists for macro-
hidden CALLS; it evaluates the conditionals, so one branch is chosen
and the expanded tree parses clean, with same-file token lines aligned
to the original. Recover definitions from it, adopting only defs that
(a) intersect a raw-tree ERROR region, (b) have their name visible on
the corresponding original source line (rejects header-inlined defs
whose physical expanded lines alias unrelated raw lines when
compile_commands include paths resolve), and (c) are absent by
qualified name from the raw pass.

Verified on the real instances: handle_process_kill (580-647) and
cbm_path_within_root (5070-5085) both index with correct line ranges.

Closes DeusData#961

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
cbm_pipeline_fqn_compute strips the file extension, so a C/C++ header
and its same-stem source collide on both the module QN and the File
QN. Node upserts match by QN, so the header's File node is merged into
the source's - the header has no node of its own and can never receive
an inbound IMPORTS edge; the reporter's zero-inbound query flags 194
of 214 files.

The module-QN unification is (at least partly) load-bearing for C/C++
declaration/definition call joins, and the File-node creation sites
overlap PR DeusData#995's pending fix for DeusData#994, so the fix is deferred to land
on top of that. This RED reproduction (non-gating repro tier) keeps
the bug tracked until then. Refs DeusData#964

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…isses

Agents naturally pass the repo folder name (codebase-memory-mcp), but
indexed project names derive from the full path (E:\project\graph\x
-> E-project-graph-x), so every graph tool answered 'project not found
or not indexed' while list_projects clearly showed the project.

get_project_arg now falls back to a cache-dir filename scan when no
<project>.db exists: a passed name matching exactly ONE indexed
project as a segment-aligned tail (-<name>.db) adopts that project's
full name (logged); zero or several matches keep the original name so
the existing not-found error with the candidate list fires. Exact
names never enter the scan, and internal-name drift stays DeusData#704's
fallback in resolve_store.

Closes DeusData#1025

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Aider has no MCP support - it reads CONVENTIONS.md but can only run
shell commands. The installer wrote the shared MCP-tool-centric
instructions there, telling the model to call search_graph(...) etc.,
tools Aider cannot invoke; a reporter reasonably asked how that was
ever supposed to work (DeusData#1032).

Aider now gets a dedicated variant: the same discovery priority
expressed as runnable codebase-memory-mcp cli commands (usable via
/run), including first-index and list_projects bootstrapping, with the
no-MCP constraint stated explicitly.

Closes DeusData#1032

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…fire

The 300ms in-process SIGALRM budget in hook-augment self-terminated
with a silent _exit(0) on real cold starts (SQLite/mmap open under
load), so PreToolUse augmentation never appeared in real sessions
(reporter: 0/24 observed) while warm manual invocations worked - and a
fired deadline was indistinguishable from a legitimate no-match run.

Default budget is now 2000ms (the settings.json hook timeout stays the
outer backstop), overridable via CBM_HOOK_DEADLINE_MS (clamped
50..10000). When the deadline fires, the handler write()s a
pre-formatted breadcrumb (deadline, pid, the env knob to raise) to
~/.cache/codebase-memory-mcp/logs/hook-augment-timeouts.log before
exiting - fd and message are prepared at arm time so the handler stays
async-signal-safe; CBM_HOOK_TIMEOUT_LOG overrides the path for tests.
Windows keeps relying on the settings.json timeout (unchanged no-op).

Deterministic reproduction: stdin is a pipe whose writer never sends,
so the read blocks past a 60ms deadline - the timer must fire, exit 0
(fail-open), and leave the breadcrumb. RED before (no breadcrumb),
GREEN after.

Closes DeusData#858

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
ha_deadline_ms was defined unconditionally but only used by the POSIX
ha_arm_deadline - unused-function under -Werror broke every Windows
leg. The knob, its constants and the doc comment now live inside the
same #ifndef _WIN32 block as the deadline machinery they configure;
Windows continues to rely solely on the settings.json hook timeout.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Extensionless bash shims under %USERPROFILE%\.claude\hooks triggered
the Windows 'How do you want to open this file?' dialog whenever an
editor (Cursor) scanned the hooks dir, and could not execute without
bash anyway. All three Claude hook scripts (discovery gate, session
reminder, subagent reminder) now install as .cmd files with cmd syntax
on Windows - @echo off bodies, caret-escaped pipes in the reminder
text, 2>/dev/null redirects - and the install removes the pre-existing
extensionless twin so upgrades stop triggering the dialog. The
settings.json command paths follow automatically since they are built
from the same script-name macros. POSIX behavior is byte-identical and
guarded (no .cmd twin).

Closes DeusData#929

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The 8e gate compared uname -s to the bare literal MINGW64_NT, which
never matches the real value (MINGW64_NT-10.0-...), so the POSIX
branch silently ran on Windows and looked for the extensionless shim
that DeusData#929 no longer installs. Branch by platform prefix: Windows
expects the .cmd shim and the absence of the legacy extensionless
twin; POSIX unchanged. Content checks (never blocks, delegates to
hook-augment) now run on both.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
cbm_writer_open used a raw ANSI-CP fopen for the hand-rolled SQLite
writer - the one file-creating call on the dump chain without a
UTF-8-to-wide conversion. On a non-ASCII Windows profile
(C:\Users\Kovacs Janos) extract and resolve succeeded but the dump
failed with pipeline.err phase=dump: fopen could not create graph.db
under the profile-derived cache dir while cbm_mkdir_p (already wide)
had happily created the directory around it. Routed through cbm_fopen,
matching DeusData#700 (pass readers) and DeusData#973 (canonicalization); POSIX is
byte-transparent.

New Windows guard test_non_ascii_cache_dump.py indexes an ASCII repo
into a non-ASCII CBM_CACHE_DIR and requires a query_graph readback
with Function count > 0 - proving the DB was written to and reopened
from the non-ASCII cache, not merely that no error surfaced. The
existing non-ASCII guard only varied the repo path against an ASCII
cache, which is why this class went unseen.

The USERPROFILE-sourced env encoding question (Layer 2 - _environ is
ANSI-CP, not UTF-8) is deliberately not bundled; it needs verification
on a real Windows box and will be filed separately if the guard alone
does not settle it.

Closes DeusData#996

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Layer 2 of DeusData#996, forced by the new guard going RED on the Windows CI
leg with the writer fix alone: _environ holds ANSI-code-page bytes,
not UTF-8, so a non-ASCII USERPROFILE or CBM_CACHE_DIR arrives either
mojibake'd or with unrepresentable characters replaced by '?' - which
is invalid in Windows paths, so every downstream wide-safe file API
fails no matter how correct it is. cbm_safe_getenv now reads the value
via GetEnvironmentVariableW and converts to genuine UTF-8
(cbm_wide_to_utf8), matching the UTF-8-path convention the rest of the
codebase assumes; missing variables honor the fallback as before, and
conversion trouble falls back to the ANSI scan rather than failing.
POSIX untouched.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
DeusData and others added 28 commits July 15, 2026 22:48
Real Laravel apps register routes through the Illuminate facade, whose
class lives in vendor/ and is never indexed - and for that style the
graph minted ZERO Route nodes, flat or grouped. Three stacked defects:

1. The callee of Route::get(...) (scoped_call_expression) was
   extracted as bare 'get' via generic field resolution, so the
   empty-resolution route fallback ('::get' suffix table) could never
   engage. The extractor now emits the qualified 'Route::get' form,
   gated to the literal Route scope AND a route-method table match so
   Cache::get / Config::get keep bare names and can never mint junk
   (guarded by an inverse test; the path gate additionally requires a
   leading '/').
2. The sequential pass had no ROUTE_REG empty-resolution fallback at
   all (its comment claimed parity with the parallel path, which has
   one) - unresolvable route registrations were dropped entirely.
   Added, mirroring the parallel callee_suffix fallback.
3. Nothing composed enclosing prefix()->group() chains (same class as
   Spring's DeusData#734). At extraction - the only place the AST still exists
   - each route call inside group closures now walks its ancestors:
   every enclosing closure that is the argument of ->group(...)
   contributes the prefix('...') found on that group call's receiver
   chain, outer-to-inner for nested groups; middleware/name/domain
   chain links are skipped.

The existing handles_laravel_php test kept passing throughout because
its fixture defines an in-tree Route class, resolving by QN - which is
exactly how this class stayed unseen.

Closes DeusData#952

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The previous commit's clang-format -i rewrote all of
test_edge_types_probe.c (+732/-358 for a 48-line test addition) - my
local formatter's whole-file drift, not real non-conformance (lint-ci
passes on the restored file). Restore the file to main's formatting
with only the two DeusData#952 tests added.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Add ObjectScript (InterSystems IRIS / Caché) as a supported language,
covering the UDL class format (.cls), MAC/INT routines (.mac/.int/.rtn),
include/macro files (.inc), and IRIS Studio Export XML.

Definition extraction (extract_defs.c): Class, Method, ClassMethod,
Property, Parameter, Index, Trigger (with body text), XData, Storage,
and Query members as graph nodes; base classes from the Extends clause.

Call dispatch resolution (extract_calls.c) — four ObjectScript patterns
that are structurally invisible to text search:
  1. ##class(Pkg.Class).Method()    explicit cross-class call
  2. ..Method()                     relative-dot self-call (the dominant
                                    intra-class form; large impact on
                                    CALLS completeness)
  3. $$$Macro                       macro expansion via a per-project
                                    table built from .inc files
  4. type inference from %New/%OpenId + declared return types

Ensemble production topology (pass_ensemble_routing.c): EnsembleItem
nodes per production component and ROUTES_TO edges resolved from
ProductionDefinition XData, plus WorkMgr .Queue("##class(X).method")
dispatch — all parsed statically at index time, no live IRIS required.

Language detection (language.c): .mac/.int/.rtn map to ObjectScript
routine directly; .cls (shared with Apex) and .inc (shared with BitBake)
are disambiguated by content, defaulting to the existing language on any
doubt so neither Apex nor BitBake detection regresses.

The two new per-project tables (macros, return types) are threaded
through a new internal cbm_extract_file_ex() so the public
cbm_extract_file() signature is unchanged.

The tree-sitter grammars for ObjectScript UDL and routine are vendored
in internal/cbm/vendored/grammars/objectscript_{udl,routine}/ from
https://github.com/intersystems/tree-sitter-objectscript (MIT, ABI 15).

Refs DeusData#462

Signed-off-by: Thomas Dyar <tdyar@intersystems.com>
Two review items applied maintainer-side to save a roundtrip:

1. Restore both vendored ObjectScript scanner.c files to the pristine
   upstream bytes (a formatter pass had rewritten indentation/braces).
   Vendored grammar files stay byte-for-byte upstream except for the
   include repoint documented in MANIFEST.md - style churn breaks
   upstream diffability on re-vendor.
2. The Studio Export XML sniff in detect_file_language used a raw
   fopen on a repository path; cbm_fopen keeps it working on
   non-ASCII Windows paths (project rule, same class as DeusData#996).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Register Oracle PL/SQL in lang_specs (packages/types/triggers as Class,
procedures/functions as Function, ref_call/branches/assign/raise) and map
.pks/.pkb/.pck/.pls/.plb/.plsql/.fnc/.trg/.bdy/.tps/.tpb. Leave .sql and
.prc unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
The ObjectScript/PL/SQL merge left duplicate CBMLanguage values, repeated plsql_* token tables, and twin lang_specs/LANG_NAMES initializer rows. Keep one canonical entry per language so the registry compiles cleanly.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Oğuz <oguzhan.saricam@gmail.com>
@ouzsrcm ouzsrcm force-pushed the feature/plsql-support branch from dc12e9d to 0b305aa Compare July 15, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request language-request Request for new language support parsing/quality Graph extraction bugs, false positives, missing edges priority/backlog Valuable contribution, lower scheduling urgency; review when maintainer capacity opens.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants