Статья продолжает знакомить с функциями для документирования баз данных PostgreSQL. Но на этот раз речь пойдет о специальных функциях, подготавливающих описания диаграмм классов на языке PlantUML.
В качестве основного средства документирования выбрана система управления проектами TRAC с подключенным плагином plantuml.
В первой половине статьи изложены комментарии к реализации функций. Во второй- исходные тексты функций, а также пошаговые инструкции включения PlantUML-скриптов в карточки TRAC, и тестирования этих скриптов на сайте PlantText UML редактора. Тем из читателей, кого интересуют только исходные тексты или пошаговые инструкции, предлагаем сразу перейти к Приложению.
Во второй части статьи обсуждаются вспомогательные функции, которые вызываются из основных функций формирования PlantUML-скриптов.
Все тексты на PLpgSQL написаны в для версии PosthgreSQL 9.6.
Содержание
Процедура umlpr_TablesScipt_FromArray — Выгрузка из списка таблиц
Процедура umlpr_TablesScipt_ByWhereOption —Выгрузка по условию
Функция umlfn_Table_fromArray —PlantUML-скрипт из списка таблиц
Функция umlfn_Table_AllGroupScript —PlantUML-скрипт по условию
Функция umlfn_Table_Script — PlantUML-скрипт таблицы с атрибутами
Функция umlfn_Attribute_String —PlantUML-скрипт атрибута таблицы
-
Назначение функций
Функции, описанные в предыдущих статьях, возвращали сведения об объектах баз данных (отношениях(таблицах), атрибутах таблиц, ограничениях (constraints), индексах и т.д.) в форме списков. Эти списки можно включать в документ, описывающий базу данных проекта, в виде таблиц. Но для документирования связей между таблицами списковая форма не слишком наглядна. Другое дело ER-диаграммы или диаграммы классов UML, такие как показана на Рис. 1. Обычно такие диаграммы создаются специальными графическими средствами, такими, например, как ERwin Data Modeler[*1] , Microsoft Visio[*2] , к недостаткам которых можно отнести отсутствие или сложность связи с существующей базой данных. Альтернативой графическим редакторам стала технология создания диаграмм из их текстовых описаний, которые создаются на на языке PlantUML. Эта технология предполагает создание UML за два шага — сначала создается описание или PlantUML-скрипт, который затем загружается в среду, преобразующую его в диаграмму.
Описываемые в этом документе функции предназначены для создания PlantUML-скриптов из данных системных каталогов PostgreSQL. Основной формат создаваемых PlantUML-скриптов соответствует требованиям плагина plantuml для системы управления проектами TRAC[*3] . Дополнительно описываемые функции могут создавать скрипты классической формы без дополнительной обёртки, необходимой для TRAC.
Общая структура функций
Головной функцией, создающей PlantUML-скрипты является umlfn_Table_fromArray. Она преобразует массив названий таблиц (отношений) в PlantUML-скрипт для последующего отображения в форме диаграммы классов UML.
Дополнительно PlantUML-скрипты могут быть созданы функцией umlfn_Table_AllGroupScript, которая по заданному условию формирует массив таблиц из каталога pg_class и вызывает головную функцию umlfn_Table_fromArray.
На Рис. 2 показана схема взаимодействия функций в процессе создания PlantUML-скрипты. На ней основные функции имеют префикс umlfn_или umltf. Остальные функции — вспомогательные, предназначены для доступа и обработки данных из системных каталогов.
Таблица 1 содержит список и краткое назначение основных функций.
Таблица 2 содержит список и краткое назначение только тех вспомогательных функций, которые не описаны в следующих статьях "Функции для документирования баз данных PostgreSQL":
В основном, эти функции создавались для удобства преобразования данных системных каталогов к формату PlantUML-скриптов.
Созданные PlantUML-скрипты могут быть выгружены в текстовый файл.
Для выгрузки PlantUML-скриптов используются хранимые процедуры, которые вызывают головную или дополнительную функции, а их результат сохраняют в текстовом файле с помощью оператора COPY TO. Схема взаимодействия основных функций и процедур приведена на Рис. 3.
Таблица 3 содержит список и краткое назначение основных процедур.
Процедура umlpr_TablesScipt_FromArray
Процедура umlpr_TablesScipt_FromArray выгружает в текстовый файл PlantUML-скрипт, созданный из массива с названиями таблиц функцией umlfn_Table_fromArray.
В качестве основных параметров процедура принимает массив названий таблиц (a_TableNameArray), путь к папке, к которую должен быть выгружен PlantUML-скрипт (a_OutputFolder) и название схемы, в пределах которой они находятся таблицы (a_SchemaName). Дополнительными параметрами являются: «Признак необходимости включения комментария атрибута» (a_NeedAttrDesc) и «Формат возвращаемого PlantUML-скрипта: 'Trac' или 'PlantTest'» (a_Mode).
Название выгружаемого файла имеет один из следующих двух видов:
plantuml_YYYY-MM-DD;
plantuml_YYYY-MM-DD_classic
Файлы с именем второго вида создаются, если параметр a_Mode имеет значение 'PlantTest'. На месте шаблона 'YYYY-MM-DD' в названии указывается дата создания файла, в которой 'YYYY' — год, 'MM' — порядковый номер месяца в году, 'DD' — день в месяце.
Скрытый текст
COPY (SELECT unnest FROM unnest(ARRAY[umlfn_Table_FromArray(a_SchemaName::VARCHAR, a_TableNameArray, FALSE,a_Mode)]))
TO ''' || a_OutputFile||'plantuml_'||TO_CHAR(current_date, 'YYYY-MM-DD')||
CASE a_Mode WHEN 'PlantTest' THEN '_classic' ELSE '' END||'.txt''';
Рисунок, содержащий оператор COPY выгружающий в текстовый файл PlantUML-скрипт, представляет собой принципиальную схему того, как результат выполнения функции umlfn_Table_fromArray. Но из-за особенностей оператора COPY в формат PlantUML-скрипта приходится внести изменения. Дело в том, что umlfn_Table_fromArray возвращает текстовую переменную, состоящую из не заключённых в кавычки подстрок, а для нормального выполнения оператора COPY наоборот каждая подстрока должна быть заключена в кавычки. При этом, после преобразования исходного формата в массив строк, в выходной файл строки выгружаются уже без кавычек.
Скрытый текст
v_PlantUML_Script:= umlfn_Table_FromArray(a_SchemaName::VARCHAR, a_TableNameArray, FALSE,a_Mode);
v_PlantUML_Script:= ''''||REPLACE(v_PlantUML_Script,CHR(13)||CHR(10),''', ''')||'''';
COPY (SELECT unnest FROM unnest(ARRAY[v_PlantUML_Script]))
TO ''' || a_OutputFile||'plantuml_'||TO_CHAR(current_date, 'YYYY-MM-DD')||
CASE a_Mode WHEN 'PlantTest' THEN '_classic' ELSE '' END||'.txt''';
В новой версии рисунка учтена необходимость предварительного заключения всех подстрок PlantUML-скрипта в кавычки. Это преобразование выполняется во второй строке.
Рис. 4 демонстрирует вариант папки с файлами, выгруженными процедурой umlpr_TablesScipt_FromArray. Содержание файлов подробно описано в разделе «Функция umlfn_Table_fromArray…»
Процедура umlpr_TablesScipt_ByWhereOption
Процедура umlpr_TablesScipt_ByWhereOption выгружает в текстовый файл PlantUML-скрипт, созданный из массива с названиями таблиц, найденных по заданному параметром условию. Непосредственно PlantUML-скрипт создаётся функцией umlfn_Table_fromArray.
В качестве основных параметров принимает условие поиска таблиц (a_WHEREoption), которое не содержит ключевое слово WHERE, путь к папке, к которую должен быть выгружен PlantUML-скрипт (a_OutputFolder) и название схемы, в пределах которой они находятся таблицы (a_SchemaName). Дополнительными параметрами являются: «Признак необходимости включения комментария атрибута» (a_NeedAttrDesc) и «Формат возвращаемого PlantUML-скрипта: 'Trac' или 'PlantTest'» (a_Mode).
Выполнение процедуры сводится к получению списка (массива) функций по заданному условию с помощью функции admfn_Table_TableNameArray, с последующим вызовом процедуры umlfn_Table_fromArray.
Скрытый текст
SELECT umlpr_TablesScipt_FromArray (admfn_Table_TableNameArray(a_SchemaName,a_WHEREoption), a_OutputFolder, a_SchemaName,v_Mode);
При описании условия выбора таблиц следует учесть названия псевдонимов (aliases) каталогов, которые используются в функции. Так для каталога pg_class используется псевдоним (alias) «tbl», а для каталога pg_namespace — «nsp».
Функция umlfn_Table_fromArray
Функция umlfn_Table_fromArray возвращает PlantUML-скрипт, созданный из массива с названиями таблиц.
В качестве основных параметров функция принимает массив названий таблиц (a_TableNameArray) и название схемы, в пределах которой они созданы (a_SchemaName). Дополнительными параметрами являются: «Признак необходимости включения комментария атрибута» (a_NeedAttrDesc) и «Формат возвращаемого PlantUML-скрипта: 'Trac' или 'PlantTest'» (a_Mode).
Скрытый текст
SELECT INTO v_TableGroupScript, v_TableGroupLegendScript
STRING_AGG (umlfn_Table_Script(a_SchemaName,tbl.relname,FALSE,cnfn_Table_LowerName(a_TableNameArray),'') AS v_TableGroupScript
'''legend center'''||CHR(13)||CHR(10)||'''<b><i><u>ТАБЛИЦЫ</u></i></b>'''|| CHR(13)||CHR(10)||''' '''|| CHR(13)||CHR(10)||
STRING_AGG (quote_literal('<b>'||UPPER(ftbl.relname)||'</b>'||'- '||COALESCE(dsc.description,''))|| CHR(13)||CHR(10),'')||
'''endlegend'''|| CHR(13)||CHR(10) AS v_TableGroupLegendScript
FROM pg_class tbl
INNER JOIN pg_namespace nsp ON tbl.relnamespace = nsp.oid
LEFT OUTER JOIN pg_Description dsc ON tbl.oid=dsc.objoid
AND dsc.objsubid=0
WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND tbl.relkind='r'
AND tbl.relname !~* E'(raster_|spatial_|geometry_|geography_)'
AND ARRAY[LOWER(tbl.relname)::TEXT] <@ cnfn_Array_LowerCase(a_TableNameArray)::TEXT[]
Основные данные функция извлекает из записи каталога pg_class, содержащего кроме записей о таблицах еще и, записи о последовательностях, представлениях, материализованных представлениях, составных типах. Поэтому для выбора таблиц используется условие relkind='r'.
Дополнительно функция обращается к данным каталогов pg_namespace и pg_description. Первый содержит названия схем базы данных, а второй - комментарии ко всем объектам БД.
Здесь важно обратить внимание на условие objsubid=0. Оно определяет комментарий к таблице, т к. значение поля objoid одинаково как для таблицы, так и для ее атрибутов. Комментарий к атрибуту таблицы содержится в записи, в которой objsubid совпадает с номером этого атрибута.
Кроме того, в условии выбора исключаются таблицы PostgreSQL-расширения (EXTENSION) postgis, а также все элементы исходного массива названий функций переводятся в нижний регистр при помощи вспомогательной функции cnfn_Array_LowerCase.
С помощью оператора SELECT из данных исходных таблиц группируются две части PlantUML-скрипта: v_TableLinksScript— список таблиц с их атрибутами, v_TableGroupLegendScript — легенда предыдущего списка, т.е. названия таблиц с комментариями к ним.
Скрытый текст
v_TableLinksScript:=umlfn_Table_LinksFromArray(a_SchemaName, a_TableNameArray);
v_TableGroupScript:=v_TableGroupScript ||COALESCE(v_TableLinksScript,'') ||v_TableGroupLegendScript||v_TableGroupScriptSuffix;
Для описания связей заданных таблиц вызывается функция umlfn_Table_LinksFromArray, которая анализирует внешние ключи заданных таблиц, устанавливает обнаруженные связи, а также обозначает характер этих связей: «*-» — многие к одному, «-*»— один ко многим, «1-1» — один к одному. Таблица 4 содержит описаниие символов связи таблиц из руководства по языку PlantUML.
Последним оператором все части PlantUML-скрипта соединяются в нужной последовательности. При этом, в начало строки добавляется признак, идентифицирующий PlantUML-скрипт, а в конец полученной строки признак PlantUML-скрипта.
Таблица 5 позволяет представить результат выполнения функции umlfn_Table_fromArray, а также состав его частей.
Скрытый текст
Таблица 5 демонстрирует PlantUML-скрипт как набор подстрок, расположенных сверху вниз. В тоже время, в теле хранимых функций и процедур эти подстроки образуют значение одной текстовой (TEXT) переменной, в которой они разделены символами конца строки (CHR(13) — «Возврат каретки» и CHR(10) — «перевод строки»). Иногда текст PlantUML-скрипта необходимо преобразовать в массив подстрок. Этим объясняется то, что каждая отдельная подстрока заключена в одиночные кавычки.
v_TableGroupScript:=ARRAY_TO_STRING(cnfn_Array_QuotesWrapperWithout(STRING_TO_ARRAY(v_TableGroupScript, CHR(13)||CHR(10))), CHR(13)||CHR(10));
Но на выходе из umlfn_Table_fromArray одиночные кавычки не нужны. Поэтому текст временно преобразуется в массив, в каждом элементе которого убираются начальная и замыкающая кавычки. А затем массив снова преобразуется в строку, помещённую в тестовой переменной.
Обычно функция возвращает PlantUML-скрипт в формате плагина plantuml для системы управления проектами TRAC, но если дополнительный параметр a_Mode имеет значение 'PlantTest'», то обёртка скрипта ограничивается «@startuml...@enduml». Где @startuml — оператор начала скрипта, @enduml—окончания.
Исходный код функции можно посмотреть здесь.
Функция umlfn_Table_AllGroupScript
Функция umlfn_Table_AllGroupScript возвращает PlantUML-скрипт, созданный таблиц, найденных по заданному параметром условию.
В качестве основных параметров функция принимает условие поиска таблиц (a_WHEREoption), которое не содержит ключевое слово WHERE, и название схемы, в пределах которой будет вестись поиск (a_SchemaName). Дополнительными параметрами являются: «Признак необходимости включения комментария аттрибута» (a_NeedAttrDesc) и «Формат возвращаемого PlantUML-скрипта:'Trac' или 'PlantTest'» (a_Mode).
В теле функции для преобразования условия выборки в массив названий таблиц вызывается функция admfn_Table_TableNameArray и этот массив затем передается в качестве параметра функции umlfn_Table_fromArray, которая уже непосредственно создает PlantUML-скрипт.
Скрытый текст
v_TableGroupScript:=umlfn_Table_FromArray(a_SchemaName, admfn_Table_TableNameArray(a_SchemaName,a_WHEREoption),a_NeedAttrDesc,v_Mode);
При описании условия выбора таблиц следует учесть названия псевдонимов (aliases) каталогов, которые используются в функции. Так для каталога pg_class используется псевдоним (alias) «tbl», а для каталога pg_namespace — «nsp».
Таблица 6 показывает пример результата, возвращаемого функцией при корректном значении параметра a_WHEREoption, в режиме a_Mode='PlantTest'.
Таблица 6. Результат выполнения функции umlfn_Table_AllGroupScript('public','tbl.relname ~* E''^Street''', FALSE, 'PlantTest')
Исходный код функции можно посмотреть здесь.
Функция umlfn_Table_Script
Функция umlfn_Table_fromArray возвращает часть PlantUML-скрипта по названию одной таблицы с её атрибутами.
В качестве основных параметров функция принимает названиие таблицы (a_TableName) и название схемы, в пределах которой они созданы (a_SchemaName). Дополнительными параметрами являются: «Признак необходимости включения комментария аттрибута» (a_NeedAttrDesc) и «Массив таблиц допустимых для поиска внешних ключей таблиц» (a_AllowedTableName).
Скрытый текст
SELECT '''object '|| a_SchemaName ||'.'||a_TableName||'{'''|| CHR(13)||CHR(10)||
STRING_AGG (umlfn_Attribute_String(oattr.oid, oattr.attnum,a_NeedAttrDesc,a_AllowedTableName),'')||
'''}'''|| CHR(13)||CHR(10)
FROM (SELECT tbl.oid, attr.attnum
FROM pg_attribute attr
WHERE attr.attrelid=( a_SchemaName ||'.'|| a_TableName)::regclass AND attr.attnum>0
ORDER BY tbl.relname,attr.attnum) oattr;
Основные данные функция извлекает из записи каталога pg_attribute, который содержит записи с данными об атрибутах таблиц, представлений, материализованных представлений, составных типов и даже функций.
Явное обращение к данным каталогов pg_namespace и pg_class отсутствует благодаря использованию псевдонима regclass, преобразующего квалифицированное имя таблицы в её системный ИД (OID).
Здесь важно обратить внимание на условие attnum>0. Оно определяет наличие типа атрибута в каталоге pg_type типов данных.
С помощью оператора SELECT из данных исходных атрибутов группируется часть PlantUML-скрипта: v_TableScript —таблица со списком её атрибутов. Описание каждого атрибута выполняется функцией umlfn_Attribute_String.
Таблица 7 позволяет представить результат выполния функции umlfn_Table_fromArray, а также состав его частей.
Исходный код функции можно посмотреть здесь.
Функция umlfn_Attribute_String
Функция umlfn_Attribute_String возвращает часть PlantUML-скрипта для одного атрибута по его названию.
В качестве основных параметров функция принимает системный идентификатор (OID) таблицы (a_TableOID) и порядковый номер атрибута в этой таблице (a_AttributeNo). Дополнительными параметрами являются: «Признак необходимости включения комментария атрибута» (a_NeedAttrDesc) и «Массив таблиц допустимых для поиска внешних ключей таблиц» (a_AllowedTableName). При этом значение последнего параметра (a_AllowedTableName) не используется. Как говорится, создан в расчёте на будущее усовершенствование функции.
Основные данные функция получает как результат выполнения другой функции admtf_Attribute_Features, которому передаются значения параметров a_TableOID и a_AttributeNo. Подробное описание этой функции можно найти в документе «Функции для документирования баз данных».
Скрытый текст
SELECT ass.AttString||STRING_AGG(ass.FKString,'')
FROM (SELECT TRIM(af.rsa_AttributeName)||':'||
' '||CASE WHEN af.rsa_Description IS NULL OR NOT a_NeedAttrDesc THEN '' ELSE '(<i>'||af.rsa_Description||'</i>)' END||
' '||af.rsa_TypeName||
' '||CASE WHEN af.rsa_isNotNULL THEN 'NOT' ELSE '' END || 'NULL' || ''||
CASE WHEN af.rsa_isAttributePK THEN '(PK'||TRIM(COALESCE(TO_CHAR(af.rsa_ColumnPKNo,'999'),''))||')'
ELSE '' END AS AttString,
'(FK'||TRIM(COALESCE(TO_CHAR(af.rsa_ColumnFKNo[af.fkInx],'999'),''))||' '||COALESCE(af.rsa_FKTableName[af.fkInx],'')||
'('||COALESCE(af.rsa_FKTableColumnName[af.fkInx],'')||')'||')' FKString
FROM (SELECT iaf.rsa_AttributeName,iaf.rsa_Description,iaf.rsa_TypeName,iaf.rsa_isNotNULL,iaf.rsa_isAttributePK,iaf.rsa_ColumnPKNo,
iaf.rsa_isAttributeFK,iaf.rsa_ColumnFKNo,iaf.rsa_FKTableName,iaf.rsa_FKTableColumnName,
GENERATE_SUBSCRIPTSs(iaf.rsa_ColumnFKNo, 1) fkInx
FROM admtf_Attribute_Features(a_TableOID , a_AttributeNo) iaf) af
ORDER BY fkInx) ass
GROUP BY ass AttString;
Некоторая громоздкость приведенного оператора SELECT вызвана тем, что функция admtf_Attribute_Features, как показывает Таблица 8, возвращает характеристики внешних ключей в виде массивов. Отсюда возникла необходимость использования функции GENERATE_SUBSCRIPTS и последующей группировкой данных о внешнем ключе. Хотя, возможно, от такой группировки следует отказаться в будущем.
Кроме того, при формировании строки с описанием атрибута учитывается значение параметра a_NeedAttrDesc, который указывает на то, добавлять ли в него комментарий к названию атрибута или нет.Остальные данные атрибута просто соединяются в порядке и формате, которые указаны в требования к языку PlantUML.
Таблица 9 содержит пример выполнения функции umlfn_Attribute_String, особенность которого в том, что параметр a_NeedAttrDesc='TRUE', а параметр a_TableOID задан при помощи псевдонима regclass, что более надёжно, чем использовать явное значение OID таблицы, т.к. последний может быть изменён в процессе эксплуатации базы данных.
Исходный код функции можно посмотреть здесь.
Функция umlfn_Table_LinksFromArray
Функция umlfn_Table_LinksFromArray возвращает часть PlantUML-скрипт со списком связей, созданных из массива с названиями таблиц. В качестве основных параметров функция принимает массив названий таблиц (a_TableNameArray) и название схемы, в пределах которой они созданы (a_SchemaName).
Основные данные функция извлекает из записей каталога pg_constraint. Каталог pg_constraint при условии contype='f' содержит записи с характеристиками внешних ключей таблиц, которые связывают записи из каталога pg_class, принадлежащие к типу relkind='r'. Для преобразования названия внешний ключей к характеристикам, соответствующей связи таблиц ('N:1','1:1') используется функция admfn_Table_LinkSign.
Дополнительно функция обращается к данным каталога pg_namespace, содержащего названия схем базы данных.
Скрытый текст
SELECT STRING_AGG(''''||LOWER(nsp.nspname)||'.'||UPPER(tbl.relname)||' '||
CASE LEFT(ls.*,1) WHEN 'N' THEN '*' ELSE '' END || '--' || CASE RIGHT(ls.*,1) WHEN 'N' THEN '*' ELSE '' END||
' '||LOWER(nsp.nspname)||'.'||UPPER(Ftbl.relname)||''''||CHR(13)||CHR(10),'')
FROM pg_class tbl
INNER JOIN pg_namespace nsp ON tbl.relnamespace = nsp.oid
INNER JOIN pg_constraint lcon ON lcon.conrelid=tbl.oid
INNER JOIN pg_class ftbl ON lcon.confrelid=ftbl.oid
INNER JOIN pg_namespace fnsp ON ftbl.relnamespace=fnsp.oid,
LATERAL admfn_Table_LinkSign(nsp.nspname,tbl.relname:: VARCHAR(256),lcon.conname:: VARCHAR(256)) ls
WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND tbl.relkind='r'
LOWER(fnsp.nspname)=LOWER(a_SchemaName) AND ftbl.relkind='r'
AND lcon.contype ='f'
AND ARRAY[tbl.relname::VARCHAR(256)] <@ admfn_Table_LowerName(a_TableNameArray)
AND ARRAY[ftbl.relname::VARCHAR(256)] <@ admfn_Table_LowerName(a_TableNameArray);
Как видно из приведённого оператора, характеристики связи, возвращаемые функцией admfn_Table_LinkSign, преобразуются так:
символ 'N' заменяется на '*';
'1' — на '' (пустую строку);
':' — на '--'.
Связываемые таблицы просто соединяются в порядке, которые указаны в требованиях к языку PlantUML.
Таблица 10 содержит пример выполнения функции umlfn_Table_LinksFromArray.
Исходный код функции можно посмотреть здесь.
ПРИЛОЖЕНИЕ
Создание процедуры umlpr_TablesScipt_FromArray
Комментарии к исходному коду процедуры можно посмотреть здесь.
Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS umlpr_TablesScipt_FromArray(a_TableNameArray VARCHAR(256)[],a_OutputFolder TEXT,a_Schema VARCHAR(256),
a_Mode VARCHAR(20));
/****************************************************************************************************************/
/* Создание файла с описанием таблиц из исходного массива в формате плагина (plantuml) */
/****************************************************************************************************************/
CREATE OR REPLACE FUNCTION umlpr_TablesScipt_FromArray
(a_TableNameArray VARCHAR(256)[], /* Исходный массив таблиц */
a_OutputFolder TEXT, /* Папка создаваемого файла*/
a_Schema VARCHAR(256) DEFAULT 'public',/*Схема исходных таблиц*/
a_Mode VARCHAR(20) default 'Trac' /* Формат возвращаемой строки:*/
/* 'Trac' -для средства управления проектам; */
/*'PlantTest' - для тестирования на сайте*/
/* https://www.planttext.com/ */
)
RETURNS INTEGER
AS
$BODY$
DECLARE
c_Schema CONSTANT name:='public';
c_NeedAttrDesc CONSTANT BOOLEAN:=FALSE; /* Признак необходимости включения комментария аттрибута */
c_TracMode CONSTANT VARCHAR(50):='Trac'; /*для средства управления проектам*/
c_PlantTestMode CONSTANT VARCHAR(50):='PlantTest'; /*для тестирования на сайте "https://www.planttext.com/" */
v_Schema VARCHAR(256); /*Схема исходных таблиц*/
v_Mode VARCHAR(20); /* Формат возвращаемой строки: 'Trac' -для средства управления проектам;*/
/* 'PlantTest' - для тестирования на сайте https://www.planttext.com/ */
v_Return_Error Integer := 0; /* Код возврата*/
v_OutputFile TEXT; /* Путь и название создаваемого файла*/
v_COPY_Statement TEXT; /* Оператор копирования скрипта в файл*/
v_PlantUML_Script TEXT; /* Сгенерированный скрипт*/
v_TableNameArray VARCHAR(256)[];/* Исходный массив таблиц */
BEGIN
v_Mode:=COALESCE(a_Mode,c_TracMode);
v_Schema:=COALESCE(a_Schema,c_Schema);
v_TableNameArray:=a_TableNameArray;
v_OutputFile:=TRIM(a_OutputFolder)||'plantuml_'||current_date||CASE v_Mode WHEN c_PlantTestMode THEN '_classic' ELSE '' END||'.txt';
v_PlantUML_Script:= umlfn_Table_FromArray(v_Schema::VARCHAR,v_TableNameArray,c_NeedAttrDesc,v_Mode);
v_PlantUML_Script:=''''||REPLACE(v_PlantUML_Script,CHR(13)||CHR(10),''', ''')||'''';
v_COPY_Statement='COPY (SELECT unnest FROM unnest(ARRAY['|| v_PlantUML_Script || '])) TO ''' || v_OutputFile||'''';
BEGIN
EXECUTE v_COPY_Statement;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'ОШИБКА ПУТИ к папке создаваемого файла. Проверьте правильность значений параметров.
ПАРАМЕТРЫ создания: ********************************************************************
Путь - "%"
Массив таблиц - "%"
Схема - "%"
***********************************************************',
v_OutputFile,v_TableNameArray,v_Schema;
RETURN -2;
END;
RETURN v_Return_Error;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION umlpr_TablesScipt_FromArray(a_TableNameArray VARCHAR(256)[],a_OutputFolder TEXT,a_Schema VARCHAR(256),a_Mode VARCHAR(20)) IS
'Создание файла с описанием таблиц из исходного массива в формате плагина (plantuml)';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT umlpr_TablesScipt_FromArray('{Street,StreetType,StreetTypeSynonym,STREETSYNONYM}'::VARCHAR(256)[],
'D:\plantuml\Scripts\','public','PlantTest' ); SELECT umlpr_TablesScipt_FromArray('{Street,StreetType,StreetTypeSynonym,STREETSYNONYM}'::VARCHAR(256)[],
'D:\plantuml\Scripts\','public');
Создание процедуры umlpr_TablesScipt_ByWhereOption
Комментарии к исходному коду процедуры можно посмотреть здесь.
Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS umlpr_TablesScipt_ByWhereOption(a_WHEREoption VARCHAR(1000),a_OutputFolder TEXT,a_Schema VARCHAR(256),
a_Mode VARCHAR(20));
/********************************************************************************************************************/
/* Создание файла с описанием таблиц по условию выбора таблиц из системного каталога в формате плагина (plantuml) */
/********************************************************************************************************************/
CREATE OR REPLACE FUNCTION umlpr_TablesScipt_ByWhereOption
(a_WHEREoption VARCHAR(1000), /* условие запроса на выбор таблиц из системного каталога */
a_OutputFolder TEXT, /* Папка создаваемого файла*/
a_Schema VARCHAR(256) DEFAULT 'public',/*Схема исходных таблиц*/
a_Mode VARCHAR(20) default 'Trac' /* Формат возвращаемой строки: */
/* 'Trac' -для средства управления проектам;*/
/*'PlantTest' - для тестирования на сайте */
/*https://www.planttext.com/ */
)
RETURNS INTEGER
AS
$BODY$
DECLARE
v_TableNameArray VARCHAR(256)[]; /* Массив выбранных таблиц */
v_Return_Error Integer := 0; /* Код возврата */
BEGIN
v_TableNameArray:=admfn_Table_TableNameArray(a_Schema,a_WHEREoption);
v_Return_Error:=umlpr_TablesScipt_FromArray(v_TableNameArray,a_OutputFolder,a_Schema,a_Mode);
RETURN v_Return_Error;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION umlpr_TablesScipt_ByWhereOption(a_WHEREoption VARCHAR(1000),a_OutputFolder TEXT,a_Schema VARCHAR(256),a_Mode VARCHAR(20)) IS 'Создание файла с описанием таблиц по условию выбора таблиц из системного каталога в формате плагина (plantuml) ';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
/*SELECT umlpr_TablesScipt_ByWhereOption('tbl.relname LIKE ''id_districts'' OR tbl.relname LIKE ''id_nps'''::VARCHAR(1000),
'D:\Projects\Enisey GIS\DB\PLpgSQL\Common\DBAdministrate\plantuml\NONScripts\' );
SELECT umlpr_TablesScipt_ByWhereOption('tbl.relname LIKE ''id_districts'' OR tbl.relname LIKE ''id_nps'''::VARCHAR(1000),
'D:\Projects\Enisey GIS\DB\PLpgSQL\Common\DBAdministrate\plantuml\Scripts\' ,'public','Trac');
SELECT umlpr_TablesScipt_ByWhereOption('tbl.relname LIKE ''id_districts'' OR tbl.relname LIKE ''id_nps'''::VARCHAR(1000),
'D:\Projects\Enisey GIS\DB\PLpgSQL\Common\DBAdministrate\plantuml\Scripts\','public','PlantTest');
*/
SELECT umlpr_TablesScipt_ByWhereOption('tbl.relname ~* E''^street''',
'D:\Projects\Enisey GIS\DB\PLpgSQL\Common\DBAdministrate\plantuml\Scripts\','public','PlantTest');
SELECT umlpr_TablesScipt_ByWhereOption('tbl.relname ~* E''^street''',
'D:\Projects\Enisey GIS\DB\PLpgSQL\Common\DBAdministrate\plantuml\Scripts\','public','PlantTest');
Создание функции umlfn_Table_fromArray
Комментарии к исходному коду функции можно посмотреть здесь.
Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS umlfn_Table_FromArray (a_SchemaName VARCHAR(100),a_TableNameArray VARCHAR(256)[],a_NeedAttrDesc BOOLEAN,a_Mode VARCHAR(20));
/********************************************************************************************************/
/* Функция возвращает строку с описанием таблиц из исходного массива в формате плагина (plantuml) */
/* к Trac — средству управления проектами и отсслеживания ошибок в программном обеспечении. */
/*------------------------------------------------------------------------------------------------------*/
/* ПРИМЕР: umlfn_Table_FromArray('public','{kmed__adres1,kmed_adres,kmed_adreskladr,kmed_adreskladr_temp,kmed_peoplebyidadreshome,kmed_radres,kmed_sadres,kmed_tadres}'); */
/*------------------------------------------------------------------------------------------------------*/
/* {{{ */
/* #!plantuml */
/* @startuml */
/* object public.KMED__ADRES1{ */
/* id_adres1 : (<i>Уникальный код записи в оглавлении </i>) integer NOT NULL (PK1) */
/* idadres : (<i>Уникальный код записи адресного элемента (Код адреса) в kmed_ADRES</i>) integer NULL */
/* idparent : (<i (...) */
/* */
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION umlfn_Table_FromArray
(a_SchemaName VARCHAR(100) default 'public', /* название схемы базы данных */
a_TableNameArray VARCHAR(256)[] default '{}', /* Исходный массив таблиц */
a_NeedAttrDesc BOOLEAN default false, /* Признак необходимости включения комментария аттрибута */
a_Mode VARCHAR(20) default 'Trac' /* Формат возвращаемого PlantUML-скрипта: 'Trac' -для средства*/
/* управления проектом; 'PlantTest' - для тестирования на сайте */
/* https://www.planttext.com/ */
)
RETURNS TEXT AS
$BODY$
DECLARE
c_SchemaName CONSTANT VARCHAR(256):='public'; /* название схемы базы данных */
c_TableKind CONSTANT CHAR:='r';
c_ForeignKeyKind CONSTANT CHAR:='f';
c_TracMode CONSTANT VARCHAR(50):='Trac'; /*для средства управления проектам*/
c_PlantTestMode CONSTANT VARCHAR(50):='PlantTest'; /*для тестирования на сайте "https://www.planttext.com/" */
c_TracTableGroupScriptPrefix CONSTANT VARCHAR(50):='''{{{'''||CHR(13)||CHR(10)||'''#!plantuml'''||CHR(13)||CHR(10)||'''@startuml'''||CHR(13)||CHR(10);
/* Начало скрипта описания группы таблиц в режиме 'Trac'*/
c_TracTableGroupScriptSuffix CONSTANT VARCHAR(50):='''@enduml'''||CHR(13)||CHR(10)||'''}}}'''||CHR(13)||CHR(10)||''' '''||CHR(13)||CHR(10)||'''----'''||CHR(13)||CHR(10);
/* Конец скрипта описания группы таблиц в режиме 'Trac'*/
c_PlantTestTableGroupScriptPrefix CONSTANT VARCHAR(50):='''@startuml'''||CHR(13)||CHR(10);
/* Начало скрипта описания группы таблиц в режиме 'PlantTest'*/
c_PlantTestTableGroupScriptSuffix CONSTANT VARCHAR(50):='''@enduml''';
/* Конец скрипта описания группы таблиц в режиме 'PlantTest'*/
c_TableGroupLegendPrefix CONSTANT VARCHAR(100):='''legend center'''||CHR(13)||CHR(10)||'''<b><i><u>ТАБЛИЦЫ</u></i></b>'''||CHR(13)||CHR(10)||''' '''||CHR(13)||CHR(10);
/* Начало раздела комментарии к таблицам группы */
c_TableGroupLegendSuffix CONSTANT VARCHAR(100):='''endlegend'''||CHR(13)||CHR(10);
/* Конец раздела комментарии к таблицам группы */
c_TableNameLegendPrefix CONSTANT VARCHAR(10):='<b>'; /* Начало наименования таблицы в комментариях*/
c_TableNameLegendSuffix CONSTANT VARCHAR(10):='</b>'; /* Окончание наименования таблицы в комментариях*/
c_TableDescriptionLegendPrefix CONSTANT VARCHAR(10):='- '; /* Начало описания таблицы в комментариях*/
c_TableDescriptionLegendSuffix CONSTANT VARCHAR(10):=CHR(13)||CHR(10); /* Окончание описания таблицы в комментариях*/
c_TableNameDelimiter CONSTANT VARCHAR(5):='.'; /* Символ-разделитель имени таблицы */
c_Blank CONSTANT VARCHAR(5):=' '; /* Символ пробела */
v_Mode VARCHAR(20); /* Формат возвращаемой строки: 'Trac' -для средства управления*/
/* проектом; 'PlantTest' - для тестирования на сайте */
/* https://www.planttext.com/ */
v_SchemaName VARCHAR(256); /* название схемы базы данных */
v_TableGroupScriptPrefix VARCHAR(50); /* Начало скрипта описания группы таблиц */
v_TableGroupScriptSuffix VARCHAR(50); /* Конец скрипта описания группы таблиц*/
v_TableGroupScript TEXT; /* Формируемый скрипт*/
v_TableLinksScript TEXT; /* Формируемый раздел связей таблиц */
v_TableGroupLegendScript TEXT; /* Формируемый раздел комментириев к таблицам группы*/
v_TableCount INTEGER; /* Счетчик таблиц группы*/
v_TableNameArray TEXT[]; /* Массив таблиц */
v_AloneTableName VARCHAR(256); /* Название одной из таблиц группы*/
v_TableDescription TEXT; /* Описание одной из таблиц группы*/
v_TablesRecord RECORD; /* Запись с данными таблицы */
v_TableOID OID; /* ИД таблицы */
v_ProcessedTableNameArray TEXT[]; /* Массив обработанных таблиц */
v_TableNameArrayRemainder TEXT[]; /* Массив необработанных таблиц */
v_Return_Error INTEGER := 0; /* Код возврата*/
--******************************************************************************************************
BEGIN
v_Mode:=COALESCE(a_Mode,c_TracMode);
v_TableGroupScriptPrefix:=CASE v_Mode WHEN c_PlantTestMode THEN c_PlantTestTableGroupScriptPrefix ELSE c_TracTableGroupScriptPrefix END;
v_TableGroupScriptSuffix:=CASE v_Mode WHEN c_PlantTestMode THEN c_PlantTestTableGroupScriptSuffix ELSE c_TracTableGroupScriptSuffix END;
v_SchemaName:=COALESCE(a_SchemaName,c_SchemaName);
v_TableNameArray:=a_TableNameArray;
v_TableNameArray:=cnfn_Array_LowerCase(a_TableNameArray);
v_TableNameArray:=string_to_array(REPLACE(REPLACE(array_to_string(v_TableNameArray,','),'''',''),'"',''),',');
v_TableGroupScript:='';
v_TableGroupLegendScript:='';
IF COALESCE(v_TableNameArray,'{}')<>'{}' THEN
v_TableCount:=0;
v_TableGroupScript:=v_TableGroupScriptPrefix;
v_TableGroupLegendScript:=c_TableGroupLegendPrefix;
FOR v_TablesRecord IN SELECT ftbl.oid AS TableOID,ftbl.relname:: VARCHAR(256)
FROM pg_class ftbl
INNER JOIN pg_namespace fnsp ON ftbl.relnamespace = fnsp.oid
LEFT OUTER JOIN pg_Description fdsc ON ftbl.oid=fdsc.objoid
AND fdsc.objsubid=0
WHERE LOWER(fnsp.nspname)=LOWER(v_SchemaName) AND ftbl.relkind=c_TableKind
AND ftbl.relname !~ '(raster_|spatial_|geometry_|geography_)'
AND ARRAY[LOWER(ftbl.relname)::TEXT] <@ v_TableNameArray
ORDER BY fnsp.nspname,ftbl.relname
LOOP
v_AloneTableName:=LOWER(v_TablesRecord.relname);
v_AloneTableName:=v_TablesRecord.relname;
v_TableCount:=v_TableCount+1;
v_TableGroupScript:=v_TableGroupScript||
umlfn_Table_Script(a_SchemaName,v_AloneTableName,a_NeedAttrDesc,v_TableNameArray);
SELECT INTO v_TableDescription rs_TableDescription FROM admtf_Table_Features (a_SchemaName,v_AloneTableName);
v_TableGroupLegendScript:=v_TableGroupLegendScript||
quote_literal(c_TableNameLegendPrefix||UPPER(v_AloneTableName)||c_TableNameLegendSuffix||
c_TableDescriptionLegendPrefix||COALESCE(v_TableDescription,''))||c_TableDescriptionLegendSuffix;
v_ProcessedTableNameArray:=array_append(v_ProcessedTableNameArray, v_AloneTableName::TEXT);
END LOOP;
v_TableGroupLegendScript:=v_TableGroupLegendScript||c_TableGroupLegendSuffix ;
v_TableLinksScript:=cnfn_Array_LowerCase(a_SchemaName,v_TableNameArray);
v_TableGroupScript:=v_TableGroupScript ||COALESCE(v_TableLinksScript,'') ||v_TableGroupLegendScript||v_TableGroupScriptSuffix; END IF;
RETURN v_TableGroupScript;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION umlfn_Table_FromArray(a_SchemaName VARCHAR(100),a_TableNameArray VARCHAR(256)[],a_NeedAttrDesc BOOLEAN,a_Mode VARCHAR(20)) IS 'Возвращает строку с описанием таблиц массива в формате плагина (plantuml) к Trac';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT umlfn_Table_FromArray('public'::VARCHAR,'{ Street,StreetType, STREETSYNONYM }'::VARCHAR(256)[]);
Создание функции umlfn_Table_AllGroupScript
Комментарии к исходному коду функции можно посмотреть здесь.
Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS umlfn_Table_AllGroupScript (a_SchemaName VARCHAR(100),a_WHEREoption VARCHAR(1000),a_NeedAttrDesc BOOLEAN,a_Mode VARCHAR(20));
/********************************************************************************************************/
/* Функция возвращает строку с описанием группы таблиц в формате плагина (plantuml) к Trac — средству */
/* управления проектами и отслеживания ошибок в программном обеспечении. */
/*------------------------------------------------------------------------------------------------------*/
/* ПРИМЕР: umlfn_Table_AllGroupScript('public','tbl.relname ~* E''^Street''',FALSE,'Trac'); */
/*------------------------------------------------------------------------------------------------------*/
/* {{{ */
/* #!plantuml */
/* @startuml */
/* object public.STREET{ */
/* wcrccode : smallint NOT NULL (PK1) */
/* localityid : integer NOT NULL (PK2) */
/* streetid : smallint NOT NULL (PK3) */
/* streettypeacrm : character(8) NULL (FK1 streettype(streettypeacrm)) */
/* streetname : character varying(150) NOT NULL */
/* streettsvector : tsvector NULL */
/* streettslitevector : tsvector NULL */
/* } */
/* object public.STREETSYNONYM{ */
/* wcrccode : smallint NOT NULL (PK1)(FK1 street(wcrccode)) */
/* localityid : integer NOT NULL (PK2)(FK2 street(localityid)) */
/* streetid : smallint NOT NULL (PK3)(FK3 street(streetid)) */
/* synonymid : smallint NOT NULL (PK4) */
/* streettypeacrm : character(8) NULL (FK1 streettype(streettypeacrm)) */
/* streetsynonymname : character varying(200) NULL */
/* } */
/* object public.STREETTYPE{ */
/* streettypeacrm : character(8) NOT NULL (PK1) */
/* streettypename : character varying(30) NULL */
/* } */
/* object public.STREETTYPESYNONYM{ */
/* streettypeacrm : character(8) NOT NULL (PK1)(FK1 streettype(streettypeacrm)) */
/* synonymid : smallint NOT NULL (PK2) */
/* streettypesynonymacrm : character varying(15) NULL */
/* } */
/* public.STREETSYNONYM *-- public.STREET */
/* public.STREETSYNONYM *-- public.STREETTYPE */
/* public.STREET *-- public.STREETTYPE */
/* public.STREETTYPESYNONYM *-- public.STREETTYPE */
/* legend center */
/* <b><i><u>ТАБЛИЦЫ</u></i></b> */
/* */
/* <b>STREET</b>- Список улиц в населенных пунктах */
/* <b>STREETSYNONYM</b>- Список синонимов назвачний улиц в населенных пунктах */
/* <b>STREETTYPE</b>- Справочник типов улиц */
/* <b>STREETTYPESYNONYM</b>- Справочник синонимов типов улиц */
/* endlegend */
/* @enduml */
/* }}} */
/* */
/* ---- */
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION umlfn_Table_AllGroupScript
(a_SchemaName VARCHAR(100) default 'public', /* название схемы базы данных */
a_WHEREoption VARCHAR(1000) default NULL, /* условие запроса на выбор таблиц из системного каталога */
a_NeedAttrDesc BOOLEAN default false, /* Признак необходимости включения комментария аттрибута */
a_Mode VARCHAR(20) default 'Trac' /* Формат возвращаемой строки: 'Trac' -для средства управления проектам; 'PlantTest' - для тестирования на сайте https://www.planttext.com/ */
)
RETURNS TEXT AS
$BODY$
DECLARE
c_TracMode CONSTANT VARCHAR(50):='Trac'; /*для средства управления проектам*/
c_PlantTestMode CONSTANT VARCHAR(50):='PlantTest'; /*для тестирования на сайте "https://www.planttext.com/ */
v_Mode VARCHAR(20); /* Формат возвращаемой строки: 'Trac' -для средства управления проектам; */
/* 'PlantTest' - для тестирования на сайте https://www.planttext.com/ */
v_TableNameArray VARCHAR(256)[]; /* Массив выбранных таблиц */
v_TableGroupScript TEXT; /* Формируемый скрипт*/
v_Return_Error Integer := 0; /* Код возврата*/
--******************************************************************************************************
BEGIN
v_Mode:=COALESCE(a_Mode,c_TracMode);
v_TableGroupScript:='';
v_TableNameArray:=admfn_Table_TableNameArray(a_SchemaName,a_WHEREoption);
IF COALESCE(v_TableNameArray,'{}')<>'{}' THEN
v_TableGroupScript:=umlfn_Table_FromArray(a_SchemaName,v_TableNameArray,a_NeedAttrDesc,v_Mode);
END IF;
RETURN v_TableGroupScript;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION umlfn_Table_AllGroupScript(a_SchemaName VARCHAR(100),a_WHEREoption VARCHAR(1000),a_NeedAttrDesc BOOLEAN,a_Mode VARCHAR(20)) IS 'Возвращает строку строку с описанием группы таблиц в формате плагина (plantuml) к Trac';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT umlfn_Table_AllGroupScript('public','tbl.relname ~* E''^Street''');
SELECT umlfn_Table_AllGroupScript('public','tbl.relname ~* E''^Street''',FALSE,'PlantTest');
Создание функции umlfn_Table_Script
Комментарии к исходному коду функции можно посмотреть здесь.
Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS umlfn_Table_Script (a_SchemaName name,a_TableName name,a_NeedAttrDesc BOOLEAN,a_AllowedTableName VARCHAR(256)[]);
/********************************************************************************************************/
/* Функция возвращает строку с описанием таблицы в формате плагина (plantuml) к Trac — средству */
/* управления проектами и отслеживания ошибок в программном обеспечении. */
/*------------------------------------------------------------------------------------------------------*/
/* ПРИМЕР: SELECT umlfn_Table_Script('public','Street',TRUE); */
/*------------------------------------------------------------------------------------------------------*/
/* 'object public.STREET{' */
/* 'wcrccode : (<i>Код страны</i>) smallint NOT NULL (PK1)(FK1 locality(wcrccode))' */
/* 'localityid : (<i>ИД населенного пункта</i>) integer NOT NULL (PK2)(FK2 locality(localityid))' */
/* 'streetid : (<i>ИД улицы населенного пункта</i>) smallint NOT NULL (PK3)' */
/* 'streettypeacrm : (<i>Акроним типа улицы</i>) character(8) NULL (FK1 streettype(streettypeacrm))' */
/* 'streetname : (<i>Наименование улицы населенного пункта</i>) character varying(150) NOT NULL' */
/* 'streettsvector : (<i>Расширенные характеристики улицы в населенном пункте, преобразованные в вектор полнотекстового поиска</i>) tsvector NULL ' */
/* 'streettslitevector : (<i>Характеристики улицы в населенном пункте, преобразованные в вектор полнотекстового поиска</i>) tsvector NULL ' */
/* '}' */
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION umlfn_Table_Script
(a_SchemaName NAME default 'public', /* название схемы базы данных */
a_TableName NAME default NULL, /* Название таблицы */
a_NeedAttrDesc BOOLEAN default false, /* Признак необходимости включения комментария аттрибута */
a_AllowedTableName VARCHAR(256)[] default '{}' /* Массив допустимых для поиска внешних ключей таблиц */
)
RETURNS TEXT AS
$BODY$
DECLARE
c_TableScriptPrefix CONSTANT VARCHAR(10):='''object '; /* Начало скрипта описания таблицы*/
c_AttributeListPrefix CONSTANT VARCHAR(5):='{'''||CHR(13)||CHR(10); /* Начало списка атрибутов */
c_AttributeListSuffix CONSTANT VARCHAR(5):='''}'''||CHR(13)||CHR(10); /* Конец списка атрибутов */
c_TableScriptSuffix CONSTANT VARCHAR(5):=' '||CHR(13)||CHR(10); /* Конец скрипта описания таблицы*/
c_TableNameDelimiter CONSTANT VARCHAR(5):='.'; /* Символ-разделитель имени таблицы */
c_Blank CONSTANT VARCHAR(5):=' '; /* Символ пробела */
v_AttributeScript TEXT; /* */
v_TableScript TEXT; /* */
v_Schema name;
v_TableName name;
v_TableOID OID;
v_AttributeName name;
v_AttributeNum SMALLINT;
v_AttributeCount INTEGER; /* Счетчик цикла*/
v_Return_Error Integer := 0;
cursor_Attributes CURSOR FOR SELECT tbl.oid, attr.attnum
FROM pg_attribute attr
INNER JOIN pg_class tbl ON tbl.oid = attr.attrelid
INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid
WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) AND attr.attnum>0
ORDER BY tbl.relname,attr.attnum;
--******************************************************************************************************
BEGIN
v_TableScript:='';
OPEN cursor_Attributes;
v_AttributeCount:=0;
v_TableScript:=c_TableScriptPrefix || LOWER(a_SchemaName) || c_TableNameDelimiter ||
CASE WHEN LEFT(TRIM(a_TableName),1) =UPPER(LEFT(TRIM(a_TableName),1))
AND SUBSTRING(TRIM(a_TableName) FROM 2) =LOWER(SUBSTRING(TRIM(a_TableName) FROM 2))
THEN UPPER(a_TableName)
WHEN a_TableName ~ E'[A-ZА-ЯЁ]' THEN '"'||a_TableName||'"'
ELSE UPPER(a_TableName) END ||c_AttributeListPrefix;
FETCH FIRST FROM cursor_Attributes INTO v_TableOID,v_AttributeNum;
WHILE FOUND
LOOP
v_AttributeCount:=v_AttributeCount+1;
v_TableScript:=v_TableScript||COALESCE(umlfn_Attribute_String(v_TableOID,v_AttributeNum,a_NeedAttrDesc,a_AllowedTableName),'');
FETCH NEXT FROM cursor_Attributes INTO v_TableOID,v_AttributeNum;
END LOOP;
CLOSE cursor_Attributes;
v_TableScript:=v_TableScript ||c_AttributeListSuffix;
RETURN v_TableScript;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION umlfn_Table_Script(a_SchemaName name,a_TableName name,a_NeedAttrDesc BOOLEAN,a_AllowedTableName VARCHAR(256)[]) IS 'Возвращает строку с описанием таблицы в формате плагина (plantuml) к Trac';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT umlfn_Table_Script('public'::VARCHAR,'Street');
Создание функции umlfn_Attribute_String
Комментарии к исходному коду функции можно посмотреть здесь.
Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS umlfn_Attribute_String (a_TableOID OID,a_AttributeNo SMALLINT,a_NeedAttrDesc BOOLEAN,a_AllowedTableName VARCHAR(256)[]);
/***************************************************************************************************************/
/* Функция возвращает строку с описанием атрибута таблицы в формате плагина (plantuml) к Trac — средству */
/* управления проектами и отслеживания ошибок в программном обеспечении. */
/*-------------------------------------------------------------------------------------------------------------*/
/* ПРИМЕРЫ: */
/* ИД_Пользователь : double (PK) */
/* ИД_Группа : double (FK1) */
/* ИД_Должность : double (FK2) */
/* Системное_Имя_Пользователя : varchar2(255) (AK) */
/* ФИО : varchar2(255) */
/* Телефоны : varchar2(255) */
/* Почта : varchar2(100) */
/* Адрес : varchar2(255) */
/* Дата_Рождения : date */
/****************************************************************************************************************/
CREATE OR REPLACE FUNCTION umlfn_Attribute_String
(a_TableOID OID, /* ИД таблицы */
a_AttributeNo SMALLINT, /* Порядковый номер атрибута в таблице*/
a_NeedAttrDesc BOOLEAN default false, /* Признак необходимости включения комментария аттрибута */
a_AllowedTableName VARCHAR(256)[] default '{}' /* Массив допустимых для поиска внешних ключей таблиц */
)
RETURNS TEXT AS
$BODY$
DECLARE
c_PrimaryKeyPrefix CONSTANT VARCHAR(5):='(PK'; /* Начало опции об участии аттрибута в первичном ключе*/
c_PrimaryKeySuffix CONSTANT VARCHAR(5):=')'; /* Окончание опции об участии аттрибута в первичном ключе*/
c_ForeignKeyPrefix CONSTANT VARCHAR(5):='(FK'; /* Начало опции об участии аттрибута во внешнем ключе*/
c_ForeignKeySuffix CONSTANT VARCHAR(5):=')'; /* Окончание опции об участии аттрибута во внешнем ключе*/
c_DescriptionPrefix CONSTANT VARCHAR(5):='(<i>'; /* Начало комментария к атрибуту*/
c_DescriptionSuffix CONSTANT VARCHAR(5):='</i>)'; /* Окончание комментария к атрибуту*/
c_EndOfAttributeString CONSTANT VARCHAR(5):=CHR(13)||CHR(10); /* Символы окончания строки описания атрибута */
c_NULL CONSTANT VARCHAR(5):='NULL'; /* Строка NULL */
c_NOT CONSTANT VARCHAR(5):='NOT'; /* Строка NOT */
c_Blank CONSTANT VARCHAR(5):=' '; /* Символ пробела */
c_AttributeDelimiter CONSTANT VARCHAR(5):=' : '; /* Символ - разделитель названия атрибута и его типа */
c_ColumnFKList_NDims CONSTANT INTEGER:=1; /* Размерность массива атрибутов внешнего ключа*/
v_NeedAttrDesc BOOLEAN; /* Признак необходимости включения комментария аттрибута */
v_AllowedTableName VARCHAR(256)[]; /* Массив допустимых для поиска внешних ключей таблиц */
v_Attribute_String TEXT; /* Строка описания атрибута*/
v_AttributeName name; /* Название атрибута*/
v_UserTypeName VARCHAR(256); /* Пользовательский тип атрибута (название домена)*/
v_TypeName VARCHAR(256); /* Тип атрибута*/
v_isNotNULL BOOLEAN; /* Признак недопустимости значения NULL*/
v_isAttributePK BOOLEAN; /* Признак вхождения атрибута в первичный ключ */
v_ColumnPKNo SMALLINT; /* Порядковый номер аттрибута в первичном ключе*/
v_Description TEXT; /* Комментари к аттрибуту */
v_isAttributeFK BOOLEAN; /* Признак вхождения атрибута во внешний ключ */
v_ColumnFKNoARRAY SMALLINT[]; /* Массив порядковых номеров атрибута во всех внешних ключах */
v_FKTableNameARRAY name[]; /* Массив названий таблиц, на которые сслылаются внешние ключи */
v_FKTableColumnNameARRAY name[]; /* Массив названий атрибутов таблицы, на которую сслылается внешний ключи */
v_ColumnFKNo SMALLINT; /* Порядковый номер атрибута текщем внешнем ключе */
v_FKTableName name; /* Название таблицы, на которую сслылается текщий внешний ключ */
v_FKTableColumnName name; /* Название атрибута таблицы, на которую сслылается внешний ключ */
v_ColumnFKIndx INTEGER; /* Текущий индекс масссива атрибутов внешнего ключа*/
v_ColumnFKLBound INTEGER; /* Нижний индекс масссива атрибутов внешнего ключа*/
v_ColumnFKUBound INTEGER; /* Верхний индекс масссива атрибутов внешнего ключа*/
v_Return_Error Integer := 0; /* Код возврата*/
--******************************************************************************************************
BEGIN
v_Attribute_String:='';
v_AllowedTableName:=COALESCE(a_AllowedTableName,'{}');
v_AllowedTableName:=cnfn_Array_LowerCase(v_AllowedTableName);
v_NeedAttrDesc:=COALESCE(a_NeedAttrDesc,false);
SELECT INTO v_AttributeName,v_TypeName,v_isNotNULL,v_isAttributePK,v_ColumnPKNo,v_Description,v_isAttributeFK,v_ColumnFKNoARRAY,
v_FKTableNameARRAY,v_FKTableColumnNameARRAY
rsa_AttributeName,rsa_TypeName,rsa_isNotNULL,rsa_isAttributePK,rsa_ColumnPKNo,rsa_Description,
rsa_isAttributeFK,rsa_ColumnFKNo,
rsa_FKTableName,rsa_FKTableColumnName
FROM admtf_Attribute_Features(a_TableOID,a_AttributeNo);
v_Attribute_String:=TRIM(v_AttributeName)||c_AttributeDelimiter||
CASE WHEN v_Description IS NULL OR NOT v_NeedAttrDesc THEN ''
ELSE c_Blank||c_DescriptionPrefix||v_Description||c_DescriptionSuffix|| c_Blank END ||
v_TypeName||
c_Blank||CASE WHEN v_isNotNULL THEN c_NOT||c_Blank ELSE '' END || c_NULL || c_Blank ||
CASE WHEN v_isAttributePK
THEN c_PrimaryKeyPrefix||TRIM(COALESCE(TO_CHAR(v_ColumnPKNo,'999'),''))||
c_PrimaryKeySuffix
ELSE '' END;
IF v_isAttributeFK THEN
v_ColumnFKLBound:=array_lower(v_ColumnFKNoARRAY,c_ColumnFKList_NDims);
v_ColumnFKUBound:=array_upper(v_ColumnFKNoARRAY,c_ColumnFKList_NDims);
v_ColumnFKIndx:=v_ColumnFKLBound;
WHILE v_ColumnFKIndx<=v_ColumnFKUBound
LOOP
v_ColumnFKNo:=v_ColumnFKNoARRAY[v_ColumnFKIndx];
v_FKTableName:=v_FKTableNameARRAY[v_ColumnFKIndx];
v_FKTableColumnName:=v_FKTableColumnNameARRAY[v_ColumnFKIndx];
IF COALESCE(a_AllowedTableName,'{}')='{}' OR ARRAY[v_FKTableName] <@ a_AllowedTableName::NAME[] THEN
v_Attribute_String:=v_Attribute_String||c_ForeignKeyPrefix||
TRIM(COALESCE(TO_CHAR(v_ColumnFKNo,'999'),''))||
' '||COALESCE(v_FKTableName,'')||'('||COALESCE(v_FKTableColumnName,'')||')'||
c_ForeignKeySuffix;
END IF;
v_ColumnFKIndx:=v_ColumnFKIndx+1;
END LOOP;
END IF;
v_Attribute_String:=quote_literal(v_Attribute_String)||c_EndOfAttributeString;
RETURN v_Attribute_String;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION umlfn_Attribute_String(a_TableOID OID,a_AttributeNo SMALLINT,a_NeedAttrDesc BOOLEAN,a_AllowedTableName VARCHAR(256)[]) IS 'Возвращает строку с описанием атрибута таблицы в формате плагина (plantuml) к Trac';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT * FROM umlfn_Attribute_String('public.Street'::REGCLASS,2::SMALLINT,TRUE);
SELECT * FROM generate_series (1,6) inx, LATERAL umlfn_Attribute_String('public.Street'::REGCLASS,inx::SMALLINT,FALSE) ORDER BY inx;
Создание функции umlfn_Table_LinksFromArray
Комментарии к исходному коду функции можно посмотреть здесь.
Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS umlfn_Table_LinksFromArray (a_SchemaName VARCHAR(100),a_TableNameArray VARCHAR(256)[]);
/************************************************************************************************************/
/* Функция возвращает строку с описанием связей таблиц из исходного массива в формате плагина (plantuml) */
/* к Trac — средству управления проектами и отсслеживания ошибок в программном обеспечении. */
/*----------------------------------------------------------------------------------------------------------*/
/* ПРИМЕР: umlfn_Table_LinksFromArray('public','{Street,StreetType,StreetSynonym,StreetTypeSynonym}'); */
/*----------------------------------------------------------------------------------------------------------*/
/* 'public.STREETSYNONYM *-- public.STREET' */
/* 'public.STREETSYNONYM *-- public.STREETTYPE' */
/* 'public.STREET *-- public.STREETTYPE' */
/* 'public.STREETTYPESYNONYM *-- public.STREETTYPE' */
/************************************************************************************************************/
CREATE OR REPLACE FUNCTION umlfn_Table_LinksFromArray
(a_SchemaName VARCHAR(100) default 'public', /* название схемы базы данных */
a_TableNameArray VARCHAR(256)[] default '{}' /* Исходный массив таблиц */
)
RETURNS TEXT AS
$BODY$
DECLARE
c_SchemaName CONSTANT VARCHAR(256):='public'; /* название схемы базы данных */
c_TableKind CONSTANT CHAR:='r';
c_ForeignKeyKind CONSTANT CHAR:='f';
c_ManySign CONSTANT CHAR:='N';
c_OneSign CONSTANT CHAR:='1';
c_PlantManySign CONSTANT VARCHAR:='*';
c_PlantOneSign CONSTANT VARCHAR:='';
c_PlantLinkMark CONSTANT VARCHAR:='--';
c_TableLinksSuffix CONSTANT VARCHAR(10):=CHR(13)||CHR(10); /* Окончание описания таблицы в комментариях*/
c_TableNameDelimiter CONSTANT VARCHAR(5):='.'; /* Символ-разделитель имени таблицы */
c_Blank CONSTANT VARCHAR(5):=' '; /* Символ пробела */
v_SchemaName VARCHAR(256); /* название схемы базы данных */
v_TableLinksScript TEXT; /* Формируемый скрипт*/
v_TableCount INTEGER; /* Чсетчик таблиц группы*/
v_TableNameArray TEXT[]; /* Массив таблиц */
v_TableName VARCHAR(256); /* Название одной из таблиц группы*/
v_FTableName VARCHAR(256); /* Название одной из таблиц группы*/
v_TableDescription TEXT; /* Описание одной из таблиц группы*/
v_TablesRecord RECORD; /* Запись с данными таблицы */
v_TableOID OID; /* ИД таблицы */
v_ForeignKeyName VARCHAR(256); /* Название внешнего ключа*/
v_ProcessedTableNameArray TEXT[]; /* Массив обработанных таблиц */
v_TableNameArrayRemainder TEXT[]; /* Массив необработанных таблиц */
v_LinkSign VARCHAR(5); /* Признак связи по отношения к таклице*/
v_LinkMarkLeftChar VARCHAR; /* Левый символ признака связи*/
v_LinkMarkRightChar VARCHAR; /* Правый символ признака связи*/
v_PlantLinkSign VARCHAR(5);
v_Return_Error Integer := 0;
--******************************************************************************************************
BEGIN
v_SchemaName:=COALESCE(a_SchemaName,c_SchemaName);
v_TableNameArray:=cnfn_Array_LowerCase(a_TableNameArray);
v_TableLinksScript:='';
IF COALESCE(v_TableNameArray,'{}')<>'{}' THEN
v_TableCount:=0;
FOR v_TablesRecord IN SELECT tbl.oid AS TableOID,tbl.relname:: VARCHAR(256),ftbl.oid AS FTableOID,
Ftbl.relname:: VARCHAR(256) AS Frelname,
lcon.oid AS ForeignKeyOID,lcon.conname:: VARCHAR(256) AS ForeignKeyName
FROM pg_class tbl
INNER JOIN pg_namespace nsp ON tbl.relnamespace = nsp.oid
LEFT OUTER JOIN pg_Description dsc ON tbl.oid=dsc.objoid
AND dsc.objsubid=0
INNER JOIN pg_constraint lcon ON lcon.conrelid=tbl.oid
INNER JOIN pg_namespace lnspc ON lcon.connamespace = lnspc.oid
INNER JOIN pg_class ftbl ON lcon.confrelid=ftbl.oid
INNER JOIN pg_namespace fnsp ON ftbl.relnamespace=fnsp.oid
WHERE LOWER(fnsp.nspname)=LOWER(v_SchemaName) AND ftbl.relkind=c_TableKind
AND lcon.contype =c_ForeignKeyKind AND LOWER(lnspc.nspname)=LOWER(v_SchemaName)
AND ftbl.relname !~* E'(raster_|spatial_|geometry_|geography_)'
AND ARRAY[tbl.relname::TEXT] <@ v_TableNameArray
AND ARRAY[ftbl.relname::TEXT] <@ v_TableNameArray
ORDER BY COUNT(*) OVER(PARTITION BY nsp.oid,tbl.oid) DESC, nsp.nspname,tbl.relname
LOOP
v_TableName:=v_TablesRecord.relname;
v_FTableName:=v_TablesRecord.Frelname;
v_ForeignKeyName:=v_TablesRecord.ForeignKeyName;
v_TableCount:=v_TableCount+1;
v_LinkSign:=admfn_Table_LinkSign(a_SchemaName,v_TableName,v_ForeignKeyName);
v_LinkMarkLeftChar:=LEFT(v_LinkSign,1);
v_LinkMarkRightChar:=RIGHT(v_LinkSign,1);
v_PlantLinkSign:=CASE v_LinkMarkLeftChar WHEN c_ManySign THEN c_PlantManySign ELSE c_PlantOneSign END ||
c_PlantLinkMark || CASE v_LinkMarkRightChar WHEN c_ManySign THEN c_PlantManySign
ELSE c_PlantOneSign END;
v_TableLinksScript:=v_TableLinksScript||''''||LOWER(v_SchemaName)||c_TableNameDelimiter||UPPER(v_TableName)||c_Blank||
v_PlantLinkSign||c_Blank||LOWER(v_SchemaName)||c_TableNameDelimiter||UPPER(v_FTableName)||''''||
c_TableLinksSuffix;
v_ProcessedTableNameArray:=array_append(v_ProcessedTableNameArray, v_TableName::TEXT);
END LOOP;
END IF;
RETURN v_TableLinksScript;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION umlfn_Table_LinksFromArray(a_SchemaName VARCHAR(100),a_TableNameArray VARCHAR(256)[]) IS 'Возвращает строку строку с описанием таблиц массива в формате плагина (plantuml) к Trac';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT umlfn_Table_LinksFromArray('public','{Street,StreetType,StreetSynonym,StreetTypeSynonym}');
ПРИЛОЖЕНИЕ 2. Дополнительные материалы
Пошаговая инструкция просмотра диаграмм классов
Скрытый текст
Посмотреть, как выглядит диаграмма классов, описанная PlantUML-скриптом можно на сайте PlantText UML editor.
Шаг 1. Перейти по ссылке https://www.planttext.com/ на страницу редактора, как показано на Рис. 5. Установить курсор на заголовке вкладки «File Manager». В открывшемся меню выбрать пункт «Import / Export».
Шаг 2. В открывшемся окне «Import/Export» в строке «Import» щёлкнуть левой клавишей мыши на кнопку «Обзор», как показано.
Откроется стандартное для Вашей операционной системы окно выбора файла. В Windows 10 оно будет выглядеть примерно так, как показано на Рис. 7.
Выбрать заранее подготовленный скрипт и нажать кнопку «Открыть».
Здесь выбирается скрипт с окончанием «PlantTest», т.к. он создан в формате готовом для отображения диаграммы классов в PlantText UML editor.
Беды не будет, если выбрать скрипт без такого окончания, т.к. как показано дальше формат скриптов для TRAC содержит дополнительные строчки в начале и в конце скрипта, которые легко удаляются.
Шаг 3. В открывшемся окне «Import/Export» в строке «Import» щёлкнуть левой клавишей мыши на кнопку «Обзор», как показано.
Как видно из Рис. 8 диаграмма классов может быть преобразованы в форматы: PNG, SVG [*5] , TXT.
Как внести изменения в PlantUML-скрипт для TRAC, чтобы увидеть диаграмму классов
PlantText UML editor работает со скриптами, построенными по правилам языка PlantUML, в соответствии с которыми основной текст скрипта должен быть заключён между операторами @startuml и @enduml. В тоже время для встраивания в плагин (plantuml) TRAC вокруг классического текста создается дополнительная оболочка.
Для того чтобы PlantText UML editor не воспринимал скрипт как ошибочный, из его текста следует удалить строки выше оператора @startuml и ниже оператора @enduml. Эти строки выделены на Рис. 10.
После удаления экран приобретёт вид, показанный на Рис. 8.
Пошаговая инструкция создания карточки TRAC
Скрытый текст
Здесь предполагается, что «Система управления проектами TRAC» уже установлена. Тем же, кого интересуют сведения о порядке установки этой системы предлагаем перейти на страницу официального сайта под названием «Руководство по установке Trac».
Вход в установленную на сервере компании систему TRAC выполняется через браузер. Формат URL установленного TRAC может выглядеть так: https://trac.company.ru/. Где вместо «company» указывается домен компании, в которой установлена система TRAC. Поэтому, как показано на Рис. 11, первый шаг представляет собой вход с сиcтему TRAC.
После входа открывается страница проекта, отображающая список проектов компании. Так как вход осуществляется с познавательными целями, то вторым шагом выбирается проект «песочница», предназначенный для изучения возможностей TRAC путём экспериментирования. Смотри Рис. 12.
Щелчок на названии проекта левой клавишей мышки, приводит к тому, что на экране появляются список карточек проекта, а также кнопки вызова различных операций над карточками. В данном случает, как показано на Рис. 13, мы создаем новую карточку, нажав на кнопку с соответствующим названием.
В открывшемся окне для создания новой карточки вводится название карточки в поле «Краткое описание», а в основное окно вставляется текст PlantUML-скрипта. Смотри Рис. 14.
Заметим, что вставляемый скрипт должен быть окружен операторами «обёртки» TRAC. На Рис. 15 эти операторы выделены красным цветом. После нажатия одной из кнопок «Предварительный просмотр» или «Создать карточку» на экране отобразится описанная скриптом диаграмма классов.
Цель достигнута — скрипт включен в карточку системы TRAC, в которой отображается диаграмма классов.