Мы создаем ПО для разработки и поддержки баз данных Oracle, и статический анализатор PL/SQL является одной из основных фич наших приложений. Кто знаком с Oracle, тот хорошо знает что такое PL/SQL.
Известная поговорка гласит: «Лучше один раз увидеть, чем сто раз услышать». Поэтому мы решили заимпрувить статический анализатор таким образом, чтобы он визуализировал код в виде блок-схем (Flowcharts) и диаграмм вызовов (Call Trees). Хоть и нарисовать блоки и их связи несложно, оптимизировать их расположение на «листе» представлялось задачей, требующей значительных усилий. Чтобы стрелки минимально пересекались и обтекали блоки, блоки объединялись в группы, и диаграмма при этом не превращалась в «кашу», нужно было потратить много сил и времени.
И тогда мы решили поискать готовое решение, дабы не изобретать велосипед. Наше внимание сразу привлек Graphviz – open source решение по визуализации диаграмм. Первые его версии были разработаны компанией AT&T, а теперь он доступен как набор утилит и библиотек, а также в исходниках под лицензией Eclipse Public License (EPL).
Его движок диаграмм использует язык описания графов DOT, который представляет собой текстовое описание структуры графа: вершины, их связи, группы и атрибуты для их визуального оформления.
Описание простейшего графа:
digraph graphname {
a -> b;
}
Если необходимо добавить узлам атрибуты, например подписи, то необходимо отдельно описать узлы:
digraph graphname {
a [label="source"]
b [label="destination"]
a -> b;
}
Далее покажу на примере простой процедуры на PL/SQL:
CREATE PROCEDURE WRITE_STRING(
P_MESSAGE_TYPE IN NUMBER
, P_MESSAGE IN VARCHAR2
)
IS
BEGIN
IF P_MESSAGE_TYPE >= LOG_LEVEL THEN
ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);
END IF;
END WRITE_STRING;
Код понятен, даже если вы не знакомы с синтаксисом PL/SQL.
А теперь опишем этот код на языке DOT. Пояснения снова излишни:
digraph G {
N0 [label="”]
N1_COND [label="P_MESSAGE_TYPE >= LOG_LEVEL ?"];
N2 [label="ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);"];
EXIT_LABEL [label="END"];
N0 -> N1_COND
N1_COND -> N2
N1_COND -> EXIT_LABEL
N2 -> EXIT_LABEL
}
Теперь можно «скормить» этот файл Graphviz, подставив соответственно вместо %PNGFILE% и %DOTFILE% имена выходного (png) и входного (dot) файлов:
dot -v -Tpng –o%PNGFILE% %DOTFILE%
При помощи Graphviz из описания выше получается такая картина:
Наглядно, но весьма аскетично.
Можно сделать диаграмму привлекательнее, добавив атрибуты для определения формы блоков (shape=diamond)
, подписи стрелок (label="Yes")
и цвета color
, fontcolor
.
Также мы можем объединить в подграф блоки, которые являются исполняемыми. Это реализуется конструкцией subgraph {}
, внутри которой мы можем перечислить имена блоков, включенных в подграф, и указать атрибуты для визуального оформления (цвет рамки).
digraph G {
comment="PL/SQL flowchart generated by ClearSQL";
node [fontname="Arial", fontsize=8, height=.2, width=.25, color="#000000", fontcolor="#000000"];
edge [fontname="Arial", fontsize=8, arrowsize=.8, color="#000000", fontcolor="#000000"];
N0 [shape=plaintext, label="", style=invis];
N1_COND [shape=diamond, conditional=true, label="P_MESSAGE_TYPE >= LOG_LEVEL ?"];
N2 [shape=box, label="ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);"];
EXIT_LABEL [shape=Msquare, label="END"];
N0 -> N1_COND [color=darkgoldenrod];
N1_COND -> N2 [label=" Yes " color="#228b22" fontcolor="#228b22"];
N1_COND -> EXIT_LABEL [label=" No " color="#ff7e40" fontcolor="#ff7e40"];
N2 -> EXIT_LABEL
subgraph "cluster_#1" {
color="#0000ff";
N1_COND; N2
}
}
Получается вполне симпатичная блок-схема, которую и людям показать не стыдно.
Чтобы код могли читать не только программисты, но и «простые смертные», мы придумали такую фишку: приделали распознавание специальных тегов в комментариях, которые бы разработчик писал как псевдокод – язык описания алгоритмов. Это помогло увеличить ценность блок-схемы, которая теперь может отображать бизнес-логику, а не только визуализировать листинг.
CREATE PROCEDURE WRITE_STRING(
P_MESSAGE_TYPE IN NUMBER
, P_MESSAGE IN VARCHAR2
)
IS
BEGIN
--## Уровень данного сообщения по отношению к порогу журналирования
--##@true Выше
--##@false Ниже
IF P_MESSAGE_TYPE >= LOG_LEVEL THEN
--## Добавляем запись в журнал
ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);
END IF;
END WRITE_STRING;
Вуаля! Graphviz дает возможность делать потрясающие визуализации с минимальными затратами.
Автор: kspksp