├── .env ├── .github ├── FUNDING.yml └── workflows │ └── ci.yaml ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── logger.go ├── mysql_test.go ├── postgresql_test.go ├── renovate.json ├── sqlite_test.go ├── sqlserver_test.go ├── xormigrate.go └── xormigrate_test.go /.env: -------------------------------------------------------------------------------- 1 | SQLITE_CONN_STRING=file::memory:?cache=shared 2 | PG_CONN_STRING="user=postgres password=postgres host=pgsql dbname=test sslmode=disable" 3 | MYSQL_CONN_STRING="root:@(mysql)/test?multiStatements=true" 4 | SQLSERVER_CONN_STRING="server=mssql; database=master; user id=sa; password=MwantsaSecurePassword1; encrypt=disable" 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [techknowlogick] 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | pull_request: 6 | branches: 7 | - main 8 | workflow_dispatch: 9 | 10 | name: ci 11 | 12 | jobs: 13 | test: 14 | strategy: 15 | matrix: 16 | # lowest and highest versions of go that are supported by the project 17 | # highest should be the latest version of go 18 | go-version: [ 1.16.x, 1.21.x ] 19 | runs-on: ubuntu-latest 20 | services: 21 | pgsql: 22 | image: postgres:12 23 | env: 24 | POSTGRES_DB: test 25 | POSTGRES_PASSWORD: postgres 26 | ports: 27 | - "5432:5432" 28 | mysql: 29 | image: mysql:8 30 | env: 31 | MYSQL_DATABASE: test 32 | MYSQL_ALLOW_EMPTY_PASSWORD: yes 33 | ports: 34 | - "3306:3306" 35 | mssql: 36 | image: mcr.microsoft.com/mssql/server:2017-latest 37 | env: 38 | ACCEPT_EULA: Y 39 | MSSQL_PID: Standard 40 | SA_PASSWORD: MwantsaSecurePassword1 41 | ports: 42 | - "1433:1433" 43 | steps: 44 | - name: Install Go 45 | uses: actions/setup-go@v4 46 | with: 47 | go-version: ${{ matrix.go-version }} 48 | - name: Checkout code 49 | uses: actions/checkout@v4 50 | - name: Restore cache 51 | uses: actions/cache@v3 52 | with: 53 | path: ~/go/pkg/mod 54 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 55 | restore-keys: | 56 | ${{ runner.os }}-go- 57 | - name: Add hosts to /etc/hosts 58 | run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 pgsql mysql mssql" | sudo tee -a /etc/hosts' 59 | - name: check fmt 60 | run: if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then exit 1; fi 61 | - name: Test 62 | run: | 63 | go test -v -tags sqlite 64 | go test -v -tags mysql 65 | go test -v -tags postgresql 66 | go test -v -tags sqlserver 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Matti Ranta (@techknowlogick) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xormigrate 2 | [![Go Report Card](https://goreportcard.com/badge/src.techknowlogick.com/xormigrate)](https://goreportcard.com/report/src.techknowlogick.com/xormigrate) 3 | [![GoDoc](https://pkg.go.dev/badge/src.techknowlogick.com/xormigrate)](https://pkg.go.dev/src.techknowlogick.com/xormigrate) 4 | 5 | ## Supported databases 6 | 7 | It supports any of the databases Xorm supports: 8 | 9 | - PostgreSQL 10 | - MySQL 11 | - SQLite 12 | - Microsoft SQL Server 13 | - [Full list of supported drivers...](https://gitea.com/xorm/xorm#drivers-support) 14 | 15 | ## Installing 16 | 17 | ```bash 18 | go get -u src.techknowlogick.com/xormigrate 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "log" 28 | 29 | "src.techknowlogick.com/xormigrate" 30 | 31 | "xorm.io/xorm" 32 | _ "github.com/mattn/go-sqlite3" 33 | ) 34 | 35 | func main() { 36 | db, err := xorm.NewEngine("sqlite3", "mydb.sqlite3") 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | 41 | m := xormigrate.New(db, []*xormigrate.Migration{ 42 | // create persons table 43 | { 44 | ID: "201608301400", 45 | // An optional description to print out to the Xormigrate logger 46 | Description: "Create the Person table", 47 | Migrate: func(tx *xorm.Engine) error { 48 | // it's a good pratice to copy the struct inside the function, 49 | // so side effects are prevented if the original struct changes during the time 50 | type Person struct { 51 | Name string 52 | } 53 | return tx.Sync2(&Person{}) 54 | }, 55 | Rollback: func(tx *xorm.Engine) error { 56 | return tx.DropTables(&Person{}) 57 | }, 58 | }, 59 | // add age column to persons 60 | { 61 | ID: "201608301415", 62 | Migrate: func(tx *xorm.Engine) error { 63 | // when table already exists, it just adds fields as columns 64 | type Person struct { 65 | Age int 66 | } 67 | return tx.Sync2(&Person{}) 68 | }, 69 | Rollback: func(tx *xorm.Engine) error { 70 | // Note: Column dropping in sqlite is not support, and you will need to do this manually 71 | _, err = tx.Exec("ALTER TABLE person DROP COLUMN age") 72 | if err != nil { 73 | return fmt.Errorf("Drop column failed: %v", err) 74 | } 75 | return nil 76 | }, 77 | }, 78 | // add pets table 79 | { 80 | ID: "201608301430", 81 | Migrate: func(tx *xorm.Engine) error { 82 | type Pet struct { 83 | Name string 84 | PersonID int 85 | } 86 | return tx.Sync2(&Pet{}) 87 | }, 88 | Rollback: func(tx *xorm.Engine) error { 89 | return tx.DropTables(&Pet{}) 90 | }, 91 | }, 92 | }) 93 | 94 | if err = m.Migrate(); err != nil { 95 | log.Fatalf("Could not migrate: %v", err) 96 | } 97 | log.Printf("Migration did run successfully") 98 | } 99 | ``` 100 | 101 | ## Having a separated function for initializing the schema 102 | 103 | If you have a lot of migrations, it can be a pain to run all them, as example, 104 | when you are deploying a new instance of the app, in a clean database. 105 | To prevent this, you can set a function that will run if no migration was run 106 | before (in a new clean database). Remember to create everything here, all tables, 107 | foreign keys and what more you need in your app. 108 | 109 | ```go 110 | type Person struct { 111 | Name string 112 | Age int 113 | } 114 | 115 | type Pet struct { 116 | Name string 117 | PersonID int 118 | } 119 | 120 | m := xormigrate.New(db, []*xormigrate.Migration{ 121 | // your migrations here 122 | }) 123 | 124 | m.InitSchema(func(tx *xorm.Engine) error { 125 | err := tx.sync2( 126 | &Person{}, 127 | &Pet{}, 128 | // all other tables of your app 129 | ) 130 | if err != nil { 131 | return err 132 | } 133 | return nil 134 | }) 135 | ``` 136 | 137 | ## Adding migration descriptions to your logging 138 | Xormigrate's logger defaults to stdout, but it can be changed to suit your needs. 139 | ```go 140 | m := xormigrate.New(db, []*xormigrate.Migration{ 141 | // your migrations here 142 | }) 143 | 144 | // Don't log anything 145 | m.NilLogger() 146 | 147 | // This is the default logger 148 | // No need to initialize this unless it was changed 149 | // [xormigrate] message 150 | m.DefaultLogger() 151 | 152 | // Or, create a logger with any io.Writer you want 153 | m.NewLogger(os.Stdout) 154 | ``` 155 | 156 | ## Credits 157 | 158 | * Based on [Gormigrate][gormmigrate] 159 | * Uses [Xorm][xorm] 160 | 161 | [xorm]: http://xorm.io/ 162 | [gormmigrate]: https://github.com/go-gormigrate/gormigrate 163 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module src.techknowlogick.com/xormigrate 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/denisenkom/go-mssqldb v0.12.3 7 | github.com/go-sql-driver/mysql v1.7.0 8 | github.com/goccy/go-json v0.10.2 // indirect 9 | github.com/joho/godotenv v1.3.0 10 | github.com/lib/pq v1.10.7 11 | github.com/mattn/go-sqlite3 v1.14.16 12 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 13 | github.com/stretchr/testify v1.8.2 14 | xorm.io/builder v0.3.13 // indirect 15 | xorm.io/xorm v1.3.3 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= 2 | gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= 3 | gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= 4 | github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= 5 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= 6 | github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= 7 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 8 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 9 | github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= 10 | github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= 11 | github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 12 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 13 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 14 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 15 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 16 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 17 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 18 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw= 20 | github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= 21 | github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= 22 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= 23 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 24 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 25 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 26 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 27 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= 28 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 29 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 30 | github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 31 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 32 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 33 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 34 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 35 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 36 | github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= 37 | github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= 38 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 39 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 40 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 41 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 42 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 43 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 44 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 45 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 46 | github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= 47 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 48 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 49 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 50 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 51 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 52 | github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= 53 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 54 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 55 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 56 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 57 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 58 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 59 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 60 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 61 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 62 | github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= 63 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 64 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 65 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 66 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 67 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 68 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 69 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 70 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 71 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 72 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 73 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 74 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 75 | github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 76 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 77 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 78 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 79 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 80 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 81 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 82 | github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 83 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 84 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 85 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 86 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 87 | github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= 88 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 89 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 90 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 91 | github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 92 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 93 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 94 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 95 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 96 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 97 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 98 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 99 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 100 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 101 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 102 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 103 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 104 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 105 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 106 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 107 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 108 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 109 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 110 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 111 | github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= 112 | github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 113 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 114 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 115 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 116 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 117 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 118 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 119 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 120 | github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 121 | github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= 122 | github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 123 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 124 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 125 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 126 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 127 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 128 | github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= 129 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 130 | github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= 131 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 132 | github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= 133 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 134 | github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= 135 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 136 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 137 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 138 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= 139 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 140 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 141 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 142 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 143 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 144 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 145 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 146 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 147 | github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 148 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 149 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 150 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 151 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 152 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 153 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 154 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 155 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 156 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 157 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 158 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 159 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 160 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 161 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 162 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 163 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 164 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 165 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= 166 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 167 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 168 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 169 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 170 | github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= 171 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 172 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 173 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 174 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 175 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 176 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 177 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 178 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 179 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 180 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 181 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 182 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 183 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 184 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 185 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 186 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 187 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 188 | golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 189 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 190 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 191 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 192 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 193 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 194 | golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= 195 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 196 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 197 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 198 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 199 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 200 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 201 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 202 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 203 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 204 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 205 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 206 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 207 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 208 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 209 | golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 210 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 211 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 212 | golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= 213 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 214 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 215 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 216 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 217 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 218 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 219 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 220 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 221 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 222 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 223 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 224 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 225 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 226 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 227 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 228 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 229 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 230 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 231 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 232 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 233 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 234 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 235 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 236 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 237 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 238 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 239 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 240 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 241 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 242 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 243 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 244 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 245 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 246 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 247 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 248 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 249 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= 250 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 251 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 252 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 253 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 254 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 255 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 256 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 257 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 258 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 259 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 260 | golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 261 | golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 262 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 263 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 264 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 265 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 266 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 267 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 268 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 269 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 270 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 271 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 272 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 273 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 274 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 275 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 276 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 277 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 278 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 279 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 280 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 281 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 282 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 283 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 284 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 285 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 286 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 287 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 288 | lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= 289 | lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= 290 | lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= 291 | modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= 292 | modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= 293 | modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= 294 | modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= 295 | modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= 296 | modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0= 297 | modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= 298 | modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= 299 | modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= 300 | modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= 301 | modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= 302 | modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= 303 | modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= 304 | modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= 305 | modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= 306 | modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= 307 | modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= 308 | modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= 309 | modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= 310 | modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 311 | modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= 312 | modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= 313 | modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= 314 | modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 315 | modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= 316 | modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 317 | modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE= 318 | modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= 319 | modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= 320 | modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= 321 | modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE= 322 | modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= 323 | modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 324 | modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= 325 | xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= 326 | xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo= 327 | xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= 328 | xorm.io/xorm v1.3.3 h1:L5/GOhvgMcwJYYRjzPf3lTTTf6JcaTd1Mb9A/Iqvccw= 329 | xorm.io/xorm v1.3.3/go.mod h1:qFJGFoVYbbIdnz2vaL5OxSQ2raleMpyRRalnq3n9OJo= 330 | -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | package xormigrate 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | ) 9 | 10 | type LoggerInterface interface { 11 | Debug(v ...interface{}) 12 | Debugf(format string, v ...interface{}) 13 | Info(v ...interface{}) 14 | Infof(format string, v ...interface{}) 15 | Warn(v ...interface{}) 16 | Warnf(format string, v ...interface{}) 17 | Error(v ...interface{}) 18 | Errorf(format string, v ...interface{}) 19 | } 20 | 21 | var ( 22 | logger LoggerInterface = defaultLogger() 23 | ) 24 | 25 | // SetLogger sets the Xormigrate logger 26 | func (x *Xormigrate) SetLogger(l LoggerInterface) { 27 | logger = l 28 | } 29 | 30 | func defaultLogger() *XormigrateLogger { 31 | return &XormigrateLogger{log.New(os.Stdout, "[xormigrate] ", 0)} 32 | } 33 | 34 | // DefaultLogger sets a Xormigrate logger with default settings 35 | // e.g. "[xormigrate] message" 36 | func (x *Xormigrate) DefaultLogger() { 37 | x.SetLogger(defaultLogger()) 38 | } 39 | 40 | // NilLogger sets a Xormigrate logger that discards all messages 41 | func (x *Xormigrate) NilLogger() { 42 | x.SetLogger(&XormigrateLogger{log.New(ioutil.Discard, "", 0)}) 43 | } 44 | 45 | // NewLogger sets a Xormigrate logger with a specified io.Writer 46 | func (x *Xormigrate) NewLogger(writer io.Writer) { 47 | x.SetLogger(&XormigrateLogger{log.New(writer, "", 0)}) 48 | } 49 | 50 | type XormigrateLogger struct { 51 | *log.Logger 52 | } 53 | 54 | // Debug prints a Debug message 55 | func (l *XormigrateLogger) Debug(v ...interface{}) { 56 | l.Logger.Print(v...) 57 | } 58 | 59 | // Debugf prints a formatted Debug message 60 | func (l *XormigrateLogger) Debugf(format string, v ...interface{}) { 61 | l.Logger.Printf(format, v...) 62 | } 63 | 64 | // Info prints an Info message 65 | func (l *XormigrateLogger) Info(v ...interface{}) { 66 | l.Logger.Print(v...) 67 | } 68 | 69 | // Infof prints a formatted Info message 70 | func (l *XormigrateLogger) Infof(format string, v ...interface{}) { 71 | l.Logger.Printf(format, v...) 72 | } 73 | 74 | // Warn prints a Warning message 75 | func (l *XormigrateLogger) Warn(v ...interface{}) { 76 | l.Logger.Print(v...) 77 | } 78 | 79 | // Warnf prints a formatted Warning message 80 | func (l *XormigrateLogger) Warnf(format string, v ...interface{}) { 81 | l.Logger.Printf(format, v...) 82 | } 83 | 84 | // Error prints an Error message 85 | func (l *XormigrateLogger) Error(v ...interface{}) { 86 | l.Logger.Print(v...) 87 | } 88 | 89 | // Errorf prints a formatted Error message 90 | func (l *XormigrateLogger) Errorf(format string, v ...interface{}) { 91 | l.Logger.Printf(format, v...) 92 | } 93 | -------------------------------------------------------------------------------- /mysql_test.go: -------------------------------------------------------------------------------- 1 | //go:build mysql 2 | // +build mysql 3 | 4 | package xormigrate 5 | 6 | import ( 7 | _ "github.com/go-sql-driver/mysql" 8 | ) 9 | 10 | func init() { 11 | databases = append(databases, database{ 12 | name: "mysql", 13 | connEnv: "MYSQL_CONN_STRING", 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /postgresql_test.go: -------------------------------------------------------------------------------- 1 | //go:build postgresql 2 | // +build postgresql 3 | 4 | package xormigrate 5 | 6 | import ( 7 | _ "github.com/lib/pq" 8 | ) 9 | 10 | func init() { 11 | databases = append(databases, database{ 12 | name: "postgres", 13 | connEnv: "PG_CONN_STRING", 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /sqlite_test.go: -------------------------------------------------------------------------------- 1 | //go:build sqlite 2 | // +build sqlite 3 | 4 | package xormigrate 5 | 6 | import ( 7 | _ "github.com/mattn/go-sqlite3" 8 | ) 9 | 10 | func init() { 11 | databases = append(databases, database{ 12 | name: "sqlite3", 13 | connEnv: "SQLITE_CONN_STRING", 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /sqlserver_test.go: -------------------------------------------------------------------------------- 1 | //go:build sqlserver 2 | // +build sqlserver 3 | 4 | package xormigrate 5 | 6 | import ( 7 | _ "github.com/denisenkom/go-mssqldb" 8 | ) 9 | 10 | func init() { 11 | databases = append(databases, database{ 12 | name: "mssql", 13 | connEnv: "SQLSERVER_CONN_STRING", 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /xormigrate.go: -------------------------------------------------------------------------------- 1 | package xormigrate // import "src.techknowlogick.com/xormigrate" 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "xorm.io/xorm" 8 | ) 9 | 10 | const ( 11 | initSchemaMigrationId = "SCHEMA_INIT" 12 | ) 13 | 14 | // MigrateFunc is the func signature for migrating. 15 | type MigrateFunc func(*xorm.Engine) error 16 | 17 | // RollbackFunc is the func signature for rollbacking. 18 | type RollbackFunc func(*xorm.Engine) error 19 | 20 | // InitSchemaFunc is the func signature for initializing the schema. 21 | type InitSchemaFunc func(*xorm.Engine) error 22 | 23 | // MigrateFuncSession is the func signature for migrating using xorm.Session. 24 | type MigrateFuncSession func(*xorm.Session) error 25 | 26 | // RollbackFuncSession is the func signature for rollbacking using xorm.Session. 27 | type RollbackFuncSession func(*xorm.Session) error 28 | 29 | // Migration represents a database migration (a modification to be made on the database). 30 | type Migration struct { 31 | // ID is the migration identifier. Usually a timestamp like "201601021504". 32 | ID string `xorm:"id"` 33 | // Description is the migration description, which is optionally printed out when the migration is ran. 34 | Description string 35 | // Migrate is a function that will be executed while running this migration. 36 | Migrate MigrateFunc `xorm:"-"` 37 | // Rollback will be executed on rollback. Can be nil. 38 | Rollback RollbackFunc `xorm:"-"` 39 | // MigrateSession is a function that will be executed while running this migration, using xorm.Session. 40 | MigrateSession MigrateFuncSession `xorm:"-"` 41 | // RollbackSession will be executed on rollback, using xorm.Session. Can be nil. 42 | RollbackSession RollbackFuncSession `xorm:"-"` 43 | // Long marks the migration an non-required migration that will likely take a long time. Must use Xormigrate.AllowLong() to be enabled. 44 | Long bool `xorm:"-"` 45 | } 46 | 47 | // Xormigrate represents a collection of all migrations of a database schema. 48 | type Xormigrate struct { 49 | db *xorm.Engine 50 | migrations []*Migration 51 | initSchema InitSchemaFunc 52 | allowLong bool 53 | } 54 | 55 | // ReservedIDError is returned when a migration is using a reserved ID 56 | type ReservedIDError struct { 57 | ID string 58 | } 59 | 60 | func (e *ReservedIDError) Error() string { 61 | return fmt.Sprintf(`xormigrate: Reserved migration ID: "%s"`, e.ID) 62 | } 63 | 64 | // DuplicatedIDError is returned when more than one migration have the same ID 65 | type DuplicatedIDError struct { 66 | ID string 67 | } 68 | 69 | func (e *DuplicatedIDError) Error() string { 70 | return fmt.Sprintf(`xormigrate: Duplicated migration ID: "%s"`, e.ID) 71 | } 72 | 73 | var ( 74 | // ErrRollbackImpossible is returned when trying to rollback a migration 75 | // that has no rollback function. 76 | ErrRollbackImpossible = errors.New("xormigrate: It's impossible to rollback this migration") 77 | 78 | // ErrNoMigrationDefined is returned when no migration is defined. 79 | ErrNoMigrationDefined = errors.New("xormigrate: No migration defined") 80 | 81 | // ErrMissingID is returned when the ID od migration is equal to "" 82 | ErrMissingID = errors.New("xormigrate: Missing ID in migration") 83 | 84 | // ErrNoRunMigration is returned when any run migration was found while 85 | // running RollbackLast 86 | ErrNoRunMigration = errors.New("xormigrate: Could not find last run migration") 87 | 88 | // ErrMigrationIDDoesNotExist is returned when migrating or rolling back to a migration ID that 89 | // does not exist in the list of migrations 90 | ErrMigrationIDDoesNotExist = errors.New("xormigrate: Tried to migrate to an ID that doesn't exist") 91 | ) 92 | 93 | // New returns a new Xormigrate. 94 | func New(db *xorm.Engine, migrations []*Migration) *Xormigrate { 95 | return &Xormigrate{ 96 | db: db, 97 | migrations: migrations, 98 | allowLong: false, 99 | } 100 | } 101 | 102 | // InitSchema sets a function that is run if no migration is found. 103 | // The idea is preventing to run all migrations when a new clean database 104 | // is being migratinx. In this function you should create all tables and 105 | // foreign key necessary to your application. 106 | func (x *Xormigrate) InitSchema(initSchema InitSchemaFunc) { 107 | x.initSchema = initSchema 108 | } 109 | 110 | // AllowLong enables migrations that are marked as probably long-running. 111 | func (x *Xormigrate) AllowLong(allow ...bool) { 112 | allowLong := true 113 | if len(allow) > 0 { 114 | allowLong = allow[0] 115 | } 116 | x.allowLong = allowLong 117 | } 118 | 119 | // Migrate executes all migrations that did not run yet. 120 | func (x *Xormigrate) Migrate() error { 121 | return x.migrate("") 122 | } 123 | 124 | // MigrateTo executes all migrations that did not run yet up to the migration that matches `migrationID`. 125 | func (x *Xormigrate) MigrateTo(migrationID string) error { 126 | if err := x.checkIDExist(migrationID); err != nil { 127 | return err 128 | } 129 | return x.migrate(migrationID) 130 | } 131 | 132 | func (x *Xormigrate) migrate(migrationID string) error { 133 | if !x.hasMigrations() { 134 | return ErrNoMigrationDefined 135 | } 136 | 137 | if err := x.checkReservedID(); err != nil { 138 | return err 139 | } 140 | 141 | if err := x.checkDuplicatedID(); err != nil { 142 | return err 143 | } 144 | 145 | if err := x.createMigrationTableIfNotExists(); err != nil { 146 | return err 147 | } 148 | 149 | if x.initSchema != nil && x.canInitializeSchema() { 150 | return x.runInitSchema() // return error or nil 151 | } 152 | 153 | for _, migration := range x.migrations { 154 | if migration.Long && !x.allowLong { 155 | logger.Debugf("skipping migration %s: long migrations are disabled", migration.ID) 156 | continue 157 | } 158 | if err := x.runMigration(migration); err != nil { 159 | return err 160 | } 161 | if migrationID != "" && migration.ID == migrationID { 162 | break 163 | } 164 | } 165 | 166 | return nil 167 | } 168 | 169 | // There are migrations to apply if either there's a defined 170 | // initSchema function or if the list of migrations is not empty. 171 | func (x *Xormigrate) hasMigrations() bool { 172 | return x.initSchema != nil || len(x.migrations) > 0 173 | } 174 | 175 | // Check whether any migration is using a reserved ID. 176 | // For now there's only have one reserved ID, but there may be more in the future. 177 | func (x *Xormigrate) checkReservedID() error { 178 | for _, m := range x.migrations { 179 | if m.ID == initSchemaMigrationId { 180 | return &ReservedIDError{ID: m.ID} 181 | } 182 | } 183 | return nil 184 | } 185 | 186 | func (x *Xormigrate) checkDuplicatedID() error { 187 | lookup := make(map[string]struct{}, len(x.migrations)) 188 | for _, m := range x.migrations { 189 | if _, ok := lookup[m.ID]; ok { 190 | return &DuplicatedIDError{ID: m.ID} 191 | } 192 | lookup[m.ID] = struct{}{} 193 | } 194 | return nil 195 | } 196 | 197 | func (x *Xormigrate) checkIDExist(migrationID string) error { 198 | for _, migrate := range x.migrations { 199 | if migrate.ID == migrationID { 200 | return nil 201 | } 202 | } 203 | return ErrMigrationIDDoesNotExist 204 | } 205 | 206 | // RollbackLast undo the last migration 207 | func (x *Xormigrate) RollbackLast() error { 208 | if len(x.migrations) == 0 { 209 | return ErrNoMigrationDefined 210 | } 211 | 212 | lastRunMigration, err := x.getLastRunMigration() 213 | if err != nil { 214 | return err 215 | } 216 | 217 | return x.RollbackMigration(lastRunMigration) // return error or nil 218 | } 219 | 220 | // RollbackTo undoes migrations up to the given migration that matches the `migrationID`. 221 | // Migration with the matching `migrationID` is not rolled back. 222 | func (x *Xormigrate) RollbackTo(migrationID string) error { 223 | if len(x.migrations) == 0 { 224 | return ErrNoMigrationDefined 225 | } 226 | 227 | if err := x.checkIDExist(migrationID); err != nil { 228 | return err 229 | } 230 | 231 | for i := len(x.migrations) - 1; i >= 0; i-- { 232 | migration := x.migrations[i] 233 | if migration.ID == migrationID { 234 | break 235 | } 236 | if x.migrationDidRun(migration) { 237 | if err := x.rollbackMigration(migration); err != nil { 238 | return err 239 | } 240 | } 241 | } 242 | 243 | return nil 244 | } 245 | 246 | func (x *Xormigrate) getLastRunMigration() (*Migration, error) { 247 | for i := len(x.migrations) - 1; i >= 0; i-- { 248 | migration := x.migrations[i] 249 | if x.migrationDidRun(migration) { 250 | return migration, nil 251 | } 252 | } 253 | return nil, ErrNoRunMigration 254 | } 255 | 256 | // RollbackMigration undo a migration. 257 | func (x *Xormigrate) RollbackMigration(m *Migration) error { 258 | return x.rollbackMigration(m) // return error or nil 259 | } 260 | 261 | func (x *Xormigrate) rollbackMigration(m *Migration) error { 262 | if m.Rollback == nil && m.RollbackSession == nil { 263 | return ErrRollbackImpossible 264 | } 265 | if len(m.Description) > 0 { 266 | logger.Errorf("Rolling back migration: %s", m.Description) 267 | } 268 | if m.Rollback != nil { 269 | if err := m.Rollback(x.db); err != nil { 270 | return err 271 | } 272 | } else { 273 | sess := x.db.NewSession() 274 | if err := m.RollbackSession(sess); err != nil { 275 | rollbackSession(sess) 276 | return err 277 | } 278 | if err := sess.Commit(); err != nil { 279 | return err 280 | } 281 | } 282 | if _, err := x.db.In("id", m.ID).Delete(&Migration{}); err != nil { 283 | return err 284 | } 285 | return nil 286 | } 287 | 288 | func (x *Xormigrate) runInitSchema() error { 289 | logger.Info("Initializing Schema") 290 | sess := x.db.NewSession() 291 | if err := x.initSchema(x.db); err != nil { 292 | rollbackSession(sess) 293 | return err 294 | } 295 | if err := sess.Commit(); err != nil { 296 | return err 297 | } 298 | if err := x.insertMigration(initSchemaMigrationId); err != nil { 299 | return err 300 | } 301 | 302 | for _, migration := range x.migrations { 303 | if err := x.insertMigration(migration.ID); err != nil { 304 | return err 305 | } 306 | } 307 | 308 | return nil 309 | } 310 | 311 | func (x *Xormigrate) runMigration(migration *Migration) error { 312 | if len(migration.ID) == 0 { 313 | return ErrMissingID 314 | } 315 | 316 | if !x.migrationDidRun(migration) { 317 | if len(migration.Description) > 0 { 318 | logger.Info(migration.Description) 319 | } 320 | if migration.Migrate != nil { 321 | if err := migration.Migrate(x.db); err != nil { 322 | return fmt.Errorf("migration %s failed: %s", migration.ID, err.Error()) 323 | } 324 | } else { 325 | sess := x.db.NewSession() 326 | if err := migration.MigrateSession(sess); err != nil { 327 | rollbackSession(sess) 328 | return fmt.Errorf("migration %s failed: %s", migration.ID, err.Error()) 329 | } 330 | if err := sess.Commit(); err != nil { 331 | return err 332 | } 333 | } 334 | 335 | if err := x.insertMigration(migration.ID); err != nil { 336 | return fmt.Errorf("inserting migration %s failed: %s", migration.ID, err.Error()) 337 | } 338 | } 339 | return nil 340 | } 341 | 342 | func (x *Xormigrate) createMigrationTableIfNotExists() error { 343 | err := x.db.Sync2(new(Migration)) 344 | return err 345 | } 346 | 347 | func (x *Xormigrate) migrationDidRun(m *Migration) bool { 348 | count, err := x.db. 349 | In("id", m.ID). 350 | Count(&Migration{}) 351 | if err != nil { 352 | return false 353 | } 354 | return count > 0 355 | } 356 | 357 | // The schema can be initialised only if it hasn't been initialised yet 358 | // and no other migration has been applied already. 359 | func (x *Xormigrate) canInitializeSchema() bool { 360 | if x.migrationDidRun(&Migration{ID: initSchemaMigrationId}) { 361 | return false 362 | } 363 | 364 | // If the ID doesn't exist, we also want the list of migrations to be empty 365 | count, err := x.db. 366 | Count(&Migration{}) 367 | if err != nil { 368 | return false 369 | } 370 | return count == 0 371 | } 372 | 373 | func (x *Xormigrate) insertMigration(id string) error { 374 | _, err := x.db.Insert(&Migration{ID: id}) 375 | return err 376 | } 377 | 378 | func rollbackSession(sess *xorm.Session) { 379 | if err := sess.Rollback(); err != nil { 380 | logger.Errorf("Failed to rollback session: %v", err) 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /xormigrate_test.go: -------------------------------------------------------------------------------- 1 | package xormigrate 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | _ "github.com/joho/godotenv/autoload" 8 | "github.com/stretchr/testify/assert" 9 | "xorm.io/xorm" 10 | ) 11 | 12 | var databases []database 13 | 14 | type database struct { 15 | name string 16 | connEnv string 17 | } 18 | 19 | var migrations = []*Migration{ 20 | { 21 | ID: "201608301400", 22 | Description: "Add Person", 23 | Migrate: func(tx *xorm.Engine) error { 24 | return tx.Sync2(&Person{}) 25 | }, 26 | Rollback: func(tx *xorm.Engine) error { 27 | return tx.DropTables(&Person{}) 28 | }, 29 | }, 30 | { 31 | ID: "201608301430", 32 | Migrate: func(tx *xorm.Engine) error { 33 | return tx.Sync2(&Pet{}) 34 | }, 35 | Rollback: func(tx *xorm.Engine) error { 36 | return tx.DropTables(&Pet{}) 37 | }, 38 | }, 39 | } 40 | 41 | var migrationsSession = []*Migration{ 42 | { 43 | ID: "201608301400", 44 | Description: "Add Person", 45 | MigrateSession: func(tx *xorm.Session) error { 46 | return tx.Sync(&Person{}) 47 | }, 48 | RollbackSession: func(tx *xorm.Session) error { 49 | return tx.DropTable(&Person{}) 50 | }, 51 | }, 52 | { 53 | ID: "201608301430", 54 | MigrateSession: func(tx *xorm.Session) error { 55 | return tx.Sync2(&Pet{}) 56 | }, 57 | RollbackSession: func(tx *xorm.Session) error { 58 | return tx.DropTable(&Pet{}) 59 | }, 60 | }, 61 | } 62 | 63 | var extendedMigrations = append(migrations, &Migration{ 64 | ID: "201807221927", 65 | Migrate: func(tx *xorm.Engine) error { 66 | return tx.Sync2(&Book{}) 67 | }, 68 | Rollback: func(tx *xorm.Engine) error { 69 | return tx.DropTables(&Book{}) 70 | }, 71 | }) 72 | 73 | type Person struct { 74 | ID int `xorm:"id"` 75 | Name string 76 | } 77 | 78 | type Pet struct { 79 | Name string 80 | PersonID int `xorm:"person_id"` 81 | } 82 | 83 | type Book struct { 84 | Name string 85 | PersonID int `xorm:"person_id"` 86 | } 87 | 88 | func TestMigration(t *testing.T) { 89 | forEachDatabase(t, func(db *xorm.Engine) { 90 | m := New(db, migrations) 91 | 92 | err := m.Migrate() 93 | assert.NoError(t, err) 94 | has, err := db.IsTableExist(&Person{}) 95 | assert.NoError(t, err) 96 | assert.True(t, has) 97 | has, err = db.IsTableExist(&Pet{}) 98 | assert.NoError(t, err) 99 | assert.True(t, has) 100 | assert.Equal(t, int64(2), tableCount(t, db)) 101 | 102 | err = m.RollbackLast() 103 | assert.NoError(t, err) 104 | has, err = db.IsTableExist(&Person{}) 105 | assert.NoError(t, err) 106 | assert.True(t, has) 107 | has, err = db.Exist(&Pet{}) 108 | assert.Error(t, err) 109 | assert.False(t, has) 110 | assert.Equal(t, int64(1), tableCount(t, db)) 111 | 112 | err = m.RollbackLast() 113 | assert.NoError(t, err) 114 | has, err = db.IsTableExist(&Person{}) 115 | assert.NoError(t, err) 116 | assert.False(t, has) 117 | has, err = db.IsTableExist(&Pet{}) 118 | assert.NoError(t, err) 119 | assert.False(t, has) 120 | assert.Equal(t, int64(0), tableCount(t, db)) 121 | }) 122 | } 123 | 124 | func TestMigrateTo(t *testing.T) { 125 | forEachDatabase(t, func(db *xorm.Engine) { 126 | m := New(db, extendedMigrations) 127 | 128 | err := m.MigrateTo("201608301430") 129 | assert.NoError(t, err) 130 | has, _ := db.IsTableExist(&Person{}) 131 | assert.True(t, has) 132 | has, _ = db.IsTableExist(&Pet{}) 133 | assert.True(t, has) 134 | has, _ = db.IsTableExist(&Book{}) 135 | assert.False(t, has) 136 | assert.Equal(t, int64(2), tableCount(t, db)) 137 | }) 138 | } 139 | 140 | func TestRollbackTo(t *testing.T) { 141 | forEachDatabase(t, func(db *xorm.Engine) { 142 | m := New(db, extendedMigrations) 143 | 144 | // First, apply all migrations. 145 | err := m.Migrate() 146 | assert.NoError(t, err) 147 | has, _ := db.IsTableExist(&Person{}) 148 | assert.True(t, has) 149 | has, _ = db.IsTableExist(&Pet{}) 150 | assert.True(t, has) 151 | has, _ = db.IsTableExist(&Book{}) 152 | assert.True(t, has) 153 | assert.Equal(t, int64(3), tableCount(t, db)) 154 | 155 | // Rollback to the first migration: only the last 2 migrations are expected to be rolled back. 156 | err = m.RollbackTo("201608301400") 157 | assert.NoError(t, err) 158 | has, _ = db.IsTableExist(&Person{}) 159 | assert.True(t, has) 160 | has, _ = db.IsTableExist(&Pet{}) 161 | assert.False(t, has) 162 | has, _ = db.IsTableExist(&Book{}) 163 | assert.False(t, has) 164 | assert.Equal(t, int64(1), tableCount(t, db)) 165 | }) 166 | } 167 | 168 | // If initSchema is defined, but no migrations are provided, 169 | // then initSchema is executed. 170 | func TestInitSchemaNoMigrations(t *testing.T) { 171 | forEachDatabase(t, func(db *xorm.Engine) { 172 | m := New(db, []*Migration{}) 173 | m.InitSchema(func(tx *xorm.Engine) error { 174 | if err := tx.Sync2(&Person{}); err != nil { 175 | return err 176 | } 177 | return tx.Sync2(&Pet{}) // return error or nil 178 | }) 179 | 180 | assert.NoError(t, m.Migrate()) 181 | // assert.True(t, db.HasTable(&Person{})) 182 | // assert.True(t, db.HasTable(&Pet{})) 183 | assert.Equal(t, int64(1), tableCount(t, db)) 184 | }) 185 | } 186 | 187 | // If initSchema is defined and migrations are provided, 188 | // then initSchema is executed and the migration IDs are stored, 189 | // even though the relevant migrations are not applied. 190 | func TestInitSchemaWithMigrations(t *testing.T) { 191 | forEachDatabase(t, func(db *xorm.Engine) { 192 | m := New(db, migrations) 193 | m.InitSchema(func(tx *xorm.Engine) error { 194 | return tx.Sync2(&Person{}) // return error or nil 195 | }) 196 | 197 | assert.NoError(t, m.Migrate()) 198 | has, _ := db.IsTableExist(&Person{}) 199 | assert.True(t, has) 200 | has, _ = db.IsTableExist(&Pet{}) 201 | assert.False(t, has) 202 | assert.Equal(t, int64(3), tableCount(t, db)) 203 | }) 204 | } 205 | 206 | // If the schema has already been initialised, 207 | // then initSchema() is not executed, even if defined. 208 | func TestInitSchemaAlreadyInitialised(t *testing.T) { 209 | type Car struct { 210 | ID int 211 | } 212 | 213 | forEachDatabase(t, func(db *xorm.Engine) { 214 | m := New(db, []*Migration{}) 215 | 216 | // Migrate with empty initialisation 217 | m.InitSchema(func(tx *xorm.Engine) error { 218 | return nil 219 | }) 220 | assert.NoError(t, m.Migrate()) 221 | 222 | // Then migrate again, this time with a non empty initialisation 223 | // This second initialisation should not happen! 224 | m.InitSchema(func(tx *xorm.Engine) error { 225 | return tx.Sync2(&Car{}) // return error or nil 226 | }) 227 | assert.NoError(t, m.Migrate()) 228 | 229 | has, _ := db.IsTableExist(&Car{}) 230 | assert.False(t, has) 231 | assert.Equal(t, int64(1), tableCount(t, db)) 232 | }) 233 | } 234 | 235 | // If the schema has not already been initialised, 236 | // but any other migration has already been applied, 237 | // then initSchema() is not executed, even if defined. 238 | func TestInitSchemaExistingMigrations(t *testing.T) { 239 | type Car struct { 240 | ID int 241 | } 242 | 243 | forEachDatabase(t, func(db *xorm.Engine) { 244 | m := New(db, migrations) 245 | 246 | // Migrate without initialisation 247 | assert.NoError(t, m.Migrate()) 248 | 249 | // Then migrate again, this time with a non empty initialisation 250 | // This initialisation should not happen! 251 | m.InitSchema(func(tx *xorm.Engine) error { 252 | return tx.Sync2(&Car{}) // return error or nil 253 | }) 254 | assert.NoError(t, m.Migrate()) 255 | 256 | has, _ := db.IsTableExist(&Car{}) 257 | assert.False(t, has) 258 | assert.Equal(t, int64(2), tableCount(t, db)) 259 | }) 260 | } 261 | 262 | func TestMigrationIDDoesNotExist(t *testing.T) { 263 | forEachDatabase(t, func(db *xorm.Engine) { 264 | m := New(db, migrations) 265 | assert.Equal(t, ErrMigrationIDDoesNotExist, m.MigrateTo("1234")) 266 | assert.Equal(t, ErrMigrationIDDoesNotExist, m.RollbackTo("1234")) 267 | assert.Equal(t, ErrMigrationIDDoesNotExist, m.MigrateTo("")) 268 | assert.Equal(t, ErrMigrationIDDoesNotExist, m.RollbackTo("")) 269 | }) 270 | } 271 | 272 | func TestMissingID(t *testing.T) { 273 | forEachDatabase(t, func(db *xorm.Engine) { 274 | migrationsMissingID := []*Migration{ 275 | { 276 | Migrate: func(tx *xorm.Engine) error { 277 | return nil 278 | }, 279 | }, 280 | } 281 | 282 | m := New(db, migrationsMissingID) 283 | assert.Equal(t, ErrMissingID, m.Migrate()) 284 | }) 285 | } 286 | 287 | func TestReservedID(t *testing.T) { 288 | forEachDatabase(t, func(db *xorm.Engine) { 289 | migrationsReservedID := []*Migration{ 290 | { 291 | ID: "SCHEMA_INIT", 292 | Migrate: func(tx *xorm.Engine) error { 293 | return nil 294 | }, 295 | }, 296 | } 297 | 298 | m := New(db, migrationsReservedID) 299 | _, isReservedIDError := m.Migrate().(*ReservedIDError) 300 | assert.True(t, isReservedIDError) 301 | }) 302 | } 303 | 304 | func TestDuplicatedID(t *testing.T) { 305 | forEachDatabase(t, func(db *xorm.Engine) { 306 | migrationsDuplicatedID := []*Migration{ 307 | { 308 | ID: "201705061500", 309 | Migrate: func(tx *xorm.Engine) error { 310 | return nil 311 | }, 312 | }, 313 | { 314 | ID: "201705061500", 315 | Migrate: func(tx *xorm.Engine) error { 316 | return nil 317 | }, 318 | }, 319 | } 320 | 321 | m := New(db, migrationsDuplicatedID) 322 | _, isDuplicatedIDError := m.Migrate().(*DuplicatedIDError) 323 | assert.True(t, isDuplicatedIDError) 324 | }) 325 | } 326 | 327 | func TestEmptyMigrationList(t *testing.T) { 328 | forEachDatabase(t, func(db *xorm.Engine) { 329 | t.Run("with empty list", func(t *testing.T) { 330 | m := New(db, []*Migration{}) 331 | err := m.Migrate() 332 | assert.Equal(t, ErrNoMigrationDefined, err) 333 | }) 334 | 335 | t.Run("with nil list", func(t *testing.T) { 336 | m := New(db, nil) 337 | err := m.Migrate() 338 | assert.Equal(t, ErrNoMigrationDefined, err) 339 | }) 340 | }) 341 | } 342 | 343 | func TestAllowLong(t *testing.T) { 344 | forEachDatabase(t, func(db *xorm.Engine) { 345 | t.Run("without AllowLong", func(t *testing.T) { 346 | m := New(db, []*Migration{{ 347 | ID: "201608301430", 348 | Long: true, 349 | Migrate: func(tx *xorm.Engine) error { 350 | return tx.Sync2(&Pet{}) 351 | }, 352 | Rollback: func(tx *xorm.Engine) error { 353 | return tx.DropTables(&Pet{}) 354 | }, 355 | }}) 356 | err := m.Migrate() 357 | assert.Nil(t, err) 358 | assert.Equal(t, int64(0), tableCount(t, db)) 359 | }) 360 | 361 | t.Run("with AllowLong", func(t *testing.T) { 362 | m := New(db, []*Migration{{ 363 | ID: "201608301430", 364 | Long: true, 365 | Migrate: func(tx *xorm.Engine) error { 366 | return tx.Sync2(&Pet{}) 367 | }, 368 | Rollback: func(tx *xorm.Engine) error { 369 | return tx.DropTables(&Pet{}) 370 | }, 371 | }}) 372 | m.AllowLong(true) 373 | err := m.Migrate() 374 | assert.Nil(t, err) 375 | assert.Equal(t, int64(1), tableCount(t, db)) 376 | }) 377 | }) 378 | } 379 | 380 | func TestMigrationSession(t *testing.T) { 381 | forEachDatabase(t, func(db *xorm.Engine) { 382 | m := New(db, migrationsSession) 383 | 384 | err := m.Migrate() 385 | assert.NoError(t, err) 386 | has, err := db.IsTableExist(&Person{}) 387 | assert.NoError(t, err) 388 | assert.True(t, has) 389 | has, err = db.IsTableExist(&Pet{}) 390 | assert.NoError(t, err) 391 | assert.True(t, has) 392 | assert.Equal(t, int64(2), tableCount(t, db)) 393 | 394 | err = m.RollbackLast() 395 | assert.NoError(t, err) 396 | has, err = db.IsTableExist(&Person{}) 397 | assert.NoError(t, err) 398 | assert.True(t, has) 399 | has, err = db.Exist(&Pet{}) 400 | assert.Error(t, err) 401 | assert.False(t, has) 402 | assert.Equal(t, int64(1), tableCount(t, db)) 403 | 404 | err = m.RollbackLast() 405 | assert.NoError(t, err) 406 | has, err = db.IsTableExist(&Person{}) 407 | assert.NoError(t, err) 408 | assert.False(t, has) 409 | has, err = db.IsTableExist(&Pet{}) 410 | assert.NoError(t, err) 411 | assert.False(t, has) 412 | assert.Equal(t, int64(0), tableCount(t, db)) 413 | }) 414 | } 415 | 416 | func tableCount(t *testing.T, db *xorm.Engine) (count int64) { 417 | count, err := db.Count(&Migration{}) 418 | assert.NoError(t, err) 419 | return 420 | } 421 | 422 | func forEachDatabase(t *testing.T, fn func(database *xorm.Engine)) { 423 | if len(databases) == 0 { 424 | panic("No database chosen for testing!") 425 | } 426 | 427 | for _, database := range databases { 428 | db, err := xorm.NewEngine(database.name, os.Getenv(database.connEnv)) 429 | assert.NoError(t, err, "Could not connect to database %s, %v", database.name, err) 430 | 431 | defer db.Close() 432 | 433 | // ensure tables do not exists 434 | assert.NoError(t, db.DropTables(&Migration{}, &Person{}, &Pet{}, &Book{})) 435 | 436 | fn(db) 437 | } 438 | } 439 | --------------------------------------------------------------------------------