Практическая подборка SQL-запросов для типовых задач анализа кода на DuckDB CPG.
Содержание¶
- Запросы к методам
- Анализ вызовов
- Управление потоком выполнения
- Поток данных
- Анализ файлов
- Шаблоны безопасности
- Качество кода
- Статистика
- Предвычисленные метрики (v6.0)
- Расширенные шаблоны
- Анализ ООП
- Ссылки и вложенность
- Результаты статического анализа
- Анализ импортов
- Соединения по идентификаторам
- Предварительные требования для PGQ
- Графовые запросы на SQL/PGQ
- Советы по использованию
- Заметки о производительности
- Дальнейшие действия
- Смотрите также
Запросы к методам¶
Найти метод по точному имени¶
SELECT id, name, full_name, filename, line_number, signature
FROM nodes_method
WHERE name = 'authenticate'
LIMIT 10;
Найти методы по шаблону¶
SELECT id, name, full_name, filename, line_number
FROM nodes_method
WHERE name LIKE '%process%'
ORDER BY name
LIMIT 50;
Методы по шаблону сигнатуры¶
SELECT name, full_name, signature, filename
FROM nodes_method
WHERE signature LIKE '%char*%' -- Методы, принимающие параметр char*
ORDER BY name
LIMIT 100;
Внешние и внутренние методы¶
-- Внешние методы (из библиотек)
SELECT name, full_name, COUNT(*) as count
FROM nodes_method
WHERE is_external = true
GROUP BY name, full_name
ORDER BY count DESC
LIMIT 20;
-- Внутренние методы (ваш код)
SELECT filename, COUNT(*) as method_count
FROM nodes_method
WHERE is_external = false
GROUP BY filename
ORDER BY method_count DESC
LIMIT 20;
Методы по количеству строк¶
SELECT
name,
filename,
line_number,
line_number_end,
(line_number_end - line_number) as lines_of_code
FROM nodes_method
WHERE line_number_end IS NOT NULL
AND (line_number_end - line_number) > 50 -- Длинные методы
ORDER BY lines_of_code DESC
LIMIT 20;
Анализ вызовов¶
Прямые вызываемые функции (Какие функции вызывает X?)¶
SELECT DISTINCT
callee.name,
callee.full_name,
callee.filename,
callee.line_number
FROM edges_call ec
JOIN nodes_call c ON ec.src = c.id
JOIN nodes_method caller ON c.containing_method_id = caller.id
JOIN nodes_method callee ON ec.dst = callee.id
WHERE caller.name = 'main'
ORDER BY callee.name
LIMIT 100;
Прямые вызывающие функции (Кто вызывает X?)¶
SELECT DISTINCT
caller.name,
caller.full_name,
caller.filename,
caller.line_number
FROM edges_call ec
JOIN nodes_call c ON ec.src = c.id
JOIN nodes_method callee ON ec.dst = callee.id
JOIN nodes_method caller ON c.containing_method_id = caller.id
WHERE callee.name = 'malloc'
ORDER BY caller.name
LIMIT 100;
Цепочка вызовов (транзитивные вызываемые функции)¶
WITH RECURSIVE call_chain AS (
-- Базовый случай: прямые вызовы из начальной функции
SELECT
ec.dst as method_id,
1 as depth,
m_start.name as start_method
FROM edges_call ec
JOIN nodes_call c ON ec.src = c.id
JOIN nodes_method m_start ON c.containing_method_id = m_start.id
WHERE m_start.name = 'main'
UNION ALL
-- Рекурсивный случай: продолжаем цепочку
SELECT
ec2.dst,
cc.depth + 1,
cc.start_method
FROM edges_call ec2
JOIN nodes_call c2 ON ec2.src = c2.id
JOIN nodes_method m2 ON c2.containing_method_id = m2.id
JOIN call_chain cc ON m2.id = cc.method_id
WHERE cc.depth < 5
)
SELECT DISTINCT
m.name,
m.full_name,
MIN(cc.depth) as min_depth
FROM call_chain cc
JOIN nodes_method m ON cc.method_id = m.id
GROUP BY m.id, m.name, m.full_name
ORDER BY min_depth, m.name
LIMIT 100;
Наиболее активные вызывающие функции (функции, совершающие наибольшее число вызовов)¶
SELECT
m.name,
m.full_name,
m.filename,
COUNT(DISTINCT c.id) as outgoing_calls
FROM nodes_method m
LEFT JOIN nodes_call c ON c.containing_method_id = m.id
GROUP BY m.id, m.name, m.full_name, m.filename
ORDER BY outgoing_calls DESC
LIMIT 20;
Наиболее часто вызываемые функции¶
SELECT
m.name,
m.full_name,
m.filename,
COUNT(ec.src) as incoming_calls
FROM nodes_method m
LEFT JOIN edges_call ec ON m.id = ec.dst
GROUP BY m.id, m.name, m.full_name, m.filename
ORDER BY incoming_calls DESC
LIMIT 20;
Пары вызовов (частые шаблоны «вызывающий — вызываемый»)¶
SELECT
caller.name as caller,
callee.name as callee,
COUNT(*) as frequency
FROM edges_call ec
JOIN nodes_call c ON ec.src = c.id
JOIN nodes_method caller ON c.containing_method_id = caller.id
JOIN nodes_method callee ON ec.dst = callee.id
GROUP BY caller.name, callee.name
ORDER BY frequency DESC
LIMIT 50;
Листовые функции (функции без исходящих вызовов)¶
SELECT
m.name,
m.full_name,
m.filename
FROM nodes_method m
WHERE NOT EXISTS (
SELECT 1
FROM nodes_call c
WHERE c.containing_method_id = m.id
)
AND m.is_external = false
ORDER BY m.name
LIMIT 100;
Корневые функции (функции, которые никто не вызывает)¶
SELECT
m.name,
m.full_name,
m.filename,
m.line_number
FROM nodes_method m
WHERE NOT EXISTS (
SELECT 1
FROM edges_call ec
WHERE ec.dst = m.id
)
AND m.is_external = false
ORDER BY m.filename, m.line_number
LIMIT 100;
Управление потоком выполнения¶
Методы с ветвлениями¶
-- Найти методы, содержащие структуры IF/SWITCH, через AST
SELECT DISTINCT
m.name,
m.full_name,
m.filename,
COUNT(DISTINCT cs.id) as branch_count
FROM nodes_method m
JOIN edges_ast ast ON m.id = ast.src
JOIN nodes_control_structure cs ON ast.dst = cs.id
WHERE cs.control_structure_type IN ('IF', 'SWITCH')
GROUP BY m.id, m.name, m.full_name, m.filename
ORDER BY branch_count DESC
LIMIT 20;
Методы с циклами¶
-- Найти методы, содержащие структуры циклов, через AST
SELECT DISTINCT
m.name,
m.full_name,
m.filename,
COUNT(DISTINCT cs.id) as loop_count
FROM nodes_method m
JOIN edges_ast ast ON m.id = ast.src
JOIN nodes_control_structure cs ON ast.dst = cs.id
WHERE cs.control_structure_type IN ('WHILE', 'FOR', 'DO')
GROUP BY m.id, m.name, m.full_name, m.filename
ORDER BY loop_count DESC
LIMIT 20;
Приближённая оценка цикломатической сложности¶
-- Оценка сложности по количеству управляющих структур
SELECT
m.name,
m.filename,
1 + COUNT(DISTINCT cs.id) as approx_complexity
FROM nodes_method m
LEFT JOIN edges_ast ast ON m.id = ast.src
LEFT JOIN nodes_control_structure cs ON ast.dst = cs.id
WHERE m.is_external = false
GROUP BY m.id, m.name, m.filename
ORDER BY approx_complexity DESC
LIMIT 30;
Обход графа потока управления (SQL/PGQ)¶
-- Следование по рёбрам CFG с использованием графа свойств
FROM GRAPH_TABLE(cpg
MATCH (start:METHOD)-[:CFG*1..10]->(node:CPG_NODE)
WHERE start.name = 'process_request'
COLUMNS (
start.name AS method_name,
node.id AS node_id,
node.node_type
)
)
LIMIT 100;
Поток данных¶
Пути потока переменных¶
WITH RECURSIVE var_flow AS (
-- Базовый случай: начальные определения
SELECT src, dst, variable, 1 as depth
FROM edges_reaching_def
WHERE variable = 'password'
UNION ALL
-- Рекурсивный случай: продолжение потока
SELECT erd.src, erd.dst, erd.variable, vf.depth + 1
FROM edges_reaching_def erd
JOIN var_flow vf ON erd.src = vf.dst
WHERE vf.depth < 10
)
SELECT
variable,
COUNT(DISTINCT src) as definition_points,
COUNT(DISTINCT dst) as use_points,
MAX(depth) as max_depth
FROM var_flow
GROUP BY variable;
Загрязнённые параметры¶
-- Методы, получающие загрязнённые входные данные
SELECT DISTINCT
m.name,
m.full_name,
p.name as parameter_name,
p.type_full_name as parameter_type
FROM nodes_method m
JOIN nodes_param p ON p.method_id = m.id
WHERE p.name IN ('userInput', 'request', 'input', 'data')
ORDER BY m.name
LIMIT 50;
Анализ файлов¶
Методы по файлам¶
SELECT
filename,
COUNT(*) as method_count,
SUM(CASE WHEN is_external THEN 1 ELSE 0 END) as external_methods,
SUM(CASE WHEN is_external THEN 0 ELSE 1 END) as internal_methods
FROM nodes_method
GROUP BY filename
ORDER BY method_count DESC
LIMIT 30;
Файлы по активности вызовов¶
SELECT
m.filename,
COUNT(DISTINCT c.id) as total_calls,
COUNT(DISTINCT c.name) as unique_calls
FROM nodes_method m
JOIN nodes_call c ON c.containing_method_id = m.id
GROUP BY m.filename
ORDER BY total_calls DESC
LIMIT 20;
Вызовы между файлами¶
SELECT
caller.filename as from_file,
callee.filename as to_file,
COUNT(*) as call_count
FROM edges_call ec
JOIN nodes_call c ON ec.src = c.id
JOIN nodes_method caller ON c.containing_method_id = caller.id
JOIN nodes_method callee ON ec.dst = callee.id
WHERE caller.filename != callee.filename
GROUP BY caller.filename, callee.filename
ORDER BY call_count DESC
LIMIT 50;
Шаблоны безопасности¶
Проблемы управления памятью¶
-- Методы, которые вызывают malloc, но не вызывают free
SELECT DISTINCT m.name, m.full_name
FROM nodes_method m
WHERE EXISTS (
SELECT 1 FROM nodes_call c
WHERE c.containing_method_id = m.id
AND c.name = 'malloc'
)
AND NOT EXISTS (
SELECT 1 FROM nodes_call c
WHERE c.containing_method_id = m.id
AND c.name = 'free'
)
LIMIT 50;
Опасные вызовы функций¶
SELECT
m.name as caller,
m.filename,
c.name as dangerous_function,
m.line_number
FROM nodes_call c
JOIN nodes_method m ON c.containing_method_id = m.id
WHERE c.name IN ('strcpy', 'sprintf', 'gets', 'system', 'eval')
ORDER BY m.filename, m.line_number
LIMIT 100;
Кандидаты на SQL-инъекцию¶
SELECT DISTINCT
m.name,
m.full_name,
m.filename
FROM nodes_method m
WHERE EXISTS (
-- Есть вызовы, связанные с SQL
SELECT 1 FROM nodes_call c
WHERE c.containing_method_id = m.id
AND (c.name LIKE '%query%' OR c.name LIKE '%execute%')
)
AND EXISTS (
-- Получает пользовательский ввод
SELECT 1 FROM nodes_param p
WHERE p.method_id = m.id
AND (p.name LIKE '%input%' OR p.name LIKE '%request%')
)
LIMIT 50;
Кандидаты на обход аутентификации¶
SELECT
m.name,
m.full_name,
m.filename
FROM nodes_method m
WHERE m.name LIKE '%auth%'
OR m.name LIKE '%login%'
OR m.name LIKE '%verify%'
ORDER BY m.name
LIMIT 100;
Качество кода¶
Богатые методы (слишком длинные)¶
SELECT
name,
filename,
line_number,
line_number_end,
(line_number_end - line_number) as loc
FROM nodes_method
WHERE line_number_end IS NOT NULL
AND (line_number_end - line_number) > 100
ORDER BY loc DESC
LIMIT 20;
Высокая исходящая связность (слишком много вызовов)¶
SELECT
m.name,
m.filename,
COUNT(DISTINCT c.id) as fan_out
FROM nodes_method m
JOIN nodes_call c ON c.containing_method_id = m.id
GROUP BY m.id, m.name, m.filename
HAVING COUNT(DISTINCT c.id) > 10
ORDER BY fan_out DESC
LIMIT 20;
Высокая входящая связность (слишком много вызывающих)¶
SELECT
m.name,
m.filename,
COUNT(DISTINCT ec.src) as fan_in
FROM nodes_method m
JOIN edges_call ec ON m.id = ec.dst
GROUP BY m.id, m.name, m.filename
HAVING COUNT(DISTINCT ec.src) > 10
ORDER BY fan_in DESC
LIMIT 20;
Неиспользуемые методы (потенциальный мёртвый код)¶
SELECT
m.name,
m.full_name,
m.filename
FROM nodes_method m
WHERE m.is_external = false
AND NOT EXISTS (
SELECT 1 FROM edges_call ec WHERE ec.dst = m.id
)
AND m.name != 'main'
AND m.name NOT LIKE 'test%' -- Исключаем точки входа
ORDER BY m.filename, m.name
LIMIT 100;
Статистика¶
Обзор CPG¶
SELECT
(SELECT COUNT(*) FROM nodes_method) as total_methods,
(SELECT COUNT(*) FROM nodes_method WHERE is_external = false) as internal_methods,
(SELECT COUNT(*) FROM nodes_call) as total_calls,
(SELECT COUNT(*) FROM edges_call) as call_edges,
(SELECT COUNT(DISTINCT filename) FROM nodes_method) as file_count;
Распределение вызовов¶
SELECT
call_count_bucket,
COUNT(*) as methods_in_bucket
FROM (
SELECT
m.id,
CASE
WHEN call_count = 0 THEN '0 вызовов'
WHEN call_count <= 5 THEN '1-5 вызовов'
WHEN call_count <= 10 THEN '6-10 вызовов'
WHEN call_count <= 20 THEN '11-20 вызовов'
ELSE '20+ вызовов'
END as call_count_bucket
FROM nodes_method m
LEFT JOIN (
SELECT
c.containing_method_id as method_id,
COUNT(c.id) as call_count
FROM nodes_call c
GROUP BY c.containing_method_id
) counts ON m.id = counts.method_id
)
GROUP BY call_count_bucket
ORDER BY
CASE call_count_bucket
WHEN '0 вызовов' THEN 1
WHEN '1-5 вызовов' THEN 2
WHEN '6-10 вызовов' THEN 3
WHEN '11-20 вызовов' THEN 4
ELSE 5
END;
Распределение языков (по расширению файла)¶
SELECT
CASE
WHEN filename LIKE '%.c' THEN 'C'
WHEN filename LIKE '%.cpp' OR filename LIKE '%.cc' THEN 'C++'
WHEN filename LIKE '%.java' THEN 'Java'
WHEN filename LIKE '%.py' THEN 'Python'
WHEN filename LIKE '%.js' THEN 'JavaScript'
ELSE 'Другое'
END as language,
COUNT(*) as method_count
FROM nodes_method
WHERE is_external = false
GROUP BY language
ORDER BY method_count DESC;
Сложность сигнатур методов¶
SELECT
m.name,
m.signature,
LENGTH(m.signature) as sig_length,
(LENGTH(m.signature) - LENGTH(REPLACE(m.signature, ',', ''))) + 1 as param_count
FROM nodes_method m
WHERE m.signature IS NOT NULL
AND m.is_external = false
ORDER BY param_count DESC, sig_length DESC
LIMIT 30;
Предвычисленные метрики (v6.0)¶
Начиная с версии 6.0, GoCPG вычисляет метрики на этапе генерации CPG и сохраняет их непосредственно в nodes_method. Это избавляет от необходимости приближённых вычислений через CTE.
Цикломатическая сложность (предвычисленная)¶
-- Методы с наивысшей цикломатической сложностью
SELECT
name,
full_name,
filename,
cyclomatic_complexity,
line_number,
line_number_end
FROM nodes_method
WHERE is_external = false
AND cyclomatic_complexity > 10
ORDER BY cyclomatic_complexity DESC
LIMIT 30;
Связность входящая и исходящая (предвычисленная)¶
-- Методы с высокой входящей связностью (много вызывающих)
SELECT name, full_name, filename, fan_in
FROM nodes_method
WHERE fan_in > 10
ORDER BY fan_in DESC
LIMIT 20;
-- Методы с высокой исходящей связностью (много вызовов)
SELECT name, full_name, filename, fan_out
FROM nodes_method
WHERE fan_out > 10
ORDER BY fan_out DESC
LIMIT 20;
Точки входа и тестовые методы¶
-- Все точки входа (main, обработчики HTTP, CLI-команды и т.д.)
SELECT name, full_name, filename, line_number
FROM nodes_method
WHERE is_entry_point = true
ORDER BY filename, line_number;
-- Тестовые методы
SELECT name, full_name, filename
FROM nodes_method
WHERE is_test = true
ORDER BY filename, name;
-- Вложенные функции (замыкания, внутренние функции)
SELECT name, full_name, filename, line_number
FROM nodes_method
WHERE is_nested = true
ORDER BY filename, line_number;
Комбинированный анализ сложности¶
-- Методы с высокой сложностью И высокой связностью — кандидаты на рефакторинг
SELECT
name,
full_name,
filename,
cyclomatic_complexity,
fan_in,
fan_out,
(line_number_end - line_number) as loc
FROM nodes_method
WHERE is_external = false
AND cyclomatic_complexity > 5
AND (fan_in > 5 OR fan_out > 5)
ORDER BY cyclomatic_complexity * (fan_in + fan_out) DESC
LIMIT 20;
Расширенные шаблоны¶
Поиск цепочек вызовов методов (A → B → C)¶
WITH chain AS (
SELECT
caller.name as method_a,
intermediate.name as method_b,
callee.name as method_c
FROM edges_call ec1
JOIN nodes_call c1 ON ec1.src = c1.id
JOIN nodes_method caller ON c1.containing_method_id = caller.id
JOIN nodes_method intermediate ON ec1.dst = intermediate.id
JOIN nodes_call c2 ON c2.containing_method_id = intermediate.id
JOIN edges_call ec2 ON c2.id = ec2.src
JOIN nodes_method callee ON ec2.dst = callee.id
WHERE caller.name = 'main'
)
SELECT DISTINCT method_a, method_b, method_c
FROM chain
LIMIT 100;
Связные компоненты (кластеры методов)¶
-- Найти методы в одном графе вызовов
WITH RECURSIVE component AS (
SELECT id, name, id as root
FROM nodes_method
WHERE name = 'main'
UNION
SELECT m.id, m.name, c.root
FROM nodes_method m
JOIN edges_call ec ON m.id = ec.dst OR m.id IN (
SELECT caller.id FROM nodes_method caller
JOIN nodes_call nc ON nc.containing_method_id = caller.id
WHERE nc.id = ec.src
)
JOIN component c ON ec.dst = c.id OR ec.src = c.id
)
SELECT
root,
COUNT(DISTINCT id) as component_size,
STRING_AGG(DISTINCT name, ', ') as methods
FROM component
GROUP BY root
ORDER BY component_size DESC
LIMIT 10;
Анализ ООП¶
Объявления типов¶
-- Все пользовательские типы (классы, структуры, интерфейсы)
SELECT
name,
full_name,
filename,
line_number,
inherits_from_type_full_name
FROM nodes_type_decl
WHERE is_external = false
ORDER BY filename, line_number;
Члены типов¶
-- Поля и свойства типов
SELECT
td.name as type_name,
mb.name as member_name,
mb.type_full_name as member_type,
mb.filename,
mb.line_number
FROM nodes_type_decl td
JOIN edges_ast ea ON td.id = ea.src
JOIN nodes_member mb ON ea.dst = mb.id
ORDER BY td.name, mb.order_index;
Иерархия наследования¶
-- Прямое наследование через таблицу рёбер
SELECT
child.name as derived_type,
child.full_name as derived_full_name,
parent_id.dst as base_type_id,
child.filename
FROM nodes_type_decl child
JOIN edges_inherits_from parent_id ON child.id = parent_id.src
ORDER BY child.name;
Типы с наибольшим числом членов¶
SELECT
td.name as type_name,
td.full_name,
td.filename,
COUNT(mb.id) as member_count
FROM nodes_type_decl td
JOIN edges_ast ea ON td.id = ea.src
JOIN nodes_member mb ON ea.dst = mb.id
WHERE td.is_external = false
GROUP BY td.id, td.name, td.full_name, td.filename
ORDER BY member_count DESC
LIMIT 20;
Ссылки и вложенность¶
Ссылки между узлами¶
-- Все ссылки из данного метода
SELECT
er.src as source_id,
er.dst as target_id
FROM edges_ref er
JOIN nodes_method m ON er.src = m.id
WHERE m.name = 'process_request'
LIMIT 100;
Вложенность узлов¶
-- Узлы, вложенные в данный метод
SELECT
ec.dst as child_id
FROM edges_contains ec
JOIN nodes_method m ON ec.src = m.id
WHERE m.name = 'authenticate'
LIMIT 100;
-- Файлы и содержащиеся в них методы
SELECT
f.name as file_name,
m.name as method_name,
m.line_number
FROM edges_contains ec
JOIN nodes_file f ON ec.src = f.id
JOIN nodes_method m ON ec.dst = m.id
ORDER BY f.name, m.line_number
LIMIT 100;
Результаты статического анализа¶
Результаты проверок (findings)¶
-- Все результаты проверок, сгруппированные по серьёзности
SELECT
severity,
category,
COUNT(*) as finding_count
FROM nodes_finding
GROUP BY severity, category
ORDER BY
CASE severity
WHEN 'error' THEN 1
WHEN 'warning' THEN 2
WHEN 'info' THEN 3
WHEN 'hint' THEN 4
ELSE 5
END,
finding_count DESC;
-- Подробный список результатов
SELECT
title,
description,
severity,
category,
rule_id,
confidence,
status
FROM nodes_finding
WHERE status = 'open'
ORDER BY severity, confidence DESC
LIMIT 50;
Метки (tags)¶
-- Метки, назначенные узлам CPG
SELECT
name,
value,
external_source,
confidence,
COUNT(*) as tag_count
FROM nodes_tag_v2
GROUP BY name, value, external_source, confidence
ORDER BY tag_count DESC
LIMIT 30;
Анализ импортов¶
Все импорты в проекте¶
SELECT
filename,
imported_entity,
imported_as,
is_wildcard,
line_number
FROM nodes_import
ORDER BY filename, line_number
LIMIT 200;
Наиболее импортируемые модули¶
SELECT
imported_entity,
COUNT(*) as import_count,
COUNT(DISTINCT filename) as file_count
FROM nodes_import
GROUP BY imported_entity
ORDER BY import_count DESC
LIMIT 30;
Файлы с наибольшим числом импортов¶
SELECT
filename,
COUNT(*) as import_count,
SUM(CASE WHEN is_wildcard THEN 1 ELSE 0 END) as wildcard_imports
FROM nodes_import
GROUP BY filename
ORDER BY import_count DESC
LIMIT 20;
Неиспользуемые импорты (приближённый анализ)¶
-- Импорты, на которые нет ссылок в вызовах
SELECT
i.filename,
i.imported_entity,
i.line_number
FROM nodes_import i
WHERE NOT EXISTS (
SELECT 1 FROM nodes_call c
WHERE c.filename = i.filename
AND c.name = SPLIT_PART(i.imported_entity, '.', -1)
)
AND i.is_wildcard = false
ORDER BY i.filename, i.line_number
LIMIT 100;
Соединения по идентификаторам¶
Многие таблицы CPG содержат столбцы с идентификаторами для прямых соединений, что эффективнее нечётких сопоставлений по LIKE.
Основные столбцы-идентификаторы¶
| Таблица | Столбец | Целевая таблица |
|---|---|---|
nodes_call |
containing_method_id |
nodes_method.id |
nodes_call |
callee_method_id |
nodes_method.id |
nodes_param |
method_id |
nodes_method.id |
nodes_method_return |
method_id |
nodes_method.id |
nodes_method_parameter_out |
method_id |
nodes_method.id |
Пример: параметры метода через идентификатор¶
-- Вместо нечёткого LIKE '%method_name%' используйте прямое соединение
SELECT
m.name as method_name,
p.name as param_name,
p.type_full_name as param_type,
p.index as param_index
FROM nodes_method m
JOIN nodes_param p ON p.method_id = m.id
WHERE m.name = 'authenticate'
ORDER BY p.index;
Пример: все вызовы в методе через идентификатор¶
SELECT
m.name as method_name,
c.name as called_function,
c.line_number
FROM nodes_method m
JOIN nodes_call c ON c.containing_method_id = m.id
WHERE m.name = 'main'
ORDER BY c.line_number;
Советы по использованию¶
- Всегда добавляйте LIMIT, чтобы избежать возврата миллионов строк
- Используйте конкретные столбцы вместо SELECT *
- Сначала тестируйте на небольших наборах данных
- Добавляйте условия WHERE, чтобы отфильтровать нерелевантные результаты
- Используйте EXPLAIN для проверки планов выполнения запросов
- Контролируйте время выполнения (для большинства запросов должно быть < 100 мс)
- Предпочитайте соединения по идентификаторам (
containing_method_id,method_id) вместоLIKE
Заметки о производительности¶
- Простые запросы: 1–3 мс
- JOIN’ы (1–2 таблицы): 2–5 мс
- Агрегации: 4–8 мс
- Рекурсивные CTE: 10–50 мс (зависит от глубины)
Все запросы выполнялись на тестовой базе данных с 5 методами и 4 вызовами.
Производительность растёт сублинейно с увеличением объёма данных благодаря использованию индексов.
Предварительные требования для PGQ¶
Для выполнения графовых запросов SQL/PGQ необходима подготовка.
Материализация полиморфного представления cpg_nodes¶
PGQ использует cpg_nodes — объединение всех таблиц узлов — в качестве полиморфной вершины графа. Перед запросами необходимо материализовать это представление:
-- Создание материализованной таблицы cpg_nodes
-- (объединение всех таблиц узлов с общими столбцами)
CREATE TABLE cpg_nodes AS
SELECT id, 'METHOD' as node_type, name, full_name, filename, line_number FROM nodes_method
UNION ALL
SELECT id, 'CALL' as node_type, name, method_full_name as full_name, filename, line_number FROM nodes_call
UNION ALL
SELECT id, 'PARAM' as node_type, name, type_full_name as full_name, filename, line_number FROM nodes_param
UNION ALL
SELECT id, 'IDENTIFIER' as node_type, name, type_full_name as full_name, filename, line_number FROM nodes_identifier
UNION ALL
SELECT id, 'LITERAL' as node_type, name, type_full_name as full_name, filename, line_number FROM nodes_literal
UNION ALL
SELECT id, 'CONTROL_STRUCTURE' as node_type, name, NULL as full_name, filename, line_number FROM nodes_control_structure
UNION ALL
SELECT id, 'TYPE_DECL' as node_type, name, full_name, filename, line_number FROM nodes_type_decl
-- ... (и остальные таблицы узлов)
;
Создание графа свойств¶
CREATE PROPERTY GRAPH cpg
VERTEX TABLES (
nodes_method LABEL METHOD,
nodes_call LABEL CALL_NODE,
nodes_type_decl LABEL TYPE_DECL,
cpg_nodes LABEL CPG_NODE
)
EDGE TABLES (
edges_call SOURCE KEY (src) REFERENCES nodes_call (id)
DESTINATION KEY (dst) REFERENCES nodes_method (id)
LABEL CALLS,
edges_ast SOURCE KEY (src) REFERENCES cpg_nodes (id)
DESTINATION KEY (dst) REFERENCES cpg_nodes (id)
LABEL AST,
edges_cfg SOURCE KEY (src) REFERENCES cpg_nodes (id)
DESTINATION KEY (dst) REFERENCES cpg_nodes (id)
LABEL CFG,
edges_reaching_def SOURCE KEY (src) REFERENCES cpg_nodes (id)
DESTINATION KEY (dst) REFERENCES cpg_nodes (id)
LABEL REACHING_DEF,
edges_ref SOURCE KEY (src) REFERENCES cpg_nodes (id)
DESTINATION KEY (dst) REFERENCES cpg_nodes (id)
LABEL REF,
edges_inherits_from SOURCE KEY (src) REFERENCES nodes_type_decl (id)
DESTINATION KEY (dst) REFERENCES cpg_nodes (id)
LABEL INHERITS_FROM
);
Замечание. GoCPG автоматически создаёт
cpg_nodesи граф свойств при генерации CPG. Ручная настройка требуется только при работе с базой данных напрямую.
Графовые запросы на SQL/PGQ¶
Расширение SQL/PGQ в DuckDB позволяет выполнять интуитивные запросы для обхода графов.
Обход графа вызовов¶
-- Найти все методы, вызываемые из main (прямые и косвенные)
-- Метка CALLS связывает CALL_NODE → METHOD
FROM GRAPH_TABLE(cpg
MATCH (caller:CALL_NODE)-[:CALLS*1..3]->(callee:METHOD)
WHERE caller.name = 'main'
COLUMNS (
caller.name AS caller,
callee.name AS callee,
callee.filename
)
)
LIMIT 100;
Анализ потока данных¶
-- Отслеживание потока данных через достижимые определения
-- REACHING_DEF связывает CPG_NODE → CPG_NODE (полиморфные узлы)
FROM GRAPH_TABLE(cpg
MATCH (src:CPG_NODE)-[:REACHING_DEF*1..5]->(sink:CPG_NODE)
WHERE src.name = 'user_input'
COLUMNS (
src.name AS source_var,
sink.name AS sink_function,
sink.line_number
)
)
LIMIT 100;
Обход AST¶
-- Найти все узлы, находящиеся под методом в AST
FROM GRAPH_TABLE(cpg
MATCH (method:METHOD)-[:AST*1..5]->(child:CPG_NODE)
WHERE method.name = 'authenticate'
COLUMNS (
method.name AS method_name,
child.id AS child_id,
child.node_type
)
)
LIMIT 100;
Иерархия типов¶
-- Найти отношения наследования
-- INHERITS_FROM связывает TYPE_DECL → CPG_NODE
FROM GRAPH_TABLE(cpg
MATCH (derived:TYPE_DECL)-[:INHERITS_FROM]->(base:CPG_NODE)
COLUMNS (
derived.name AS derived_type,
base.full_name AS base_type
)
)
Дальнейшие действия¶
- Попробуйте эти запросы в своей базе данных CPG
- Измените параметры в соответствии со своими требованиями
- Комбинируйте шаблоны для сложного анализа
- Контролируйте производительность и оптимизируйте при необходимости
- Добавьте свои собственные шаблоны в эту книгу рецептов!
Смотрите также¶
- Модули анализа — документация CFGAnalyzer, FieldSensitiveTracer
- Система гипотез — генерация гипотез безопасности
- Документация по DuckDB SQL/PGQ
- Спецификация CPG