{"id":14974088,"url":"https://github.com/tenderpro/pgm","last_synced_at":"2026-02-23T00:35:27.764Z","repository":{"id":68184893,"uuid":"67916383","full_name":"TenderPro/pgm","owner":"TenderPro","description":"Postgresql code load tool","archived":false,"fork":false,"pushed_at":"2018-05-24T18:54:18.000Z","size":95,"stargazers_count":1,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-26T13:27:06.967Z","etag":null,"topics":["development-workflow","plpgsql","postgresql","preprocessor"],"latest_commit_sha":null,"homepage":"","language":"PLpgSQL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TenderPro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-09-11T07:26:14.000Z","updated_at":"2020-08-31T20:14:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"9adaf204-1f74-4eca-a0c7-487828323613","html_url":"https://github.com/TenderPro/pgm","commit_stats":{"total_commits":97,"total_committers":5,"mean_commits":19.4,"dds":0.4020618556701031,"last_synced_commit":"2d2964c02486fbea464786f25088f92d0e16b26a"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/TenderPro/pgm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TenderPro%2Fpgm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TenderPro%2Fpgm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TenderPro%2Fpgm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TenderPro%2Fpgm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TenderPro","download_url":"https://codeload.github.com/TenderPro/pgm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TenderPro%2Fpgm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278646783,"owners_count":26021512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["development-workflow","plpgsql","postgresql","preprocessor"],"created_at":"2024-09-24T13:49:56.129Z","updated_at":"2025-10-06T17:06:02.493Z","avatar_url":"https://github.com/TenderPro.png","language":"PLpgSQL","funding_links":[],"categories":[],"sub_categories":[],"readme":"\npgm. Postgresql manager\n=======================\n\npgm - это shell-скрипт для создания, обновления и удаления объектов БД.\nВ текущей версии скрипт поддерживает только СУБД Postgresql.\n\nАлгоритм работы заключается в препроцессинге .sql файлов из заданных каталогов (пакетов) и генерации скрипта для psql,\nзапускающего в заданной БД подготовленные файлы.\n\n## Быстрый старт\n\nВыполняется после установки [pg-skel](https://github.com/TenderPro/pg-skel).\n\nТекущий каталог - место для нового sql-проекта.\n\nСоздаем в нем подкаталог (или подмодуль или симлинк) pgm:\n```\ngit clone https://github.com/TenderPro/pgm.git\n```\n\nПроверяем наличие используемых программ\n```\nbash pgm/pgm.sh check\n```\nЕсли не все Ок - надо установить недостающее штатными средствами ОС.\n\nСоздаем файл настроек .config\n```\nbash pgm/pgm.sh init\n```\n\nРедактируем .config. Надо прописать пользователя с правами создания БД. Его можно создать, используя [Доступ к БД под суперпользователем](https://github.com/TenderPro/pg-skel#superuser).\n\nВ параметре **DB_TEMPLATE** надо указать имя шаблона БД, созданного с помощью [pg-skel](https://github.com/TenderPro/pg-skel).\n\nСоздание БД\n```\nbash pgm/pgm.sh createdb\n```\n\nСоздание объектов pgm\n```\nSQLROOT=pgm/sql bash pgm/pgm.sh creatif ws utils\n```\n\nСоздание файлов пакета sample\n```\nbash pgm/pgm.sh init sample\n```\nПосле этой операции будут созданы первичные файлы в каталоге `sql/`. (См ниже [Размещение SQL-кода](#struct)).\n\nЗагрузка пакета demo в БД\n```\nbash pgm/pgm.sh create sample\n```\n\nПовторная загрузка пакета sample с предварительным удалением\n```\nbash pgm/pgm.sh recreate sample\n```\n\nКомпиляция хранимого кода пакета sample\n```\nbash pgm/pgm.sh make sample\n```\n\n## Структура БД\n\nРабота с БД является развитием идеи разделения БД на три составляющих:\n\n1. Оперативные данные (ОД) - таблицы, которые изменяются в процессе эксплуатации\n2. Внешние связи этих таблиц (FOREIGN KEY, DEFAULT)\n3. Все остальные объекты (изменяются только в процессе разработки)\n\nРазделение оперативных (вводимых в процессе эксплуатации) и справочных (вводимых в процессе разработки системы) данных реализовано следующим образом:\n\n1. Все таблицы оперативных данных создаются в схеме `wsd`\n2. Справочные данные создаются в индивидуальных схемах \n3. Весь код поддержки изменения данных (и их чтения) создается в индивидуальных схемах\n\nКроме этого, код и данные поддержки pgm размещаются в схеме `ws`.\n\nТакая реализация позволяет полностью удалить весь код пакета (методы, триггеры, справочные данные), сохранив оперативные данные (команда `drop`) или удалив и их (команда `erase`), т.е. не нужно писать скрипт обновления версии А до версии В, достаточно удалить пакет (или все), обновить ПО (`git pull`) и создать пакет(ы) заново (`create`).\n\nПод **схемой** понимается схема БД (создаваемая командой `CREATE SCHEMA`).\nВесь код создания объектов схемы размещается в одноименном схеме каталоге.\n\n**Пакет** - логическое объединение нескольких схем. Может состоять и из одной схемы.\n\n## Команды pgm\n\nСкрипт pgm реализует выполнение в БД операций:\n\n* `init` - создать .config по шаблону\n* `init PKG` - создать каталог пакета и шаблоны файлов\n* `create PKGS` - создать объекты БД\n* `creatif PKGS` - создать объекты БД, если их нет\n* `recreate PKGS` - создать объекты БД, предварительно удалив\n* `make PKGS` - выполнить компилируемый код (CREATE OR REPLACE) после сделанного ранее create\n* `drop PKGS` - удалить объектв БД (кроме wsd)\n* `erase PKGS` - очистить бд (включая удаление wsd)\n* `createdb` - создать БД\n* `dump SCHEMA` - дамп заданной схемы\n* `restore SCHEMA` - восстановление дампа\n\nгде\n\n* PKGS - список имен пакетов в порядке создания\n* SCHEMA - имя схемы БД\n\nРабота скрипта заключается в формировании соответствующего файла var/build/build.sql и выполнении его в psql.\n\n## Struct\n\nРазмещение SQL-кода\n\nSQL-код размещается в каталогах схемы\n\n* sql/PKG/NN_SCHEMA/ (NN - порядковый номер обработки схемы при обработке пакета)\n* sql/PKG/ (если SCHEMA=PKG)\n\nКаталог схемы содержит .sql файлы.\nФормат имени .sql файла - `MM_descr.sql` - файл с типом MM и описанием descr.\n\nтип MM имеет значения:\n\n* 00 - drop/erase: удаление связей текущей схемы с другими схемами\n* 01 - erase: удаление защищенных объектов из других схем (wsd)\n* 02 - drop/erase: удаление текущей схемы (02_drop)\n* 10 - init: инициализация до создания схемы\n* 11 - init: создание схемы, после выполнения 11* имя схемы из имени каталога добавится в путь поиска\n* 12 - init: зависимости от других пакетов, создание доменов и типов\n* 1[4-9] - общие файлы для init и make, код, не имеющий зависимостей от объектов, может использоваться при создании таблиц\n* 2x - создание таблиц\n* 3x - ф-и для представлений\n* 4x - представления\n* 5x - основной код функций\n* 6x - код триггеров\n* 7x - создание триггеров\n* 8x - наполнение таблиц\n* 9x - тесты\n\nФайлы выполняются в порядке сортировки имен.\n\nДля каждой из операций выбираются файлы по соответствующей маске:\n\n* init:   [1-9]?_*.sql\n* make:   1[4-9]_*.sql, [3-6]?_*.sql, 9?_*.sql\n* drop:   00_*.sql, 02_*.sql\n* erase:  0?_*.sql\n\n## Код, меняющий схему wsd\n\nВ каждом пакете код, который производит изменения в схеме оперативных данных (wsd), решает одну из следующих задач:\n\n* **инициализация**, создание таблиц в схеме wsd (20_wsd_000.sql)\n* **привязка** объектов пакета в схеме wsd (создание внешних ключей и триггеров - 8?_*_wsd_000.sql)\n* **очистка** схемы wsd от объектов пакета (01_drop_wsd.sql)\n* **удаление** связей объектов схемы wsd с объектами пакета (00_cleanup.sql)\n\nЗадачи **привязка** и **удаление** выполняются при стандартном обновлении пакета (`create` и `drop` соответственно), **очистка** выполняется при полном удалении пакета (`erase`), а **инициализация** должна выполняться только перед привязкой, которая производится впервые или после очистки.\n\nОсобенности **инициализации** реализованы следующим образом:\n\n* Файл, содержащий команды **инициализации**, имеет в имени суффикс `_wsd_NNN.sql`\n* При первом выполнении файла с таким суффиксом (по команде `create` ), его атрибуты (включая контрольную сумму) сохраняются в таблице `wsd.pkg_script_protected`\n* При наличии файла в этой таблице, при выполнении `create` (после `drop`) его повторный запуск не производится. При изменении контрольной суммы выводится уведомление об этом.\n* Удаление строки из `wsd.pkg_script_protected` производится при выполнении `erase pkg` автоматически.\n\n## Обновление версий\n\nИспользуемая техника создания объектов БД позволяет обновлять все схемы БД посредством цепочки `drop`, `git update`, `create`. \nЗадача обновления схемы `wsd` решается следующим образом:\n\n* После установки релиза прекращается изменение существующих файлов `*_wsd_000.sql`\n* Для изменений схемы wsd создаются новые файлы, (`*_wsd_001.sql` итд)\n* При обновлении системы каждый такой файл отработает на БД однократно\n\n## Зависимости пакетов\n\nСогласно принятой архитектуре, любой пакет ничего не знает о пакетах, которые будут добавлены в БД после него.\nТ.е., если есть *pkg_B*, использующий данные (или код) из *pkg_A*, то *pkg_A* об этом ничего не знает. \nЭто порождает необходимость существования механизма, который бы\n\n* Запретил установку *pkg_B* при отсутствии установленного *pkg_A*\n* Запретил удаление *pkg_A* при наличии установленного *pkg_B*\n\nЭтот механизм реализовывается добавлением в sql-каталог *Пакета_В* файла `12_deps.sql`, содержащего инструкцию вида\n\n```sql\nINSERT INTO ws.pkg_required_by(code) VALUES ('Пакет_А');\n```\n\n## Внешние ключи\n\nВ некоторых случаях пакетам необходимо менять внутренние данные в схемах других пакетов (например, справочники файл-сервера). Т.е. возникает ситуация, когда\n\n1) **pkg_A** создает таблицу `wsd.T1`, которая ссылается на таблицу пакета `pkg_A.T2` внешним ключом `FK1`\n\nПример:\n\n```\nПакет FS создает wsd.file_link, поля которой (class_id, folder_code) REFERENCES fs.folder(class_id, code)\n```\n\n2) **pkg_B** для работы с `wsd.T1` добавляет строки в `pkg_A.T2`\n\nПример:\n\n```\nПакет wiki добавляет в fs.folder(class_id, code) VALUES (12, 'files') - папку для файлов wiki\n```\n \n* 3. В процессе эксплуатации происходит наполнение данными таблицы `wsd.Т1`\n\nВ результате возникают вопросы\n\n* удалять ли внешний ключ `FK1`, если удаляются пакеты **pkg_A** или **pkg_B**, но остаются данные в `wsd.T1`?\n* создавать ли внешний ключ `FK1` при повторном создании **pkg_A** или **pkg_B**?\n\nЭти вопросы решаются следующим образом:\n\n* регистрируется зависимость **pkg_B** от **pkg_A**\n```sql\nINSERT INTO ws.pkg_required_by(code) VALUES ('fs');\n```\n, чем запрещается:\n* создание **pkg_B** при отсутствии **pkg_A**\n* удаление **pkg_A** при наличии **pkg_B**\n\n* **pkg_A** не создает внешний ключ `FK1`, а регистрирует его в таблице `wsd.pkg_fkey_protected`\n```sql\nINSERT INTO wsd.pkg_fkey_protected (rel, wsd_rel, wsd_col) VALUES\n  ('fs.folder', 'file_link',  'class_id, folder_code')\n;\n```\n\n* регистрируется зависимость данных **pkg_B** от внешнего ключа **pkg_A**\n```sql\nINSERT INTO wsd.pkg_fkey_required_by (pkg, rel) VALUES ('fs','fs.folder');\n```\n\nВ результате внешний ключ `FK1`\n\n* удаляется перед удалением первого же зависящего от него пакета\n* при наличии данных в `wsd`, создается после создания всех зависящих от него пакетов\n\nТ.е. при удалении **pkg_B** можно удалять строки из `pkg_A.T2`, а при создании - добавлять:\n\n* После создания пакета - создаются все еще несуществующие зарегистрированные FK присоединенных пакетом таблиц \n* Перед удалением пакета - удаляются все зарегистрированные пакетом зависимости FK\n\n## Значения по умолчанию\n\nСо значениями по умолчанию, если они заданы функцией, имеет место картина, аналогичная внешним ключам:\n\n1) **pkg_A** создает таблицу `wsd.T1`, у которой поле `F1` имеет DEFAULT - результат вызова функции из некоторого пакета **pkg_C**\n\nПример:\n\nТаблица acc.permission имеет поле `pkg DEFAULT ws.pg_pkg()`\n\nВ результате возникают вопросы\n\n* Как избежать удаления поля `F1` при удалении схемы **pkg_C**?\n* Как восстановить DEFAULT при повторном создании пакета **pkg_C**?\n\nЭти вопросы решаются следующим образом:\n\n* **pkg_A** не задает DEFAULT, а регистрирует его в таблице `wsd.pkg_default_protected`\n\n```sql\nINSERT INTO wsd.pkg_default_protected (pkg, schema, wsd_rel, wsd_col, func) VALUES ('acc', 'acc', 'permission', 'pkg', 'ws.pg_pkg()');\n```\n, в результате этого\n\n  * После создания пакета, этот DEFAULT создается автоматически\n  * Перед удалением пакета, DEFAULT автоматически удаляется.\n\n\n## Тесты\n\nТесты размещаются в файлах `9?_*.sql` и выполняются внутри транзакций `init` и `make`. Вывод теста сравнивается с содержимым \nфайла `9?_*.md` и при несовпадении возникает ошибка.\n\nНаличие ошибок тестов отменяет выполнение основной команды\n\n### Именование тестов `9X_name.sql`.\n\nНомер нужен только для того, чтобы гарантировать порядок выполнения. В тестах он вообще не важен, поэтому все они могут иметь \nпрефикс \"90_\". 91 или 92 - это ни о чем не говорит. В файле должен быть комментарий о том, что тестируется. Смысл теста оформляется краткой фразой в латиннице (DESCRIPTION) в соответствии с типом ws.d_code и применить ее дважды:\n\n1. в имени файла - `90_DESCRIPTION.sql`\n2. внутри файла, написав `SELECT ws.test('DESCRIPTION');`\n\nЧтобы наделить 9Х хоть каким-то смыслом, есть рекомендация: Номер 90 использовать для тестов, не связанных с проверкой наличия в БД данных (когда объекты создаются и тут же удаляются), 91 - для проверки корректности данных в БД.\n\n### Macro для тестов.\n\nЕсли один и тот же блок кода надо выполнить в тесте несколько раз с разными параметрами, этот код помещается в 9X_name.macro.sql и используется в тесте:\n```\n\\set FILE :TEST .macro.sql\n\\set VAR 1\n\\i :FILE\n\n\\set VAR 2\n\\i :FILE\n```\n### Оформление тестов\n\nКак можно увидеть из расширения, файлы `9?_*.md` имеют синтаксис markdown. В этот файл помещаются\n\n* названия тестов\n* тексты SQL-запросов\n* результаты SQL-запросов\n\nЭти файлы не пишутся руками, при первом запуске pgm формирует такой файл в `var/build/PKG/` и, если результат теста приемлем, достаточно скопировать\nего в `sql/PKG/`.\n\nДля того, чтобы `9?_*.md` был сгенерирован корректно, при создании теста `9?_*.sql` используются следующие правила:\n\n1. В заголовок попадает результат запроса, который завершается `; -- BOT` (Begin Of Test)\n\nЭто может быть любой запрос, но рекомендуется использовать вызов вида `SELECT ws.test('TITLE'); -- BOT`, эта функция не только \nвозвращает `TITLE`, но и делает `RAISE WARNING` в специальном формате, который pgm использует для вывода в консоли имени выполняемого теста.\n\n2. Документируется только запрос, который завершается `; -- EOT` (End Of Test)\n\nТекст этого запроса форматируется как код SQL, результат - как таблица в markdown.\nТ.к. текст запроса сохраняется как \"текущий буфер запроса psql\", в него попадают также и предшествующие многострочные SQL-комментарии (вида `/* .. */`). \nСодержимое этого буфера psql сбрасывает после каждого запроса, но если после предыдущего запроса есть многострочный комментарий, который не надо\nпереносить в .md, достаточно после него очистить буфер запроса (инструкцией `\\r`).\n\n## .config\n\npgm при запуске подключает файл `.config' и использует из него следующие переменные:\n\n```\nDB_NAME=iac1\nPG_HOST=127.0.0.1\nPG_PORT=5432\n```\n\nПример этого файла с полным списком переменных можно сгеренить командой `bash pgm.sh init`\n\n## TODO\n\n* удалять из pkg_script_protected по имени пакета\n* вылетать по ошибке если в пакете при выполнении drop/create не найдено файлов\n* добавить команду test\n* добавить команду bench\n* перенести код в из ws в pgm\n\n* sql/upd - код обновления версий:\n\nСодержит подкаталоги с именами, соответствующими номеру обновления(версии)\n\nNNN/ - каталог обновления\n    MM-* - файл с обновлением\nФайлы выполняются в порядке сортировки имен, в рамках одной транзакции, однократно.\n\n## См также\n\n* http://sqitch.org/\n* https://github.com/dimitri/regresql\n\nLicense\n-------\n\nThis project is licensed under the terms of the MIT license. See the [LICENSE](LICENSE) file for the full license text.\n\nCopyright (c) 2010 - 2017 [Tender.Pro](http://www.tender.pro)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenderpro%2Fpgm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftenderpro%2Fpgm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenderpro%2Fpgm/lists"}