Skip to content

v1.16.0

Choose a tag to compare

@github-actions github-actions released this 03 Jun 13:03
· 13 commits to master since this release

Добавлено

  • Точная привязка рёбер графа вызовов к методу (callee_key) + точный режим get_callers / find_call_hierarchy. Корневая неточность реверс-графа была в том, что рёбра хранились только по имени (callee_name) без привязки к конкретному методу — одноимённые ПриЗаписи/ОбработкаПроведения из сотен модулей при обходе склеивались в ложные звенья. Теперь каждое ребро при построении пытается разрешиться в стабильный текстовый ключ цели callee_key = "<rel_path>::<casefold(метод)>" (схема builder v14, новая nullable-колонка calls.callee_key + индекс).
    • Ключ основан на rel_path (UNIQUE, переживает инкремент и пересоздание rowid изменённого модуля) — а не на methods.id (rowid переназначается при перестройке → висячие ссылки). Один helper построения ключа общий для build-time и query-time → побайтовое совпадение.
    • Резолвятся только два доказуемо-безопасных tier'а: local (голый B() → метод того же модуля) и common_exported (A.B() → экспортный метод общего модуля A). Object/manager-вызовы (Справочники.X.Метод, Контрагент.Метод) и платформенные/внешние методы намеренно не резолвятся (callee_key IS NULL) — это методы переменной/менеджера/платформы, автоматический резолв дал бы ложные привязки. Неоднозначность → тоже NULL. Нерезолвленные рёбра честно остаются на матче по имени (recall не теряется).
    • get_callersfind_callers_context): если цель разрешается в один модуль — точный режим (матч по callee_key, коллизии одноимённых методов из других модулей отсекаются) + нерезолвленные name-рёбра как fallback. В _meta добавлены exact_rows / fallback_rows / exact_available — точные рёбра отделены от эвристических.
    • find_call_hierarchy(name, …, module_hint='') — новый параметр module_hint дизамбигуирует КОРЕНЬ для одноимённых объектных методов (формы: rel_path | 'Документ.X'/'Document.X' | голый object_name). На глубине обход распространяет rel_path найденного caller'а → точность сохраняется автоматически. Cycle protection переведён с visited-by-name на visited-by-target (в точных ветках), чтобы одноимённые caller'ы из разных модулей не схлопывались. Новая форма результата: узел {name, target_hint, target_key, meta, callers} + top-level _meta (exact_available, root_exact, exact_targets/fallback_targets, exact_rows/fallback_rows, node_budget_exceeded/visited_cap).
    • Агрегаты резолва пишутся в index_meta: calls_total / calls_resolved.
    • Help/strategy-слой обновлён в 4 местах (_reg, рецепт «иерархия вызовов», disambiguation-правило, slim/full-упоминания), чтобы module_hint и поля доверия _meta дошли до агента.

