Repository: amacneil/dbmate
Pull request #306 (2022-06-15 14:40:34)

Adds flyway to the comparison table
Resolves https://github.com/amacneil/dbmate/issues/249 for flyway

Some notes which could change the ticks:
- `Timestamp-versioned migration files` I checked this one because a developer can put the timestamp in their migration file name (this was the SOP for the team when I used flyway).  Timestamp versions is not done as default/automatically.  Additionally, flyway also has an `installed_on` column in their migration table.  So dates can be tracked multiple ways.
- `Automatically load .env file` Flyway automatically loads its config file, not sure if this is the same thing.
- `No separate configuration file` there is a config file, however a developer could choose to write a script using only environmental variables
- `Language/framework independent` can be Java dependent, but has a command-line option making it language and platform agnostic.


Patch:
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 6b8710a..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,1 +0,0 @@
-.git
diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md
new file mode 100644
index 0000000..46a6073
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug.md
@@ -0,0 +1,18 @@
+---
+name: "🐞 Bug report"
+about: Report broken functionality or incorrect documentation
+labels: "bug"
+---
+
+**Description**
+
+
+- Version:
+- Database:
+- Operating System:
+
+**Steps To Reproduce**
+<!--- Include the minimum possible code or example data to reproduce the problem. -->
+
+
+**Expected Behavior**
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..33d1f15
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: true
+contact_links:
+  - name: "🙋‍♀️ Question"
+    url: https://github.com/amacneil/dbmate/discussions
+    about: Search discussions or ask our community for help
+  - name: "🚀 Feature request"
+    url: https://github.com/amacneil/dbmate/discussions
+    about: Search existing feature requests or share a new idea
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 1230149..ca79ca5 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,6 +1,6 @@
 version: 2
 updates:
-  - package-ecosystem: "github-actions"
-    directory: "/"
+  - package-ecosystem: github-actions
+    directory: /
     schedule:
-      interval: "daily"
+      interval: weekly
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c0ea3b9..4d831fa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -68,6 +68,12 @@ jobs:
       - run: dist/dbmate-${{ matrix.os }}-${{ matrix.arch }} --help
         if: ${{ matrix.arch == 'amd64' }}
 
+      - name: Upload build artifacts
+        uses: actions/upload-artifact@v3
+        with:
+          name: ${{ matrix.os }}-${{ matrix.arch }}
+          path: dist/dbmate-*
+
       - name: Publish binaries
         uses: softprops/action-gh-release@v1
         if: ${{ startsWith(github.ref, 'refs/tags/v') }}
@@ -148,7 +154,7 @@ jobs:
             type=semver,pattern={{major}}
 
       - name: Publish Docker image
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
         with:
           context: .
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f544654..7762056 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -9,7 +9,7 @@ jobs:
     name: Bump Homebrew formula
     runs-on: ubuntu-latest
     steps:
-      - uses: mislav/bump-homebrew-formula-action@v1
+      - uses: mislav/bump-homebrew-formula-action@v2
         with:
           formula-name: dbmate
         env:
diff --git a/.golangci.yml b/.golangci.yml
index e985ffd..9519ffc 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,7 +1,6 @@
 linters:
   enable:
     - bodyclose
-    - deadcode
     - depguard
     - errcheck
     - goimports
@@ -13,12 +12,10 @@ linters:
     - revive
     - rowserrcheck
     - staticcheck
-    - structcheck
     - typecheck
     - unconvert
     - unparam
     - unused
-    - varcheck
     - whitespace
 
 linters-settings:
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index dbac111..23f8f76 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,4 +1,4 @@
 // -*- jsonc -*-
 {
-  "recommendations": ["esbenp.prettier-vscode"]
+  "recommendations": ["esbenp.prettier-vscode", "golang.go"]
 }
diff --git a/.vscode/settings.json b/.vscode/settings.json
index cff2122..88d9b26 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,5 +6,8 @@
   "files.insertFinalNewline": true,
   "files.trimFinalNewlines": true,
   "files.trimTrailingWhitespace": true,
-  "go.formatTool": "goimports"
+  "go.formatTool": "goimports",
+  "[go]": {
+    "editor.defaultFormatter": "golang.go"
+  }
 }
diff --git a/Dockerfile b/Dockerfile
index 01c1c7b..5c3f4c5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,7 @@
 # development image
-FROM golang:1.18 as dev
+FROM golang:1.19 as dev
 WORKDIR /src
+RUN git config --global --add safe.directory /src
 
 # install database clients
 RUN apt-get update \
@@ -14,7 +15,7 @@ RUN apt-get update \
 
 # golangci-lint
 RUN curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
-	| sh -s -- -b /usr/local/bin v1.45.2
+	| sh -s -- -b /usr/local/bin v1.50.1
 
 # download modules
 COPY go.* /src/
@@ -26,6 +27,7 @@ RUN make build
 FROM alpine as release
 RUN apk add --no-cache \
 	mariadb-client \
+	mariadb-connector-c \
 	postgresql-client \
 	sqlite \
 	tzdata
diff --git a/README.md b/README.md
index 050e1ab..ee25229 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
 # Dbmate
 
