Статья продолжает знакомить с функциями для документирования баз данных 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_TableNameArrayVARCHAR(256)[], /* Исходный массив таблиц */ a_OutputFolder TEXT, /* Папка создаваемого файла*/ a_SchemaVARCHAR(256) DEFAULT 'public',/*Схема исходных таблиц*/ a_ModeVARCHAR(20) default 'Trac'/* Формат возвращаемой строки:*/ /* 'Trac' -для средства управления проектам; */ /*'PlantTest' - для тестирования на сайте*/ /* https://www.planttext.com/ */ ) RETURNS INTEGER AS $BODY$ DECLARE c_SchemaCONSTANT name:='public'; c_NeedAttrDescCONSTANT BOOLEAN:=FALSE;/* Признак необходимости включения комментария аттрибута */ c_TracModeCONSTANT VARCHAR(50):='Trac'; /*для средства управления проектам*/ c_PlantTestModeCONSTANT VARCHAR(50):='PlantTest'; /*для тестирования на сайте "https://www.planttext.com/" */ v_SchemaVARCHAR(256); /*Схема исходных таблиц*/ v_ModeVARCHAR(20); /* Формат возвращаемой строки: 'Trac' -для средства управления проектам;*/ /* 'PlantTest' - для тестирования на сайте https://www.planttext.com/ */ v_Return_Error Integer := 0; /* Код возврата*/ v_OutputFile TEXT; /* Путь и название создаваемого файла*/ v_COPY_StatementTEXT; /* Оператор копирования скрипта в файл*/ v_PlantUML_ScriptTEXT; /* Сгенерированный скрипт*/ v_TableNameArrayVARCHAR(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_WHEREoptionVARCHAR(1000), /* условие запроса на выбор таблиц из системного каталога */ a_OutputFolderTEXT, /* Папка создаваемого файла*/ a_SchemaVARCHAR(256) DEFAULT 'public',/*Схема исходных таблиц*/ a_ModeVARCHAR(20) default 'Trac'/* Формат возвращаемой строки: */ /* 'Trac' -для средства управления проектам;*/ /*'PlantTest' - для тестирования на сайте */ /*https://www.planttext.com/ */ ) RETURNS INTEGER AS $BODY$ DECLARE v_TableNameArrayVARCHAR(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_SchemaNameVARCHAR(100) default 'public',/* название схемы базы данных*/ a_TableNameArrayVARCHAR(256)[]default '{}',/* Исходный массив таблиц */ a_NeedAttrDescBOOLEAN 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_PlantTestTableGroupScriptPrefixCONSTANT VARCHAR(50):='''@startuml'''||CHR(13)||CHR(10); /* Начало скрипта описания группы таблиц в режиме 'PlantTest'*/ c_PlantTestTableGroupScriptSuffixCONSTANT 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_TableGroupScriptPrefixVARCHAR(50);/* Начало скрипта описания группы таблиц */ v_TableGroupScriptSuffixVARCHAR(50); /* Конец скрипта описания группы таблиц*/ v_TableGroupScript TEXT; /* Формируемый скрипт*/ v_TableLinksScript TEXT; /* Формируемый раздел связей таблиц */ v_TableGroupLegendScriptTEXT; /* Формируемый раздел комментириев к таблицам группы*/ 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_SchemaNameVARCHAR(100) default 'public',/* название схемы базы данных*/ a_WHEREoptionVARCHAR(1000) default NULL, /* условие запроса на выбор таблиц из системного каталога */ a_NeedAttrDescBOOLEAN 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_TableNameArrayVARCHAR(256)[]; /* Массив выбранных таблиц */ v_TableGroupScriptTEXT; /* Формируемый скрипт*/ 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_SchemaNameNAME default 'public',/* название схемы базы данных*/ a_TableNameNAME default NULL,/* Название таблицы */ a_NeedAttrDescBOOLEAN default false,/* Признак необходимости включения комментария аттрибута */ a_AllowedTableNameVARCHAR(256)[]default '{}'/* Массив допустимых для поиска внешних ключей таблиц */ ) RETURNS TEXT AS $BODY$ DECLARE c_TableScriptPrefixCONSTANT VARCHAR(10):='''object ';/* Начало скрипта описания таблицы*/ c_AttributeListPrefixCONSTANT VARCHAR(5):='{'''||CHR(13)||CHR(10);/* Начало списка атрибутов */ c_AttributeListSuffixCONSTANT VARCHAR(5):='''}'''||CHR(13)||CHR(10);/* Конец списка атрибутов */ c_TableScriptSuffixCONSTANT VARCHAR(5):=' '||CHR(13)||CHR(10);/* Конец скрипта описания таблицы*/ c_TableNameDelimiterCONSTANT VARCHAR(5):='.'; /* Символ-разделитель имени таблицы*/ c_BlankCONSTANT VARCHAR(5):=' '; /* Символ пробела*/ v_AttributeScriptTEXT;/* */ v_TableScriptTEXT;/* */ v_Schemaname; v_TableNamename; v_TableOIDOID; v_AttributeNamename; v_AttributeNum SMALLINT; v_AttributeCountINTEGER;/* Счетчик цикла*/ 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_TableOIDOID,/* ИД таблицы */ a_AttributeNoSMALLINT,/* Порядковый номер атрибута в таблице*/ a_NeedAttrDescBOOLEAN default false,/* Признак необходимости включения комментария аттрибута */ a_AllowedTableNameVARCHAR(256)[]default '{}'/* Массив допустимых для поиска внешних ключей таблиц */ ) RETURNS TEXT AS $BODY$ DECLARE c_PrimaryKeyPrefixCONSTANT VARCHAR(5):='(PK';/* Начало опции об участии аттрибута в первичном ключе*/ c_PrimaryKeySuffixCONSTANT VARCHAR(5):=')';/* Окончание опции об участии аттрибута в первичном ключе*/ c_ForeignKeyPrefixCONSTANT VARCHAR(5):='(FK';/* Начало опции об участии аттрибута во внешнем ключе*/ c_ForeignKeySuffixCONSTANT VARCHAR(5):=')';/* Окончание опции об участии аттрибута во внешнем ключе*/ c_DescriptionPrefixCONSTANT VARCHAR(5):='(<i>';/* Начало комментария к атрибуту*/ c_DescriptionSuffixCONSTANT VARCHAR(5):='</i>)';/* Окончание комментария к атрибуту*/ c_EndOfAttributeStringCONSTANT VARCHAR(5):=CHR(13)||CHR(10); /* Символы окончания строки описания атрибута*/ c_NULLCONSTANT VARCHAR(5):='NULL';/* Строка NULL */ c_NOTCONSTANT VARCHAR(5):='NOT';/* Строка NOT */ c_BlankCONSTANT VARCHAR(5):=' ';/* Символ пробела */ c_AttributeDelimiterCONSTANT VARCHAR(5):=' : '; /* Символ - разделитель названия атрибута и его типа*/ c_ColumnFKList_NDims CONSTANT INTEGER:=1;/* Размерность массива атрибутов внешнего ключа*/ v_NeedAttrDescBOOLEAN;/* Признак необходимости включения комментария аттрибута */ v_AllowedTableNameVARCHAR(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_DescriptionTEXT;/* Комментари к аттрибуту */ v_isAttributeFK BOOLEAN;/* Признак вхождения атрибута во внешний ключ */ v_ColumnFKNoARRAYSMALLINT[];/* Массив порядковых номеров атрибута во всех внешних ключах */ v_FKTableNameARRAYname[];/* Массив названий таблиц, на которые сслылаются внешние ключи */ v_FKTableColumnNameARRAY name[];/* Массив названий атрибутов таблицы, на которую сслылается внешний ключи */ v_ColumnFKNoSMALLINT;/* Порядковый номер атрибута текщем внешнем ключе */ v_FKTableName name;/* Название таблицы, на которую сслылается текщий внешний ключ */ v_FKTableColumnName name;/* Название атрибута таблицы, на которую сслылается внешний ключ */ v_ColumnFKIndxINTEGER;/* Текущий индекс масссива атрибутов внешнего ключа*/ v_ColumnFKLBoundINTEGER;/* Нижний индекс масссива атрибутов внешнего ключа*/ v_ColumnFKUBoundINTEGER;/* Верхний индекс масссива атрибутов внешнего ключа*/ 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_SchemaNameVARCHAR(100) default 'public',/* название схемы базы данных*/ a_TableNameArrayVARCHAR(256)[]default '{}'/* Исходный массив таблиц */ ) RETURNS TEXT AS $BODY$ DECLARE c_SchemaNameCONSTANT VARCHAR(256):='public';/* название схемы базы данных*/ c_TableKindCONSTANT CHAR:='r'; c_ForeignKeyKindCONSTANT CHAR:='f'; c_ManySignCONSTANT CHAR:='N'; c_OneSignCONSTANT CHAR:='1'; c_PlantManySignCONSTANT VARCHAR:='*'; c_PlantOneSignCONSTANT VARCHAR:=''; c_PlantLinkMarkCONSTANT VARCHAR:='--'; c_TableLinksSuffix CONSTANT VARCHAR(10):=CHR(13)||CHR(10);/* Окончание описания таблицы в комментариях*/ c_TableNameDelimiterCONSTANT VARCHAR(5):='.'; /* Символ-разделитель имени таблицы*/ c_BlankCONSTANT VARCHAR(5):=' '; /* Символ пробела*/ v_SchemaNameVARCHAR(256);/* название схемы базы данных*/ v_TableLinksScriptTEXT;/* Формируемый скрипт*/ v_TableCountINTEGER;/* Чсетчик таблиц группы*/ v_TableNameArrayTEXT[];/* Массив таблиц */ v_TableName VARCHAR(256);/* Название одной из таблиц группы*/ v_FTableName VARCHAR(256);/* Название одной из таблиц группы*/ v_TableDescriptionTEXT;/* Описание одной из таблиц группы*/ v_TablesRecordRECORD;/* Запись с данными таблицы */ v_TableOIDOID;/* ИД таблицы */ v_ForeignKeyName VARCHAR(256);/* Название внешнего ключа*/ v_ProcessedTableNameArrayTEXT[];/* Массив обработанных таблиц */ v_TableNameArrayRemainderTEXT[];/* Массив необработанных таблиц */ v_LinkSignVARCHAR(5);/* Признак связи по отношения к таклице*/ v_LinkMarkLeftCharVARCHAR;/* Левый символ признака связи*/ v_LinkMarkRightCharVARCHAR;/* Правый символ признака связи*/ v_PlantLinkSignVARCHAR(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, в которой отображается диаграмма классов.
ссылка на оригинал статьи https://habr.com/ru/articles/841776/
Добавить комментарий