diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact index 191cb9f4..609a53b7 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact @@ -1517,7 +1517,8 @@ class Impact: L = self.code(r['file']); text = L[r['line'] - 1] if r['line'] <= len(L) else '' for qn in re.findall(rf'([A-Za-z_$][\w$]*)\s*\.\s*{re.escape(r["name"])}\b', text): quals.append((r['file'], r['line'], r['name'], qn)) W('ref', refs); W('qualifier', sorted(set(quals))) - W('registration', ax_registration.registrations(g.q, g.site_file)) + regs = ax_registration.registrations(g.q, g.site_file) # computed once: reg_key_fact below reads it too + W('registration', regs) trs = []; trf = set() # a reference on a module-level type alias's own lines belongs to the alias, not the module (#784) import graph_sql @@ -1599,7 +1600,7 @@ class Impact: regk = [] # `rd` and not `decl`: `decl` is the dec_literal list above for rd, _f, _l, kind, key, _why in (ax_registration.decoration_keys(g.q, g.site_file) + ax_registration.value_route_registrations(g.q, g.site_file) - + ax_registration.registrations(g.q, g.site_file) + tregs): + + regs + tregs): if rd in g.sym and key: regk.append((rd, kind, key)) W('reg_key_fact', sorted(set(regk))) # the callables in test files: a test that publishes a handler table's key drives the handler, and is not diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-index b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-index index 3b6fa191..19c50a44 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-index +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-index @@ -702,6 +702,9 @@ CREATE INDEX IF NOT EXISTS ce_callee ON call_edges(callee_method_id); CREATE IND -- lookups by id and by file + line: without them each per-site or per-test query scans the whole table CREATE INDEX IF NOT EXISTS symbols_id ON symbols(id); CREATE INDEX IF NOT EXISTS refs_file_line ON refs(file, line); CREATE INDEX IF NOT EXISTS literals_file_line ON literals(file, line); +-- the fields a caller reads: via_base_rows asks per single-target call site, and without the index each ask +-- scanned the whole table (65,797 rows × 5,162 sites on an 8,619-file repository = 24 s of a 171 s warm → 1.4 s) +CREATE INDEX IF NOT EXISTS fa_caller ON field_access(caller_id); -- who calls a method: one row per (site, target), with the engine's tier. caller_id is symbols.id: a -- Java call written in a field initializer has the TYPE as its caller, and that row is kept, not dropped CREATE VIEW callers AS diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path index 997e29fe..55b02956 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path @@ -969,10 +969,16 @@ class G: unfoll = [(r['lib'], r['n']) for r in rows if r['from_site']][:cap] return named, unfoll def site_file(self, raw): - """a call site's file as the repo sees it: Java bundles store absolute paths; the index's paths table maps them""" - if self.has('paths'): - r = self.q("SELECT rel FROM paths WHERE raw = ?", raw) - if r: return r[0]['rel'] + """a call site's file as the repo sees it: Java bundles store absolute paths; the index's paths table maps them. + The map is read ONCE PER GRAPH, not once per row: this ran a query per call (one `has`, one lookup), and the + facts export calls it for every call edge — 319k edges on an 8,619-file repository made 638k round-trips + through sqlite, which was most of the export's time (reg_key_fact 36 s, calls 34 s, registration 31 s, + via_base 25 s of a 171 s warm).""" + m = getattr(self, '_site_files', None) + if m is None: + m = self._site_files = {r['raw']: r['rel'] for r in self.q("SELECT raw, rel FROM paths")} if self.has('paths') else {} + got = m.get(raw) + if got is not None: return got return os.path.relpath(raw, self.repo).replace(os.sep, '/') if os.path.isabs(raw) and raw.startswith(self.repo) else raw # ── facts: exported once per graph, reused while graph.sqlite is unchanged ───────────────────────────────