-[![GitHub Build](https://img.shields.io/github/workflow/status/amacneil/dbmate/CI/main)](https://github.com/amacneil/dbmate/actions?query=branch%3Amain+event%3Apush+workflow%3ACI)
-[![Go Report Card](https://goreportcard.com/badge/github.com/amacneil/dbmate)](https://goreportcard.com/report/github.com/amacneil/dbmate)
-[![GitHub Release](https://img.shields.io/github/release/amacneil/dbmate.svg)](https://github.com/amacneil/dbmate/releases)
+[![Version](https://img.shields.io/github/release/amacneil/dbmate.svg)](https://github.com/amacneil/dbmate/releases)
+[![Go Report](https://goreportcard.com/badge/github.com/amacneil/dbmate)](https://goreportcard.com/report/github.com/amacneil/dbmate)
 
 Dbmate is a database migration tool, to keep your database schema in sync across multiple developers and your production servers.
 
@@ -407,26 +406,24 @@ You can customize the name of this table using the `--migrations-table` flag or
 
 Why another database schema migration tool? Dbmate was inspired by many other tools, primarily [Active Record Migrations](http://guides.rubyonrails.org/active_record_migrations.html), with the goals of being trivial to configure, and language & framework independent. Here is a comparison between dbmate and other popular migration tools.
 
-|                                                              | [dbmate](https://github.com/amacneil/dbmate) | [goose](https://github.com/pressly/goose) | [sql-migrate](https://github.com/rubenv/sql-migrate) | [golang-migrate](https://github.com/golang-migrate/migrate) | [activerecord](http://guides.rubyonrails.org/active_record_migrations.html) | [sequelize](http://docs.sequelizejs.com/manual/tutorial/migrations.html) |
-| ------------------------------------------------------------ | :------------------------------------------: | :---------------------------------------: | :--------------------------------------------------: | :---------------------------------------------------------: | :-------------------------------------------------------------------------: | :----------------------------------------------------------------------: |
+|                                                              | [dbmate](https://github.com/amacneil/dbmate) | [goose](https://github.com/pressly/goose) | [sql-migrate](https://github.com/rubenv/sql-migrate) | [golang-migrate](https://github.com/golang-migrate/migrate) | [activerecord](http://guides.rubyonrails.org/active_record_migrations.html) | [sequelize](http://docs.sequelizejs.com/manual/tutorial/migrations.html) | [flyway](https://flywaydb.org/) |
+| ------------------------------------------------------------ | :------------------------------------------: | :---------------------------------------: | :--------------------------------------------------: | :---------------------------------------------------------: | :-------------------------------------------------------------------------: | :----------------------------------------------------------------------: | :-----------------------------: |
 | **Features**                                                 |
-| Plain SQL migration files                                    |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                                                                             |                                                                          |
-| Support for creating and dropping databases                  |              :white_check_mark:              |                                           |                                                      |                                                             |                             :white_check_mark:                              |                                                                          |
-| Support for saving schema dump files                         |              :white_check_mark:              |                                           |                                                      |                                                             |                             :white_check_mark:                              |                                                                          |
-| Timestamp-versioned migration files                          |              :white_check_mark:              |            :white_check_mark:             |                                                      |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |
-| Custom schema migrations table                               |              :white_check_mark:              |                                           |                  :white_check_mark:                  |                                                             |                                                                             |                            :white_check_mark:                            |
-| Ability to wait for database to become ready                 |              :white_check_mark:              |                                           |                                                      |                                                             |                                                                             |                                                                          |
-| Database connection string loaded from environment variables |              :white_check_mark:              |                                           |                                                      |                                                             |                                                                             |                                                                          |
-| Automatically load .env file                                 |              :white_check_mark:              |                                           |                                                      |                                                             |                                                                             |                                                                          |
-| No separate configuration file                               |              :white_check_mark:              |                                           |                                                      |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |
-| Language/framework independent                               |              :white_check_mark:              |        :eight_pointed_black_star:         |              :eight_pointed_black_star:              |                     :white_check_mark:                      |                                                                             |                                                                          |
+| Plain SQL migration files                                    |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                                                                             |                                                                          |       :white_check_mark:        |
+| Support for creating and dropping databases                  |              :white_check_mark:              |                                           |                                                      |                                                             |                             :white_check_mark:                              |                                                                          |                                 |
+| Support for saving schema dump files                         |              :white_check_mark:              |                                           |                                                      |                                                             |                             :white_check_mark:                              |                                                                          |                                 |
+| Timestamp-versioned migration files                          |              :white_check_mark:              |            :white_check_mark:             |                                                      |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |                                 |
+| Custom schema migrations table                               |              :white_check_mark:              |                                           |                  :white_check_mark:                  |                                                             |                                                                             |                            :white_check_mark:                            |       :white_check_mark:        |
+| Ability to wait for database to become ready                 |              :white_check_mark:              |                                           |                                                      |                                                             |                                                                             |                                                                          |                                 |
+| Database connection string loaded from environment variables |              :white_check_mark:              |                                           |                                                      |                                                             |                                                                             |                                                                          |       :white_check_mark:        |
+| Automatically load .env file                                 |              :white_check_mark:              |                                           |                                                      |                                                             |                                                                             |                                                                          |                                 |
+| No separate configuration file                               |              :white_check_mark:              |                                           |                                                      |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |                                 |
+| Language/framework independent                               |              :white_check_mark:              |            :white_check_mark:             |                                                      |                     :white_check_mark:                      |                                                                             |                                                                          |       :white_check_mark:        |
 | **Drivers**                                                  |
-| PostgreSQL                                                   |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |
-| MySQL                                                        |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |
-| SQLite                                                       |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |
-| CliсkHouse                                                   |              :white_check_mark:              |                                           |                                                      |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |
-
-> :eight_pointed_black_star: In theory these tools could be used with other languages, but a Go development environment is required because binary builds are not provided.
+| PostgreSQL                                                   |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |       :white_check_mark:        |
+| MySQL                                                        |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |       :white_check_mark:        |
+| SQLite                                                       |              :white_check_mark:              |            :white_check_mark:             |                  :white_check_mark:                  |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |       :white_check_mark:        |
+| CliсkHouse                                                   |              :white_check_mark:              |                                           |                                                      |                     :white_check_mark:                      |                             :white_check_mark:                              |                            :white_check_mark:                            |                                 |
 
 _If you notice any inaccuracies in this table, please [propose a change](https://github.com/amacneil/dbmate/edit/main/README.md)._
 
diff --git a/docker-compose.yml b/docker-compose.yml
index 3134144..e9a370f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -34,4 +34,4 @@ services:
       POSTGRES_PASSWORD: postgres
 
   clickhouse:
-    image: yandex/clickhouse-server:19.16
+    image: clickhouse/clickhouse-server:22.8
diff --git a/go.mod b/go.mod
index acb0de8..c7a1b67 100644
--- a/go.mod
+++ b/go.mod
@@ -1,23 +1,24 @@
 module github.com/amacneil/dbmate
 
-go 1.18
+go 1.19
 
 require (
 	github.com/ClickHouse/clickhouse-go v1.5.4
-	github.com/go-sql-driver/mysql v1.6.0
+	github.com/go-sql-driver/mysql v1.7.0
 	github.com/joho/godotenv v1.4.0
-	github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d
-	github.com/lib/pq v1.10.4
-	github.com/mattn/go-sqlite3 v1.14.12
-	github.com/stretchr/testify v1.7.0
-	github.com/urfave/cli/v2 v2.4.0
+	github.com/lib/pq v1.10.7
+	github.com/mattn/go-sqlite3 v1.14.16
+	github.com/stretchr/testify v1.8.1
+	github.com/urfave/cli/v2 v2.24.2
+	github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04
 )
 
 require (
 	github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
+	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
-	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
+	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 3d2f062..de2df82 100644
--- a/go.sum
+++ b/go.sum
@@ -1,29 +1,26 @@
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
 github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
 github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
 github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
 github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
 github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
-github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
+github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
 github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
 github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d h1:cVtBfNW5XTHiKQe7jDaDBSh/EVM4XLPutLAGboIXuM0=
-github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
-github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
+github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
-github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
-github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
+github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -31,14 +28,21 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I=
-github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/urfave/cli/v2 v2.24.2 h1:q1VA+ofZ8SWfEKB9xXHUD4QZaeI9e+ItEqSbfH2JBXk=
+github.com/urfave/cli/v2 v2.24.2/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
+github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w=
+github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04/go.mod h1:FiwNQxz6hGoNFBC4nIx+CxZhI3nne5RmIOlT/MXcSD4=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/main.go b/main.go
index 04a777f..b32c594 100644
--- a/main.go
+++ b/main.go
@@ -33,6 +33,7 @@ func NewApp() *cli.App {
 	app.Usage = "A lightweight, framework-independent database migration tool."
 	app.Version = dbmate.Version
 
+	defaultDB := dbmate.New(nil)
 	app.Flags = []cli.Flag{
 		&cli.StringFlag{
 			Name:    "url",
@@ -49,20 +50,20 @@ func NewApp() *cli.App {
 			Name:    "migrations-dir",
 			Aliases: []string{"d"},
 			EnvVars: []string{"DBMATE_MIGRATIONS_DIR"},
-			Value:   dbmate.DefaultMigrationsDir,
+			Value:   defaultDB.MigrationsDir,
 			Usage:   "specify the directory containing migration files",
 		},
 		&cli.StringFlag{
 			Name:    "migrations-table",
 			EnvVars: []string{"DBMATE_MIGRATIONS_TABLE"},
-			Value:   dbmate.DefaultMigrationsTableName,
+			Value:   defaultDB.MigrationsTableName,
 			Usage:   "specify the database table to record migrations in",
 		},
 		&cli.StringFlag{
 			Name:    "schema-file",
 			Aliases: []string{"s"},
 			EnvVars: []string{"DBMATE_SCHEMA_FILE"},
-			Value:   dbmate.DefaultSchemaFile,
+			Value:   defaultDB.SchemaFile,
 			Usage:   "specify the schema file location",
 		},
 		&cli.BoolFlag{
@@ -79,7 +80,7 @@ func NewApp() *cli.App {
 			Name:    "wait-timeout",
 			EnvVars: []string{"DBMATE_WAIT_TIMEOUT"},
 			Usage:   "timeout for --wait flag",
-			Value:   dbmate.DefaultWaitTimeout,
+			Value:   defaultDB.WaitTimeout,
 		},
 	}
 
@@ -231,9 +232,9 @@ func action(f func(*dbmate.DB, *cli.Context) error) cli.ActionFunc {
 		db.MigrationsTableName = c.String("migrations-table")
 		db.SchemaFile = c.String("schema-file")
 		db.WaitBefore = c.Bool("wait")
-		overrideTimeout := c.Duration("wait-timeout")
-		if overrideTimeout != 0 {
-			db.WaitTimeout = overrideTimeout
+		waitTimeout := c.Duration("wait-timeout")
+		if waitTimeout != 0 {
+			db.WaitTimeout = waitTimeout
 		}
 
 		return f(db, c)
diff --git a/pkg/dbmate/db.go b/pkg/dbmate/db.go
index 5c1c3a2..92ee4a8 100644
--- a/pkg/dbmate/db.go
+++ b/pkg/dbmate/db.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"net/url"
 	"os"
 	"path/filepath"
@@ -15,21 +16,6 @@ import (
 	"github.com/amacneil/dbmate/pkg/dbutil"
 )
 
-// DefaultMigrationsDir specifies default directory to find migration files
-const DefaultMigrationsDir = "./db/migrations"
-
-// DefaultMigrationsTableName specifies default database tables to record migraitons in
-const DefaultMigrationsTableName = "schema_migrations"
-
-// DefaultSchemaFile specifies default location for schema.sql
-const DefaultSchemaFile = "./db/schema.sql"
-
-// DefaultWaitInterval specifies length of time between connection attempts
-const DefaultWaitInterval = time.Second
-
-// DefaultWaitTimeout specifies maximum time for connection attempts
-const DefaultWaitTimeout = 60 * time.Second
-
 // Error codes
 var (
 	ErrNoMigrationFiles      = errors.New("no migration files found")
@@ -44,23 +30,35 @@ var (
 	ErrCreateDirectory       = errors.New("unable to create directory")
 )
 
+// migrationFileRegexp pattern for valid migration files
+var migrationFileRegexp = regexp.MustCompile(`^(\d+).*\.sql$`)
+
 // DB allows dbmate actions to be performed on a specified database
 type DB struct {
-	AutoDumpSchema      bool
-	DatabaseURL         *url.URL
-	MigrationsDir       string
+	// AutoDumpSchema generates schema.sql after each action
+	AutoDumpSchema bool
+	// DatabaseURL is the database connection string
+	DatabaseURL *url.URL
+	// FS allows overriding the filesystem
+	FS fs.FS
+	// Log is the interface to write stdout
+	Log io.Writer
+	// MigrationsDir specifies the directory to find migration files
+	MigrationsDir string
+	// MigrationsTableName specifies the database table to record migrations in
 	MigrationsTableName string
-	SchemaFile          string
-	Verbose             bool
-	WaitBefore          bool
-	WaitInterval        time.Duration
-	WaitTimeout         time.Duration
-	Log                 io.Writer
+	// SchemaFile specifies the location for schema.sql file
+	SchemaFile string
+	// Verbose prints the result of each statement execution
+	Verbose bool
+	// WaitBefore will wait for database to become available before running any actions
+	WaitBefore bool
+	// WaitInterval specifies length of time between connection attempts
+	WaitInterval time.Duration
+	// WaitTimeout specifies maximum time for connection attempts
+	WaitTimeout time.Duration
 }
 
-// migrationFileRegexp pattern for valid migration files
-var migrationFileRegexp = regexp.MustCompile(`^\d.*\.sql$`)
-
 // StatusResult represents an available migration status
 type StatusResult struct {
 	Filename string
@@ -72,18 +70,20 @@ func New(databaseURL *url.URL) *DB {
 	return &DB{
 		AutoDumpSchema:      true,
 		DatabaseURL:         databaseURL,
-		MigrationsDir:       DefaultMigrationsDir,
-		MigrationsTableName: DefaultMigrationsTableName,
-		SchemaFile:          DefaultSchemaFile,
-		WaitBefore:          false,
-		WaitInterval:        DefaultWaitInterval,
-		WaitTimeout:         DefaultWaitTimeout,
+		FS:                  os.DirFS("."),
 		Log:                 os.Stdout,
+		MigrationsDir:       "./db/migrations",
+		MigrationsTableName: "schema_migrations",
+		SchemaFile:          "./db/schema.sql",
+		Verbose:             false,
+		WaitBefore:          false,
+		WaitInterval:        time.Second,
+		WaitTimeout:         60 * time.Second,
 	}
 }
 
-// GetDriver initializes the appropriate database driver
-func (db *DB) GetDriver() (Driver, error) {
+// Driver initializes the appropriate database driver
+func (db *DB) Driver() (Driver, error) {
 	if db.DatabaseURL == nil || db.DatabaseURL.Scheme == "" {
 		return nil, ErrInvalidURL
 	}
@@ -95,22 +95,18 @@ func (db *DB) GetDriver() (Driver, error) {
 
 	config := DriverConfig{
 		DatabaseURL:         db.DatabaseURL,
-		MigrationsTableName: db.MigrationsTableName,
 		Log:                 db.Log,
+		MigrationsTableName: db.MigrationsTableName,
 	}
+	drv := driverFunc(config)
 
-	return driverFunc(config), nil
-}
-
-// Wait blocks until the database server is available. It does not verify that
-// the specified database exists, only that the host is ready to accept connections.
-func (db *DB) Wait() error {
-	drv, err := db.GetDriver()
-	if err != nil {
-		return err
+	if db.WaitBefore {
+		if err := db.wait(drv); err != nil {
+			return nil, err
+		}
 	}
 
-	return db.wait(drv)
+	return drv, nil
 }
 
 func (db *DB) wait(drv Driver) error {
@@ -140,18 +136,23 @@ func (db *DB) wait(drv Driver) error {
 	return fmt.Errorf("%w: %s", ErrCantConnect, err)
 }
 
-// CreateAndMigrate creates the database (if necessary) and runs migrations
-func (db *DB) CreateAndMigrate() error {
-	drv, err := db.GetDriver()
+// Wait blocks until the database server is available. It does not verify that
+// the specified database exists, only that the host is ready to accept connections.
+func (db *DB) Wait() error {
+	drv, err := db.Driver()
 	if err != nil {
 		return err
 	}
 
-	if db.WaitBefore {
-		err := db.wait(drv)
-		if err != nil {
-			return err
-		}
+	// if db.WaitBefore is true, wait() will get called twice, no harm
+	return db.wait(drv)
+}
+
+// CreateAndMigrate creates the database (if necessary) and runs migrations
+func (db *DB) CreateAndMigrate() error {
+	drv, err := db.Driver()
+	if err != nil {
+		return err
 	}
 
 	// create database if it does not already exist
@@ -165,61 +166,36 @@ func (db *DB) CreateAndMigrate() error {
 	}
 
 	// migrate
-	return db.migrate(drv)
+	return db.Migrate()
 }
 
 // Create creates the current database
 func (db *DB) Create() error {
-	drv, err := db.GetDriver()
+	drv, err := db.Driver()
 	if err != nil {
 		return err
 	}
 
-	if db.WaitBefore {
-		err := db.wait(drv)
-		if err != nil {
-			return err
-		}
-	}
-
 	return drv.CreateDatabase()
 }
 
 // Drop drops the current database (if it exists)
 func (db *DB) Drop() error {
-	drv, err := db.GetDriver()
+	drv, err := db.Driver()
 	if err != nil {
 		return err
 	}
 
-	if db.WaitBefore {
-		err := db.wait(drv)
-		if err != nil {
-			return err
-		}
-	}
-
 	return drv.DropDatabase()
 }
 
 // DumpSchema writes the current database schema to a file
 func (db *DB) DumpSchema() error {
-	drv, err := db.GetDriver()
+	drv, err := db.Driver()
 	if err != nil {
 		return err
 	}
 
-	return db.dumpSchema(drv)
-}
-
-func (db *DB) dumpSchema(drv Driver) error {
-	if db.WaitBefore {
-		err := db.wait(drv)
-		if err != nil {
-			return err
-		}
-	}
-
 	sqlDB, err := db.openDatabaseForMigration(drv)
 	if err != nil {
 		return err
@@ -319,59 +295,41 @@ func (db *DB) openDatabaseForMigration(drv Driver) (*sql.DB, error) {
 
 // Migrate migrates database to the latest version
 func (db *DB) Migrate() error {
-	drv, err := db.GetDriver()
+	drv, err := db.Driver()
 	if err != nil {
 		return err
 	}
 
-	return db.migrate(drv)
-}
-
-func (db *DB) migrate(drv Driver) error {
-	files, err := findMigrationFiles(db.MigrationsDir, migrationFileRegexp)
+	migrations, err := db.FindMigrations()
 	if err != nil {
 		return err
 	}
 
-	if len(files) == 0 {
+	if len(migrations) == 0 {
 		return ErrNoMigrationFiles
 	}
 
-	if db.WaitBefore {
-		err := db.wait(drv)
-		if err != nil {
-			return err
-		}
-	}
-
 	sqlDB, err := db.openDatabaseForMigration(drv)
 	if err != nil {
 		return err
 	}
 	defer dbutil.MustClose(sqlDB)
 
-	applied, err := drv.SelectMigrations(sqlDB, -1)
-	if err != nil {
-		return err
-	}
-
-	for _, filename := range files {
-		ver := migrationVersion(filename)
-		if ok := applied[ver]; ok {
-			// migration already applied
+	for _, migration := range migrations {
+		if migration.Applied {
 			continue
 		}
 
-		fmt.Fprintf(db.Log, "Applying: %s\n", filename)
+		fmt.Fprintf(db.Log, "Applying: %s\n", migration.FileName)
 
-		up, _, err := parseMigration(filepath.Join(db.MigrationsDir, filename))
+		parsed, err := migration.Parse()
 		if err != nil {
 			return err
 		}
 
 		execMigration := func(tx dbutil.Transaction) error {
 			// run actual migration
-			result, err := tx.Exec(up.Contents)
+			result, err := tx.Exec(parsed.Up)
 			if err != nil {
 				return err
 			} else if db.Verbose {
@@ -379,10 +337,10 @@ func (db *DB) migrate(drv Driver) error {
 			}
 
 			// record migration
-			return drv.InsertMigration(tx, ver)
+			return drv.InsertMigration(tx, migration.Version)
 		}
 
-		if up.Options.Transaction() {
+		if parsed.UpOptions.Transaction() {
 			// begin transaction
 			err = doTransaction(sqlDB, execMigration)
 		} else {
@@ -397,7 +355,7 @@ func (db *DB) migrate(drv Driver) error {
 
 	// automatically update schema file, silence errors
 	if db.AutoDumpSchema {
-		_ = db.dumpSchema(drv)
+		_ = db.DumpSchema()
 	}
 
 	return nil
@@ -414,104 +372,111 @@ func (db *DB) printVerbose(result sql.Result) {
 	}
 }
 
-func findMigrationFiles(dir string, re *regexp.Regexp) ([]string, error) {
-	files, err := os.ReadDir(dir)
+// FindMigrations lists all available migrations
+func (db *DB) FindMigrations() ([]Migration, error) {
+	drv, err := db.Driver()
+	if err != nil {
+		return nil, err
+	}
+
+	sqlDB, err := drv.Open()
+	if err != nil {
+		return nil, err
+	}
+	defer dbutil.MustClose(sqlDB)
+
+	// find applied migrations
+	appliedMigrations := map[string]bool{}
+	migrationsTableExists, err := drv.MigrationsTableExists(sqlDB)
+	if err != nil {
+		return nil, err
+	}
+
+	if migrationsTableExists {
+		appliedMigrations, err = drv.SelectMigrations(sqlDB, -1)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// find filesystem migrations
+	files, err := fs.ReadDir(db.FS, filepath.Clean(db.MigrationsDir))
 	if err != nil {
-		return nil, fmt.Errorf("%w `%s`", ErrMigrationDirNotFound, dir)
+		return nil, fmt.Errorf("%w `%s`", ErrMigrationDirNotFound, db.MigrationsDir)
 	}
 
-	matches := []string{}
+	migrations := []Migration{}
 	for _, file := range files {
 		if file.IsDir() {
 			continue
 		}
 
-		name := file.Name()
-		if !re.MatchString(name) {
+		matches := migrationFileRegexp.FindStringSubmatch(file.Name())
+		if len(matches) < 2 {
 			continue
 		}
 
-		matches = append(matches, name)
-	}
-
-	sort.Strings(matches)
-
-	return matches, nil
-}
-
-func findMigrationFile(dir string, ver string) (string, error) {
-	if ver == "" {
-		panic("migration version is required")
-	}
-
-	ver = regexp.QuoteMeta(ver)
-	re := regexp.MustCompile(fmt.Sprintf(`^%s.*\.sql$`, ver))
-
-	files, err := findMigrationFiles(dir, re)
-	if err != nil {
-		return "", err
-	}
+		migration := Migration{
+			Applied:  false,
+			FileName: matches[0],
+			FilePath: filepath.Join(db.MigrationsDir, matches[0]),
+			FS:       db.FS,
+			Version:  matches[1],
+		}
+		if ok := appliedMigrations[migration.Version]; ok {
+			migration.Applied = true
+		}
 
-	if len(files) == 0 {
-		return "", fmt.Errorf("%w: %s*.sql", ErrMigrationNotFound, ver)
+		migrations = append(migrations, migration)
 	}
 
-	return files[0], nil
-}
+	sort.Slice(migrations, func(i, j int) bool {
+		return migrations[i].FileName < migrations[j].FileName
+	})
 
-func migrationVersion(filename string) string {
-	return regexp.MustCompile(`^\d+`).FindString(filename)
+	return migrations, nil
 }
 
 // Rollback rolls back the most recent migration
 func (db *DB) Rollback() error {
-	drv, err := db.GetDriver()
+	drv, err := db.Driver()
 	if err != nil {
 		return err
 	}
 
-	if db.WaitBefore {
-		err := db.wait(drv)
-		if err != nil {
-			return err
-		}
-	}
-
 	sqlDB, err := db.openDatabaseForMigration(drv)
 	if err != nil {
 		return err
 	}
 	defer dbutil.MustClose(sqlDB)
 
-	applied, err := drv.SelectMigrations(sqlDB, 1)
+	// find last applied migration
+	var latest *Migration
+	migrations, err := db.FindMigrations()
 	if err != nil {
 		return err
 	}
 
-	// grab most recent applied migration (applied has len=1)
-	latest := ""
-	for ver := range applied {
-		latest = ver
-	}
-	if latest == "" {
-		return ErrNoRollback
+	for _, migration := range migrations {
+		if migration.Applied {
+			latest = &migration
+		}
 	}
 
-	filename, err := findMigrationFile(db.MigrationsDir, latest)
-	if err != nil {
-		return err
+	if latest == nil {
+		return ErrNoRollback
 	}
 
-	fmt.Fprintf(db.Log, "Rolling back: %s\n", filename)
+	fmt.Fprintf(db.Log, "Rolling back: %s\n", latest.FileName)
 
-	_, down, err := parseMigration(filepath.Join(db.MigrationsDir, filename))
+	parsed, err := latest.Parse()
 	if err != nil {
 		return err
 	}
 
 	execMigration := func(tx dbutil.Transaction) error {
 		// rollback migration
-		result, err := tx.Exec(down.Contents)
+		result, err := tx.Exec(parsed.Down)
 		if err != nil {
 			return err
 		} else if db.Verbose {
@@ -519,10 +484,10 @@ func (db *DB) Rollback() error {
 		}
 
 		// remove migration record
-		return drv.DeleteMigration(tx, latest)
+		return drv.DeleteMigration(tx, latest.Version)
 	}
 
-	if down.Options.Transaction() {
+	if parsed.DownOptions.Transaction() {
 		// begin transaction
 		err = doTransaction(sqlDB, execMigration)
 	} else {
@@ -536,7 +501,7 @@ func (db *DB) Rollback() error {
 
 	// automatically update schema file, silence errors
 	if db.AutoDumpSchema {
-		_ = db.dumpSchema(drv)
+		_ = db.DumpSchema()
 	}
 
 	return nil
@@ -544,12 +509,7 @@ func (db *DB) Rollback() error {
 
 // Status shows the status of all migrations
 func (db *DB) Status(quiet bool) (int, error) {
-	drv, err := db.GetDriver()
-	if err != nil {
-		return -1, err
-	}
-
-	results, err := db.CheckMigrationsStatus(drv)
+	results, err := db.FindMigrations()
 	if err != nil {
 		return -1, err
 	}
@@ -559,10 +519,10 @@ func (db *DB) Status(quiet bool) (int, error) {
 
 	for _, res := range results {
 		if res.Applied {
-			line = fmt.Sprintf("[X] %s", res.Filename)
+			line = fmt.Sprintf("[X] %s", res.FileName)
 			totalApplied++
 		} else {
-			line = fmt.Sprintf("[ ] %s", res.Filename)
+			line = fmt.Sprintf("[ ] %s", res.FileName)
 		}
 		if !quiet {
 			fmt.Fprintln(db.Log, line)
@@ -578,51 +538,3 @@ func (db *DB) Status(quiet bool) (int, error) {
 
 	return totalPending, nil
 }
-
-// CheckMigrationsStatus returns the status of all available mgirations
-func (db *DB) CheckMigrationsStatus(drv Driver) ([]StatusResult, error) {
-	files, err := findMigrationFiles(db.MigrationsDir, migrationFileRegexp)
-	if err != nil {
-		return nil, err
-	}
-
-	if len(files) == 0 {
-		return nil, ErrNoMigrationFiles
-	}
-
-	sqlDB, err := drv.Open()
-	if err != nil {
-		return nil, err
-	}
-	defer dbutil.MustClose(sqlDB)
-
-	applied := map[string]bool{}
-
-	migrationsTableExists, err := drv.MigrationsTableExists(sqlDB)
-	if err != nil {
-		return nil, err
-	}
-
-	if migrationsTableExists {
-		applied, err = drv.SelectMigrations(sqlDB, -1)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	var results []StatusResult
-
-	for _, filename := range files {
-		ver := migrationVersion(filename)
-		res := StatusResult{Filename: filename}
-		if ok := applied[ver]; ok {
-			res.Applied = true
-		} else {
-			res.Applied = false
-		}
-
-		results = append(results, res)
-	}
-
-	return results, nil
-}
diff --git a/pkg/dbmate/driver.go b/pkg/dbmate/driver.go
index 730a29b..4ad2b74 100644
--- a/pkg/dbmate/driver.go
+++ b/pkg/dbmate/driver.go
@@ -26,8 +26,8 @@ type Driver interface {
 // DriverConfig holds configuration passed to driver constructors
 type DriverConfig struct {
 	DatabaseURL         *url.URL
-	MigrationsTableName string
 	Log                 io.Writer
+	MigrationsTableName string
 }
 
 // DriverFunc represents a driver constructor
diff --git a/pkg/dbmate/migration.go b/pkg/dbmate/migration.go
index 7199daa..c2df7ea 100644
--- a/pkg/dbmate/migration.go
+++ b/pkg/dbmate/migration.go
@@ -2,43 +2,49 @@ package dbmate
 
 import (
 	"errors"
-	"os"
+	"io/fs"
 	"regexp"
 	"strings"
 )
 
-// MigrationOptions is an interface for accessing migration options
-type MigrationOptions interface {
-	Transaction() bool
+// Migration represents an available migration and status
+type Migration struct {
+	Applied  bool
+	FileName string
+	FilePath string
+	FS       fs.FS
+	Version  string
 }
 
-type migrationOptions map[string]string
+// Parse a migration
+func (m *Migration) Parse() (*ParsedMigration, error) {
+	bytes, err := fs.ReadFile(m.FS, m.FilePath)
+	if err != nil {
+		return nil, err
+	}
 
-// Transaction returns whether or not this migration should run in a transaction
-// Defaults to true.
-func (m migrationOptions) Transaction() bool {
-	return m["transaction"] != "false"
+	return parseMigrationContents(string(bytes))
 }
 
-// Migration contains the migration contents and options
-type Migration struct {
-	Contents string
-	Options  MigrationOptions
+// ParsedMigration contains the migration contents and options
+type ParsedMigration struct {
+	Up          string
+	UpOptions   ParsedMigrationOptions
+	Down        string
+	DownOptions ParsedMigrationOptions
 }
 
-// NewMigration constructs a Migration object
-func NewMigration() Migration {
-	return Migration{Contents: "", Options: make(migrationOptions)}
+// ParsedMigrationOptions is an interface for accessing migration options
+type ParsedMigrationOptions interface {
+	Transaction() bool
 }
 
-// parseMigration reads a migration file and returns (up Migration, down Migration, error)
-func parseMigration(path string) (Migration, Migration, error) {
-	data, err := os.ReadFile(path)
-	if err != nil {
-		return NewMigration(), NewMigration(), err
-	}
-	up, down, err := parseMigrationContents(string(data))
-	return up, down, err
+type migrationOptions map[string]string
+
+// Transaction returns whether or not this migration should run in a transaction
+// Defaults to true.
+func (m migrationOptions) Transaction() bool {
+	return m["transaction"] != "false"
 }
 
 var (
@@ -53,7 +59,7 @@ var (
 
 // Error codes
 var (
-	ErrParseMissingUp      = errors.New("dbmate requires each migration to define an up bock with '-- migrate:up'")
+	ErrParseMissingUp      = errors.New("dbmate requires each migration to define an up block with '-- migrate:up'")
 	ErrParseUnexpectedStmt = errors.New("dbmate does not support statements defined outside of the '-- migrate:up' or '-- migrate:down' blocks")
 )
 
@@ -62,17 +68,14 @@ var (
 // block and the second representing the "down" block. This function
 // requires that at least an up block was defined and will otherwise
 // return an error.
-func parseMigrationContents(contents string) (Migration, Migration, error) {
-	up := NewMigration()
-	down := NewMigration()
-
+func parseMigrationContents(contents string) (*ParsedMigration, error) {
 	upDirectiveStart, upDirectiveEnd, hasDefinedUpBlock := getMatchPositions(contents, upRegExp)
 	downDirectiveStart, downDirectiveEnd, hasDefinedDownBlock := getMatchPositions(contents, downRegExp)
 
 	if !hasDefinedUpBlock {
-		return up, down, ErrParseMissingUp
+		return nil, ErrParseMissingUp
 	} else if statementsPrecedeMigrateBlocks(contents, upDirectiveStart, downDirectiveStart) {
-		return up, down, ErrParseUnexpectedStmt
+		return nil, ErrParseUnexpectedStmt
 	}
 
 	upEnd := len(contents)
@@ -89,13 +92,13 @@ func parseMigrationContents(contents string) (Migration, Migration, error) {
 	upDirective := substring(contents, upDirectiveStart, upDirectiveEnd)
 	downDirective := substring(contents, downDirectiveStart, downDirectiveEnd)
 
-	up.Options = parseMigrationOptions(upDirective)
-	up.Contents = substring(contents, upDirectiveStart, upEnd)
-
-	down.Options = parseMigrationOptions(downDirective)
-	down.Contents = substring(contents, downDirectiveStart, downEnd)
-
-	return up, down, nil
+	parsed := ParsedMigration{
+		Up:          substring(contents, upDirectiveStart, upEnd),
+		UpOptions:   parseMigrationOptions(upDirective),
+		Down:        substring(contents, downDirectiveStart, downEnd),
+		DownOptions: parseMigrationOptions(downDirective),
+	}
+	return &parsed, nil
 }
 
 // parseMigrationOptions parses the migration options out of a block
@@ -103,10 +106,9 @@ func parseMigrationContents(contents string) (Migration, Migration, error) {
 //
 // For example:
 //
-//     fmt.Printf("%#v", parseMigrationOptions("-- migrate:up transaction:false"))
-//     // migrationOptions{"transaction": "false"}
-//
-func parseMigrationOptions(contents string) MigrationOptions {
+//	fmt.Printf("%#v", parseMigrationOptions("-- migrate:up transaction:false"))
+//	// migrationOptions{"transaction": "false"}
+func parseMigrationOptions(contents string) ParsedMigrationOptions {
 	options := make(migrationOptions)
 
 	// strip away the -- migrate:[up|down] part
@@ -154,7 +156,6 @@ func parseMigrationOptions(contents string) MigrationOptions {
 // -- migrate:up
 // create table users (id serial, status status_type);
 // `, 54, -1)
-//
 func statementsPrecedeMigrateBlocks(contents string, upDirectiveStart, downDirectiveStart int) bool {
 	until := upDirectiveStart
 
diff --git a/pkg/dbmate/version.go b/pkg/dbmate/version.go
index 7928b14..7f893aa 100644
--- a/pkg/dbmate/version.go
+++ b/pkg/dbmate/version.go
@@ -1,4 +1,4 @@
 package dbmate
 
 // Version of dbmate
-const Version = "1.15.0"
+const Version = "1.16.2"
diff --git a/pkg/driver/clickhouse/clickhouse.go b/pkg/driver/clickhouse/clickhouse.go
index 4bd8aab..c785002 100644
--- a/pkg/driver/clickhouse/clickhouse.go
+++ b/pkg/driver/clickhouse/clickhouse.go
@@ -143,7 +143,7 @@ func (drv *Driver) DropDatabase() error {
 func (drv *Driver) schemaDump(db *sql.DB, buf *bytes.Buffer, databaseName string) error {
 	buf.WriteString("\n--\n-- Database schema\n--\n\n")
 
-	buf.WriteString("CREATE DATABASE " + drv.quoteIdentifier(databaseName) + " IF NOT EXISTS;\n\n")
+	buf.WriteString("CREATE DATABASE IF NOT EXISTS" + drv.quoteIdentifier(databaseName) + ";\n\n")
 
 	tables, err := dbutil.QueryColumn(db, "show tables")
 	if err != nil {
@@ -233,7 +233,7 @@ func (drv *Driver) DatabaseExists() (bool, error) {
 // MigrationsTableExists checks if the schema_migrations table exists
 func (drv *Driver) MigrationsTableExists(db *sql.DB) (bool, error) {
 	exists := false
-	err := db.QueryRow("EXISTS TABLE $1", drv.quotedMigrationsTableName()).
+	err := db.QueryRow(fmt.Sprintf("EXISTS TABLE %s", drv.quotedMigrationsTableName())).
 		Scan(&exists)
 	if err == sql.ErrNoRows {
 		return false, nil
diff --git a/pkg/driver/mysql/mysql.go b/pkg/driver/mysql/mysql.go
index ab042c8..f495e90 100644
--- a/pkg/driver/mysql/mysql.go
+++ b/pkg/driver/mysql/mysql.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io"
 	"net/url"
+	"regexp"
 	"strings"
 
 	"github.com/amacneil/dbmate/pkg/dbmate"
@@ -192,7 +193,17 @@ func (drv *Driver) DumpSchema(db *sql.DB) ([]byte, error) {
 	}
 
 	schema = append(schema, migrations...)
-	return dbutil.TrimLeadingSQLComments(schema)
+	schema, err = dbutil.TrimLeadingSQLComments(schema)
+	if err != nil {
+		return nil, err
+	}
+	return trimAutoincrementValues(schema), nil
+}
+
+// trimAutoincrementValues removes AUTO_INCREMENT values from MySQL schema dumps
+func trimAutoincrementValues(data []byte) []byte {
+	aiPattern := regexp.MustCompile(" AUTO_INCREMENT=[0-9]*")
+	return aiPattern.ReplaceAll(data, []byte(""))
 }
 
 // DatabaseExists determines whether the database exists
diff --git a/pkg/driver/postgres/postgres.go b/pkg/driver/postgres/postgres.go
index 93dcf06..8002833 100644
--- a/pkg/driver/postgres/postgres.go
+++ b/pkg/driver/postgres/postgres.go
@@ -48,7 +48,7 @@ func connectionString(u *url.URL) string {
 	}
 
 	// default hostname
-	if hostname == "" {
+	if hostname == "" && query.Get("host") == "" {
 		switch runtime.GOOS {
 		case "linux":
 			query.Set("host", "/var/run/postgresql")
diff --git a/pkg/driver/sqlite/sqlite.go b/pkg/driver/sqlite/sqlite.go
index 0009710..d4150d3 100644
--- a/pkg/driver/sqlite/sqlite.go
+++ b/pkg/driver/sqlite/sqlite.go
@@ -131,7 +131,7 @@ func (drv *Driver) schemaMigrationsDump(db *sql.DB) ([]byte, error) {
 // DumpSchema returns the current database schema
 func (drv *Driver) DumpSchema(db *sql.DB) ([]byte, error) {
 	path := ConnectionString(drv.databaseURL)
-	schema, err := dbutil.RunCommand("sqlite3", path, ".schema")
+	schema, err := dbutil.RunCommand("sqlite3", path, ".schema --nosys")
 	if err != nil {
 		return nil, err
 	}


Test patch:
diff --git a/pkg/dbmate/db_test.go b/pkg/dbmate/db_test.go
index dcfb0d6..100558a 100644
--- a/pkg/dbmate/db_test.go
+++ b/pkg/dbmate/db_test.go
@@ -5,6 +5,7 @@ import (
 	"os"
 	"path/filepath"
 	"testing"
+	"testing/fstest"
 	"time"
 
 	"github.com/amacneil/dbmate/pkg/dbmate"
@@ -13,8 +14,8 @@ import (
 	_ "github.com/amacneil/dbmate/pkg/driver/postgres"
 	_ "github.com/amacneil/dbmate/pkg/driver/sqlite"
 
-	"github.com/kami-zh/go-capturer"
 	"github.com/stretchr/testify/require"
+	"github.com/zenizh/go-capturer"
 )
 
 var testdataDir string
@@ -52,21 +53,21 @@ func TestNew(t *testing.T) {
 func TestGetDriver(t *testing.T) {
 	t.Run("missing URL", func(t *testing.T) {
 		db := dbmate.New(nil)
-		drv, err := db.GetDriver()
+		drv, err := db.Driver()
 		require.Nil(t, drv)
 		require.EqualError(t, err, "invalid url, have you set your --url flag or DATABASE_URL environment variable?")
 	})
 
 	t.Run("missing schema", func(t *testing.T) {
 		db := dbmate.New(dbutil.MustParseURL("//hi"))
-		drv, err := db.GetDriver()
+		drv, err := db.Driver()
 		require.Nil(t, drv)
 		require.EqualError(t, err, "invalid url, have you set your --url flag or DATABASE_URL environment variable?")
 	})
 
 	t.Run("invalid driver", func(t *testing.T) {
 		db := dbmate.New(dbutil.MustParseURL("foo://bar"))
-		drv, err := db.GetDriver()
+		drv, err := db.Driver()
 		require.EqualError(t, err, "unsupported driver: foo")
 		require.Nil(t, drv)
 	})
@@ -260,7 +261,7 @@ func TestMigrate(t *testing.T) {
 	for _, u := range testURLs() {
 		t.Run(u.Scheme, func(t *testing.T) {
 			db := newTestDB(t, u)
-			drv, err := db.GetDriver()
+			drv, err := db.Driver()
 			require.NoError(t, err)
 
 			// drop and recreate database
@@ -295,7 +296,7 @@ func TestUp(t *testing.T) {
 	for _, u := range testURLs() {
 		t.Run(u.Scheme, func(t *testing.T) {
 			db := newTestDB(t, u)
-			drv, err := db.GetDriver()
+			drv, err := db.Driver()
 			require.NoError(t, err)
 
 			// drop database
@@ -328,14 +329,21 @@ func TestRollback(t *testing.T) {
 	for _, u := range testURLs() {
 		t.Run(u.Scheme, func(t *testing.T) {
 			db := newTestDB(t, u)
-			drv, err := db.GetDriver()
+			drv, err := db.Driver()
 			require.NoError(t, err)
 
-			// drop, recreate, and migrate database
+			// drop and create database
 			err = db.Drop()
 			require.NoError(t, err)
 			err = db.Create()
 			require.NoError(t, err)
+
+			// rollback should return error
+			err = db.Rollback()
+			require.Error(t, err)
+			require.ErrorContains(t, err, "can't rollback: no migrations have been applied")
+
+			// migrate database
 			err = db.Migrate()
 			require.NoError(t, err)
 
@@ -369,11 +377,11 @@ func TestRollback(t *testing.T) {
 	}
 }
 
-func TestStatus(t *testing.T) {
+func TestFindMigrations(t *testing.T) {
 	for _, u := range testURLs() {
 		t.Run(u.Scheme, func(t *testing.T) {
 			db := newTestDB(t, u)
-			drv, err := db.GetDriver()
+			drv, err := db.Driver()
 			require.NoError(t, err)
 
 			// drop, recreate, and migrate database
@@ -388,7 +396,7 @@ func TestStatus(t *testing.T) {
 			defer dbutil.MustClose(sqlDB)
 
 			// two pending
-			results, err := db.CheckMigrationsStatus(drv)
+			results, err := db.FindMigrations()
 			require.NoError(t, err)
 			require.Len(t, results, 2)
 			require.False(t, results[0].Applied)
@@ -402,7 +410,7 @@ func TestStatus(t *testing.T) {
 			require.NoError(t, err)
 
 			// two applied
-			results, err = db.CheckMigrationsStatus(drv)
+			results, err = db.FindMigrations()
 			require.NoError(t, err)
 			require.Len(t, results, 2)
 			require.True(t, results[0].Applied)
@@ -413,7 +421,7 @@ func TestStatus(t *testing.T) {
 			require.NoError(t, err)
 
 			// one applied, one pending
-			results, err = db.CheckMigrationsStatus(drv)
+			results, err = db.FindMigrations()
 			require.NoError(t, err)
 			require.Len(t, results, 2)
 			require.True(t, results[0].Applied)
@@ -421,3 +429,57 @@ func TestStatus(t *testing.T) {
 		})
 	}
 }
+
+func TestFindMigrationsFS(t *testing.T) {
+	mapFS := fstest.MapFS{
+		"db/migrations/20151129054053_test_migration.sql": {},
+		"db/migrations/001_test_migration.sql": {
+			Data: []byte(`-- migrate:up
+create table users (id serial, name text);
+-- migrate:down
+drop table users;
+`),
+		},
+		"db/migrations/002_test_migration.sql":                {},
+		"db/migrations/003_not_sql.txt":                       {},
+		"db/migrations/missing_version.sql":                   {},
+		"db/not_migrations/20151129054053_test_migration.sql": {},
+	}
+
+	u := dbutil.MustParseURL(os.Getenv("POSTGRES_TEST_URL"))
+	db := newTestDB(t, u)
+	db.FS = mapFS
+
+	// drop and recreate database
+	err := db.Drop()
+	require.NoError(t, err)
+	err = db.Create()
+	require.NoError(t, err)
+
+	actual, err := db.FindMigrations()
+	require.NoError(t, err)
+
+	// test migrations are correct and in order
+	require.Equal(t, "001_test_migration.sql", actual[0].FileName)
+	require.Equal(t, "db/migrations/001_test_migration.sql", actual[0].FilePath)
+	require.Equal(t, "001", actual[0].Version)
+	require.Equal(t, false, actual[0].Applied)
+
+	require.Equal(t, "002_test_migration.sql", actual[1].FileName)
+	require.Equal(t, "db/migrations/002_test_migration.sql", actual[1].FilePath)
+	require.Equal(t, "002", actual[1].Version)
+	require.Equal(t, false, actual[1].Applied)
+
+	require.Equal(t, "20151129054053_test_migration.sql", actual[2].FileName)
+	require.Equal(t, "db/migrations/20151129054053_test_migration.sql", actual[2].FilePath)
+	require.Equal(t, "20151129054053", actual[2].Version)
+	require.Equal(t, false, actual[2].Applied)
+
+	// test parsing first migration
+	parsed, err := actual[0].Parse()
+	require.Nil(t, err)
+	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n", parsed.Up)
+	require.True(t, parsed.UpOptions.Transaction())
+	require.Equal(t, "-- migrate:down\ndrop table users;\n", parsed.Down)
+	require.True(t, parsed.DownOptions.Transaction())
+}
diff --git a/pkg/dbmate/migration_test.go b/pkg/dbmate/migration_test.go
index e01d390..8e9d90d 100644
--- a/pkg/dbmate/migration_test.go
+++ b/pkg/dbmate/migration_test.go
@@ -2,10 +2,38 @@ package dbmate
 
 import (
 	"testing"
+	"testing/fstest"
 
 	"github.com/stretchr/testify/require"
 )
 
+func TestParse(t *testing.T) {
+	fs := fstest.MapFS{
+		"bar/123_foo.sql": {
+			Data: []byte(`-- migrate:up
+create table users (id serial, name text);
+-- migrate:down
+drop table users;
+`),
+		},
+	}
+
+	migration := &Migration{
+		Applied:  false,
+		FileName: "123_foo.sql",
+		FilePath: "bar/123_foo.sql",
+		FS:       fs,
+		Version:  "123",
+	}
+
+	parsed, err := migration.Parse()
+	require.Nil(t, err)
+	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n", parsed.Up)
+	require.True(t, parsed.UpOptions.Transaction())
+	require.Equal(t, "-- migrate:down\ndrop table users;\n", parsed.Down)
+	require.True(t, parsed.DownOptions.Transaction())
+}
+
 func TestParseMigrationContents(t *testing.T) {
 	// It supports the typical use case.
 	migration := `-- migrate:up
@@ -13,14 +41,14 @@ create table users (id serial, name text);
 -- migrate:down
 drop table users;`
 
-	up, down, err := parseMigrationContents(migration)
+	parsed, err := parseMigrationContents(migration)
 	require.Nil(t, err)
 
-	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n", up.Contents)
-	require.Equal(t, true, up.Options.Transaction())
+	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n", parsed.Up)
+	require.Equal(t, true, parsed.UpOptions.Transaction())
 
-	require.Equal(t, "-- migrate:down\ndrop table users;", down.Contents)
-	require.Equal(t, true, down.Options.Transaction())
+	require.Equal(t, "-- migrate:down\ndrop table users;", parsed.Down)
+	require.Equal(t, true, parsed.DownOptions.Transaction())
 
 	// It does not require space between the '--' and 'migrate'
 	migration = `
@@ -31,14 +59,14 @@ create table users (id serial, name text);
 drop table users;
 `
 
-	up, down, err = parseMigrationContents(migration)
+	parsed, err = parseMigrationContents(migration)
 	require.Nil(t, err)
 
-	require.Equal(t, "--migrate:up\ncreate table users (id serial, name text);\n\n", up.Contents)
-	require.Equal(t, true, up.Options.Transaction())
+	require.Equal(t, "--migrate:up\ncreate table users (id serial, name text);\n\n", parsed.Up)
+	require.Equal(t, true, parsed.UpOptions.Transaction())
 
-	require.Equal(t, "--migrate:down\ndrop table users;\n", down.Contents)
-	require.Equal(t, true, down.Options.Transaction())
+	require.Equal(t, "--migrate:down\ndrop table users;\n", parsed.Down)
+	require.Equal(t, true, parsed.DownOptions.Transaction())
 
 	// It is acceptable for down to be defined before up
 	migration = `-- migrate:down
@@ -47,14 +75,14 @@ drop table users;
 create table users (id serial, name text);
 `
 
-	up, down, err = parseMigrationContents(migration)
+	parsed, err = parseMigrationContents(migration)
 	require.Nil(t, err)
 
-	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n", up.Contents)
-	require.Equal(t, true, up.Options.Transaction())
+	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n", parsed.Up)
+	require.Equal(t, true, parsed.UpOptions.Transaction())
 
-	require.Equal(t, "-- migrate:down\ndrop table users;\n", down.Contents)
-	require.Equal(t, true, down.Options.Transaction())
+	require.Equal(t, "-- migrate:down\ndrop table users;\n", parsed.Down)
+	require.Equal(t, true, parsed.DownOptions.Transaction())
 
 	// It supports turning transactions off for a given migration block,
 	// e.g., the below would not work in Postgres inside a transaction.
@@ -63,23 +91,23 @@ create table users (id serial, name text);
 ALTER TYPE colors ADD VALUE 'orange' AFTER 'red';
 `
 
-	up, down, err = parseMigrationContents(migration)
+	parsed, err = parseMigrationContents(migration)
 	require.Nil(t, err)
 
-	require.Equal(t, "-- migrate:up transaction:false\nALTER TYPE colors ADD VALUE 'orange' AFTER 'red';\n", up.Contents)
-	require.Equal(t, false, up.Options.Transaction())
+	require.Equal(t, "-- migrate:up transaction:false\nALTER TYPE colors ADD VALUE 'orange' AFTER 'red';\n", parsed.Up)
+	require.Equal(t, false, parsed.UpOptions.Transaction())
 
-	require.Equal(t, "", down.Contents)
-	require.Equal(t, true, down.Options.Transaction())
+	require.Equal(t, "", parsed.Down)
+	require.Equal(t, true, parsed.DownOptions.Transaction())
 
 	// It does *not* support omitting the up block.
 	migration = `-- migrate:down
 drop table users;
 `
 
-	_, _, err = parseMigrationContents(migration)
+	_, err = parseMigrationContents(migration)
 	require.NotNil(t, err)
-	require.Equal(t, "dbmate requires each migration to define an up bock with '-- migrate:up'", err.Error())
+	require.Equal(t, "dbmate requires each migration to define an up block with '-- migrate:up'", err.Error())
 
 	// It allows leading comments and whitespace preceding the migrate blocks
 	migration = `
@@ -93,14 +121,14 @@ create table users (id serial, name text);
 drop table users;
 `
 
-	up, down, err = parseMigrationContents(migration)
+	parsed, err = parseMigrationContents(migration)
 	require.Nil(t, err)
 
-	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n\n", up.Contents)
-	require.Equal(t, true, up.Options.Transaction())
+	require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);\n\n", parsed.Up)
+	require.Equal(t, true, parsed.UpOptions.Transaction())
 
-	require.Equal(t, "-- migrate:down\ndrop table users;\n", down.Contents)
-	require.Equal(t, true, down.Options.Transaction())
+	require.Equal(t, "-- migrate:down\ndrop table users;\n", parsed.Down)
+	require.Equal(t, true, parsed.DownOptions.Transaction())
 
 	// It does *not* allow arbitrary statements preceding the migrate blocks
 	migration = `
@@ -116,7 +144,7 @@ ALTER TABLE users
 DROP COLUMN status;
 `
 
-	_, _, err = parseMigrationContents(migration)
+	_, err = parseMigrationContents(migration)
 	require.NotNil(t, err)
 	require.Equal(t, "dbmate does not support statements defined outside of the '-- migrate:up' or '-- migrate:down' blocks", err.Error())
 
@@ -126,7 +154,7 @@ ALTER TABLE users
 ADD COLUMN status status_type DEFAULT 'active';
 `
 
-	_, _, err = parseMigrationContents(migration)
+	_, err = parseMigrationContents(migration)
 	require.NotNil(t, err)
-	require.Equal(t, "dbmate requires each migration to define an up bock with '-- migrate:up'", err.Error())
+	require.Equal(t, "dbmate requires each migration to define an up block with '-- migrate:up'", err.Error())
 }
diff --git a/pkg/driver/clickhouse/clickhouse_test.go b/pkg/driver/clickhouse/clickhouse_test.go
index 6c85c58..28698d1 100644
--- a/pkg/driver/clickhouse/clickhouse_test.go
+++ b/pkg/driver/clickhouse/clickhouse_test.go
@@ -14,7 +14,7 @@ import (
 
 func testClickHouseDriver(t *testing.T) *Driver {
 	u := dbutil.MustParseURL(os.Getenv("CLICKHOUSE_TEST_URL"))
-	drv, err := dbmate.New(u).GetDriver()
+	drv, err := dbmate.New(u).Driver()
 	require.NoError(t, err)
 
 	return drv.(*Driver)
@@ -40,7 +40,7 @@ func prepTestClickHouseDB(t *testing.T) *sql.DB {
 
 func TestGetDriver(t *testing.T) {
 	db := dbmate.New(dbutil.MustParseURL("clickhouse://"))
-	drvInterface, err := db.GetDriver()
+	drvInterface, err := db.Driver()
 	require.NoError(t, err)
 
 	// driver should have URL and default migrations table set
@@ -180,7 +180,11 @@ func TestClickHouseDatabaseExists_Error(t *testing.T) {
 	drv.databaseURL.RawQuery = values.Encode()
 
 	exists, err := drv.DatabaseExists()
-	require.EqualError(t, err, "code: 192, message: Unknown user invalid")
+	require.EqualError(
+		t,
+		err,
+		"code: 516, message: invalid: Authentication failed: password is incorrect or there is no user with such name",
+	)
 	require.Equal(t, false, exists)
 }
 
@@ -193,7 +197,16 @@ func TestClickHouseCreateMigrationsTable(t *testing.T) {
 		// migrations table should not exist
 		count := 0
 		err := db.QueryRow("select count(*) from schema_migrations").Scan(&count)
-		require.EqualError(t, err, "code: 60, message: Table dbmate_test.schema_migrations doesn't exist.")
+		require.EqualError(
+			t,
+			err,
+			"code: 60, message: Table dbmate_test.schema_migrations doesn't exist",
+		)
+
+		// use driver function to check the same as above
+		exists, err := drv.MigrationsTableExists(db)
+		require.NoError(t, err)
+		require.Equal(t, false, exists)
 
 		// create table
 		err = drv.CreateMigrationsTable(db)
@@ -203,6 +216,11 @@ func TestClickHouseCreateMigrationsTable(t *testing.T) {
 		err = db.QueryRow("select count(*) from schema_migrations").Scan(&count)
 		require.NoError(t, err)
 
+		// use driver function to check the same as above
+		exists, err = drv.MigrationsTableExists(db)
+		require.NoError(t, err)
+		require.Equal(t, true, exists)
+
 		// create table should be idempotent
 		err = drv.CreateMigrationsTable(db)
 		require.NoError(t, err)
@@ -218,7 +236,16 @@ func TestClickHouseCreateMigrationsTable(t *testing.T) {
 		// migrations table should not exist
 		count := 0
 		err := db.QueryRow("select count(*) from \"testMigrations\"").Scan(&count)
-		require.EqualError(t, err, "code: 60, message: Table dbmate_test.testMigrations doesn't exist.")
+		require.EqualError(
+			t,
+			err,
+			"code: 60, message: Table dbmate_test.testMigrations doesn't exist",
+		)
+
+		// use driver function to check the same as above
+		exists, err := drv.MigrationsTableExists(db)
+		require.NoError(t, err)
+		require.Equal(t, false, exists)
 
 		// create table
 		err = drv.CreateMigrationsTable(db)
@@ -228,6 +255,11 @@ func TestClickHouseCreateMigrationsTable(t *testing.T) {
 		err = db.QueryRow("select count(*) from \"testMigrations\"").Scan(&count)
 		require.NoError(t, err)
 
+		// use driver function to check the same as above
+		exists, err = drv.MigrationsTableExists(db)
+		require.NoError(t, err)
+		require.Equal(t, true, exists)
+
 		// create table should be idempotent
 		err = drv.CreateMigrationsTable(db)
 		require.NoError(t, err)
diff --git a/pkg/driver/mysql/mysql_test.go b/pkg/driver/mysql/mysql_test.go
index 6bab279..70a3113 100644
--- a/pkg/driver/mysql/mysql_test.go
+++ b/pkg/driver/mysql/mysql_test.go
@@ -14,7 +14,7 @@ import (
 
 func testMySQLDriver(t *testing.T) *Driver {
 	u := dbutil.MustParseURL(os.Getenv("MYSQL_TEST_URL"))
-	drv, err := dbmate.New(u).GetDriver()
+	drv, err := dbmate.New(u).Driver()
 	require.NoError(t, err)
 
 	return drv.(*Driver)
@@ -40,7 +40,7 @@ func prepTestMySQLDB(t *testing.T) *sql.DB {
 
 func TestGetDriver(t *testing.T) {
 	db := dbmate.New(dbutil.MustParseURL("mysql://"))
-	drvInterface, err := db.GetDriver()
+	drvInterface, err := db.Driver()
 	require.NoError(t, err)
 
 	// driver should have URL and default migrations table set
@@ -183,6 +183,34 @@ func TestMySQLDumpSchema(t *testing.T) {
 	require.Contains(t, err.Error(), "Unknown database 'fakedb'")
 }
 
+func TestMySQLDumpSchemaContainsNoAutoIncrement(t *testing.T) {
+	drv := testMySQLDriver(t)
+
+	db := prepTestMySQLDB(t)
+	defer dbutil.MustClose(db)
+	err := drv.CreateMigrationsTable(db)
+	require.NoError(t, err)
+
+	// create table with AUTO_INCREMENT column
+	_, err = db.Exec(`create table foo_table (id int not null primary key auto_increment)`)
+	require.NoError(t, err)
+
+	// create a record
+	_, err = db.Exec(`insert into foo_table values ()`)
+	require.NoError(t, err)
+
+	// AUTO_INCREMENT should appear on the table definition
+	var tblName, tblCreate string
+	err = db.QueryRow(`show create table foo_table`).Scan(&tblName, &tblCreate)
+	require.NoError(t, err)
+	require.Contains(t, tblCreate, "AUTO_INCREMENT=")
+
+	// AUTO_INCREMENT should not appear in the dump
+	schema, err := drv.DumpSchema(db)
+	require.NoError(t, err)
+	require.NotContains(t, string(schema), "AUTO_INCREMENT=")
+}
+
 func TestMySQLDatabaseExists(t *testing.T) {
 	drv := testMySQLDriver(t)
 
diff --git a/pkg/driver/postgres/postgres_test.go b/pkg/driver/postgres/postgres_test.go
index 7705d6e..fd29f00 100644
--- a/pkg/driver/postgres/postgres_test.go
+++ b/pkg/driver/postgres/postgres_test.go
@@ -15,7 +15,7 @@ import (
 
 func testPostgresDriver(t *testing.T) *Driver {
 	u := dbutil.MustParseURL(os.Getenv("POSTGRES_TEST_URL"))
-	drv, err := dbmate.New(u).GetDriver()
+	drv, err := dbmate.New(u).Driver()
 	require.NoError(t, err)
 
 	return drv.(*Driver)
@@ -41,7 +41,7 @@ func prepTestPostgresDB(t *testing.T) *sql.DB {
 
 func TestGetDriver(t *testing.T) {
 	db := dbmate.New(dbutil.MustParseURL("postgres://"))
-	drvInterface, err := db.GetDriver()
+	drvInterface, err := db.Driver()
 	require.NoError(t, err)
 
 	// driver should have URL and default migrations table set
@@ -78,6 +78,8 @@ func TestConnectionString(t *testing.T) {
 		{"postgres://bob:secret@myhost:1234/foo?host=/var/run/postgresql", "postgres://bob:secret@:1234/foo?host=%2Fvar%2Frun%2Fpostgresql"},
 		{"postgres://bob:secret@localhost/foo?socket=/var/run/postgresql", "postgres://bob:secret@:5432/foo?host=%2Fvar%2Frun%2Fpostgresql"},
 		{"postgres:///foo?socket=/var/run/postgresql", "postgres://:5432/foo?host=%2Fvar%2Frun%2Fpostgresql"},
+		{"postgres://bob:secret@/foo?socket=/var/run/postgresql", "postgres://bob:secret@:5432/foo?host=%2Fvar%2Frun%2Fpostgresql"},
+		{"postgres://bob:secret@/foo?host=/var/run/postgresql", "postgres://bob:secret@:5432/foo?host=%2Fvar%2Frun%2Fpostgresql"},
 	}
 
 	for _, c := range cases {
diff --git a/pkg/driver/sqlite/sqlite_test.go b/pkg/driver/sqlite/sqlite_test.go
index 858957b..010373d 100644
--- a/pkg/driver/sqlite/sqlite_test.go
+++ b/pkg/driver/sqlite/sqlite_test.go
@@ -16,7 +16,7 @@ import (
 
 func testSQLiteDriver(t *testing.T) *Driver {
 	u := dbutil.MustParseURL(os.Getenv("SQLITE_TEST_URL"))
-	drv, err := dbmate.New(u).GetDriver()
+	drv, err := dbmate.New(u).Driver()
 	require.NoError(t, err)
 
 	return drv.(*Driver)
@@ -42,7 +42,7 @@ func prepTestSQLiteDB(t *testing.T) *sql.DB {
 
 func TestGetDriver(t *testing.T) {
 	db := dbmate.New(dbutil.MustParseURL("sqlite://"))
-	drvInterface, err := db.GetDriver()
+	drvInterface, err := db.Driver()
 	require.NoError(t, err)
 
 	// driver should have URL and default migrations table set
@@ -180,22 +180,29 @@ func TestSQLiteDumpSchema(t *testing.T) {
 	err = drv.InsertMigration(db, "abc2")
 	require.NoError(t, err)
 
+	// create a table that will trigger `sqlite_sequence` system table
+	_, err = db.Exec("CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT)")
+	require.NoError(t, err)
+
 	// DumpSchema should return schema
 	schema, err := drv.DumpSchema(db)
 	require.NoError(t, err)
+	require.Contains(t, string(schema), "CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT)")
 	require.Contains(t, string(schema), "CREATE TABLE IF NOT EXISTS \"test_migrations\"")
 	require.Contains(t, string(schema), ");\n-- Dbmate schema migrations\n"+
 		"INSERT INTO \"test_migrations\" (version) VALUES\n"+
 		"  ('abc1'),\n"+
 		"  ('abc2');\n")
 
+	// sqlite_* tables should not be present in the dump (.schema --nosys)
+	require.NotContains(t, string(schema), "sqlite_")
+
 	// DumpSchema should return error if command fails
 	drv.databaseURL = dbutil.MustParseURL(".")
 	schema, err = drv.DumpSchema(db)
 	require.Nil(t, schema)
 	require.Error(t, err)
-	require.EqualError(t, err, "Error: unable to open database \"/.\": "+
-		"unable to open database file")
+	require.EqualError(t, err, "Error: unable to open database \"/.\": unable to open database file")
 }
 
 func TestSQLiteDatabaseExists(t *testing.T) {