Исправлено

  • Производительность get_callers / find_call_hierarchy — выражение-индекс по имени метода вместо leading-wildcard скана. Матч вызывающих по имени использовал callee_name = ? OR callee_name LIKE '%.имя'; ведущий % в LIKE не использует индекс → полный скан таблицы calls (~3.7M строк) на каждый узел. Поскольку callee_name всегда имеет ≤1 точку (регекс (\w+)\.(\w+)), суффикс после точки = имя метода → матч сведён к равенству по индексируемому выражению. Новый индекс idx_calls_callee_short_SCHEMA_SQL, единый источник выражения _callee_short_expr); запрос строит общий helper _callee_match_clause (голое имя → суффикс-индекс; ввод с точкой Модуль.Метод → полный callee_name по idx_calls_callee). BUILDER_VERSION не менялся (индекс — чистая оптимизация, запрос корректен и без него); v13→v14 и так форсит rebuild, а уже собранные v14-базы получают индекс через безусловный self-heal _ensure_callee_short_index при любом update() (вкл. no-op). EXPLAIN-страж в тестах фиксирует использование индекса (защита от молчаливого дрейфа выражения / потери COLLATE NOCASE).
  • Производительность find_call_hierarchy — резолвер цели и проверка пустоты calls. End-to-end замер на живой ERP (≈3.7M рёбер, 493K методов) показал, что headline-кейс find_call_hierarchy('ОбработкаПроведения', depth=2) без hint оставался медленным из-за двух per-node издержек, не покрытых индексом по имени: (1) _resolve_target_key делал полный SCAN 493K методов — запрос methods JOIN modules WHERE rel_path=? AND py_lower(name)=py_lower(?) не имел индекса на FK-колонке methods(module_id)py_lower(col) убивает idx_meth_name); (2) get_callers и IndexReader.has_calls начинали с SELECT COUNT(*) FROM calls (полный счёт ~3.7M ≈112мс) для простой проверки «таблица не пуста». Исправлено: новый индекс idx_meth_module ON methods(module_id)_SCHEMA_SQL + безусловный self-heal _ensure_meth_module_index при update(), как у idx_calls_callee_short) → резолвер уходит со SCAN methods на индексный SEARCH (module_id=?); обе проверки пустоты заменены на SELECT 1 FROM calls LIMIT 1 (O(1)). BUILDER_VERSION не менялся. Перф-стражи в тестах: EXPLAIN резолвера использует idx_meth_module, а trace-страж запрещает нефильтрованный COUNT(*) FROM calls в get_callers/has_calls.
  • Защита find_call_hierarchy от взрыва обхода на широком корне без hint. Корректный visited-by-target (одноимённые методы из разных модулей — разные узлы) на корне-«магните» вроде ОбработкаПроведения (его зовут ~сотни документов, каждый своим ОбработкаПроведения) разворачивался в сотни точных узлов. Добавлен backstop _HIERARCHY_VISITED_CAP=2000: BFS по уровням останавливается до следующего дорогого lookup'а, возвращает частичное (связное, shallow-first) дерево и честно помечает _meta.node_budget_exceeded=True + visited_cap. Семантика visited-by-target не изменена (однофамильцы по-прежнему не схлопываются). Недостижим для малых/средних деревьев.
  • Ложные рёбра графа вызовов из многострочных текстов запросов. Извлечение вызовов (_extract_calls_from_body) переведено с построчного _strip_code_line на многострочный сканер _scan_module. Функции языка запросов внутри многострочных строковых литералов (тексты запросов 1С с |-продолжением: ЕСТЬNULL(, ВЫРАЗИТЬ(, СУММА(, ЧИСЛО() больше не попадают в граф как ложные рёбра. ""-эскейпы и // внутри строкового литерала тоже трактуются корректно. _strip_code_line сохранён (используется в FS-fallback find_callers_context).

Совместимость

  • Схема builder_version 13 → 14. Апгрейд индекса со старее v14 выполняет разовый полный rebuild (новая колонка callee_key создаётся и заполняется при пересборке). v13-индекс, открытый на ЧТЕНИЕ до фоновой пересборки, работает в name-based режиме без падения: get_callers отдаёт exact_available=False, точный режим выключен (capability-check на наличие колонки).
  • Изменение семантики get_callers для НЕэкспортного метода с module_hint: старая эвристика «non-export → только свой файл» заменена точным callee_key; same-module local-вызов резолвится точно, кросс-модульная (нерезолвленная) ссылка честно отдаётся как fallback-ребро (recall сохранён, помечено в _meta).

Тесты

  • Новый файл tests/test_call_resolution.py: Фаза A (ложные query-рёбра, ""///-эскейпы, абсолютная нумерация строк после многострочной строки), build-time ключи (common-exported/local/object-manager→NULL/платформа→NULL), точный режим get_callers (уникальный экспортный + отсечение коллизий + fallback), find_call_hierarchy (hint на корне, propagation на глубину, visited-by-target, форма результата), нормализация module_hint (RU/EN/rel_path/bare), инкремент-устойчивость ключа при смене rowid цели, чтение v13-индекса без rebuild, миграция v13→14, help-слой. Перф-фикс: наличие idx_calls_callee_short после build (вкл. пустой репозиторий) и после no-op update() (self-heal вне delta-блока), EXPLAIN-страж использования индекса + MULTI-INDEX OR, recall-паритет суффикса для голого и dotted-ввода, инвариант ≤1 точки в callee_name, контракт и срабатывание node-budget guard. Резолвер-индекс: наличие idx_meth_module после build/пустого-репо/no-op-update(), EXPLAIN-страж использования индекса резолвером (через IndexReader._conn — там есть UDF py_lower), неизменность резолва ключа по всем формам hint; trace-страж против нефильтрованного COUNT(*) FROM calls + поведение проверки пустоты.
  • Бамп builder_version-ассертов до 14 в существующих тестах.

Полный список изменений: CHANGELOG.md