{"id":14063311,"url":"https://github.com/dmytro-demchyna/schema-keeper","last_synced_at":"2025-10-27T05:31:33.891Z","repository":{"id":62541050,"uuid":"174324368","full_name":"dmytro-demchyna/schema-keeper","owner":"dmytro-demchyna","description":":nut_and_bolt: Database development kit for PostgreSQL","archived":false,"fork":false,"pushed_at":"2022-12-28T10:42:02.000Z","size":163,"stargazers_count":12,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-01T02:41:26.146Z","etag":null,"topics":["database-structure","deployment","plpgsql","postgresql","stored-procedures"],"latest_commit_sha":null,"homepage":"","language":"PHP","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/dmytro-demchyna.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-07T10:38:25.000Z","updated_at":"2023-10-17T22:30:06.000Z","dependencies_parsed_at":"2023-01-31T06:10:14.621Z","dependency_job_id":null,"html_url":"https://github.com/dmytro-demchyna/schema-keeper","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmytro-demchyna%2Fschema-keeper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmytro-demchyna%2Fschema-keeper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmytro-demchyna%2Fschema-keeper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmytro-demchyna%2Fschema-keeper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmytro-demchyna","download_url":"https://codeload.github.com/dmytro-demchyna/schema-keeper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238445840,"owners_count":19473821,"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","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":["database-structure","deployment","plpgsql","postgresql","stored-procedures"],"created_at":"2024-08-13T07:03:15.908Z","updated_at":"2025-10-27T05:31:28.540Z","avatar_url":"https://github.com/dmytro-demchyna.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"# SchemaKeeper\n\n[![Latest Stable Version](https://img.shields.io/packagist/v/schema-keeper/schema-keeper.svg?color=blue)](https://packagist.org/packages/schema-keeper/schema-keeper)\n[![Minimum PHP Version](https://img.shields.io/packagist/php-v/schema-keeper/schema-keeper.svg?color=blue)](https://php.net/)\n[![Minimum PostgreSQL Version](https://img.shields.io/badge/postgreSQL-%3E%3D9.4-blue.svg)](https://www.postgresql.org/)\n[![Build Status](https://img.shields.io/travis/com/dmytro-demchyna/schema-keeper/master.svg)](https://travis-ci.com/dmytro-demchyna/schema-keeper)\n[![Coverage](https://img.shields.io/codecov/c/github/dmytro-demchyna/schema-keeper/master.svg)](https://codecov.io/gh/dmytro-demchyna/schema-keeper)\n[![License](https://img.shields.io/github/license/dmytro-demchyna/schema-keeper.svg)](https://github.com/dmytro-demchyna/schema-keeper/blob/master/LICENSE)\n\nTrack a structure of your PostgreSQL database in a VCS using SchemaKeeper.\n\nSchemaKeeper provides 3 functions:\n1. `save` \u0026mdash; saves a database structure as separate text files to a specified directory\n1. `verify` \u0026mdash; detects changes between an actual database structure and the saved one\n1. `deploy` \u0026mdash; deploys stored procedures to a database from the saved structure\n\nSchemaKeeper allows to use `gitflow` principles for a database development. Each branch contains its own database structure dump, and when branches are merged, dumps are merged too.\n\n## Table of contents\n- [Installation](#installation)\n    - [Composer](#composer)\n    - [PHAR](#phar)\n    - [Docker](#docker)\n- [Basic usage](#basic-usage)\n    - [save](#save)\n    - [verify](#verify)\n    - [deploy](#deploy)\n- [Extended usage](#extended-usage)\n    - [PHPUnit](#phpunit)\n    - [Custom transaction block](#custom-transaction-block)\n- [Workflow recommendations](#workflow-recommendations)\n    - [Safe deploy to a production](#safe-deploy-to-a-production)\n    - [Conflicts resolving](#conflicts-resolving)\n- [Extra links](#extra-links)\n- [Contributing](#contributing)\n\n## Installation\n\n\u003e If you choose the installation via Composer or PHAR, please, install [psql](https://www.postgresql.org/docs/current/app-psql.html) app on machines where SchemaKeeper will be used. A Docker build includes pre-installed [psql](https://www.postgresql.org/docs/current/app-psql.html).\n\n### Composer\n\n```bash\n$ composer require schema-keeper/schema-keeper\n```\n\n### PHAR\n\n```bash\n$ wget https://github.com/dmytro-demchyna/schema-keeper/releases/latest/download/schemakeeper.phar\n```\n\n### Docker\n\n```bash\n$ docker pull dmytrodemchyna/schema-keeper\n```\n\n## Basic Usage\n\nCreate a `config.php` file:\n\n```php\n\u003c?php\n\nuse SchemaKeeper\\Provider\\PostgreSQL\\PSQLParameters;\n\n// Connection parameters\n$params = new PSQLParameters('localhost', 5432, 'dbname', 'username', 'password');\n\n// These schemas will be ignored\n$params-\u003esetSkippedSchemas(['information_schema', 'pg_%']);\n\n// These extensions will be ignored\n$params-\u003esetSkippedExtensions(['pgtap']);\n\n// The path to psql executable\n$params-\u003esetExecutable('/bin/psql');\n\nreturn $params;\n```\n\nNow you can use the `schemakeeper` binary. It returns exit-code `0` on success and exit-code `1` on failure.\n\n### save\n\n```bash\n$ schemakeeper -c config.php -d /project_path/db_name save\n```\n\nThe command above saves a database structure to a `/project_path/db_name` directory. \n\n- /project_path/db_name:\n    - structure:\n        - public:\n            - functions:\n                - func1(int8).sql\n            - materialized_views:\n                - mat_view1.txt\n            - sequences:\n                - sequence1.txt\n            - tables:\n                - table1.txt\n            - triggers:\n                - trigger1.sql\n            - types:\n                - type1.txt\n            - views:\n                - view1.txt\n        - schema2:\n            - views:\n                - view2.txt\n        - ...\n    - extensions:\n        - plpgsql.txt\n\nExamples of conversion database structure to files:\n\nObject type         | Schema         | Name                                     | Relative file path                 | File content\n--------------------|----------------|------------------------------------------|------------------------------------|---------------\nTable               | public         | table1                                   | ./public/tables/table1.txt         | A description of the table structure obtained by `\\d` [meta](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMANDS) command\nStored procedure    | public         | func1(param bigint)                      | ./public/functions/func1(int8).sql | A definition of the stored procedure, including a `CREATE OR REPLACE FUNCTION` block, obtained by [pg_get_functiondef](https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG-TABLE)\nView                | schema2        | view2                                    | ./schema2/views/view2.txt          | A description of the view structure obtained by `\\d+` [meta](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMANDS) command\n...                 | ...            | ...                                      | ...                                | ...\n\nThe file path stores information about a type, a scheme and a name of a object. This approach makes an easier navigation through the database structure, as well as code review of changes in VCS.\n\n### verify\n\n```bash\n$ schemakeeper -c config.php -d /project_path/db_name verify\n```\n\nThe command above compares an actual database structure with the previously saved in `/project_path/db_name` one and displays an information about changed objects.\n\nIf changes exists, the `verify` will returns an exit-code `1`.\n\nAn alternative way to find changes is to call the `save` again, specifying the same directory `/project_path/db_name`, and check changes in the VCS. Since objects from the database are stored in separate files, the VCS will show only changed objects. A main disadvantage of this way \u0026mdash; a need to overwrite files.\n\n### deploy\n\n```bash\n$ schemakeeper -c config.php -d /project_path/db_name deploy\n```\n\nThe command above deploys stored procedures from the `/project_path/db_name` to the actual database.\n\nYou can edit a source code of stored procedures in the same way as a rest of an application source code. Modification of a stored procedure occurs by making changes to the corresponding file in the `/project_path/db_name` directory, which is automatically reflected in the VCS.\n\nFor example, to create a new stored procedure in the `public` schema, just create a new file with a `.sql` extension in the `/project_path/db_name/structure/public/functions` directory, place a source code of the stored procedure into it, including a `CREATE OR REPLACE FUNCTION` block, then call the `deploy`. Similarly occur modifying or removal of stored procedures. Thus, the code simultaneously enters both the VCS and the database.\n\nThe `deploy` changes parameters of a function or a return type without additional actions, while with a classical approach it would be necessary to first perform `DROP FUNCTION`, and only then `CREATE OR REPLACE FUNCTION`.\n\nUnfortunately, in some situations `deploy` is not able to automatically apply changes. For example, if you try to delete a trigger function, that is used by at least one trigger. Such situations must be solved manually using migration files.\n\nThe `deploy` transfers changes only from stored procedures. To transfer other changes, please, use migration files (for example, [doctrine/migrations](https://packagist.org/packages/doctrine/migrations)).\n\nMigrations must be applied before the `deploy` to resolve possible problem situations.\n\n\u003e The `deploy` is designed to work with stored procedures written in [PL/pgSQL](https://www.postgresql.org/docs/current/plpgsql.html). Using with other languages may be less effective or impossible.\n\n## Extended usage\n\nYou can inject SchemaKeeper to your own code.\n\n```php\n\u003c?php\n\nuse SchemaKeeper\\Keeper;\nuse SchemaKeeper\\Provider\\PostgreSQL\\PSQLParameters;\n\n$host = 'localhost';\n$port = 5432;\n$dbName = 'dbname';\n$user = 'username';\n$password = 'password';\n\n$dsn = 'pgsql:dbname=' . $dbName . ';host=' . $host.';port='.$port;\n$conn = new PDO($dsn, $user, $password, [PDO::ATTR_ERRMODE =\u003e PDO::ERRMODE_EXCEPTION]);\n\n$params = new PSQLParameters($host, $port, $dbName, $user, $password);\n$keeper = new Keeper($conn, $params);\n```\n\n```php\n\u003c?php\n\n$keeper-\u003esaveDump('path_to_dump');\n$keeper-\u003everifyDump('path_to_dump');\n$keeper-\u003edeployDump('path_to_dump');\n```\n\n### PHPUnit\n\nYou can wrap `verifyDump` into a PHPUnit test:\n\n```php\n\u003c?php\n\nclass SchemaTest extends \\PHPUnit\\Framework\\TestCase\n{\n    function testOk()\n    {\n        // Initialize $keeper here...\n        \n        try {\n            $keeper-\u003everifyDump('/path_to_dump');\n        } catch (\\SchemaKeeper\\Exception\\NotEquals $e) {\n            $expectedFormatted = print_r($e-\u003egetExpected(), true);\n            $actualFormatted = print_r($e-\u003egetActual(), true);\n\n            // assertEquals will show the detailed diff between the saved dump and actual database\n            self::assertEquals($expectedFormatted, $actualFormatted);\n        }\n    }\n}\n\n```\n\n### Custom transaction block\n\nYou can wrap `deployDump` into a custom transaction block:\n\n```php\n\u003c?php\n\n// Initialize $conn and $dbParams here...\n\n$keeper = new \\SchemaKeeper\\Keeper($conn, $dbParams);\n\n$conn-\u003ebeginTransaction();\n\ntry {\n    $result = $keeper-\u003edeployDump('/path_to_dump');\n    \n    // $result-\u003egetDeleted() - these functions were deleted from the current database\n    // $result-\u003egetCreated() - these functions were created in the current database\n    // $result-\u003egetChanged() - these functions were changed in the current database\n\n    $conn-\u003ecommit();\n} catch (\\Exception $e) {\n    $conn-\u003erollBack();\n}\n```\n\n## Workflow recommendations\n\n### Safe deploy to a production\n\nA dump of a database structure saved in a VCS allows you to check a production database for exact match to a required structure. This ensures that only intended changes were transferred to the production-DB by deploy.\n\nSince the PostgreSQL [DDL](https://www.postgresql.org/docs/current/ddl.html) is [transactional](https://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis), the following deployment order is recommended:\n1. Start transaction\n1. Apply all migrations in the transaction\n1. In the same transaction, perform `deployDump`\n1. Perform `verifyDump`. If there are no errors, execute `COMMIT`. If there are errors, execute `ROLLBACK`\n\n### Conflicts resolving\nA possible conflict situation: *branch1* and *branch2* are branched from *develop*. They haven't conflict with *develop*, but have conflict with each other. A goal is to merge *branch1* and *branch2* into *develop*. \n\nFirst, merge *branch1* into *develop*, then merge *develop* into *branch2*, resolve conflicts in *branch2*, and then merge *branch2* into *develop*. At the stage of conflict resolution inside *branch2*, you may have to correct a migration file in *branch2* to match the final dump that contains merge results.\n\n## Extra links\n\nIf you are not satisfied with SchemaKeeper, look at the list of another tools: https://wiki.postgresql.org/wiki/Change_management_tools_and_techniques\n\n## Contributing\nAny contributions are welcome.\n\nPlease refer to [CONTRIBUTING.md](https://github.com/dmytro-demchyna/schema-keeper/blob/master/.github/CONTRIBUTING.md) for information on how to contribute to SchemaKeeper.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmytro-demchyna%2Fschema-keeper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmytro-demchyna%2Fschema-keeper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmytro-demchyna%2Fschema-keeper/lists"}