{"id":27135557,"url":"https://github.com/alphaone1/dmorph","last_synced_at":"2026-05-02T03:08:07.588Z","repository":{"id":286488684,"uuid":"961386082","full_name":"AlphaOne1/dmorph","owner":"AlphaOne1","description":"Database Migrations","archived":false,"fork":false,"pushed_at":"2025-04-09T01:53:15.000Z","size":66,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-10T00:09:28.597Z","etag":null,"topics":["database","golang","migrations"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlphaOne1.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":"2025-04-06T12:11:59.000Z","updated_at":"2025-04-09T01:53:18.000Z","dependencies_parsed_at":"2025-04-10T00:09:28.520Z","dependency_job_id":null,"html_url":"https://github.com/AlphaOne1/dmorph","commit_stats":null,"previous_names":["alphaone1/dmorph"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaOne1%2Fdmorph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaOne1%2Fdmorph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaOne1%2Fdmorph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaOne1%2Fdmorph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlphaOne1","download_url":"https://codeload.github.com/AlphaOne1/dmorph/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131317,"owners_count":21052819,"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","golang","migrations"],"created_at":"2025-04-08T01:48:38.954Z","updated_at":"2026-05-02T03:08:07.582Z","avatar_url":"https://github.com/AlphaOne1.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- SPDX-FileCopyrightText: 2026 The DMorph contributors.\n     SPDX-License-Identifier: MPL-2.0\n--\u003e\n\n\u003c!-- markdownlint-disable MD013 MD033 MD041 --\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"dmorph_logo.svg\" width=\"40%\" alt=\"Logo\"\u003e\u003cbr\u003e\n    \u003ca href=\"https://github.com/AlphaOne1/dmorph/blob/HEAD/go.mod\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/go-mod/go-version/AlphaOne1/dmorph\"\n             alt=\"Go Version\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/AlphaOne1/dmorph/releases\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/v/release/AlphaOne1/dmorph\"\n             alt=\"Latest Release\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/AlphaOne1/dmorph/actions/workflows/test.yml\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://github.com/AlphaOne1/dmorph/actions/workflows/test.yml/badge.svg\"\n             alt=\"Test Pipeline Result\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/AlphaOne1/dmorph/actions/workflows/codeql.yml\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://github.com/AlphaOne1/dmorph/actions/workflows/codeql.yml/badge.svg\"\n             alt=\"CodeQL Pipeline Result\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/AlphaOne1/dmorph/actions/workflows/security.yml\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://github.com/AlphaOne1/dmorph/actions/workflows/security.yml/badge.svg\"\n             alt=\"Security Pipeline Result\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/AlphaOne1/dmorph\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://goreportcard.com/badge/github.com/AlphaOne1/dmorph\"\n             alt=\"Go Report Card\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://app.codecov.io/gh/AlphaOne1/dmorph\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://codecov.io/gh/AlphaOne1/dmorph/graph/badge.svg\"\n             alt=\"Code Coverage\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://coderabbit.ai\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n       \u003cimg src=\"https://img.shields.io/coderabbit/prs/github/AlphaOne1/dmorph\"\n            alt=\"CodeRabbit Reviews\"\u003e\n    \u003c/a\u003e\n    \u003c!--\u003ca href=\"https://www.bestpractices.dev/projects/0000\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://www.bestpractices.dev/projects/0000/badge\"\n             alt=\"OpenSSF Best Practises\"\u003e\n    \u003c/a\u003e--\u003e\n    \u003ca href=\"https://scorecard.dev/viewer/?uri=github.com/AlphaOne1/dmorph\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://api.scorecard.dev/projects/github.com/AlphaOne1/dmorph/badge\"\n             alt=\"OpenSSF Scorecard\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://api.reuse.software/info/github.com/AlphaOne1/dmorph\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://api.reuse.software/badge/github.com/AlphaOne1/dmorph\"\n            alt=\"REUSE compliance\"\u003e\n    \u003c/a\u003e\n    \u003c!-- \u003ca href=\"https://slsa.dev\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://slsa.dev/images/gh-badge-level3.svg\"\n             alt=\"SLSA Level 3\"\u003e\n    \u003c/a\u003e --\u003e\n    \u003ca href=\"https://app.fossa.com/projects/git%2Bgithub.com%2FAlphaOne1%2Fdmorph?ref=badge_shield\u0026issueType=license\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://app.fossa.com/api/projects/git%2Bgithub.com%2FAlphaOne1%2Fdmorph.svg?type=shield\u0026issueType=license\"\n            alt=\"FOSSA Status\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://app.fossa.com/projects/git%2Bgithub.com%2FAlphaOne1%2Fdmorph?ref=badge_shield\u0026issueType=security\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://app.fossa.com/api/projects/git%2Bgithub.com%2FAlphaOne1%2Fdmorph.svg?type=shield\u0026issueType=security\"\n             alt=\"FOSSA Status\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://pkg.go.dev/github.com/AlphaOne1/dmorph\"\n       rel=\"external noopener noreferrer\"\n       target=\"_blank\"\u003e\n        \u003cimg src=\"https://pkg.go.dev/badge/github.com/AlphaOne1/dmorph.svg\"\n             alt=\"GoDoc Reference\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\u003c!-- markdownlint-enable MD013 MD033 MD041 --\u003e\n\nDMorph\n======\n\n*DMorph* (pronounced [diˈmɔʁf]) is a database migration library. Programs that use a database and\nhave to preserve the data between versions can utilize *DMorph* to apply the necessary migration\nsteps. If a program can afford to lose all data between version upgrades, this library is not\nnecessary.\n\nIncludes direct support for the following relational database management systems:\n\n* [CSVQ](https://mithrandie.github.io/csvq/)\n* [IBM Db2](https://www.ibm.com/db2/)\n* [Microsoft SQL Server](https://www.microsoft.com/sql-server)\n* [MySQL](https://www.mysql.com/) \u0026 [MariaDB](https://mariadb.org/)\n* [Oracle Database](https://www.oracle.com/database/)\n* [PostgreSQL](https://www.postgresql.org)\n* [SQLite](https://www.sqlite.org)\n\nAdditional database management systems can be included providing the necessary queries.\nWhile *DMorph* offers support for these database management systems, it does depend on anything\nthan the Go standard library. Any other dependencies are solely for testing purposes and do not\naffect users of this library.\n\n\nInstallation\n------------\n\nTo install *DMorph*, you can use the following command:\n\n```bash\n$ go get github.com/AlphaOne1/dmorph\n```\n\n\u003c!--Builds are secured with SLSA Level 3 provenance via slsa-framework/slsa-github-generator.\nThe downloaded source archive together with the provenance file `multiple.intoto.jsonl`\ncan be verified using the [slsa-verifier](https://github.com/slsa-framework/slsa-verifier/)\n(replace the `\u003cVERSION\u003e` with the one you actually downloaded):\n\n```bash\n$ slsa-verifier verify-artifact dmorph-src-v\u003cVERSION\u003e.tar.gz \\\n    --provenance-path multiple.intoto.jsonl                  \\\n    --source-uri github.com/AlphaOne1/dmorph                 \\\n    --source-tag v\u003cVERSION\u003e\n```--\u003e\n\nGetting Started\n---------------\n\n*DMorph* applies migrations to a database.\nA migration is a series of steps, defined either in an SQL file or programmatically.\n\n\n### Migration from File\n\nA typical migration file consists of a sequence of SQL statements. Each statement needs to be\nfinalized with a semicolon `;`. If a semicolon is found alone at the beginning of a line, all\nprevious statements, that were not yet executed, are executed in one call to Exec in a\ntransaction. A migration is executed completely inside a transaction. If any of the steps of\na migration fails, a rollback is issued and the process stops. Take care, that not all database\nmanagement systems offer a rollback of DDL (CREATE, DROP, ...) statements.\n\nAn example for a migration inside a file `01_base_tables` is as follows:\n\n```sql\nCREATE TABLE tab0 (\n    id string PRIMARY KEY\n)\n;\n\nCREATE TABLE tab1 (\n    id string PRIMARY KEY\n)\n;\n```\n\nIt can be applied to an already open database with the following snipped:\n\n```go\npackage testprog\n\nimport (\n    \"database/sql\"\n    \"github.com/AlphaOne1/dmorph\"\n)\n\nfunc migrate(db *sql.DB) error {\n    return dmorph.Run(db,\n        dmorph.WithDialect(dmorph.DialectSQLite()),\n        dmorph.WithMigrationFromFile(\"01_base_tables.sql\"))\n}\n\n...\n```\n\nIn this example just one file is used, the `WithMigrationFromFile` can be given multiple times.\nMigrations are executed in alphabetical order of their key. For files the key is the file's name.\nThe `WithDialect` option is used to select the correct SQL dialect, as *DMorph* does not have\na means to get that information (yet).\n\n\n### Migrations from Folder\n\nAs normally multiple migrations are to be executed, they can be assembled in a folder and then\nexecuted together. As stated before, the order of multiple files is determined by their\nalphabetically ordered name.\n\nTaken the example from [above](#migration-from-file), split into two files, like prepared in\n[testData/](testData/).\n\n```go\npackage testprog\n\nimport (\n    \"database/sql\"\n    _ \"embed\"\n    \"io/fs\"\n    \"github.com/AlphaOne1/dmorph\"\n)\n\n//go:embed testData\nvar migrationFS embed.FS\n\nfunc migrate(db *sql.DB) error {\n    sub, subErr := fs.Sub(migrationFS, \"testData\")\n\n    if subErr != nil {\n        return subErr\n    }\n\n    return dmorph.Run(db,\n        dmorph.WithDialect(dmorph.DialectSQLite()),\n        dmorph.WithMigrationsFromFS(sub.(fs.ReadDirFS)))\n}\n\n...\n```\n\n### Programmatic Migration\n\nSometimes SQL alone is not sufficient to achieve the migration desired. Maybe the data needs to be\nprogrammatically changed, checked or otherwise processed. For *DMorph* a migration is presented as\nan interface:\n\n```go\ntype Migration interface {\n    Key() string              // identifier, used for ordering\n    Migrate(tx *sql.Tx) error // migration functionality\n}\n```\n\nThe `WithMigrationFromF...` family of options constructs these migrations for convenience. An example\nmigration fulfilling this interface could look like this:\n\n```go\ntype CustomMigration struct {}\n\nfunc (m CustomMigration) Key() string {\n    return \"0001_custom\"\n}\n\nfunc (m CustomMigration) Migrate(tx *sql.Tx) error {\n    _, err := tx.Exec(`CREATE TABLE tab0(id INTEGER PRIMARY KEY)`)\n    return err\n}\n```\n\nInside the `Migrate` function the transaction state should not be modified.\n`Commit` and `Rollback` are handled by *DMorph* as needed. As seen in the example, a potentiel error\nis returned plain to the caller.\n\nThis newly created migration can then be passed to *DMorph* as follows:\n\n```go\nfunc migrate(db *sql.DB) error {\n    return dmorph.Run(db,\n        dmorph.WithDialect(dmorph.DialectSQLite()),\n        dmorph.WithMigrations(CustomMigration{}))\n}\n```\n\n### New SQL Dialect\n\n*DMorph* uses the Dialect interface to adapt to different database management systems:\n\n```go\ntype Dialect interface {\n    EnsureMigrationTableExists(db *sql.DB, tableName string) error\n    AppliedMigrations(db *sql.DB, tableName string) ([]string, error)\n    RegisterMigration(tx *sql.Tx, id string, tableName string) error\n}\n```\n\nIt contains a convenience wrapper, `BaseDialect`, that fits most database systems. It implements the\nabove functions using a set of user supplied SQL statements:\n\n```go\ntype BaseDialect struct {\n    CreateTemplate   string // statement ensuring the existence of the migration table\n    AppliedTemplate  string // statement getting applied migrations ordered by application date\n    RegisterTemplate string // statement registering a migration\n}\n```\n\nAll the included SQL dialects use the `BaseDialect` to implement their functionality. The tests for\n*DMorph* are done using the [SQLite dialect](dialect_sqlite.go).\n\nAs the migration table name can be user supplied, the statements need to have placeholders that will\nfill the final table name. As there might be special characters, it is always enclosed in the\nidentifier enclosing characters of the database.\n\n*DMorph* uses the `ValidTableNameRex` regular expression, to check if a table name is principally\nvalid. The regular expression may be adapted, but it is strongly advised to only do so in pressing\ncircumstances.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falphaone1%2Fdmorph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falphaone1%2Fdmorph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falphaone1%2Fdmorph/lists"}