Showing preview only (708K chars total). Download the full file or copy to clipboard to get everything.
Repository: pressly/goose
Branch: main
Commit: e7bd535b62f2
Files: 268
Total size: 642.6 KB
Directory structure:
gitextract_en9629pt/
├── .github/
│ ├── dependabot.yaml
│ └── workflows/
│ ├── ci.yaml
│ ├── integration.yaml
│ ├── lint.yaml
│ └── release.yaml
├── .gitignore
├── .golangci.yaml
├── .goreleaser.yaml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── cmd/
│ └── goose/
│ ├── driver_clickhouse.go
│ ├── driver_mssql.go
│ ├── driver_mysql.go
│ ├── driver_no_mysql.go
│ ├── driver_postgres.go
│ ├── driver_sqlite3.go
│ ├── driver_turso.go
│ ├── driver_vertica.go
│ ├── driver_ydb.go
│ ├── main.go
│ └── main_test.go
├── create.go
├── create_test.go
├── database/
│ ├── dialect/
│ │ ├── querier.go
│ │ └── querier_extended.go
│ ├── dialects.go
│ ├── doc.go
│ ├── sql_extended.go
│ ├── store.go
│ ├── store_extended.go
│ └── store_test.go
├── db.go
├── dialect.go
├── down.go
├── examples/
│ ├── README.md
│ ├── go-migrations/
│ │ ├── 00001_create_users_table.sql
│ │ ├── 00002_rename_root.go
│ │ ├── 00003_add_user_no_tx.go
│ │ ├── README.md
│ │ └── main.go
│ └── sql-migrations/
│ ├── 00001_create_users_table.sql
│ ├── 00002_rename_root.sql
│ ├── 00003_no_transaction.sql
│ └── README.md
├── fix.go
├── fix_test.go
├── globals.go
├── globals_test.go
├── go.mod
├── go.sum
├── goose.go
├── goose_cli_test.go
├── goose_embed_test.go
├── helpers.go
├── helpers_test.go
├── install.sh
├── internal/
│ ├── controller/
│ │ └── store.go
│ ├── dialects/
│ │ ├── clickhouse.go
│ │ ├── dsql.go
│ │ ├── mysql.go
│ │ ├── postgres.go
│ │ ├── redshift.go
│ │ ├── spanner.go
│ │ ├── sqlite3.go
│ │ ├── sqlserver.go
│ │ ├── starrocks.go
│ │ ├── tidb.go
│ │ ├── turso.go
│ │ ├── vertica.go
│ │ └── ydb.go
│ ├── gooseutil/
│ │ ├── resolve.go
│ │ └── resolve_test.go
│ ├── legacystore/
│ │ └── legacystore.go
│ ├── migrationstats/
│ │ ├── migration_go.go
│ │ ├── migration_sql.go
│ │ ├── migrationstats.go
│ │ ├── migrationstats_test.go
│ │ └── migrationstats_walker.go
│ ├── sqlparser/
│ │ ├── parse.go
│ │ ├── parse_test.go
│ │ ├── parser.go
│ │ ├── parser_test.go
│ │ └── testdata/
│ │ ├── envsub/
│ │ │ ├── test01/
│ │ │ │ ├── 01.down.golden.sql
│ │ │ │ ├── 01.up.golden.sql
│ │ │ │ ├── 02.up.golden.sql
│ │ │ │ ├── 03.up.golden.sql
│ │ │ │ ├── 04.up.golden.sql
│ │ │ │ └── input.sql
│ │ │ ├── test02/
│ │ │ │ ├── 01.up.golden.sql
│ │ │ │ ├── 02.up.golden.sql
│ │ │ │ ├── 03.up.golden.sql
│ │ │ │ └── input.sql
│ │ │ └── test03/
│ │ │ ├── 01.up.golden.sql
│ │ │ └── input.sql
│ │ ├── invalid/
│ │ │ └── up/
│ │ │ ├── a.sql
│ │ │ ├── b.sql
│ │ │ ├── c.sql
│ │ │ └── d.sql
│ │ ├── valid-txn/
│ │ │ ├── 00001_create_users_table.sql
│ │ │ ├── 00002_rename_root.sql
│ │ │ └── 00003_no_transaction.sql
│ │ └── valid-up/
│ │ ├── test01/
│ │ │ ├── 01.up.golden.sql
│ │ │ ├── 02.up.golden.sql
│ │ │ ├── 03.up.golden.sql
│ │ │ └── input.sql
│ │ ├── test02/
│ │ │ ├── 01.up.golden.sql
│ │ │ └── input.sql
│ │ ├── test03/
│ │ │ ├── 01.up.golden.sql
│ │ │ └── input.sql
│ │ ├── test04/
│ │ │ ├── 01.up.golden.sql
│ │ │ ├── 02.up.golden.sql
│ │ │ ├── 03.up.golden.sql
│ │ │ └── input.sql
│ │ ├── test05/
│ │ │ ├── 01.up.golden.sql
│ │ │ ├── 02.up.golden.sql
│ │ │ └── input.sql
│ │ ├── test06/
│ │ │ ├── 01.up.golden.sql
│ │ │ ├── 02.up.golden.sql
│ │ │ ├── 03.up.golden.sql
│ │ │ ├── 04.up.golden.sql
│ │ │ ├── 05.up.golden.sql
│ │ │ └── input.sql
│ │ ├── test07/
│ │ │ ├── 01.up.golden.sql
│ │ │ └── input.sql
│ │ ├── test08/
│ │ │ ├── 01.up.golden.sql
│ │ │ ├── 02.up.golden.sql
│ │ │ ├── 03.up.golden.sql
│ │ │ ├── 04.up.golden.sql
│ │ │ ├── 05.up.golden.sql
│ │ │ ├── 06.up.golden.sql
│ │ │ └── input.sql
│ │ └── test09/
│ │ ├── 01.up.golden.sql
│ │ └── input.sql
│ └── testing/
│ ├── go.mod
│ ├── go.sum
│ ├── integration/
│ │ ├── README.md
│ │ ├── database_test.go
│ │ ├── integration.go
│ │ ├── locking/
│ │ │ ├── postgres_locking_test.go
│ │ │ └── postgres_table_locking_test.go
│ │ └── testdata/
│ │ └── migrations/
│ │ ├── clickhouse/
│ │ │ ├── 00001_a.sql
│ │ │ ├── 00002_b.sql
│ │ │ └── 00003_c.sql
│ │ ├── clickhouse-remote/
│ │ │ └── 00001_a.sql
│ │ ├── clickhouse-remote-backup/
│ │ │ └── taxi_zone_lookup.csv
│ │ ├── mysql/
│ │ │ ├── 00001_table.sql
│ │ │ ├── 00002_insert.sql
│ │ │ ├── 00003_alter.sql
│ │ │ ├── 00004_empty.sql
│ │ │ ├── 00005_no_tx.sql
│ │ │ └── 00006_complex.sql
│ │ ├── postgres/
│ │ │ ├── 00001_table.sql
│ │ │ ├── 00002_insert.sql
│ │ │ ├── 00003_alter.sql
│ │ │ ├── 00004_empty.sql
│ │ │ ├── 00005_no_tx.sql
│ │ │ └── 00006_complex.sql
│ │ ├── spanner/
│ │ │ ├── 00001_table.sql
│ │ │ ├── 00002_insert.sql
│ │ │ ├── 00003_alter.sql
│ │ │ ├── 00004_empty.sql
│ │ │ ├── 00005_no_tx.sql
│ │ │ └── 00006_view.sql
│ │ ├── starrocks/
│ │ │ ├── 00001_a.sql
│ │ │ ├── 00002_b.sql
│ │ │ └── 00003_c.sql
│ │ ├── turso/
│ │ │ ├── 00001_table.sql
│ │ │ ├── 00002_insert.sql
│ │ │ ├── 00003_alter.sql
│ │ │ ├── 00004_empty.sql
│ │ │ └── 00005_no_tx.sql
│ │ └── ydb/
│ │ ├── 00001_a.sql
│ │ ├── 00002_b.sql
│ │ ├── 00003_c.sql
│ │ ├── 00004_d.sql
│ │ ├── 00005_e.sql
│ │ ├── 00006_f.sql
│ │ ├── 00007_g.sql
│ │ └── 00008_h.sql
│ └── testdb/
│ ├── clickhouse.go
│ ├── container_healthcheck.go
│ ├── mariadb.go
│ ├── options.go
│ ├── postgres.go
│ ├── spanner.go
│ ├── starrocks.go
│ ├── testdb.go
│ ├── turso.go
│ └── ydb.go
├── lock/
│ ├── internal/
│ │ ├── store/
│ │ │ ├── postgres.go
│ │ │ └── store.go
│ │ └── table/
│ │ ├── config.go
│ │ └── locker.go
│ ├── locker.go
│ ├── locktesting/
│ │ └── locktesting.go
│ ├── postgres.go
│ ├── session_locker_options.go
│ ├── table_locker_options.go
│ └── table_locker_options_test.go
├── log.go
├── migrate.go
├── migrate_test.go
├── migration.go
├── migration_sql.go
├── osfs.go
├── pkg/
│ └── dockermanage/
│ ├── doc.go
│ ├── dockerpostgres/
│ │ ├── postgres.go
│ │ └── postgres_test.go
│ ├── manager.go
│ └── options.go
├── provider.go
├── provider_collect.go
├── provider_collect_test.go
├── provider_errors.go
├── provider_options.go
├── provider_options_test.go
├── provider_run.go
├── provider_run_test.go
├── provider_test.go
├── provider_types.go
├── redo.go
├── register.go
├── reset.go
├── scripts/
│ └── release-notes.sh
├── status.go
├── testdata/
│ ├── migrations/
│ │ ├── 00001_users_table.sql
│ │ ├── 00002_posts_table.sql
│ │ ├── 00003_comments_table.sql
│ │ ├── 00004_insert_data.sql
│ │ └── 00005_posts_view.sql
│ ├── no-versioning/
│ │ ├── migrations/
│ │ │ ├── 00001_a.sql
│ │ │ ├── 00002_b.sql
│ │ │ └── 00003_c.sql
│ │ └── seed/
│ │ ├── 00001_a.sql
│ │ └── 00002_b.sql
│ └── testdata.go
├── tests/
│ └── gomigrations/
│ ├── error/
│ │ ├── gomigrations_error_test.go
│ │ └── testdata/
│ │ ├── 001_up_no_tx.go
│ │ ├── 002_ERROR_insert_no_tx.go
│ │ ├── 003_truncate.go
│ │ └── 004_ERROR_insert.go
│ ├── register/
│ │ ├── register_test.go
│ │ └── testdata/
│ │ ├── 001_addmigration.go
│ │ ├── 002_addmigrationnotx.go
│ │ ├── 003_addmigrationcontext.go
│ │ └── 004_addmigrationnotxcontext.go
│ └── success/
│ ├── gomigrations_success_test.go
│ └── testdata/
│ ├── 001_up_down.go
│ ├── 002_up_only.go
│ ├── 003_down_only.go
│ ├── 004_empty.go
│ ├── 005_up_down_no_tx.go
│ ├── 006_up_only_no_tx.go
│ ├── 007_down_only_no_tx.go
│ ├── 008_empty_no_tx.go
│ ├── 009_up_down_ctx.go
│ ├── 010_up_only_ctx.go
│ ├── 011_down_only_ctx.go
│ ├── 012_empty_ctx.go
│ ├── 013_up_down_no_tx_ctx.go
│ ├── 014_up_only_no_tx_ctx.go
│ ├── 015_down_only_no_tx_ctx.go
│ └── 016_empty_no_tx_ctx.go
├── up.go
├── up_test.go
└── version.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yaml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "saturday"
assignees:
- "mfridman"
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
day: "saturday"
groups:
gomod:
patterns:
- "*"
assignees:
- "mfridman"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
================================================
FILE: .github/workflows/ci.yaml
================================================
name: Goose CI
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Run unit tests
timeout-minutes: 10
strategy:
matrix:
go-version: [oldstable, stable]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go-version }}
- name: Check Go code formatting
run: |
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
gofmt -s -l .
echo "Please format Go code by running: go fmt ./..."
exit 1
fi
- name: Install tparse
run: |
mkdir -p $HOME/.local/bin
curl -L -o $HOME/.local/bin/tparse https://github.com/mfridman/tparse/releases/latest/download/tparse_linux_x86_64
chmod +x $HOME/.local/bin/tparse
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Run tests
run: |
make add-gowork
mkdir -p bin
go vet ./...
go build ./...
make test-packages
- name: Install GoReleaser
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.go-version == 'stable'
uses: goreleaser/goreleaser-action@v7
with:
install-only: true
distribution: goreleaser
version: "~> v2"
- name: Gorelease dry-run
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.go-version == 'stable'
run: |
goreleaser release --skip=publish --snapshot --fail-fast --clean
================================================
FILE: .github/workflows/integration.yaml
================================================
name: Goose integration tests
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Run integration tests
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: "stable"
- name: Install tparse
run: |
mkdir -p $HOME/.local/bin
curl -L -o $HOME/.local/bin/tparse https://github.com/mfridman/tparse/releases/latest/download/tparse_linux_x86_64
chmod +x $HOME/.local/bin/tparse
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Run full integration tests
run: |
make test-integration
================================================
FILE: .github/workflows/lint.yaml
================================================
name: golangci
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: "stable"
- run: make add-gowork
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: latest
github-token: ${{ secrets.GITHUB_TOKEN }}
args: --timeout=2m --verbose
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
# Optional: if set to true then the all caching functionality will be complete disabled,
# takes precedence over all other caching options.
# skip-cache: true
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true
================================================
FILE: .github/workflows/release.yaml
================================================
name: goreleaser
on:
push:
tags:
- '*'
permissions:
contents: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: actions/setup-go@v6
with:
go-version: stable
- name: Generate release notes
continue-on-error: true
run: ./scripts/release-notes.sh ${{github.ref_name}} > ${{runner.temp}}/release_notes.txt
- run: make add-gowork
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser
version: "~> v2"
args: release --clean --release-notes=${{runner.temp}}/release_notes.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
.idea
.vscode
.DS_Store
*.swp
*.test
# Files output by tests
/bin
# Coverage files
coverage.out
coverage.html
# Local testing
.envrc
*.FAIL
dist/
release_notes.txt
go.work
go.work.sum
================================================
FILE: .golangci.yaml
================================================
version: "2"
linters:
default: none
enable:
- errcheck
- govet
- ineffassign
- misspell
- staticcheck
- testifylint
- unused
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
================================================
FILE: .goreleaser.yaml
================================================
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
#
# See https://goreleaser.com/customization/ for more information.
version: 2
project_name: goose
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
binary: goose
main: ./cmd/goose
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
ldflags:
# The v prefix is stripped by goreleaser, so we need to add it back.
# https://goreleaser.com/customization/templates/#fnref:version-prefix
- "-s -w -X main.version=v{{ .Version }}"
archives:
- formats:
- binary
name_template: >-
{{ .ProjectName }}_{{- tolower .Os }}_{{- if eq .Arch "amd64" }}x86_64{{- else }}{{ .Arch }}{{ end }}
checksum:
name_template: "checksums.txt"
snapshot:
version_template: "{{ incpatch .Version }}-next"
changelog:
use: github-native
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [v3.27.0] - 2026-02-22
### Added
- Preliminary Spanner dialect support (#966)
### Changed
- **Minimum Go version is now 1.25**
- SQL migration templates no longer include `StatementBegin` and `StatementEnd` annotations. These
are only needed for complex statements containing semicolons (e.g., stored procedures). See
[docs](https://pressly.github.io/goose/documentation/annotations/#complex-statements) for details.
- Various dependency upgrades
## [v3.26.0] - 2025-10-03
- Add `*slog.Logger` support to goose provider via option `WithSlog` (#989)
- Add convenience `WithTableName` provider option (#985)
- Minor bug fixes and dependency upgrades
- Add general purpose `Locker` interface to support DB locking with a table-based Postgres
implementation via `lock.NewPostgresTableLocker` (#993 for more details)
- Unlike `SessionLocker`, this uses the `*sql.DB` connection pool
- Add `WithLocker` option to goose provider
## [v3.25.0] - 2025-08-24
- Upgrade go deps (#976)
- Remove references/tests for vertica and add deprecation warnings (#978)
- Add Aurora DSQL as a new database dialect to goose `Provider` (#971)
- Add DDL isolation support for Aurora DSQL compatibility (#970)
- Update Apply to respect no versioning option (#950)
- Expose dialect `Querier` (#939)
## [v3.24.3]
- Add `GOOSE_TABLE` environment variable -- lower priority than `-table` flag, but higher than the
default table name. (#932)
- Dependency updates
## [v3.24.2]
- Add `TableExists` table existence check for the mysql dialect (#895)
- Upgrade **minimum Go version to 1.23**
- Various dependency updates
## [v3.24.1]
- Fix regression (`v3.23.1` and `v3.24.0`) in postgres migration table existence check for
non-default schema. (#882, #883, #884).
## [v3.24.0]
- Add support for loading environment variables from `.env` files, enabled by default.
- The default file name is `.env`, but can be changed with the `-env=<filename>` flag.
- To disable this feature, set `-env=none`.
## [v3.23.1]
- Store implementations can **optionally** implement the `TableExists` method to provide optimized
table existence checks (#860)
- Default postgres Store implementation updated to use `pg_tables` system catalog, more to follow
- Backward compatible change - existing implementations will continue to work without modification
```go
TableExists(ctx context.Context, db database.DBTxConn) (bool, error)
```
## [v3.23.0]
- Add `WithLogger` to `NewProvider` to allow custom loggers (#833)
- Update Provider `WithVerbose` behavior to log all SQL statements (#851)
- Upgrade dependencies and rebuild binaries with latest Go version (`go1.23.3`)
## [v3.22.1]
- Upgrade dependencies and rebuild binaries with latest Go version (`go1.23.1`)
## [v3.22.0]
- Minimum Go version is now 1.21
- Add Unwrap to PartialError (#815)
- Allow flags anywhere on the CLI (#814)
`goose` uses the default Go `flag` parsing library, which means flags **must** be defined before the
first positional argument. We've updated this behavior to allow flags to be defined anywhere. For
more details, see [blog post](https://mfridman.com/blog/2024/allowing-flags-anywhere-on-the-cli/).
- Update `WithDisableGlobalRegistry` behavior (#783). When set, this will ignore globally-registered
migrationse entirely instead of the previous behavior of raising an error. Specifically, the
following check is removed:
```go
if len(global) > 0 {
return nil, errors.New("global registry disabled, but provider has registered go migrations")
}
```
This enables creating isolated goose provider(s) in legacy environments where global migrations may
be registered. Without updating this behavior, it would be impossible to use
`WithDisableGlobalRegistry` in combination with provider-scoped `WithGoMigrations`.
- Postgres, updated schema to use identity instead of serial and make `tstamp` not nullable (#556)
```diff
- id serial NOT NULL,
+ id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
- tstamp timestamp NULL default now(),
+ tstamp timestamp NOT NULL DEFAULT now()
```
- MySQL, updated schema to not use SERIAL alias (#816)
```diff
- id serial NOT NULL,
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
```
## [v3.21.1]
- Add `GetVersions` method to `goose.Provider`, returns the current (max db) version and the latest
(max filesystem) version. (#756)
- Clarify `GetLatestVersion` method MUST return `ErrVersionNotFound` if no latest migration is
found. Previously it was returning a -1 and nil error, which was inconsistent with the rest of the
API surface.
- Add `GetLatestVersion` implementations to all existing dialects. This is an optimization to avoid
loading all migrations when only the latest version is needed. This uses the `max` function in SQL
to get the latest version_id irrespective of the order of applied migrations.
- Refactor existing portions of the code to use the new `GetLatestVersion` method.
## [v3.21.0]
- Retracted. Broken release, please use v3.21.1 instead.
## [v3.20.0]
- Expand the `Store` interface by adding a `GetLatestVersion` method and make the interface public.
- Add a (non-blocking) method to check if there are pending migrations to the `goose.Provider`
(#751):
```go
func (p *Provider) HasPending(context.Context) (bool, error) {}
```
The underlying implementation **does not respect the `SessionLocker`** (if one is enabled) and can
be used to check for pending migrations without blocking or being blocked by other operations.
- The methods `.Up`, `.UpByOne`, and `.UpTo` from `goose.Provider` will invoke `.HasPending` before
acquiring a lock with `SessionLocker` (if enabled). This addresses an edge case in
Kubernetes-style deployments where newer pods with long-running migrations prevent older pods -
which have all known migrations applied - from starting up due to an advisory lock. For more
details, refer to https://github.com/pressly/goose/pull/507#discussion_r1266498077 and #751.
- Move integration tests to `./internal/testing` and make it a separate Go module. This will allow
us to have a cleaner top-level go.mod file and avoid imports unrelated to the goose project. See
[integration/README.md](https://github.com/pressly/goose/blob/d0641b5bfb3bd5d38d95fe7a63d7ddf2d282234d/internal/testing/integration/README.md)
for more details. This shouldn't affect users of the goose library.
## [v3.19.2] - 2024-03-13
- Remove duckdb support. The driver uses Cgo and we've decided to remove it until we can find a
better solution. If you were using duckdb with goose, please let us know by opening an issue.
## [v3.19.1] - 2024-03-11
- Fix selecting dialect for `redshift`
- Add `GOOSE_MIGRATION_DIR` documentation
- Bump github.com/opencontainers/runc to `v1.1.12` (security fix)
- Update CI tests for go1.22
- Make goose annotations case-insensitive
- All `-- +goose` annotations are now case-insensitive. This means that `-- +goose Up` and `--
+goose up` are now equivalent. This change was made to improve the user experience and to make the
annotations more consistent.
## [v3.19.0] - 2024-03-11
- Use [v3.19.1] instead. This was tagged but not released and does not contain release binaries.
## [v3.18.0] - 2024-01-31
- Add environment variable substitution for SQL migrations. (#604)
- This feature is **disabled by default**, and can be enabled by adding an annotation to the
migration file:
```sql
-- +goose ENVSUB ON
```
- When enabled, goose will attempt to substitute environment variables in the SQL migration
queries until the end of the file, or until the annotation `-- +goose ENVSUB OFF` is found. For
example, if the environment variable `REGION` is set to `us_east_1`, the following SQL migration
will be substituted to `SELECT * FROM regions WHERE name = 'us_east_1';`
```sql
-- +goose ENVSUB ON
-- +goose Up
SELECT * FROM regions WHERE name = '${REGION}';
```
- Add native [Turso](https://turso.tech/) support with libsql driver. (#658)
- Fixed query for list migrations in YDB (#684)
## [v3.17.0] - 2023-12-15
- Standardised the MIT license (#647)
- Improve provider `Apply()` errors, add `ErrNotApplied` when attempting to rollback a migration
that has not been previously applied. (#660)
- Add `WithDisableGlobalRegistry` option to `NewProvider` to disable the global registry. (#645)
- Add `-timeout` flag to CLI to set the maximum allowed duration for queries to run. Default remains
no timeout. (#627)
- Add optional logging in `Provider` when `WithVerbose` option is supplied. (#668)
⚠️ Potential Breaking Change ⚠️
- Update `goose create` to use UTC time instead of local time. (#242)
## [v3.16.0] - 2023-11-12
- Added YDB support. (#592)
- Fix sqlserver query to ensure DB version. (#601)
- Allow setting / resetting the global Go migration registry. (#602)
- `SetGlobalMigrations` and `ResetGlobalMigrations` functions have been added.
- Introduce `NewGoMigration` for constructing Go migrations.
- Add initial implementation of `goose.NewProvider`.
🎉 Read more about this new feature here:
https://pressly.github.io/goose/blog/2023/goose-provider/
The motivation behind the Provider was simple - to reduce global state and make goose easier to
consume as an imported package.
Here's a quick summary:
- Avoid global state
- Make Provider safe to use concurrently
- Unlock (no pun intended) new features, such as database locking
- Make logging configurable
- Better error handling with proper return values
- Double down on Go migrations
- ... and more!
## [v3.15.1] - 2023-10-10
- Fix regression that prevented registering Go migrations that didn't have the corresponding files
available in the filesystem. (#588)
- If Go migrations have been registered globally, but there are no .go files in the filesystem,
**always include** them.
- If Go migrations have been registered, and there are .go files in the filesystem, **only
include** those migrations. This was the original motivation behind #553.
- If there are .go files in the filesystem but not registered, **raise an error**. This is to
prevent accidentally adding valid looking Go migration files without explicitly registering
them.
## [v3.15.0] - 2023-08-12
- Fix `sqlparser` to avoid skipping the last statement when it's not terminated with a semicolon
within a StatementBegin/End block. (#580)
- Add `**go1.21**` to the CI matrix.
- Bump minimum version of module in go.mod to `go1.19`.
- Fix version output when installing pre-built binaries (#585).
## [v3.14.0] - 2023-07-26
- Filter registered Go migrations from the global map with corresponding .go files from the
filesystem.
- The code previously assumed all .go migrations would be in the same folder, so this should not
be a breaking change.
- See #553 for more details
- Improve output log message for applied up migrations. #562
- Fix an issue where `AddMigrationNoTxContext` was registering the wrong source because it skipped
too many frames. #572
- Improve binary version output when using go install.
## [v3.13.4] - 2023-07-07
- Fix pre-built binary versioning and make small improvements to GoReleaser config.
- Fix an edge case in the `sqlparser` where the last up statement may be ignored if it's
unterminated with a semicolon and followed by a `-- +goose Down` annotation.
- Trim `Logger` interface to `Printf` and `Fatalf` methods only. Projects that have previously
implemented the `Logger` interface should not be affected, and can remove unused methods.
## [v3.13.1] - 2023-07-03
- Add pre-built binaries with GoReleaser and update the build process.
## [v3.13.0] - 2023-06-29
- Add a changelog to the project, based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Update go.mod and retract all `v3.12.X` tags. They were accidentally pushed and contain a
reference to the wrong Go module.
- Fix `up` and `up -allowing-missing` behavior.
- Fix empty version in log output.
- Add new `context.Context`-aware functions and methods, for both sql and go migrations.
- Return error when no migration files found or dir is not a directory.
[Unreleased]: https://github.com/pressly/goose/compare/v3.27.0...HEAD
[v3.27.0]: https://github.com/pressly/goose/compare/v3.26.0...v3.27.0
[v3.26.0]: https://github.com/pressly/goose/compare/v3.25.0...v3.26.0
[v3.25.0]: https://github.com/pressly/goose/compare/v3.24.3...v3.25.0
[v3.24.3]: https://github.com/pressly/goose/compare/v3.24.2...v3.24.3
[v3.24.2]: https://github.com/pressly/goose/compare/v3.24.1...v3.24.2
[v3.24.1]: https://github.com/pressly/goose/compare/v3.24.0...v3.24.1
[v3.24.0]: https://github.com/pressly/goose/compare/v3.23.1...v3.24.0
[v3.23.1]: https://github.com/pressly/goose/compare/v3.23.0...v3.23.1
[v3.23.0]: https://github.com/pressly/goose/compare/v3.22.1...v3.23.0
[v3.22.1]: https://github.com/pressly/goose/compare/v3.22.0...v3.22.1
[v3.22.0]: https://github.com/pressly/goose/compare/v3.21.1...v3.22.0
[v3.21.1]: https://github.com/pressly/goose/compare/v3.20.0...v3.21.1
[v3.21.0]: https://github.com/pressly/goose/compare/v3.20.0...v3.21.0
[v3.20.0]: https://github.com/pressly/goose/compare/v3.19.2...v3.20.0
[v3.19.2]: https://github.com/pressly/goose/compare/v3.19.1...v3.19.2
[v3.19.1]: https://github.com/pressly/goose/compare/v3.19.0...v3.19.1
[v3.19.0]: https://github.com/pressly/goose/compare/v3.18.0...v3.19.0
[v3.18.0]: https://github.com/pressly/goose/compare/v3.17.0...v3.18.0
[v3.17.0]: https://github.com/pressly/goose/compare/v3.16.0...v3.17.0
[v3.16.0]: https://github.com/pressly/goose/compare/v3.15.1...v3.16.0
[v3.15.1]: https://github.com/pressly/goose/compare/v3.15.0...v3.15.1
[v3.15.0]: https://github.com/pressly/goose/compare/v3.14.0...v3.15.0
[v3.14.0]: https://github.com/pressly/goose/compare/v3.13.4...v3.14.0
[v3.13.4]: https://github.com/pressly/goose/compare/v3.13.1...v3.13.4
[v3.13.1]: https://github.com/pressly/goose/compare/v3.13.0...v3.13.1
[v3.13.0]: https://github.com/pressly/goose/releases/tag/v3.13.0
================================================
FILE: LICENSE
================================================
MIT License
Original work Copyright (c) 2012 Liam Staskawicz
Modified work Copyright (c) 2016 Vojtech Vitek
Modified work Copyright (c) 2021 Michael Fridman, Vojtech Vitek
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: Makefile
================================================
GO_TEST_FLAGS ?= -race -count=1 -v -timeout=5m -json
# These are the default values for the test database. They can be overridden
DB_USER ?= dbuser
DB_PASSWORD ?= password1
DB_NAME ?= testdb
DB_POSTGRES_PORT ?= 5433
DB_MYSQL_PORT ?= 3307
DB_CLICKHOUSE_PORT ?= 9001
DB_YDB_PORT ?= 2136
DB_TURSO_PORT ?= 8080
DB_STARROCKS_PORT ?= 9030
list-build-tags:
@echo "Available build tags:"
@echo "$$(rg -o --trim 'no_[a-zA-Z0-9_]+' ./cmd/goose \
--no-line-number --no-filename | sort | uniq | \
xargs -n 4 | column -t | sed 's/^/ /')"
.PHONY: dist
dist:
@mkdir -p ./bin
@rm -f ./bin/*
GOOS=darwin GOARCH=amd64 go build -o ./bin/goose-darwin64 ./cmd/goose
GOOS=linux GOARCH=amd64 go build -o ./bin/goose-linux64 ./cmd/goose
GOOS=linux GOARCH=386 go build -o ./bin/goose-linux386 ./cmd/goose
GOOS=windows GOARCH=amd64 go build -o ./bin/goose-windows64.exe ./cmd/goose
GOOS=windows GOARCH=386 go build -o ./bin/goose-windows386.exe ./cmd/goose
.PHONY: clean
clean:
@find . -type f -name '*.FAIL' -delete
.PHONY: lint
lint: tools
@golangci-lint run ./... --fix
.PHONY: tools
tools:
@go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
@go install github.com/mfridman/tparse@main
test-packages:
go test $(GO_TEST_FLAGS) $$(go list ./... | grep -v -e /bin -e /cmd -e /examples) |\
tparse --follow -sort=elapsed -trimpath=auto -all
test-packages-short:
go test -test.short $(GO_TEST_FLAGS) $$(go list ./... | grep -v -e /bin -e /cmd -e /examples) |\
tparse --follow -sort=elapsed
coverage-short:
go test ./ -test.short $(GO_TEST_FLAGS) -cover -coverprofile=coverage.out | tparse --follow -sort=elapsed
go tool cover -html=coverage.out
coverage:
go test ./ $(GO_TEST_FLAGS) -cover -coverprofile=coverage.out | tparse --follow -sort=elapsed
go tool cover -html=coverage.out
test-lock-coverage:
go test ./internal/testing/integration/locking ./lock/internal/... -cover -coverpkg=./lock/internal/... -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
@echo "Lock package coverage: $$(go tool cover -func=coverage.out | tail -1 | awk '{print $$3}')"
open coverage.html
#
# Integration-related targets
#
add-gowork:
@[ -f go.work ] || go work init
@[ -f go.work.sum ] || go work use -r .
remove-gowork:
rm -rf go.work go.work.sum
upgrade-integration-deps:
cd ./internal/testing && go get -u ./... && go mod tidy
test-postgres-long: add-gowork test-postgres
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run='(TestPostgresProviderLocking|TestPostgresSessionLocker)' |\
tparse --follow -sort=elapsed
test-postgres: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run="^TestPostgres$$" | tparse --follow -sort=elapsed
test-spanner: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run='TestSpanner' | tparse --follow -sort=elapsed
test-clickhouse: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run='(TestClickhouse|TestClickhouseRemote)' |\
tparse --follow -sort=elapsed
test-mysql: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run='TestMySQL' | tparse --follow -sort=elapsed
test-turso: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run='TestTurso' | tparse --follow -sort=elapsed
test-ydb: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run='TestYDB' | tparse --follow -sort=elapsed
test-starrocks: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration -run='TestStarrocks' | tparse --follow -sort=elapsed
test-integration: add-gowork
go test $(GO_TEST_FLAGS) ./internal/testing/integration/... | tparse --follow -sort=elapsed -trimpath=auto -all
#
# Docker-related targets
#
docker-cleanup:
docker stop -t=0 $$(docker ps --filter="label=goose_test" -aq)
docker-postgres:
docker run --rm -d \
-e POSTGRES_USER=$(DB_USER) \
-e POSTGRES_PASSWORD=$(DB_PASSWORD) \
-e POSTGRES_DB=$(DB_NAME) \
-p $(DB_POSTGRES_PORT):5432 \
-l goose_test \
postgres:14-alpine -c log_statement=all
echo "postgres://$(DB_USER):$(DB_PASSWORD)@localhost:$(DB_POSTGRES_PORT)/$(DB_NAME)?sslmode=disable"
docker-mysql:
docker run --rm -d \
-e MYSQL_ROOT_PASSWORD=rootpassword1 \
-e MYSQL_DATABASE=$(DB_NAME) \
-e MYSQL_USER=$(DB_USER) \
-e MYSQL_PASSWORD=$(DB_PASSWORD) \
-p $(DB_MYSQL_PORT):3306 \
-l goose_test \
mysql:8.0.31
echo "mysql://$(DB_USER):$(DB_PASSWORD)@localhost:$(DB_MYSQL_PORT)/$(DB_NAME)?parseTime=true"
docker-clickhouse:
docker run --rm -d \
-e CLICKHOUSE_DB=$(DB_NAME) \
-e CLICKHOUSE_USER=$(DB_USER) \
-e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 \
-e CLICKHOUSE_PASSWORD=$(DB_PASSWORD) \
-p $(DB_CLICKHOUSE_PORT):9000/tcp \
-l goose_test \
clickhouse/clickhouse-server:23-alpine
echo "clickhouse://$(DB_USER):$(DB_PASSWORD)@localhost:$(DB_CLICKHOUSE_PORT)/$(DB_NAME)"
docker-turso:
docker run --rm -d \
-p $(DB_TURSO_PORT):8080 \
-l goose_test \
ghcr.io/tursodatabase/libsql-server:v0.22.10
================================================
FILE: README.md
================================================
# goose
<img align="right" width="125" src="assets/goose_logo.png">
[](https://github.com/pressly/goose/actions/workflows/ci.yaml)
[](https://pkg.go.dev/github.com/pressly/goose/v3)
[](https://goreportcard.com/report/github.com/pressly/goose/v3)
Goose is a database migration tool. Both a CLI and a library.
Manage your **database schema** by creating incremental SQL changes or Go functions.
#### Features
- Works against multiple databases:
- Postgres, MySQL, Spanner, SQLite, YDB, ClickHouse, MSSQL, Vertica, and
more.
- Supports Go migrations written as plain functions.
- Supports [embedded](https://pkg.go.dev/embed/) migrations.
- Out-of-order migrations.
- Seeding data.
- Environment variable substitution in SQL migrations.
- ... and more.
# Install
```shell
go install github.com/pressly/goose/v3/cmd/goose@latest
```
This will install the `goose` binary to your `$GOPATH/bin` directory.
Binary too big? Build a lite version by excluding the drivers you don't need:
```shell
go build -tags='no_postgres no_mysql no_sqlite3 no_ydb' -o goose ./cmd/goose
# Available build tags:
# no_clickhouse no_libsql no_mssql no_mysql
# no_postgres no_sqlite3 no_vertica no_ydb
```
For macOS users `goose` is available as a [Homebrew
Formulae](https://formulae.brew.sh/formula/goose#default):
```shell
brew install goose
```
See [installation documentation](https://pressly.github.io/goose/installation/) for more details.
# Usage
<details>
<summary>Click to show <code>goose help</code> output</summary>
```
Usage: goose DRIVER DBSTRING [OPTIONS] COMMAND
or
Set environment key
GOOSE_DRIVER=DRIVER
GOOSE_DBSTRING=DBSTRING
GOOSE_MIGRATION_DIR=MIGRATION_DIR
Usage: goose [OPTIONS] COMMAND
Drivers:
postgres
mysql
sqlite3
spanner
mssql
redshift
tidb
clickhouse
ydb
starrocks
turso
Examples:
goose sqlite3 ./foo.db status
goose sqlite3 ./foo.db create init sql
goose sqlite3 ./foo.db create add_some_column sql
goose sqlite3 ./foo.db create fetch_user_data go
goose sqlite3 ./foo.db up
goose postgres "user=postgres dbname=postgres sslmode=disable" status
goose mysql "user:password@/dbname?parseTime=true" status
goose spanner "projects/project/instances/instance/databases/database" status
goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" status
goose tidb "user:password@/dbname?parseTime=true" status
goose mssql "sqlserver://user:password@hostname:1433?database=master" status
goose clickhouse "tcp://127.0.0.1:9000" status
goose ydb "grpcs://localhost:2135/local?go_query_mode=scripting&go_fake_tx=scripting&go_query_bind=declare,numeric" status
goose starrocks "user:password@/dbname?parseTime=true&interpolateParams=true" status
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose status
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose create init sql
GOOSE_DRIVER=postgres GOOSE_DBSTRING="user=postgres dbname=postgres sslmode=disable" goose status
GOOSE_DRIVER=mysql GOOSE_DBSTRING="user:password@/dbname" goose status
GOOSE_DRIVER=redshift GOOSE_DBSTRING="postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" goose status
GOOSE_DRIVER=clickhouse GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status
Options:
-allow-missing
applies missing (out-of-order) migrations
-certfile string
file path to root CA's certificates in pem format (only support on mysql)
-dir string
directory with migration files (default ".", can be set via the GOOSE_MIGRATION_DIR env variable).
-h print help
-no-color
disable color output (NO_COLOR env variable supported)
-no-versioning
apply migration commands with no versioning, in file order, from directory pointed to
-s use sequential numbering for new migrations
-ssl-cert string
file path to SSL certificates in pem format (only support on mysql)
-ssl-key string
file path to SSL key in pem format (only support on mysql)
-table string
migrations table name (default "goose_db_version"). If you use a schema that is not `public`, you should set `schemaname.goose_db_version` when running commands.
-timeout duration
maximum allowed duration for queries to run; e.g., 1h13m
-v enable verbose mode
-version
print version
Commands:
up Migrate the DB to the most recent version available
up-by-one Migrate the DB up by 1
up-to VERSION Migrate the DB to a specific VERSION
down Roll back the version by 1
down-to VERSION Roll back to a specific VERSION
redo Re-run the latest migration
reset Roll back all migrations
status Dump the migration status for the current DB
version Print the current version of the database
create NAME [sql|go] Creates new migration file with the current timestamp
fix Apply sequential ordering to migrations
validate Check migration files without running them
```
</details>
Commonly used commands:
[create](#create)<span> • </span> [up](#up)<span> • </span> [up-to](#up-to)<span> • </span> [down](#down)<span> • </span> [down-to](#down-to)<span> • </span> [status](#status)<span> • </span> [version](#version)
## create
Create a new SQL migration.
$ goose create add_some_column sql
$ Created new file: 20170506082420_add_some_column.sql
$ goose -s create add_some_column sql
$ Created new file: 00001_add_some_column.sql
Edit the newly created file to define the behavior of your migration.
You can also create a Go migration, if you then invoke it with [your own goose
binary](#go-migrations):
$ goose create fetch_user_data go
$ Created new file: 20170506082421_fetch_user_data.go
## up
Apply all available migrations.
$ goose up
$ OK 001_basics.sql
$ OK 002_next.sql
$ OK 003_and_again.go
## up-to
Migrate up to a specific version.
$ goose up-to 20170506082420
$ OK 20170506082420_create_table.sql
## up-by-one
Migrate up a single migration from the current version
$ goose up-by-one
$ OK 20170614145246_change_type.sql
## down
Roll back a single migration from the current version.
$ goose down
$ OK 003_and_again.go
## down-to
Roll back migrations to a specific version.
$ goose down-to 20170506082527
$ OK 20170506082527_alter_column.sql
Or, roll back all migrations (careful!):
$ goose down-to 0
## status
Print the status of all migrations:
$ goose status
$ Applied At Migration
$ =======================================
$ Sun Jan 6 11:25:03 2013 -- 001_basics.sql
$ Sun Jan 6 11:25:03 2013 -- 002_next.sql
$ Pending -- 003_and_again.go
Note: for MySQL [parseTime flag](https://github.com/go-sql-driver/mysql#parsetime) must be enabled.
Note: for MySQL
[`multiStatements`](https://github.com/go-sql-driver/mysql?tab=readme-ov-file#multistatements) must
be enabled. This is required when writing multiple queries separated by ';' characters in a single
sql file.
## version
Print the current version of the database:
$ goose version
$ goose: version 002
# Environment Variables
If you prefer to use environment variables, instead of passing the driver and database string as
arguments, you can set the following environment variables:
**1. Via environment variables:**
```shell
export GOOSE_DRIVER=DRIVER
export GOOSE_DBSTRING=DBSTRING
export GOOSE_MIGRATION_DIR=MIGRATION_DIR
export GOOSE_TABLE=TABLENAME
```
**2. Via `.env` files with corresponding variables. `.env` file example**:
```env
GOOSE_DRIVER=postgres
GOOSE_DBSTRING=postgres://admin:admin@localhost:5432/admin_db
GOOSE_MIGRATION_DIR=./migrations
GOOSE_TABLE=custom.goose_migrations
```
Loading from `.env` files is enabled by default. To disable this feature, set the `-env=none` flag.
If you want to load from a specific file, set the `-env` flag to the file path.
For more details about environment variables, see the [official documentation on environment
variables](https://pressly.github.io/goose/documentation/environment-variables/).
# Migrations
goose supports migrations written in SQL or in Go.
## SQL Migrations
A sample SQL migration looks like:
```sql
-- +goose Up
CREATE TABLE post (
id int NOT NULL,
title text,
body text,
PRIMARY KEY(id)
);
-- +goose Down
DROP TABLE post;
```
Each migration file must have exactly one `-- +goose Up` annotation. The `-- +goose Down` annotation
is optional. If the file has both annotations, then the `-- +goose Up` annotation **must** come
first.
Notice the annotations in the comments. Any statements following `-- +goose Up` will be executed as
part of a forward migration, and any statements following `-- +goose Down` will be executed as part
of a rollback.
By default, all migrations are run within a transaction. Some statements like `CREATE DATABASE`,
however, cannot be run within a transaction. You may optionally add `-- +goose NO TRANSACTION` to
the top of your migration file in order to skip transactions within that specific migration file.
Both Up and Down migrations within this file will be run without transactions.
By default, SQL statements are delimited by semicolons - in fact, query statements must end with a
semicolon to be properly recognized by goose.
By default, all migrations are run on the public schema. If you want to use a different schema,
specify the schema name using the table option like `-table='schemaname.goose_db_version`.
More complex statements (PL/pgSQL) that have semicolons within them must be annotated with `--
+goose StatementBegin` and `-- +goose StatementEnd` to be properly recognized. For example:
```sql
-- +goose Up
-- +goose StatementBegin
CREATE OR REPLACE FUNCTION histories_partition_creation( DATE, DATE )
returns void AS $$
DECLARE
create_query text;
BEGIN
FOR create_query IN SELECT
'CREATE TABLE IF NOT EXISTS histories_'
|| TO_CHAR( d, 'YYYY_MM' )
|| ' ( CHECK( created_at >= timestamp '''
|| TO_CHAR( d, 'YYYY-MM-DD 00:00:00' )
|| ''' AND created_at < timestamp '''
|| TO_CHAR( d + INTERVAL '1 month', 'YYYY-MM-DD 00:00:00' )
|| ''' ) ) inherits ( histories );'
FROM generate_series( $1, $2, '1 month' ) AS d
LOOP
EXECUTE create_query;
END LOOP; -- LOOP END
END; -- FUNCTION END
$$
language plpgsql;
-- +goose StatementEnd
```
Goose supports environment variable substitution in SQL migrations through annotations. To enable
this feature, use the `-- +goose ENVSUB ON` annotation before the queries where you want
substitution applied. It stays active until the `-- +goose ENVSUB OFF` annotation is encountered.
You can use these annotations multiple times within a file.
This feature is disabled by default for backward compatibility with existing scripts.
For `PL/pgSQL` functions or other statements where substitution is not desired, wrap the annotations
explicitly around the relevant parts. For example, to exclude escaping the `$$` characters:
```sql
-- +goose StatementBegin
CREATE OR REPLACE FUNCTION test_func()
RETURNS void AS $$
-- +goose ENVSUB ON
BEGIN
RAISE NOTICE '${SOME_ENV_VAR}';
END;
-- +goose ENVSUB OFF
$$ LANGUAGE plpgsql;
-- +goose StatementEnd
```
<details>
<summary>Supported expansions (click here to expand):</summary>
- `${VAR}` or $VAR - expands to the value of the environment variable `VAR`
- `${VAR:-default}` - expands to the value of the environment variable `VAR`, or `default` if `VAR`
is unset or null
- `${VAR-default}` - expands to the value of the environment variable `VAR`, or `default` if `VAR`
is unset
- `${VAR?err_msg}` - expands to the value of the environment variable `VAR`, or prints `err_msg` and
error if `VAR` unset
- ~~`${VAR:?err_msg}` - expands to the value of the environment variable `VAR`, or prints `err_msg`
and error if `VAR` unset or null.~~ **THIS IS NOT SUPPORTED**
See
[mfridman/interpolate](https://github.com/mfridman/interpolate?tab=readme-ov-file#supported-expansions)
for more details on supported expansions.
</details>
## Embedded sql migrations
Go 1.16 introduced new feature: [compile-time embedding](https://pkg.go.dev/embed/) files into
binary and corresponding [filesystem abstraction](https://pkg.go.dev/io/fs/).
This feature can be used only for applying existing migrations. Modifying operations such as `fix`
and `create` will continue to operate on OS filesystem even if using embedded files. This is
expected behaviour because `io/fs` interfaces allows read-only access.
Make sure to configure the correct SQL dialect, see [dialect.go](./dialect.go) for supported SQL
dialects.
Example usage, assuming that SQL migrations are placed in the `migrations` directory:
```go
package main
import (
"database/sql"
"embed"
"github.com/pressly/goose/v3"
)
//go:embed migrations/*.sql
var embedMigrations embed.FS
func main() {
var db *sql.DB
// setup database
goose.SetBaseFS(embedMigrations)
if err := goose.SetDialect("postgres"); err != nil {
panic(err)
}
if err := goose.Up(db, "migrations"); err != nil {
panic(err)
}
// run app
}
```
Note that we pass `"migrations"` as directory argument in `Up` because embedding saves directory
structure.
## Go Migrations
1. Create your own goose binary, see [example](./examples/go-migrations)
2. Import `github.com/pressly/goose`
3. Register your migration functions
4. Include your `migrations` package into Go build: in `main.go`, `import _ "github.com/me/myapp/migrations"`
5. Run goose command, ie. `goose.Up(db *sql.DB, dir string)`
A [sample Go migration 00002_users_add_email.go file](./examples/go-migrations/00002_rename_root.go)
looks like:
```go
package migrations
import (
"database/sql"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigration(Up, Down)
}
func Up(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin' WHERE username='root';")
if err != nil {
return err
}
return nil
}
func Down(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='root' WHERE username='admin';")
if err != nil {
return err
}
return nil
}
```
Note that Go migration files must begin with a numeric value, followed by an underscore, and must
not end with `*_test.go`.
# Hybrid Versioning
Please, read the [versioning
problem](https://github.com/pressly/goose/issues/63#issuecomment-428681694) first.
By default, if you attempt to apply missing (out-of-order) migrations `goose` will raise an error.
However, If you want to apply these missing migrations pass goose the `-allow-missing` flag, or if
using as a library supply the functional option `goose.WithAllowMissing()` to Up, UpTo or UpByOne.
However, we strongly recommend adopting a hybrid versioning approach, using both timestamps and
sequential numbers. Migrations created during the development process are timestamped and sequential
versions are ran on production. We believe this method will prevent the problem of conflicting
versions when writing software in a team environment.
To help you adopt this approach, `create` will use the current timestamp as the migration version.
When you're ready to deploy your migrations in a production environment, we also provide a helpful
`fix` command to convert your migrations into sequential order, while preserving the timestamp
ordering. We recommend running `fix` in the CI pipeline, and only when the migrations are ready for
production.
## Credit
The gopher mascot was designed by [Renée French](https://reneefrench.blogspot.com/) / [CC
3.0.](https://creativecommons.org/licenses/by/3.0/) For more info check out the [Go
Blog](https://go.dev/blog/gopher). Adapted by Ellen.
## License
Licensed under [MIT License](./LICENSE)
================================================
FILE: cmd/goose/driver_clickhouse.go
================================================
//go:build !no_clickhouse
package main
import (
_ "github.com/ClickHouse/clickhouse-go/v2"
)
================================================
FILE: cmd/goose/driver_mssql.go
================================================
//go:build !no_mssql
package main
import (
_ "github.com/microsoft/go-mssqldb"
)
================================================
FILE: cmd/goose/driver_mysql.go
================================================
//go:build !no_mysql
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"log"
"os"
"github.com/go-sql-driver/mysql"
_ "github.com/ziutek/mymysql/godrv"
)
// normalizeMySQLDSN parses the dsn used with the mysql driver to always have
// the parameter `parseTime` set to true. This allows internal goose logic
// to assume that DATETIME/DATE/TIMESTAMP can be scanned into the time.Time
// type.
func normalizeDBString(driver string, str string, certfile string, sslcert string, sslkey string) string {
if driver == "mysql" {
isTLS := certfile != ""
if isTLS {
if err := registerTLSConfig(certfile, sslcert, sslkey); err != nil {
log.Fatalf("goose run: %v", err)
}
}
var err error
str, err = normalizeMySQLDSN(str, isTLS)
if err != nil {
log.Fatalf("failed to normalize MySQL connection string: %v", err)
}
}
return str
}
const tlsConfigKey = "custom"
func normalizeMySQLDSN(dsn string, tls bool) (string, error) {
config, err := mysql.ParseDSN(dsn)
if err != nil {
return "", err
}
config.ParseTime = true
if tls {
config.TLSConfig = tlsConfigKey
}
return config.FormatDSN(), nil
}
func registerTLSConfig(pemfile string, sslcert string, sslkey string) error {
rootCertPool := x509.NewCertPool()
pem, err := os.ReadFile(pemfile)
if err != nil {
return err
}
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
return fmt.Errorf("failed to append PEM: %q", pemfile)
}
tlsConfig := &tls.Config{
RootCAs: rootCertPool,
}
if sslcert != "" && sslkey != "" {
cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
if err != nil {
return fmt.Errorf("failed to load x509 keypair: %w", err)
}
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
}
return mysql.RegisterTLSConfig(tlsConfigKey, tlsConfig)
}
================================================
FILE: cmd/goose/driver_no_mysql.go
================================================
//go:build no_mysql
package main
func normalizeDBString(driver string, str string, certfile string, sslcert string, sslkey string) string {
return str
}
================================================
FILE: cmd/goose/driver_postgres.go
================================================
//go:build !no_postgres
package main
import (
_ "github.com/jackc/pgx/v5/stdlib"
)
================================================
FILE: cmd/goose/driver_sqlite3.go
================================================
//go:build !no_sqlite3 && !(windows && arm64)
package main
import (
_ "modernc.org/sqlite"
)
================================================
FILE: cmd/goose/driver_turso.go
================================================
//go:build !no_libsql
package main
import (
_ "github.com/tursodatabase/libsql-client-go/libsql"
)
================================================
FILE: cmd/goose/driver_vertica.go
================================================
//go:build !no_vertica
package main
import (
_ "github.com/vertica/vertica-sql-go"
)
================================================
FILE: cmd/goose/driver_ydb.go
================================================
//go:build !no_ydb
package main
import (
_ "github.com/ydb-platform/ydb-go-sdk/v3"
)
================================================
FILE: cmd/goose/main.go
================================================
package main
import (
"context"
"database/sql"
"errors"
"flag"
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"runtime/debug"
"sort"
"strconv"
"strings"
"text/tabwriter"
"text/template"
"github.com/joho/godotenv"
"github.com/mfridman/xflag"
"github.com/pressly/goose/v3"
"github.com/pressly/goose/v3/internal/migrationstats"
)
var (
DefaultMigrationDir = "."
flags = flag.NewFlagSet("goose", flag.ExitOnError)
dir = flags.String("dir", DefaultMigrationDir, "directory with migration files, (GOOSE_MIGRATION_DIR env variable supported)")
table = flags.String("table", "", "migrations table name")
verbose = flags.Bool("v", false, "enable verbose mode")
help = flags.Bool("h", false, "print help")
versionFlag = flags.Bool("version", false, "print version")
certfile = flags.String("certfile", "", "file path to root CA's certificates in pem format (only support on mysql)")
sequential = flags.Bool("s", false, "use sequential numbering for new migrations")
allowMissing = flags.Bool("allow-missing", false, "applies missing (out-of-order) migrations")
sslcert = flags.String("ssl-cert", "", "file path to SSL certificates in pem format (only support on mysql)")
sslkey = flags.String("ssl-key", "", "file path to SSL key in pem format (only support on mysql)")
noVersioning = flags.Bool("no-versioning", false, "apply migration commands with no versioning, in file order, from directory pointed to")
noColor = flags.Bool("no-color", false, "disable color output (NO_COLOR env variable supported)")
timeout = flags.Duration("timeout", 0, "maximum allowed duration for queries to run; e.g., 1h13m")
envFile = flags.String("env", "", "load environment variables from file (default .env)")
)
var version string
func main() {
ctx := context.Background()
flags.Usage = usage
if err := xflag.ParseToEnd(flags, os.Args[1:]); err != nil {
log.Fatalf("failed to parse args: %v", err)
return
}
if *versionFlag {
buildInfo, ok := debug.ReadBuildInfo()
if version == "" && ok && buildInfo != nil && buildInfo.Main.Version != "" {
version = buildInfo.Main.Version
}
fmt.Printf("goose version: %s\n", strings.TrimSpace(version))
return
}
switch *envFile {
case "":
// Best effort to load default .env file
_ = godotenv.Load()
case "none":
// Do not load any env file
default:
if err := godotenv.Load(*envFile); err != nil {
log.Fatalf("failed to load env file: %v", err)
}
}
envConfig := loadEnvConfig()
if *verbose {
goose.SetVerbose(true)
}
if *sequential {
goose.SetSequential(true)
}
// The order of precedence should be: flag > env variable > default value.
goose.SetTableName(firstNonEmpty(*table, envConfig.table, goose.DefaultTablename))
args := flags.Args()
if *help {
flags.Usage()
return
}
if len(args) == 0 {
flags.Usage()
os.Exit(1)
}
// The -dir option has not been set, check whether the env variable is set
// before defaulting to ".".
if *dir == DefaultMigrationDir && envConfig.dir != "" {
*dir = envConfig.dir
}
switch args[0] {
case "init":
if err := gooseInit(*dir); err != nil {
log.Fatalf("goose run: %v", err)
}
return
case "create":
if err := goose.RunContext(ctx, "create", nil, *dir, args[1:]...); err != nil {
log.Fatalf("goose run: %v", err)
}
return
case "fix":
if err := goose.RunContext(ctx, "fix", nil, *dir); err != nil {
log.Fatalf("goose run: %v", err)
}
return
case "env":
for _, env := range envConfig.listEnvs() {
fmt.Printf("%s=%q\n", env.Name, env.Value)
}
return
case "validate":
if err := printValidate(*dir, *verbose); err != nil {
log.Fatalf("goose validate: %v", err)
}
return
case "beta":
remain := args[1:]
if len(remain) == 0 {
log.Println("goose beta: missing subcommand")
os.Exit(1)
}
switch remain[0] {
case "drivers":
printDrivers()
}
return
}
args = mergeArgs(envConfig, args)
if len(args) < 3 {
flags.Usage()
os.Exit(1)
}
driver, dbstring, command := args[0], args[1], args[2]
db, err := goose.OpenDBWithDriver(driver, normalizeDBString(driver, dbstring, *certfile, *sslcert, *sslkey))
if err != nil {
log.Fatalf("-dbstring=%q: %v", dbstring, err)
}
defer func() {
if err := db.Close(); err != nil {
log.Fatalf("goose: failed to close DB: %v", err)
}
}()
arguments := []string{}
if len(args) > 3 {
arguments = append(arguments, args[3:]...)
}
options := []goose.OptionsFunc{}
if *noColor || envConfig.noColor {
options = append(options, goose.WithNoColor(true))
}
if *allowMissing {
options = append(options, goose.WithAllowMissing())
}
if *noVersioning {
options = append(options, goose.WithNoVersioning())
}
if timeout != nil && *timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, *timeout)
defer cancel()
}
if err := goose.RunWithOptionsContext(
ctx,
command,
db,
*dir,
arguments,
options...,
); err != nil {
log.Fatalf("goose run: %v", err)
}
}
func printDrivers() {
drivers := mergeDrivers(sql.Drivers())
if len(drivers) == 0 {
fmt.Println("No drivers found")
return
}
fmt.Println("Available drivers:")
for _, driver := range drivers {
fmt.Printf(" %s\n", driver)
}
}
// mergeDrivers merges drivers with a common prefix into a single line.
func mergeDrivers(drivers []string) []string {
driverMap := make(map[string][]string)
for _, driver := range drivers {
parts := strings.Split(driver, "/")
if len(parts) > 1 {
// Merge drivers with a common prefix "/"
prefix := parts[0]
driverMap[prefix] = append(driverMap[prefix], driver)
} else {
// Add drivers without a prefix directly
driverMap[driver] = append(driverMap[driver], driver)
}
}
var merged []string
for _, versions := range driverMap {
sort.Strings(versions)
merged = append(merged, strings.Join(versions, ", "))
}
sort.Strings(merged)
return merged
}
func mergeArgs(config *envConfig, args []string) []string {
if len(args) < 1 {
return args
}
if s := config.driver; s != "" {
args = append([]string{s}, args...)
}
if s := config.dbstring; s != "" {
args = append([]string{args[0], s}, args[1:]...)
}
return args
}
func usage() {
fmt.Println(usagePrefix)
flags.PrintDefaults()
fmt.Println(usageCommands)
}
var (
usagePrefix = `Usage: goose DRIVER DBSTRING [OPTIONS] COMMAND
or
Set environment key
GOOSE_DRIVER=DRIVER
GOOSE_DBSTRING=DBSTRING
GOOSE_MIGRATION_DIR=MIGRATION_DIR
Usage: goose [OPTIONS] COMMAND
Drivers:
postgres
mysql
sqlite3
spanner
mssql
redshift
tidb
clickhouse
ydb
starrocks
turso
Examples:
goose sqlite3 ./foo.db status
goose sqlite3 ./foo.db create init sql
goose sqlite3 ./foo.db create add_some_column sql
goose sqlite3 ./foo.db create fetch_user_data go
goose sqlite3 ./foo.db up
goose postgres "user=postgres dbname=postgres sslmode=disable" status
goose mysql "user:password@/dbname?parseTime=true" status
goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" status
goose tidb "user:password@/dbname?parseTime=true" status
goose mssql "sqlserver://user:password@dbname:1433?database=master" status
goose clickhouse "tcp://127.0.0.1:9000" status
goose ydb "grpcs://localhost:2135/local?go_query_mode=scripting&go_fake_tx=scripting&go_query_bind=declare,numeric" status
goose turso "libsql://dbname.turso.io?authToken=token" status
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose status
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose create init sql
GOOSE_DRIVER=postgres GOOSE_DBSTRING="user=postgres dbname=postgres sslmode=disable" goose status
GOOSE_DRIVER=mysql GOOSE_DBSTRING="user:password@/dbname" goose status
GOOSE_DRIVER=redshift GOOSE_DBSTRING="postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" goose status
GOOSE_DRIVER=turso GOOSE_DBSTRING="libsql://dbname.turso.io?authToken=token" goose status
GOOSE_DRIVER=clickhouse GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status
Options:
`
usageCommands = `
Commands:
up Migrate the DB to the most recent version available
up-by-one Migrate the DB up by 1
up-to VERSION Migrate the DB to a specific VERSION
down Roll back the version by 1
down-to VERSION Roll back to a specific VERSION
redo Re-run the latest migration
reset Roll back all migrations
status Dump the migration status for the current DB
version Print the current version of the database
create NAME [sql|go] Creates new migration file with the current timestamp
fix Apply sequential ordering to migrations
validate Check migration files without running them
`
)
var sqlMigrationTemplate = template.Must(template.New("goose.sql-migration").Parse(`-- Thank you for giving goose a try!
--
-- This file was automatically created running goose init. If you're familiar with goose
-- feel free to remove/rename this file, write some SQL and goose up. Briefly,
--
-- Documentation can be found here: https://pressly.github.io/goose
--
-- A single goose .sql file holds both Up and Down migrations.
--
-- All goose .sql files are expected to have a -- +goose Up annotation.
-- The -- +goose Down annotation is optional, but recommended, and must come after the Up annotation.
--
-- The -- +goose NO TRANSACTION annotation may be added to the top of the file to run statements
-- outside a transaction. Both Up and Down migrations within this file will be run without a transaction.
--
-- More complex statements that have semicolons within them must be annotated with
-- the -- +goose StatementBegin and -- +goose StatementEnd annotations to be properly recognized.
--
-- Use GitHub issues for reporting bugs and requesting features, enjoy!
-- +goose Up
SELECT 'up SQL query';
-- +goose Down
SELECT 'down SQL query';
`))
// initDir will create a directory with an empty SQL migration file.
func gooseInit(dir string) error {
if dir == "" || dir == DefaultMigrationDir {
dir = "migrations"
}
_, err := os.Stat(dir)
switch {
case errors.Is(err, fs.ErrNotExist):
case err == nil, errors.Is(err, fs.ErrExist):
return fmt.Errorf("directory already exists: %s", dir)
default:
return err
}
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
return goose.CreateWithTemplate(nil, dir, sqlMigrationTemplate, "initial", "sql")
}
func gatherFilenames(filename string) ([]string, error) {
stat, err := os.Stat(filename)
if err != nil {
return nil, err
}
var filenames []string
if stat.IsDir() {
for _, pattern := range []string{"*.sql", "*.go"} {
file, err := filepath.Glob(filepath.Join(filename, pattern))
if err != nil {
return nil, err
}
filenames = append(filenames, file...)
}
} else {
filenames = append(filenames, filename)
}
sort.Strings(filenames)
return filenames, nil
}
func printValidate(filename string, verbose bool) error {
filenames, err := gatherFilenames(filename)
if err != nil {
return err
}
stats, err := migrationstats.GatherStats(
migrationstats.NewFileWalker(filenames...),
false,
)
if err != nil {
return err
}
// TODO(mf): we should introduce a --debug flag, which allows printing
// more internal debug information and leave verbose for additional information.
if !verbose {
return nil
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.TabIndent)
fmtPattern := "%v\t%v\t%v\t%v\t%v\t\n"
fmt.Fprintf(w, fmtPattern, "Type", "Txn", "Up", "Down", "Name")
fmt.Fprintf(w, fmtPattern, "────", "───", "──", "────", "────")
for _, m := range stats {
txnStr := "✔"
if !m.Tx {
txnStr = "✘"
}
fmt.Fprintf(w, fmtPattern,
strings.TrimPrefix(filepath.Ext(m.FileName), "."),
txnStr,
m.UpCount,
m.DownCount,
filepath.Base(m.FileName),
)
}
return w.Flush()
}
type envConfig struct {
driver string
dbstring string
dir string
table string
noColor bool
}
func loadEnvConfig() *envConfig {
noColorBool, _ := strconv.ParseBool(envOr("NO_COLOR", "false"))
return &envConfig{
driver: envOr("GOOSE_DRIVER", ""),
dbstring: envOr("GOOSE_DBSTRING", ""),
table: envOr("GOOSE_TABLE", ""),
dir: envOr("GOOSE_MIGRATION_DIR", DefaultMigrationDir),
// https://no-color.org/
noColor: noColorBool,
}
}
func (c *envConfig) listEnvs() []envVar {
return []envVar{
{Name: "GOOSE_DRIVER", Value: c.driver},
{Name: "GOOSE_DBSTRING", Value: c.dbstring},
{Name: "GOOSE_MIGRATION_DIR", Value: c.dir},
{Name: "GOOSE_TABLE", Value: c.table},
{Name: "NO_COLOR", Value: strconv.FormatBool(c.noColor)},
}
}
type envVar struct {
Name string
Value string
}
// envOr returns os.Getenv(key) if set, or else default.
func envOr(key, def string) string {
val := os.Getenv(key)
if val == "" {
val = def
}
return val
}
// firstNonEmpty returns the first non-empty string from the provided input or an empty string if all are empty.
func firstNonEmpty(values ...string) string {
for _, v := range values {
if v != "" {
return v
}
}
return ""
}
================================================
FILE: cmd/goose/main_test.go
================================================
package main
import (
"testing"
)
func TestFirstNonEmpty(t *testing.T) {
tests := []struct {
name string
input []string
expected string
}{
{
name: "no values",
input: []string{},
expected: "",
},
{
name: "all empty values",
input: []string{"", "", ""},
expected: "",
},
{
name: "single non-empty value at start",
input: []string{"value", "", ""},
expected: "value",
},
{
name: "single non-empty value in middle",
input: []string{"", "value", ""},
expected: "value",
},
{
name: "single non-empty value at end",
input: []string{"", "", "value"},
expected: "value",
},
{
name: "multiple non-empty values",
input: []string{"first", "second", "third"},
expected: "first",
},
{
name: "mixed empty and non-empty values",
input: []string{"", "value1", "", "value2"},
expected: "value1",
},
{
name: "only one value, empty",
input: []string{""},
expected: "",
},
{
name: "only one value, non-empty",
input: []string{"value"},
expected: "value",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := firstNonEmpty(tt.input...)
if result != tt.expected {
t.Errorf("expected %q, got %q", tt.expected, result)
}
})
}
}
================================================
FILE: create.go
================================================
package goose
import (
"database/sql"
"errors"
"fmt"
"os"
"path/filepath"
"text/template"
"time"
)
type tmplVars struct {
Version string
CamelName string
}
var (
sequential = false
)
// SetSequential set whether to use sequential versioning instead of timestamp based versioning
func SetSequential(s bool) {
sequential = s
}
// Create writes a new blank migration file.
func CreateWithTemplate(db *sql.DB, dir string, tmpl *template.Template, name, migrationType string) error {
version := time.Now().UTC().Format(timestampFormat)
if sequential {
// always use DirFS here because it's modifying operation
migrations, err := collectMigrationsFS(osFS{}, dir, minVersion, maxVersion, registeredGoMigrations)
if err != nil && !errors.Is(err, ErrNoMigrationFiles) {
return err
}
vMigrations, err := migrations.versioned()
if err != nil {
return err
}
if last, err := vMigrations.Last(); err == nil {
version = fmt.Sprintf(seqVersionTemplate, last.Version+1)
} else {
version = fmt.Sprintf(seqVersionTemplate, int64(1))
}
}
filename := fmt.Sprintf("%v_%v.%v", version, snakeCase(name), migrationType)
if tmpl == nil {
if migrationType == "go" {
tmpl = goSQLMigrationTemplate
} else {
tmpl = sqlMigrationTemplate
}
}
path := filepath.Join(dir, filename)
if _, err := os.Stat(path); !os.IsNotExist(err) {
return fmt.Errorf("failed to create migration file: %w", err)
}
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("failed to create migration file: %w", err)
}
defer f.Close()
vars := tmplVars{
Version: version,
CamelName: camelCase(name),
}
if err := tmpl.Execute(f, vars); err != nil {
return fmt.Errorf("failed to execute tmpl: %w", err)
}
log.Printf("Created new file: %s", f.Name())
return nil
}
// Create writes a new blank migration file.
func Create(db *sql.DB, dir, name, migrationType string) error {
return CreateWithTemplate(db, dir, nil, name, migrationType)
}
var sqlMigrationTemplate = template.Must(template.New("goose.sql-migration").Parse(`-- +goose Up
SELECT 'up SQL query';
-- +goose Down
SELECT 'down SQL query';
`))
var goSQLMigrationTemplate = template.Must(template.New("goose.go-migration").Parse(`package migrations
import (
"context"
"database/sql"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigrationContext(up{{.CamelName}}, down{{.CamelName}})
}
func up{{.CamelName}}(ctx context.Context, tx *sql.Tx) error {
// This code is executed when the migration is applied.
return nil
}
func down{{.CamelName}}(ctx context.Context, tx *sql.Tx) error {
// This code is executed when the migration is rolled back.
return nil
}
`))
================================================
FILE: create_test.go
================================================
package goose
import (
"fmt"
"os"
"os/exec"
"strings"
"testing"
"time"
)
func TestSequential(t *testing.T) {
t.Parallel()
if testing.Short() {
t.Skip("skip long running test")
}
dir := t.TempDir()
defer os.Remove("./bin/create-goose") // clean up
commands := []string{
"go build -o ./bin/create-goose ./cmd/goose",
fmt.Sprintf("./bin/create-goose -s -dir=%s create create_table", dir),
fmt.Sprintf("./bin/create-goose -s -dir=%s create add_users", dir),
fmt.Sprintf("./bin/create-goose -s -dir=%s create add_indices", dir),
fmt.Sprintf("./bin/create-goose -s -dir=%s create update_users", dir),
}
for _, cmd := range commands {
args := strings.Split(cmd, " ")
time.Sleep(1 * time.Second)
cmd := exec.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
}
}
files, err := os.ReadDir(dir)
if err != nil {
t.Fatal(err)
}
// check that the files are in order
for i, f := range files {
expected := fmt.Sprintf("%05v", i+1)
if !strings.HasPrefix(f.Name(), expected) {
t.Errorf("failed to find %s prefix in %s", expected, f.Name())
}
}
}
================================================
FILE: database/dialect/querier.go
================================================
package dialect
// Querier is the interface that wraps the basic methods to create a dialect specific query.
//
// It is intended tio be using with [database.NewStoreFromQuerier] to create a new [database.Store]
// implementation based on a custom querier.
type Querier interface {
// CreateTable returns the SQL query string to create the db version table.
CreateTable(tableName string) string
// InsertVersion returns the SQL query string to insert a new version into the db version table.
InsertVersion(tableName string) string
// DeleteVersion returns the SQL query string to delete a version from the db version table.
DeleteVersion(tableName string) string
// GetMigrationByVersion returns the SQL query string to get a single migration by version.
//
// The query should return the timestamp and is_applied columns.
GetMigrationByVersion(tableName string) string
// ListMigrations returns the SQL query string to list all migrations in descending order by id.
//
// The query should return the version_id and is_applied columns.
ListMigrations(tableName string) string
// GetLatestVersion returns the SQL query string to get the last version_id from the db version
// table. Returns a nullable int64 value.
GetLatestVersion(tableName string) string
}
================================================
FILE: database/dialect/querier_extended.go
================================================
package dialect
// QuerierExtender extends the [Querier] interface with optional database-specific optimizations.
// While not required, implementing these methods can improve performance.
//
// IMPORTANT: This interface may be expanded in future versions. Implementors must be prepared to
// update their implementations when new methods are added.
//
// Example compile-time check:
//
// var _ QuerierExtender = (*CustomQuerierExtended)(nil)
//
// In short, it's exported to allow implementors to have a compile-time check that they are
// implementing the interface correctly.
type QuerierExtender interface {
Querier
// TableExists returns a database-specific SQL query to check if a table exists. For example,
// implementations might query system catalogs like pg_tables or sqlite_master. Return empty
// string if not supported.
TableExists(tableName string) string
}
================================================
FILE: database/dialects.go
================================================
package database
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/pressly/goose/v3/database/dialect"
"github.com/pressly/goose/v3/internal/dialects"
)
// Dialect is the type of database dialect.
type Dialect string
const (
DialectCustom Dialect = ""
DialectClickHouse Dialect = "clickhouse"
DialectAuroraDSQL Dialect = "dsql"
DialectMSSQL Dialect = "mssql"
DialectMySQL Dialect = "mysql"
DialectPostgres Dialect = "postgres"
DialectRedshift Dialect = "redshift"
DialectSQLite3 Dialect = "sqlite3"
DialectSpanner Dialect = "spanner"
DialectStarrocks Dialect = "starrocks"
DialectTiDB Dialect = "tidb"
DialectTurso Dialect = "turso"
DialectYdB Dialect = "ydb"
// DEPRECATED: Vertica support is deprecated and will be removed in a future release.
DialectVertica Dialect = "vertica"
)
// NewStore returns a new [Store] implementation for the given dialect.
func NewStore(d Dialect, tableName string) (Store, error) {
if d == DialectCustom {
return nil, errors.New("custom dialect is not supported")
}
lookup := map[Dialect]dialect.Querier{
DialectClickHouse: dialects.NewClickhouse(),
DialectAuroraDSQL: dialects.NewAuroraDSQL(),
DialectMSSQL: dialects.NewSqlserver(),
DialectMySQL: dialects.NewMysql(),
DialectPostgres: dialects.NewPostgres(),
DialectRedshift: dialects.NewRedshift(),
DialectSQLite3: dialects.NewSqlite3(),
DialectSpanner: dialects.NewSpanner(),
DialectStarrocks: dialects.NewStarrocks(),
DialectTiDB: dialects.NewTidb(),
DialectTurso: dialects.NewTurso(),
DialectVertica: dialects.NewVertica(),
DialectYdB: dialects.NewYDB(),
}
querier, ok := lookup[d]
if !ok {
return nil, fmt.Errorf("unknown dialect: %q", d)
}
return NewStoreFromQuerier(tableName, querier)
}
// NewStoreFromQuerier returns a new [Store] implementation for the given querier.
//
// Most of the time you should use [NewStore] instead of this function, as it will automatically
// create a dialect-specific querier for you. This function is useful if you want to use a custom
// querier that is not part of the standard dialects.
func NewStoreFromQuerier(tableName string, querier dialect.Querier) (Store, error) {
if tableName == "" {
return nil, errors.New("table name must not be empty")
}
if querier == nil {
return nil, errors.New("querier must not be nil")
}
return &store{
tableName: tableName,
querier: newQueryController(querier),
}, nil
}
type store struct {
tableName string
querier *queryController
}
var _ Store = (*store)(nil)
func (s *store) Tablename() string {
return s.tableName
}
func (s *store) CreateVersionTable(ctx context.Context, db DBTxConn) error {
q := s.querier.CreateTable(s.tableName)
if _, err := db.ExecContext(ctx, q); err != nil {
return fmt.Errorf("failed to create version table %q: %w", s.tableName, err)
}
return nil
}
func (s *store) Insert(ctx context.Context, db DBTxConn, req InsertRequest) error {
q := s.querier.InsertVersion(s.tableName)
if _, err := db.ExecContext(ctx, q, req.Version, true); err != nil {
return fmt.Errorf("failed to insert version %d: %w", req.Version, err)
}
return nil
}
func (s *store) Delete(ctx context.Context, db DBTxConn, version int64) error {
q := s.querier.DeleteVersion(s.tableName)
if _, err := db.ExecContext(ctx, q, version); err != nil {
return fmt.Errorf("failed to delete version %d: %w", version, err)
}
return nil
}
func (s *store) GetMigration(
ctx context.Context,
db DBTxConn,
version int64,
) (*GetMigrationResult, error) {
q := s.querier.GetMigrationByVersion(s.tableName)
var result GetMigrationResult
if err := db.QueryRowContext(ctx, q, version).Scan(
&result.Timestamp,
&result.IsApplied,
); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("%w: %d", ErrVersionNotFound, version)
}
return nil, fmt.Errorf("failed to get migration %d: %w", version, err)
}
return &result, nil
}
func (s *store) GetLatestVersion(ctx context.Context, db DBTxConn) (int64, error) {
q := s.querier.GetLatestVersion(s.tableName)
var version sql.NullInt64
if err := db.QueryRowContext(ctx, q).Scan(&version); err != nil {
return -1, fmt.Errorf("failed to get latest version: %w", err)
}
if !version.Valid {
return -1, fmt.Errorf("latest %w", ErrVersionNotFound)
}
return version.Int64, nil
}
func (s *store) ListMigrations(
ctx context.Context,
db DBTxConn,
) ([]*ListMigrationsResult, error) {
q := s.querier.ListMigrations(s.tableName)
rows, err := db.QueryContext(ctx, q)
if err != nil {
return nil, fmt.Errorf("failed to list migrations: %w", err)
}
defer rows.Close()
var migrations []*ListMigrationsResult
for rows.Next() {
var result ListMigrationsResult
if err := rows.Scan(&result.Version, &result.IsApplied); err != nil {
return nil, fmt.Errorf("failed to scan list migrations result: %w", err)
}
migrations = append(migrations, &result)
}
if err := rows.Err(); err != nil {
return nil, err
}
return migrations, nil
}
//
//
//
// Additional methods that are not part of the core Store interface, but are extended by the
// [controller.StoreController] type.
//
//
//
func (s *store) TableExists(ctx context.Context, db DBTxConn) (bool, error) {
q := s.querier.TableExists(s.tableName)
if q == "" {
return false, errors.ErrUnsupported
}
var exists bool
// Note, we do not pass the table name as an argument to the query, as the query should be
// pre-defined by the dialect.
if err := db.QueryRowContext(ctx, q).Scan(&exists); err != nil {
return false, fmt.Errorf("failed to check if table exists: %w", err)
}
return exists, nil
}
var _ dialect.Querier = (*queryController)(nil)
type queryController struct{ dialect.Querier }
// newQueryController returns a new QueryController that wraps the given Querier.
func newQueryController(querier dialect.Querier) *queryController {
return &queryController{Querier: querier}
}
// Optional methods
// TableExists returns the SQL query string to check if the version table exists. If the Querier
// does not implement this method, it will return an empty string.
//
// Returns a boolean value.
func (c *queryController) TableExists(tableName string) string {
if t, ok := c.Querier.(interface{ TableExists(string) string }); ok {
return t.TableExists(tableName)
}
return ""
}
================================================
FILE: database/doc.go
================================================
// Package database defines a generic [Store] interface for goose to use when interacting with the
// database. It is meant to be generic and not tied to any specific database technology.
//
// At a high level, a [Store] is responsible for:
// - Creating a version table
// - Inserting and deleting a version
// - Getting a specific version
// - Listing all applied versions
//
// Use the [NewStore] function to create a [Store] for one of the supported dialects.
//
// For more advanced use cases, it's possible to implement a custom [Store] for a database that
// goose does not support.
package database
================================================
FILE: database/sql_extended.go
================================================
package database
import (
"context"
"database/sql"
)
// DBTxConn is a thin interface for common methods that is satisfied by *sql.DB, *sql.Tx and
// *sql.Conn.
//
// There is a long outstanding issue to formalize a std lib interface, but alas. See:
// https://github.com/golang/go/issues/14468
type DBTxConn interface {
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}
var (
_ DBTxConn = (*sql.DB)(nil)
_ DBTxConn = (*sql.Tx)(nil)
_ DBTxConn = (*sql.Conn)(nil)
)
================================================
FILE: database/store.go
================================================
package database
import (
"context"
"errors"
"time"
)
var (
// ErrVersionNotFound must be returned by [GetMigration] or [GetLatestVersion] when a migration
// does not exist.
ErrVersionNotFound = errors.New("version not found")
// ErrNotImplemented must be returned by methods that are not implemented.
ErrNotImplemented = errors.New("not implemented")
)
// Store is an interface that defines methods for tracking and managing migrations. It is used by
// the goose package to interact with a database. By defining a Store interface, multiple
// implementations can be created to support different databases without reimplementing the
// migration logic.
//
// This package provides several dialects that implement the Store interface. While most users won't
// need to create their own Store, if you need to support a database that isn't currently supported,
// you can implement your own!
type Store interface {
// Tablename is the name of the version table. This table is used to record applied migrations
// and must not be an empty string.
Tablename() string
// CreateVersionTable creates the version table, which is used to track migrations.
CreateVersionTable(ctx context.Context, db DBTxConn) error
// Insert a version id into the version table.
Insert(ctx context.Context, db DBTxConn, req InsertRequest) error
// Delete a version id from the version table.
Delete(ctx context.Context, db DBTxConn, version int64) error
// GetMigration retrieves a single migration by version id. If the query succeeds, but the
// version is not found, this method must return [ErrVersionNotFound].
GetMigration(ctx context.Context, db DBTxConn, version int64) (*GetMigrationResult, error)
// GetLatestVersion retrieves the last applied migration version. If no migrations exist, this
// method must return [ErrVersionNotFound].
GetLatestVersion(ctx context.Context, db DBTxConn) (int64, error)
// ListMigrations retrieves all migrations sorted in descending order by id or timestamp. If
// there are no migrations, return empty slice with no error. Typically this method will return
// at least one migration, because the initial version (0) is always inserted into the version
// table when it is created.
ListMigrations(ctx context.Context, db DBTxConn) ([]*ListMigrationsResult, error)
}
type InsertRequest struct {
Version int64
// TODO(mf): in the future, we maybe want to expand this struct so implementors can store
// additional information. See the following issues for more information:
// - https://github.com/pressly/goose/issues/422
// - https://github.com/pressly/goose/issues/288
}
type GetMigrationResult struct {
Timestamp time.Time
IsApplied bool
}
type ListMigrationsResult struct {
Version int64
IsApplied bool
}
================================================
FILE: database/store_extended.go
================================================
package database
import "context"
// StoreExtender is an extension of the Store interface that provides optional optimizations and
// database-specific features. While not required by the core goose package, implementing these
// methods can improve performance and functionality for specific databases.
//
// IMPORTANT: This interface may be expanded in future versions. Implementors MUST be prepared to
// update their implementations when new methods are added, either by implementing the new
// functionality or returning [errors.ErrUnsupported].
//
// The goose package handles these extended capabilities through a [controller.StoreController],
// which automatically uses optimized methods when available while falling back to default behavior
// when they're not implemented.
//
// Example usage to verify implementation:
//
// var _ StoreExtender = (*CustomStoreExtended)(nil)
//
// In short, it's exported to allows implementors to have a compile-time check that they are
// implementing the interface correctly.
type StoreExtender interface {
Store
// TableExists checks if the migrations table exists in the database. Implementing this method
// allows goose to optimize table existence checks by using database-specific system catalogs
// (e.g., pg_tables for PostgreSQL, sqlite_master for SQLite) instead of generic SQL queries.
//
// Return [errors.ErrUnsupported] if the database does not provide an efficient way to check
// table existence.
TableExists(ctx context.Context, db DBTxConn) (bool, error)
}
================================================
FILE: database/store_test.go
================================================
package database_test
import (
"context"
"database/sql"
"errors"
"path/filepath"
"testing"
"github.com/pressly/goose/v3/database"
"github.com/stretchr/testify/require"
"go.uber.org/multierr"
"modernc.org/sqlite"
)
// The goal of this test is to verify the database store package works as expected. This test is not
// meant to be exhaustive or test every possible database dialect. It is meant to verify the Store
// interface works against a real database.
func TestDialectStore(t *testing.T) {
t.Parallel()
t.Run("invalid", func(t *testing.T) {
// Test empty table name.
_, err := database.NewStore(database.DialectSQLite3, "")
require.Error(t, err)
// Test unknown dialect.
_, err = database.NewStore("unknown-dialect", "foo")
require.Error(t, err)
// Test empty dialect.
_, err = database.NewStore("", "foo")
require.Error(t, err)
})
// Test generic behavior.
t.Run("sqlite3", func(t *testing.T) {
db, err := sql.Open("sqlite", ":memory:")
require.NoError(t, err)
testStore(context.Background(), t, database.DialectSQLite3, db, func(t *testing.T, err error) {
t.Helper()
var sqliteErr *sqlite.Error
ok := errors.As(err, &sqliteErr)
require.True(t, ok)
require.Equal(t, 1, sqliteErr.Code()) // Generic error (SQLITE_ERROR)
require.Contains(t, sqliteErr.Error(), "table test_goose_db_version already exists")
})
})
t.Run("ListMigrations", func(t *testing.T) {
dir := t.TempDir()
db, err := sql.Open("sqlite", filepath.Join(dir, "sql_embed.db"))
require.NoError(t, err)
store, err := database.NewStore(database.DialectSQLite3, "foo")
require.NoError(t, err)
err = store.CreateVersionTable(context.Background(), db)
require.NoError(t, err)
insert := func(db *sql.DB, version int64) error {
return store.Insert(context.Background(), db, database.InsertRequest{Version: version})
}
require.NoError(t, insert(db, 1))
require.NoError(t, insert(db, 3))
require.NoError(t, insert(db, 2))
res, err := store.ListMigrations(context.Background(), db)
require.NoError(t, err)
require.Len(t, res, 3)
// Check versions are in descending order: [2, 3, 1]
require.EqualValues(t, 2, res[0].Version)
require.EqualValues(t, 3, res[1].Version)
require.EqualValues(t, 1, res[2].Version)
})
}
// testStore tests various store operations.
//
// If alreadyExists is not nil, it will be used to assert the error returned by CreateVersionTable
// when the version table already exists.
func testStore(
ctx context.Context,
t *testing.T,
d database.Dialect,
db *sql.DB,
alreadyExists func(t *testing.T, err error),
) {
const (
tableName = "test_goose_db_version"
)
store, err := database.NewStore(d, tableName)
require.NoError(t, err)
// Create the version table.
err = runTx(ctx, db, func(tx *sql.Tx) error {
return store.CreateVersionTable(ctx, tx)
})
require.NoError(t, err)
// Create the version table again. This should fail.
err = runTx(ctx, db, func(tx *sql.Tx) error {
return store.CreateVersionTable(ctx, tx)
})
require.Error(t, err)
if alreadyExists != nil {
alreadyExists(t, err)
}
// Get the latest version. There should be none.
_, err = store.GetLatestVersion(ctx, db)
require.ErrorIs(t, err, database.ErrVersionNotFound)
// List migrations. There should be none.
err = runConn(ctx, db, func(conn *sql.Conn) error {
res, err := store.ListMigrations(ctx, conn)
require.NoError(t, err)
require.Empty(t, res, 0)
return nil
})
require.NoError(t, err)
// Insert 5 migrations in addition to the zero migration.
for i := range 6 {
err = runConn(ctx, db, func(conn *sql.Conn) error {
err := store.Insert(ctx, conn, database.InsertRequest{Version: int64(i)})
require.NoError(t, err)
latest, err := store.GetLatestVersion(ctx, conn)
require.NoError(t, err)
require.Equal(t, latest, int64(i))
return nil
})
require.NoError(t, err)
}
// List migrations. There should be 6.
err = runConn(ctx, db, func(conn *sql.Conn) error {
res, err := store.ListMigrations(ctx, conn)
require.NoError(t, err)
require.Len(t, res, 6)
// Check versions are in descending order.
for i := range 6 {
require.EqualValues(t, res[i].Version, 5-i)
}
return nil
})
require.NoError(t, err)
// Delete 3 migrations backwards
for i := 5; i >= 3; i-- {
err = runConn(ctx, db, func(conn *sql.Conn) error {
err := store.Delete(ctx, conn, int64(i))
require.NoError(t, err)
latest, err := store.GetLatestVersion(ctx, conn)
require.NoError(t, err)
require.Equal(t, latest, int64(i-1))
return nil
})
require.NoError(t, err)
}
// List migrations. There should be 3.
err = runConn(ctx, db, func(conn *sql.Conn) error {
res, err := store.ListMigrations(ctx, conn)
require.NoError(t, err)
require.Len(t, res, 3)
// Check that the remaining versions are in descending order.
for i := range 3 {
require.EqualValues(t, res[i].Version, 2-i)
}
return nil
})
require.NoError(t, err)
// Get remaining migrations one by one.
for i := range 3 {
err = runConn(ctx, db, func(conn *sql.Conn) error {
res, err := store.GetMigration(ctx, conn, int64(i))
require.NoError(t, err)
require.True(t, res.IsApplied)
require.False(t, res.Timestamp.IsZero())
return nil
})
require.NoError(t, err)
}
// Delete remaining migrations one by one and use all 3 connection types:
// 1. *sql.Tx
err = runTx(ctx, db, func(tx *sql.Tx) error {
err := store.Delete(ctx, tx, 2)
require.NoError(t, err)
latest, err := store.GetLatestVersion(ctx, tx)
require.NoError(t, err)
require.EqualValues(t, 1, latest)
return nil
})
require.NoError(t, err)
// 2. *sql.Conn
err = runConn(ctx, db, func(conn *sql.Conn) error {
err := store.Delete(ctx, conn, 1)
require.NoError(t, err)
latest, err := store.GetLatestVersion(ctx, conn)
require.NoError(t, err)
require.EqualValues(t, 0, latest)
return nil
})
require.NoError(t, err)
// 3. *sql.DB
err = store.Delete(ctx, db, 0)
require.NoError(t, err)
_, err = store.GetLatestVersion(ctx, db)
require.ErrorIs(t, err, database.ErrVersionNotFound)
// List migrations. There should be none.
err = runConn(ctx, db, func(conn *sql.Conn) error {
res, err := store.ListMigrations(ctx, conn)
require.NoError(t, err)
require.Empty(t, res)
return nil
})
require.NoError(t, err)
// Try to get a migration that does not exist.
err = runConn(ctx, db, func(conn *sql.Conn) error {
_, err := store.GetMigration(ctx, conn, 0)
require.Error(t, err)
require.ErrorIs(t, err, database.ErrVersionNotFound)
return nil
})
require.NoError(t, err)
}
func runTx(ctx context.Context, db *sql.DB, fn func(*sql.Tx) error) (retErr error) {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
if retErr != nil {
retErr = multierr.Append(retErr, tx.Rollback())
}
}()
if err := fn(tx); err != nil {
return err
}
return tx.Commit()
}
func runConn(ctx context.Context, db *sql.DB, fn func(*sql.Conn) error) (retErr error) {
conn, err := db.Conn(ctx)
if err != nil {
return err
}
defer func() {
if retErr != nil {
retErr = multierr.Append(retErr, conn.Close())
}
}()
if err := fn(conn); err != nil {
return err
}
return conn.Close()
}
================================================
FILE: db.go
================================================
package goose
import (
"database/sql"
"fmt"
)
// OpenDBWithDriver creates a connection to a database, and modifies goose internals to be
// compatible with the supplied driver by calling SetDialect.
func OpenDBWithDriver(driver string, dbstring string) (*sql.DB, error) {
if err := SetDialect(driver); err != nil {
return nil, err
}
// The Go ecosystem has added more and more drivers over the years. As a result, there's no
// longer a one-to-one match between the driver name and the dialect name. For instance, there's
// no "redshift" driver, but that's the internal dialect name within goose. Hence, we need to
// convert the dialect name to a supported driver name. This conversion is a best-effort
// attempt, as we can't support both lib/pq and pgx, which some users might have.
//
// We recommend users to create a [NewProvider] with the desired dialect, open a connection
// using their preferred driver, and provide the *sql.DB to goose. This approach removes the
// need for mapping dialects to drivers, rendering this function unnecessary.
switch driver {
case "mssql":
driver = "sqlserver"
case "tidb":
driver = "mysql"
case "spanner":
driver = "spanner"
case "turso":
driver = "libsql"
case "sqlite3":
driver = "sqlite"
case "postgres", "redshift":
driver = "pgx"
case "starrocks":
driver = "mysql"
}
switch driver {
case "postgres", "pgx", "sqlite3", "sqlite", "spanner", "mysql", "sqlserver", "clickhouse", "vertica", "azuresql", "ydb", "libsql", "starrocks":
return sql.Open(driver, dbstring)
default:
return nil, fmt.Errorf("unsupported driver %s", driver)
}
}
================================================
FILE: dialect.go
================================================
package goose
import (
"fmt"
"github.com/pressly/goose/v3/database"
"github.com/pressly/goose/v3/internal/legacystore"
)
// Dialect is the type of database dialect. It is an alias for [database.Dialect].
type Dialect = database.Dialect
const (
DialectCustom Dialect = database.DialectCustom
DialectClickHouse Dialect = database.DialectClickHouse
DialectMSSQL Dialect = database.DialectMSSQL
DialectMySQL Dialect = database.DialectMySQL
DialectPostgres Dialect = database.DialectPostgres
DialectRedshift Dialect = database.DialectRedshift
DialectSQLite3 Dialect = database.DialectSQLite3
DialectSpanner Dialect = database.DialectSpanner
DialectStarrocks Dialect = database.DialectStarrocks
DialectTiDB Dialect = database.DialectTiDB
DialectTurso Dialect = database.DialectTurso
DialectYdB Dialect = database.DialectYdB
// Dialects only available to the [Provider].
DialectAuroraDSQL Dialect = database.DialectAuroraDSQL
// DEPRECATED: Vertica support is deprecated and will be removed in a future release.
DialectVertica Dialect = database.DialectVertica
)
func init() {
store, _ = legacystore.NewStore(DialectPostgres)
}
var store legacystore.Store
// SetDialect sets the dialect to use for the goose package.
func SetDialect(s string) error {
var d Dialect
switch s {
case "postgres", "pgx":
d = DialectPostgres
case "mysql":
d = DialectMySQL
case "sqlite3", "sqlite":
d = DialectSQLite3
case "spanner":
d = DialectSpanner
case "mssql", "azuresql", "sqlserver":
d = DialectMSSQL
case "redshift":
d = DialectRedshift
case "tidb":
d = DialectTiDB
case "clickhouse":
d = DialectClickHouse
case "vertica":
d = DialectVertica
case "ydb":
d = DialectYdB
case "turso":
d = DialectTurso
case "starrocks":
d = DialectStarrocks
default:
return fmt.Errorf("%q: unknown dialect", s)
}
var err error
store, err = legacystore.NewStore(d)
return err
}
================================================
FILE: down.go
================================================
package goose
import (
"context"
"database/sql"
"fmt"
)
// Down rolls back a single migration from the current version.
func Down(db *sql.DB, dir string, opts ...OptionsFunc) error {
ctx := context.Background()
return DownContext(ctx, db, dir, opts...)
}
// DownContext rolls back a single migration from the current version.
func DownContext(ctx context.Context, db *sql.DB, dir string, opts ...OptionsFunc) error {
option := &options{}
for _, f := range opts {
f(option)
}
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
if err != nil {
return err
}
if option.noVersioning {
if len(migrations) == 0 {
return nil
}
currentVersion := migrations[len(migrations)-1].Version
// Migrate only the latest migration down.
return downToNoVersioning(ctx, db, migrations, currentVersion-1)
}
currentVersion, err := GetDBVersionContext(ctx, db)
if err != nil {
return err
}
current, err := migrations.Current(currentVersion)
if err != nil {
return fmt.Errorf("migration %v: %w", currentVersion, err)
}
return current.DownContext(ctx, db)
}
// DownTo rolls back migrations to a specific version.
func DownTo(db *sql.DB, dir string, version int64, opts ...OptionsFunc) error {
ctx := context.Background()
return DownToContext(ctx, db, dir, version, opts...)
}
// DownToContext rolls back migrations to a specific version.
func DownToContext(ctx context.Context, db *sql.DB, dir string, version int64, opts ...OptionsFunc) error {
option := &options{}
for _, f := range opts {
f(option)
}
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
if err != nil {
return err
}
if option.noVersioning {
return downToNoVersioning(ctx, db, migrations, version)
}
for {
currentVersion, err := GetDBVersionContext(ctx, db)
if err != nil {
return err
}
if currentVersion == 0 {
log.Printf("goose: no migrations to run. current version: %d", currentVersion)
return nil
}
current, err := migrations.Current(currentVersion)
if err != nil {
log.Printf("goose: migration file not found for current version (%d), error: %s", currentVersion, err)
return err
}
if current.Version <= version {
log.Printf("goose: no migrations to run. current version: %d", currentVersion)
return nil
}
if err = current.DownContext(ctx, db); err != nil {
return err
}
}
}
// downToNoVersioning applies down migrations down to, but not including, the
// target version.
func downToNoVersioning(ctx context.Context, db *sql.DB, migrations Migrations, version int64) error {
var finalVersion int64
for i := len(migrations) - 1; i >= 0; i-- {
if version >= migrations[i].Version {
finalVersion = migrations[i].Version
break
}
migrations[i].noVersioning = true
if err := migrations[i].DownContext(ctx, db); err != nil {
return err
}
}
log.Printf("goose: down to current file version: %d", finalVersion)
return nil
}
================================================
FILE: examples/README.md
================================================
# 1. [SQL migrations](sql-migrations)
# 2. [Go migrations](go-migrations)
================================================
FILE: examples/go-migrations/00001_create_users_table.sql
================================================
-- +goose Up
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT,
name TEXT,
surname TEXT
);
INSERT INTO users VALUES
(0, 'root', '', ''),
(1, 'vojtechvitek', 'Vojtech', 'Vitek');
-- +goose Down
DROP TABLE users;
================================================
FILE: examples/go-migrations/00002_rename_root.go
================================================
package main
import (
"context"
"database/sql"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigrationContext(Up00002, Down00002)
}
func Up00002(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, "UPDATE users SET username='admin' WHERE username='root';")
return err
}
func Down00002(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, "UPDATE users SET username='root' WHERE username='admin';")
return err
}
================================================
FILE: examples/go-migrations/00003_add_user_no_tx.go
================================================
package main
import (
"context"
"database/sql"
"errors"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigrationNoTxContext(Up00003, Down00003)
}
func Up00003(ctx context.Context, db *sql.DB) error {
id, err := getUserID(db, "jamesbond")
if err != nil {
return err
}
if id == 0 {
query := "INSERT INTO users (username, name, surname) VALUES ($1, $2, $3)"
if _, err := db.ExecContext(ctx, query, "jamesbond", "James", "Bond"); err != nil {
return err
}
}
return nil
}
func getUserID(db *sql.DB, username string) (int, error) {
var id int
err := db.QueryRow("SELECT id FROM users WHERE username = $1", username).Scan(&id)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return 0, err
}
return id, nil
}
func Down00003(ctx context.Context, db *sql.DB) error {
query := "DELETE FROM users WHERE username = $1"
if _, err := db.ExecContext(ctx, query, "jamesbond"); err != nil {
return err
}
return nil
}
================================================
FILE: examples/go-migrations/README.md
================================================
# SQL + Go migrations
## This example: Custom goose binary with built-in Go migrations
```bash
$ go build -o goose-custom *.go
```
```bash
$ ./goose-custom sqlite3 ./foo.db status
Applied At Migration
=======================================
Pending -- 00001_create_users_table.sql
Pending -- 00002_rename_root.go
Pending -- 00003_add_user_no_tx.go
$ ./goose-custom sqlite3 ./foo.db up
OK 00001_create_users_table.sql (711.58µs)
OK 00002_rename_root.go (302.08µs)
OK 00003_add_user_no_tx.go (648.71µs)
goose: no migrations to run. current version: 3
$ ./goose-custom sqlite3 ./foo.db status
Applied At Migration
=======================================
00001_create_users_table.sql
00002_rename_root.go
00003_add_user_no_tx.go
```
## Best practice: Split migrations into a standalone package
1. Move [main.go](main.go) into your `cmd/` directory
2. Rename package name in all `*_.go` migration files from `main` to `migrations`.
3. Import this `migrations` package from your custom [cmd/main.go](main.go) file:
```go
import (
// Invoke init() functions within migrations pkg.
_ "github.com/pressly/goose/example/migrations-go"
)
```
================================================
FILE: examples/go-migrations/main.go
================================================
// This is custom goose binary with sqlite3 support only.
package main
import (
"context"
"flag"
"log"
"os"
"github.com/pressly/goose/v3"
_ "modernc.org/sqlite"
)
var (
flags = flag.NewFlagSet("goose", flag.ExitOnError)
dir = flags.String("dir", ".", "directory with migration files")
)
func main() {
if err := flags.Parse(os.Args[1:]); err != nil {
log.Fatalf("goose: failed to parse flags: %v", err)
}
args := flags.Args()
if len(args) < 3 {
flags.Usage()
return
}
dbstring, command := args[1], args[2]
db, err := goose.OpenDBWithDriver("sqlite", dbstring)
if err != nil {
log.Fatalf("goose: failed to open DB: %v", err)
}
defer func() {
if err := db.Close(); err != nil {
log.Fatalf("goose: failed to close DB: %v", err)
}
}()
arguments := []string{}
if len(args) > 3 {
arguments = append(arguments, args[3:]...)
}
ctx := context.Background()
if err := goose.RunContext(ctx, command, db, *dir, arguments...); err != nil {
log.Fatalf("goose %v: %v", command, err)
}
}
================================================
FILE: examples/sql-migrations/00001_create_users_table.sql
================================================
-- +goose Up
CREATE TABLE users (
id int NOT NULL PRIMARY KEY,
username text,
name text,
surname text
);
INSERT INTO users VALUES
(0, 'root', '', ''),
(1, 'vojtechvitek', 'Vojtech', 'Vitek');
-- +goose Down
DROP TABLE users;
================================================
FILE: examples/sql-migrations/00002_rename_root.sql
================================================
-- +goose Up
-- +goose StatementBegin
UPDATE users SET username='admin' WHERE username='root';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
UPDATE users SET username='root' WHERE username='admin';
-- +goose StatementEnd
================================================
FILE: examples/sql-migrations/00003_no_transaction.sql
================================================
-- +goose NO TRANSACTION
-- +goose Up
CREATE TABLE post (
id int NOT NULL,
title text,
body text,
PRIMARY KEY(id)
);
-- +goose Down
DROP TABLE post;
================================================
FILE: examples/sql-migrations/README.md
================================================
# SQL migrations only
See [this example](../go-migrations) for Go migrations.
```bash
$ go install github.com/pressly/goose/v3/cmd/goose@latest
```
```bash
$ goose sqlite3 ./foo.db status
Applied At Migration
=======================================
Pending -- 00001_create_users_table.sql
Pending -- 00002_rename_root.sql
$ goose sqlite3 ./foo.db up
OK 00001_create_users_table.sql
OK 00002_rename_root.sql
goose: no migrations to run. current version: 2
$ goose sqlite3 ./foo.db status
Applied At Migration
=======================================
Mon Jun 19 21:56:00 2017 -- 00001_create_users_table.sql
Mon Jun 19 21:56:00 2017 -- 00002_rename_root.sql
```
================================================
FILE: fix.go
================================================
package goose
import (
"fmt"
"os"
"path/filepath"
"strings"
)
const seqVersionTemplate = "%05v"
func Fix(dir string) error {
// always use osFS here because it's modifying operation
migrations, err := collectMigrationsFS(osFS{}, dir, minVersion, maxVersion, registeredGoMigrations)
if err != nil {
return err
}
// split into timestamped and versioned migrations
tsMigrations, err := migrations.timestamped()
if err != nil {
return err
}
vMigrations, err := migrations.versioned()
if err != nil {
return err
}
// Initial version.
version := int64(1)
if last, err := vMigrations.Last(); err == nil {
version = last.Version + 1
}
// fix filenames by replacing timestamps with sequential versions
for _, tsm := range tsMigrations {
oldPath := tsm.Source
newPath := strings.Replace(
oldPath,
fmt.Sprintf("%d", tsm.Version),
fmt.Sprintf(seqVersionTemplate, version),
1,
)
if err := os.Rename(oldPath, newPath); err != nil {
return err
}
log.Printf("RENAMED %s => %s", filepath.Base(oldPath), filepath.Base(newPath))
version++
}
return nil
}
================================================
FILE: fix_test.go
================================================
package goose
import (
"fmt"
"os"
"os/exec"
"strings"
"testing"
"time"
)
func TestFix(t *testing.T) {
t.Parallel()
if testing.Short() {
t.Skip("skip long running test")
}
dir := t.TempDir()
defer os.Remove("./bin/fix-goose") // clean up
commands := []string{
"go build -o ./bin/fix-goose ./cmd/goose",
fmt.Sprintf("./bin/fix-goose -dir=%s create create_table", dir),
fmt.Sprintf("./bin/fix-goose -dir=%s create add_users", dir),
fmt.Sprintf("./bin/fix-goose -dir=%s create add_indices", dir),
fmt.Sprintf("./bin/fix-goose -dir=%s create update_users", dir),
fmt.Sprintf("./bin/fix-goose -dir=%s fix", dir),
}
for _, cmd := range commands {
args := strings.Split(cmd, " ")
time.Sleep(1 * time.Second)
cmd := exec.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
}
}
files, err := os.ReadDir(dir)
if err != nil {
t.Fatal(err)
}
// check that the files are in order
for i, f := range files {
expected := fmt.Sprintf("%05v", i+1)
if !strings.HasPrefix(f.Name(), expected) {
t.Errorf("failed to find %s prefix in %s", expected, f.Name())
}
}
// add more migrations and then fix it
commands = []string{
fmt.Sprintf("./bin/fix-goose -dir=%s create remove_column", dir),
fmt.Sprintf("./bin/fix-goose -dir=%s create create_books_table", dir),
fmt.Sprintf("./bin/fix-goose -dir=%s fix", dir),
}
for _, cmd := range commands {
args := strings.Split(cmd, " ")
time.Sleep(1 * time.Second)
out, err := exec.Command(args[0], args[1:]...).CombinedOutput()
if err != nil {
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
}
}
files, err = os.ReadDir(dir)
if err != nil {
t.Fatal(err)
}
// check that the files still in order
for i, f := range files {
expected := fmt.Sprintf("%05v", i+1)
if !strings.HasPrefix(f.Name(), expected) {
t.Errorf("failed to find %s prefix in %s", expected, f.Name())
}
}
}
================================================
FILE: globals.go
================================================
package goose
import (
"errors"
"fmt"
"path/filepath"
)
var (
registeredGoMigrations = make(map[int64]*Migration)
)
// ResetGlobalMigrations resets the global Go migrations registry.
//
// Not safe for concurrent use.
func ResetGlobalMigrations() {
registeredGoMigrations = make(map[int64]*Migration)
}
// SetGlobalMigrations registers Go migrations globally. It returns an error if a migration with the
// same version has already been registered. Go migrations must be constructed using the
// [NewGoMigration] function.
//
// Not safe for concurrent use.
func SetGlobalMigrations(migrations ...*Migration) error {
for _, m := range migrations {
if _, ok := registeredGoMigrations[m.Version]; ok {
return fmt.Errorf("go migration with version %d already registered", m.Version)
}
if err := checkGoMigration(m); err != nil {
return fmt.Errorf("invalid go migration: %w", err)
}
registeredGoMigrations[m.Version] = m
}
return nil
}
func checkGoMigration(m *Migration) error {
if !m.construct {
return errors.New("must use NewGoMigration to construct migrations")
}
if !m.Registered {
return errors.New("must be registered")
}
if m.Type != TypeGo {
return fmt.Errorf("type must be %q", TypeGo)
}
if m.Version < 1 {
return errors.New("version must be greater than zero")
}
if m.Source != "" {
if filepath.Ext(m.Source) != ".go" {
return fmt.Errorf("source must have .go extension: %q", m.Source)
}
// If the source is set, expect it to be a path with a numeric component that matches the
// version. This field is not intended to be used for descriptive purposes.
version, err := NumericComponent(m.Source)
if err != nil {
return fmt.Errorf("invalid source: %w", err)
}
if version != m.Version {
return fmt.Errorf("version:%d does not match numeric component in source %q", m.Version, m.Source)
}
}
if err := checkGoFunc(m.goUp); err != nil {
return fmt.Errorf("up function: %w", err)
}
if err := checkGoFunc(m.goDown); err != nil {
return fmt.Errorf("down function: %w", err)
}
if m.UpFnContext != nil && m.UpFnNoTxContext != nil {
return errors.New("must specify exactly one of UpFnContext or UpFnNoTxContext")
}
if m.UpFn != nil && m.UpFnNoTx != nil {
return errors.New("must specify exactly one of UpFn or UpFnNoTx")
}
if m.DownFnContext != nil && m.DownFnNoTxContext != nil {
return errors.New("must specify exactly one of DownFnContext or DownFnNoTxContext")
}
if m.DownFn != nil && m.DownFnNoTx != nil {
return errors.New("must specify exactly one of DownFn or DownFnNoTx")
}
return nil
}
func checkGoFunc(f *GoFunc) error {
if f.RunTx != nil && f.RunDB != nil {
return errors.New("must specify exactly one of RunTx or RunDB")
}
switch f.Mode {
case TransactionEnabled, TransactionDisabled:
// No functions, but mode is set. This is not an error. It means the user wants to
// record a version with the given mode but not run any functions.
default:
return fmt.Errorf("invalid mode: %d", f.Mode)
}
if f.RunDB != nil && f.Mode != TransactionDisabled {
return fmt.Errorf("transaction mode must be disabled or unspecified when RunDB is set")
}
if f.RunTx != nil && f.Mode != TransactionEnabled {
return fmt.Errorf("transaction mode must be enabled or unspecified when RunTx is set")
}
return nil
}
================================================
FILE: globals_test.go
================================================
package goose
import (
"context"
"database/sql"
"testing"
"github.com/stretchr/testify/require"
)
func TestNewGoMigration(t *testing.T) {
t.Run("valid_both_nil", func(t *testing.T) {
m := NewGoMigration(1, nil, nil)
// roundtrip
require.EqualValues(t, 1, m.Version)
require.Equal(t, TypeGo, m.Type)
require.True(t, m.Registered)
require.EqualValues(t, -1, m.Next)
require.EqualValues(t, -1, m.Previous)
require.Empty(t, m.Source)
require.Nil(t, m.UpFnNoTxContext)
require.Nil(t, m.DownFnNoTxContext)
require.Nil(t, m.UpFnContext)
require.Nil(t, m.DownFnContext)
require.Nil(t, m.UpFn)
require.Nil(t, m.DownFn)
require.Nil(t, m.UpFnNoTx)
require.Nil(t, m.DownFnNoTx)
require.NotNil(t, m.goUp)
require.NotNil(t, m.goDown)
require.Equal(t, TransactionEnabled, m.goUp.Mode)
require.Equal(t, TransactionEnabled, m.goDown.Mode)
})
t.Run("all_set", func(t *testing.T) {
// This will eventually be an error when registering migrations.
m := NewGoMigration(
1,
&GoFunc{RunTx: func(context.Context, *sql.Tx) error { return nil }, RunDB: func(context.Context, *sql.DB) error { return nil }},
&GoFunc{RunTx: func(context.Context, *sql.Tx) error { return nil }, RunDB: func(context.Context, *sql.DB) error { return nil }},
)
// check only functions
require.NotNil(t, m.UpFn)
require.NotNil(t, m.UpFnContext)
require.NotNil(t, m.UpFnNoTx)
require.NotNil(t, m.UpFnNoTxContext)
require.NotNil(t, m.DownFn)
require.NotNil(t, m.DownFnContext)
require.NotNil(t, m.DownFnNoTx)
require.NotNil(t, m.DownFnNoTxContext)
})
}
func TestTransactionMode(t *testing.T) {
t.Cleanup(ResetGlobalMigrations)
runDB := func(context.Context, *sql.DB) error { return nil }
runTx := func(context.Context, *sql.Tx) error { return nil }
err := SetGlobalMigrations(
NewGoMigration(1, &GoFunc{RunTx: runTx, RunDB: runDB}, nil), // cannot specify both
)
require.Error(t, err)
require.Contains(t, err.Error(), "up function: must specify exactly one of RunTx or RunDB")
err = SetGlobalMigrations(
NewGoMigration(1, nil, &GoFunc{RunTx: runTx, RunDB: runDB}), // cannot specify both
)
require.Error(t, err)
require.Contains(t, err.Error(), "down function: must specify exactly one of RunTx or RunDB")
err = SetGlobalMigrations(
NewGoMigration(1, &GoFunc{RunTx: runTx, Mode: TransactionDisabled}, nil), // invalid explicit mode tx
)
require.Error(t, err)
require.Contains(t, err.Error(), "up function: transaction mode must be enabled or unspecified when RunTx is set")
err = SetGlobalMigrations(
NewGoMigration(1, nil, &GoFunc{RunTx: runTx, Mode: TransactionDisabled}), // invalid explicit mode tx
)
require.Error(t, err)
require.Contains(t, err.Error(), "down function: transaction mode must be enabled or unspecified when RunTx is set")
err = SetGlobalMigrations(
NewGoMigration(1, &GoFunc{RunDB: runDB, Mode: TransactionEnabled}, nil), // invalid explicit mode no-tx
)
require.Error(t, err)
require.Contains(t, err.Error(), "up function: transaction mode must be disabled or unspecified when RunDB is set")
err = SetGlobalMigrations(
NewGoMigration(1, nil, &GoFunc{RunDB: runDB, Mode: TransactionEnabled}), // invalid explicit mode no-tx
)
require.Error(t, err)
require.Contains(t, err.Error(), "down function: transaction mode must be disabled or unspecified when RunDB is set")
t.Run("default_mode", func(t *testing.T) {
t.Cleanup(ResetGlobalMigrations)
m := NewGoMigration(1, nil, nil)
err = SetGlobalMigrations(m)
require.NoError(t, err)
require.Len(t, registeredGoMigrations, 1)
registered := registeredGoMigrations[1]
require.NotNil(t, registered.goUp)
require.NotNil(t, registered.goDown)
require.Equal(t, TransactionEnabled, registered.goUp.Mode)
require.Equal(t, TransactionEnabled, registered.goDown.Mode)
migration2 := NewGoMigration(2, nil, nil)
// reset so we can check the default is set
migration2.goUp.Mode, migration2.goDown.Mode = 0, 0
err = SetGlobalMigrations(migration2)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid go migration: up function: invalid mode: 0")
migration3 := NewGoMigration(3, nil, nil)
// reset so we can check the default is set
migration3.goDown.Mode = 0
err = SetGlobalMigrations(migration3)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid go migration: down function: invalid mode: 0")
})
t.Run("unknown_mode", func(t *testing.T) {
m := NewGoMigration(1, nil, nil)
m.goUp.Mode, m.goDown.Mode = 3, 3 // reset to default
err := SetGlobalMigrations(m)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid mode: 3")
})
}
func TestLegacyFunctions(t *testing.T) {
t.Cleanup(ResetGlobalMigrations)
runDB := func(context.Context, *sql.DB) error { return nil }
runTx := func(context.Context, *sql.Tx) error { return nil }
assertMigration := func(t *testing.T, m *Migration, version int64) {
t.Helper()
require.Equal(t, version, m.Version)
require.Equal(t, TypeGo, m.Type)
require.True(t, m.Registered)
require.EqualValues(t, -1, m.Next)
require.EqualValues(t, -1, m.Previous)
require.Empty(t, m.Source)
}
t.Run("all_tx", func(t *testing.T) {
t.Cleanup(ResetGlobalMigrations)
err := SetGlobalMigrations(
NewGoMigration(1, &GoFunc{RunTx: runTx}, &GoFunc{RunTx: runTx}),
)
require.NoError(t, err)
require.Len(t, registeredGoMigrations, 1)
m := registeredGoMigrations[1]
assertMigration(t, m, 1)
// Legacy functions.
require.Nil(t, m.UpFnNoTxContext)
require.Nil(t, m.DownFnNoTxContext)
// Context-aware functions.
require.NotNil(t, m.goUp)
require.NotNil(t, m.UpFnContext)
require.NotNil(t, m.goDown)
require.NotNil(t, m.DownFnContext)
// Always nil
require.NotNil(t, m.UpFn)
require.NotNil(t, m.DownFn)
require.Nil(t, m.UpFnNoTx)
require.Nil(t, m.DownFnNoTx)
})
t.Run("all_db", func(t *testing.T) {
t.Cleanup(ResetGlobalMigrations)
err := SetGlobalMigrations(
NewGoMigration(2, &GoFunc{RunDB: runDB}, &GoFunc{RunDB: runDB}),
)
require.NoError(t, err)
require.Len(t, registeredGoMigrations, 1)
m := registeredGoMigrations[2]
assertMigration(t, m, 2)
// Legacy functions.
require.NotNil(t, m.UpFnNoTxContext)
require.NotNil(t, m.goUp)
require.NotNil(t, m.DownFnNoTxContext)
require.NotNil(t, m.goDown)
// Context-aware functions.
require.Nil(t, m.UpFnContext)
require.Nil(t, m.DownFnContext)
// Always nil
require.Nil(t, m.UpFn)
require.Nil(t, m.DownFn)
require.NotNil(t, m.UpFnNoTx)
require.NotNil(t, m.DownFnNoTx)
})
}
func TestGlobalRegister(t *testing.T) {
t.Cleanup(ResetGlobalMigrations)
// runDB := func(context.Context, *sql.DB) error { return nil }
runTx := func(context.Context, *sql.Tx) error { return nil }
// Success.
err := SetGlobalMigrations([]*Migration{}...)
require.NoError(t, err)
err = SetGlobalMigrations(
NewGoMigration(1, &GoFunc{RunTx: runTx}, nil),
)
require.NoError(t, err)
// Try to register the same migration again.
err = SetGlobalMigrations(
NewGoMigration(1, &GoFunc{RunTx: runTx}, nil),
)
require.Error(t, err)
require.Contains(t, err.Error(), "go migration with version 1 already registered")
err = SetGlobalMigrations(&Migration{Registered: true, Version: 2, Type: TypeGo})
require.Error(t, err)
require.Contains(t, err.Error(), "must use NewGoMigration to construct migrations")
}
func TestCheckMigration(t *testing.T) {
// Success.
err := checkGoMigration(NewGoMigration(1, nil, nil))
require.NoError(t, err)
// Failures.
err = checkGoMigration(&Migration{})
require.Error(t, err)
require.Contains(t, err.Error(), "must use NewGoMigration to construct migrations")
err = checkGoMigration(&Migration{construct: true})
require.Error(t, err)
require.Contains(t, err.Error(), "must be registered")
err = checkGoMigration(&Migration{construct: true, Registered: true})
require.Error(t, err)
require.Contains(t, err.Error(), `type must be "go"`)
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo})
require.Error(t, err)
require.Contains(t, err.Error(), "version must be greater than zero")
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, goUp: &GoFunc{}, goDown: &GoFunc{}})
require.Error(t, err)
require.Contains(t, err.Error(), "up function: invalid mode: 0")
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, goUp: &GoFunc{Mode: TransactionEnabled}, goDown: &GoFunc{}})
require.Error(t, err)
require.Contains(t, err.Error(), "down function: invalid mode: 0")
// Success.
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, goUp: &GoFunc{Mode: TransactionEnabled}, goDown: &GoFunc{Mode: TransactionEnabled}})
require.NoError(t, err)
// Failures.
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, Source: "foo"})
require.Error(t, err)
require.Contains(t, err.Error(), `source must have .go extension: "foo"`)
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, Source: "foo.go"})
require.Error(t, err)
require.Contains(t, err.Error(), `no filename separator '_' found`)
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 2, Source: "00001_foo.sql"})
require.Error(t, err)
require.Contains(t, err.Error(), `source must have .go extension: "00001_foo.sql"`)
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 2, Source: "00001_foo.go"})
require.Error(t, err)
require.Contains(t, err.Error(), `version:2 does not match numeric component in source "00001_foo.go"`)
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
UpFnContext: func(context.Context, *sql.Tx) error { return nil },
UpFnNoTxContext: func(context.Context, *sql.DB) error { return nil },
goUp: &GoFunc{Mode: TransactionEnabled},
goDown: &GoFunc{Mode: TransactionEnabled},
})
require.Error(t, err)
require.Contains(t, err.Error(), "must specify exactly one of UpFnContext or UpFnNoTxContext")
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
DownFnContext: func(context.Context, *sql.Tx) error { return nil },
DownFnNoTxContext: func(context.Context, *sql.DB) error { return nil },
goUp: &GoFunc{Mode: TransactionEnabled},
goDown: &GoFunc{Mode: TransactionEnabled},
})
require.Error(t, err)
require.Contains(t, err.Error(), "must specify exactly one of DownFnContext or DownFnNoTxContext")
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
UpFn: func(*sql.Tx) error { return nil },
UpFnNoTx: func(*sql.DB) error { return nil },
goUp: &GoFunc{Mode: TransactionEnabled},
goDown: &GoFunc{Mode: TransactionEnabled},
})
require.Error(t, err)
require.Contains(t, err.Error(), "must specify exactly one of UpFn or UpFnNoTx")
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
DownFn: func(*sql.Tx) error { return nil },
DownFnNoTx: func(*sql.DB) error { return nil },
goUp: &GoFunc{Mode: TransactionEnabled},
goDown: &GoFunc{Mode: TransactionEnabled},
})
require.Error(t, err)
require.Contains(t, err.Error(), "must specify exactly one of DownFn or DownFnNoTx")
}
================================================
FILE: go.mod
================================================
module github.com/pressly/goose/v3
go 1.25.0
require (
github.com/ClickHouse/clickhouse-go/v2 v2.43.0
github.com/containerd/errdefs v1.0.0
github.com/go-sql-driver/mysql v1.9.3
github.com/jackc/pgx/v5 v5.8.0
github.com/joho/godotenv v1.5.1
github.com/mfridman/interpolate v0.0.2
github.com/mfridman/xflag v0.1.0
github.com/microsoft/go-mssqldb v1.9.6
github.com/moby/moby/api v1.54.0
github.com/moby/moby/client v0.3.0
github.com/sethvargo/go-retry v0.3.0
github.com/stretchr/testify v1.11.1
github.com/tursodatabase/libsql-client-go v0.0.0-20251219100830-236aa1ff8acc
github.com/vertica/vertica-sql-go v1.3.5
github.com/ydb-platform/ydb-go-sdk/v3 v3.127.0
github.com/ziutek/mymysql v1.5.4
go.uber.org/multierr v1.11.0
golang.org/x/sync v0.20.0
modernc.org/sqlite v1.46.1
)
require (
filippo.io/edwards25519 v1.2.0 // indirect
github.com/ClickHouse/ch-go v0.71.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coder/websocket v1.8.14 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elastic/go-sysinfo v1.15.4 // indirect
github.com/elastic/go-windows v1.0.2 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/paulmach/orb v0.12.0 // indirect
github.com/pierrec/lz4/v4 v4.1.25 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/procfs v0.19.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/ydb-platform/ydb-go-genproto v0.0.0-20260128080146-c4ed16b24b37 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.1 // indirect
modernc.org/libc v1.68.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
retract (
v3.21.0 // Invalid replace directives
v3.12.2 // Invalid module reference
v3.12.1 // Invalid module reference
v3.12.0 // Invalid module reference
)
================================================
FILE: go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ClickHouse/ch-go v0.71.0 h1:bUdZ/EZj/LcVHsMqaRUP2holqygrPWQKeMjc6nZoyRM=
github.com/ClickHouse/ch-go v0.71.0/go.mod h1:NwbNc+7jaqfY58dmdDUbG4Jl22vThgx1cYjBw0vtgXw=
github.com/ClickHouse/clickhouse-go/v2 v2.43.0 h1:fUR05TrF1GyvLDa/mAQjkx7KbgwdLRffs2n9O3WobtE=
github.com/ClickHouse/clickhouse-go/v2 v2.43.0/go.mod h1:o6jf7JM/zveWC/PP277BLxjHy5KjnGX/jfljhM4s34g=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elastic/go-sysinfo v1.8.1/go.mod h1:JfllUnzoQV/JRYymbH3dO1yggI3mV2oTKSXsDHM+uIM=
github.com/elastic/go-sysinfo v1.15.4 h1:A3zQcunCxik14MgXu39cXFXcIw2sFXZ0zL886eyiv1Q=
github.com/elastic/go-sysinfo v1.15.4/go.mod h1:ZBVXmqS368dOn/jvijV/zHLfakWTYHBZPk3G244lHrU=
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI=
github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M=
github.com/mfridman/xflag v0.1.0/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE=
github.com/microsoft/go-mssqldb v1.9.6 h1:1MNQg5UiSsokiPz3++K2KPx4moKrwIqly1wv+RyCKTw=
github.com/microsoft/go-mssqldb v1.9.6/go.mod h1:yYMPDufyoF2vVuVCUGtZARr06DKFIhMrluTcgWlXpr4=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=
github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=
github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/paulmach/orb v0.12.0 h1:z+zOwjmG3MyEEqzv92UN49Lg1JFYx0L9GpGKNVDKk1s=
github.com/paulmach/orb v0.12.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM=
github.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tursodatabase/libsql-client-go v0.0.0-20251219100830-236aa1ff8acc h1:lzi/5fg2EfinRlh3v//YyIhnc4tY7BTqazQGwb1ar+0=
github.com/tursodatabase/libsql-client-go v0.0.0-20251219100830-236aa1ff8acc/go.mod h1:08inkKyguB6CGGssc/JzhmQWwBgFQBgjlYFjxjRh7nU=
github.com/vertica/vertica-sql-go v1.3.5 h1:IrfH2WIgzZ45yDHyjVFrXU2LuKNIjF5Nwi90a6cfgUI=
github.com/vertica/vertica-sql-go v1.3.5/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20260128080146-c4ed16b24b37 h1:kUXMT/fM/DpDT66WQgRUf3I8VOAWjypkMf52W5PChwA=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20260128080146-c4ed16b24b37/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
github.com/ydb-platform/ydb-go-sdk/v3 v3.127.0 h1:OfHS9ZkZgCy6y/CJ9N8123DXrgaY2BPxWsQiQ8e3wC8=
github.com/ydb-platform/ydb-go-sdk/v3 v3.127.0/go.mod h1:stS1mQYjbJvwwYaYzKyFY9eMiuVXWWXQA6T+SpOLg9c=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.30.2 h1:4yPaaq9dXYXZ2V8s1UgrC3KIj580l2N4ClrLwnbv2so=
modernc.org/ccgo/v4 v4.30.2/go.mod h1:yZMnhWEdW0qw3EtCndG1+ldRrVGS+bIwyWmAWzS0XEw=
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ=
modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
================================================
FILE: goose.go
================================================
package goose
import (
"context"
"database/sql"
"fmt"
"io/fs"
"strconv"
)
// Deprecated: VERSION will no longer be supported in the next major release.
const VERSION = "v3.18.0"
var (
minVersion = int64(0)
maxVersion = int64((1 << 63) - 1)
timestampFormat = "20060102150405"
verbose = false
noColor = false
// base fs to lookup migrations
baseFS fs.FS = osFS{}
)
// SetVerbose set the goose verbosity mode
func SetVerbose(v bool) {
verbose = v
}
// SetBaseFS sets a base FS to discover migrations. It can be used with 'embed' package.
// Calling with 'nil' argument leads to default behaviour: discovering migrations from os filesystem.
// Note that modifying operations like Create will use os filesystem anyway.
func SetBaseFS(fsys fs.FS) {
if fsys == nil {
fsys = osFS{}
}
baseFS = fsys
}
// Run runs a goose command.
//
// Deprecated: Use RunContext.
func Run(command string, db *sql.DB, dir string, args ...string) error {
ctx := context.Background()
return RunContext(ctx, command, db, dir, args...)
}
// RunContext runs a goose command.
func RunContext(ctx context.Context, command string, db *sql.DB, dir string, args ...string) error {
return run(ctx, command, db, dir, args)
}
// RunWithOptions runs a goose command with options.
//
// Deprecated: Use RunWithOptionsContext.
func RunWithOptions(command string, db *sql.DB, dir string, args []string, options ...OptionsFunc) error {
ctx := context.Background()
return RunWithOptionsContext(ctx, command, db, dir, args, options...)
}
// RunWithOptionsContext runs a goose command with options.
func RunWithOptionsContext(ctx context.Context, command string, db *sql.DB, dir string, args []string, options ...OptionsFunc) error {
return run(ctx, command, db, dir, args, options...)
}
func run(ctx context.Context, command string, db *sql.DB, dir string, args []string, options ...OptionsFunc) error {
switch command {
case "up":
if err := UpContext(ctx, db, dir, options...); err != nil {
return err
}
case "up-by-one":
if err := UpByOneContext(ctx, db, dir, options...); err != nil {
return err
}
case "up-to":
if len(args) == 0 {
return fmt.Errorf("up-to must be of form: goose DRIVER DBSTRING [OPTIONS] up-to VERSION")
}
version, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
return fmt.Errorf("version must be a number (got '%s')", args[0])
}
if err := UpToContext(ctx, db, dir, version, options...); err != nil {
return err
}
case "create":
if len(args) == 0 {
return fmt.Errorf("create must be of form: goose DRIVER DBSTRING [OPTIONS] create NAME [go|sql]")
}
migrationType := "go"
if len(args) == 2 {
migrationType = args[1]
}
if err := Create(db, dir, args[0], migrationType); err != nil {
return err
}
case "down":
if err := DownContext(ctx, db, dir, options...); err != nil {
return err
}
case "down-to":
if len(args) == 0 {
return fmt.Errorf("down-to must be of form: goose DRIVER DBSTRING [OPTIONS] down-to VERSION")
}
version, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
return fmt.Errorf("version must be a number (got '%s')", args[0])
}
if err := DownToContext(ctx, db, dir, version, options...); err != nil {
return err
}
case "fix":
if err := Fix(dir); err != nil {
return err
}
case "redo":
if err := RedoContext(ctx, db, dir, options...); err != nil {
return err
}
case "reset":
if err := ResetContext(ctx, db, dir, options...); err != nil {
return err
}
case "status":
if err := StatusContext(ctx, db, dir, options...); err != nil {
return err
}
case "version":
if err := VersionContext(ctx, db, dir, options...); err != nil {
return err
}
default:
return fmt.Errorf("%q: no such command", command)
}
return nil
}
================================================
FILE: goose_cli_test.go
================================================
package goose_test
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/require"
_ "modernc.org/sqlite"
)
const (
// gooseTestBinaryVersion is utilized in conjunction with a linker variable to set the version
// of a binary created solely for testing purposes. It is used to test the --version flag.
gooseTestBinaryVersion = "v0.0.0"
)
func TestFullBinary(t *testing.T) {
t.Parallel()
cli := buildGooseCLI(t, false)
out, err := cli.run("--version")
require.NoError(t, err)
require.Equal(t, "goose version: "+gooseTestBinaryVersion+"\n", out)
}
func TestLiteBinary(t *testing.T) {
t.Parallel()
cli := buildGooseCLI(t, true)
t.Run("binary_version", func(t *testing.T) {
t.Parallel()
out, err := cli.run("--version")
require.NoError(t, err)
require.Equal(t, "goose version: "+gooseTestBinaryVersion+"\n", out)
})
t.Run("default_binary", func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
total := countSQLFiles(t, "testdata/migrations")
commands := []struct {
cmd string
out string
}{
{"up", "goose: successfully migrated database to version: " + strconv.Itoa(total)},
{"version", "goose: version " + strconv.Itoa(total)},
{"down", "OK"},
{"version", "goose: version " + strconv.Itoa(total-1)},
{"status", ""},
{"reset", "OK"},
{"version", "goose: version 0"},
}
for _, c := range commands {
out, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), c.cmd)
require.NoError(t, err)
require.Contains(t, out, c.out)
}
})
t.Run("gh_issue_532", func(t *testing.T) {
// https://github.com/pressly/goose/issues/532
t.Parallel()
dir := t.TempDir()
total := countSQLFiles(t, "testdata/migrations")
_, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), "up")
require.NoError(t, err)
out, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), "up")
require.NoError(t, err)
require.Contains(t, out, "goose: no migrations to run. current version: "+strconv.Itoa(total))
out, err = cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), "version")
require.NoError(t, err)
require.Contains(t, out, "goose: version "+strconv.Itoa(total))
})
t.Run("gh_issue_293", func(t *testing.T) {
// https://github.com/pressly/goose/issues/293
t.Parallel()
dir := t.TempDir()
total := countSQLFiles(t, "testdata/migrations")
commands := []struct {
cmd string
out string
}{
{"up", "goose: successfully migrated database to version: " + strconv.Itoa(total)},
{"version", "goose: version " + strconv.Itoa(total)},
{"down", "OK"},
{"down", "OK"},
{"version", "goose: version " + strconv.Itoa(total-2)},
{"up", "goose: successfully migrated database to version: " + strconv.Itoa(total)},
{"status", ""},
}
for _, c := range commands {
out, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), c.cmd)
require.NoError(t, err)
require.Contains(t, out, c.out)
}
})
t.Run("gh_issue_336", func(t *testing.T) {
// https://github.com/pressly/goose/issues/336
t.Parallel()
dir := t.TempDir()
_, err := cli.run("-dir="+dir, "sqlite3", filepath.Join(dir, "sql.db"), "up")
require.Error(t, err)
require.Contains(t, err.Error(), "goose run: no migration files found")
})
t.Run("create_and_fix", func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
createEmptyFile(t, dir, "00001_alpha.sql")
createEmptyFile(t, dir, "00003_bravo.sql")
createEmptyFile(t, dir, "20230826163141_charlie.sql")
createEmptyFile(t, dir, "20230826163151_delta.go")
total, err := os.ReadDir(dir)
require.NoError(t, err)
require.Len(t, total, 4)
migrationFiles := []struct {
name string
fileType string
}{
{"echo", "sql"},
{"foxtrot", "go"},
{"golf", ""},
}
for i, f := range migrationFiles {
args := []string{"-dir=" + dir, "create", f.name}
if f.fileType != "" {
args = append(args, f.fileType)
}
out, err := cli.run(args...)
require.NoError(t, err)
require.Contains(t, out, "Created new file")
// ensure different timestamps, granularity is 1 second
if i < len(migrationFiles)-1 {
time.Sleep(1100 * time.Millisecond)
}
}
total, err = os.ReadDir(dir)
require.NoError(t, err)
require.Len(t, total, 7)
out, err := cli.run("-dir="+dir, "fix")
require.NoError(t, err)
require.Contains(t, out, "RENAMED")
files, err := os.ReadDir(dir)
require.NoError(t, err)
require.Len(t, files, 7)
expected := []string{
"00001_alpha.sql",
"00003_bravo.sql",
"00004_charlie.sql",
"00005_delta.go",
"00006_echo.sql",
"00007_foxtrot.go",
"00008_golf.go",
}
for i, f := range files {
require.Equal(t, f.Name(), expected[i])
}
})
}
type gooseBinary struct {
binaryPath string
}
func (g gooseBinary) run(params ...string) (string, error) {
cmd := exec.Command(g.binaryPath, params...)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to run goose command: %v\nout: %v", err, string(out))
}
return string(out), nil
}
// buildGooseCLI builds goose test binary, which is used for testing goose CLI. It is built with all
// drivers enabled, unless lite is true, in which case all drivers are disabled except sqlite3
func buildGooseCLI(t *testing.T, lite bool) gooseBinary {
t.Helper()
binName := "goose-test"
dir := t.TempDir()
output := filepath.Join(dir, binName)
// usage: go build [-o output] [build flags] [packages]
args := []string{
"build",
"-o", output,
"-ldflags=-s -w -X main.version=" + gooseTestBinaryVersion,
}
if lite {
args = append(args, "-tags=no_clickhouse no_mssql no_mysql no_vertica no_postgres")
}
args = append(args, "./cmd/goose")
build := exec.Command("go", args...)
out, err := build.CombinedOutput()
if err != nil {
t.Fatalf("failed to build %s binary: %v: %s", binName, err, string(out))
}
return gooseBinary{
binaryPath: output,
}
}
func countSQLFiles(t *testing.T, dir string) int {
t.Helper()
files, err := filepath.Glob(filepath.Join(dir, "*.sql"))
require.NoError(t, err)
return len(files)
}
func createEmptyFile(t *testing.T, dir, name string) {
t.Helper()
path := filepath.Join(dir, name)
f, err := os.Create(path)
require.NoError(t, err)
defer f.Close()
}
================================================
FILE: goose_embed_test.go
================================================
package goose_test
import (
"database/sql"
"embed"
"io/fs"
"os"
"path/filepath"
"testing"
"github.com/pressly/goose/v3"
"github.com/stretchr/testify/require"
_ "modernc.org/sqlite"
)
//go:embed testdata/migrations/*.sql
var embedMigrations embed.FS
func TestEmbeddedMigrations(t *testing.T) {
dir := t.TempDir()
// not using t.Parallel here to avoid races
db, err := sql.Open("sqlite", filepath.Join(dir, "sql_embed.db"))
require.NoError(t, err)
db.SetMaxOpenConns(1)
migrationFiles, err := fs.ReadDir(embedMigrations, "testdata/migrations")
require.NoError(t, err)
total := len(migrationFiles)
// decouple from existing structure
fsys, err := fs.Sub(embedMigrations, "testdata/migrations")
require.NoError(t, err)
goose.SetBaseFS(fsys)
t.Cleanup(func() { goose.SetBaseFS(nil) })
require.NoError(t, goose.SetDialect("sqlite3"))
t.Run("migration_cycle", func(t *testing.T) {
err := goose.Up(db, ".")
require.NoError(t, err)
ver, err := goose.GetDBVersion(db)
require.NoError(t, err)
require.EqualValues(t, ver, total)
err = goose.Reset(db, ".")
require.NoError(t, err)
ver, err = goose.GetDBVersion(db)
require.NoError(t, err)
require.EqualValues(t, 0, ver)
})
t.Run("create_uses_os_fs", func(t *testing.T) {
dir := t.TempDir()
err := goose.Create(db, dir, "test", "sql")
require.NoError(t, err)
paths, _ := filepath.Glob(filepath.Join(dir, "*test.sql"))
require.NotEmpty(t, paths)
err = goose.Fix(dir)
require.NoError(t, err)
_, err = os.Stat(filepath.Join(dir, "00001_test.sql"))
require.NoError(t, err)
})
}
================================================
FILE: helpers.go
================================================
package goose
import (
"bytes"
"strings"
"unicode"
"unicode/utf8"
)
type camelSnakeStateMachine int
const ( // _$$_This is some text, OK?!
idle camelSnakeStateMachine = iota // 0 ↑ ↑ ↑
firstAlphaNum // 1 ↑ ↑ ↑ ↑ ↑
alphaNum // 2 ↑↑↑ ↑ ↑↑↑ ↑↑↑ ↑
delimiter // 3 ↑ ↑ ↑ ↑ ↑
)
func (s camelSnakeStateMachine) next(r rune) camelSnakeStateMachine {
switch s {
case idle:
if isAlphaNum(r) {
return firstAlphaNum
}
case firstAlphaNum:
if isAlphaNum(r) {
return alphaNum
}
return delimiter
case alphaNum:
if !isAlphaNum(r) {
return delimiter
}
case delimiter:
if isAlphaNum(r) {
return firstAlphaNum
}
return idle
}
return s
}
func camelCase(str string) string {
var b strings.Builder
stateMachine := idle
for i := 0; i < len(str); {
r, size := utf8.DecodeRuneInString(str[i:])
i += size
stateMachine = stateMachine.next(r)
switch stateMachine {
case firstAlphaNum:
b.WriteRune(unicode.ToUpper(r))
case alphaNum:
b.WriteRune(unicode.ToLower(r))
}
}
return b.String()
}
func snakeCase(str string) string {
var b bytes.Buffer
stateMachine := idle
for i := 0; i < len(str); {
r, size := utf8.DecodeRuneInString(str[i:])
i += size
stateMachine = stateMachine.next(r)
switch stateMachine {
case firstAlphaNum, alphaNum:
b.WriteRune(unicode.ToLower(r))
case delimiter:
b.WriteByte('_')
}
}
if stateMachine == idle {
return string(bytes.TrimSuffix(b.Bytes(), []byte{'_'}))
}
return b.String()
}
func isAlphaNum(r rune) bool {
return unicode.IsLetter(r) || unicode.IsNumber(r)
}
================================================
FILE: helpers_test.go
================================================
package goose
import (
"testing"
)
func TestCamelSnake(t *testing.T) {
t.Parallel()
tt := []struct {
in string
camel string
snake string
}{
{in: "Add updated_at to users table", camel: "AddUpdatedAtToUsersTable", snake: "add_updated_at_to_users_table"},
{in: "$()&^%(_--crazy__--input$)", camel: "CrazyInput", snake: "crazy_input"},
}
for _, test := range tt {
if got := camelCase(test.in); got != test.camel {
t.Errorf("unexpected CamelCase for input(%q), got %q, want %q", test.in, got, test.camel)
}
if got := snakeCase(test.in); got != test.snake {
t.Errorf("unexpected snake_case for input(%q), got %q, want %q", test.in, got, test.snake)
}
}
}
================================================
FILE: install.sh
================================================
#!/bin/sh
# Adapted from the Deno installer: Copyright 2019 the Deno authors. All rights reserved. MIT license.
# Ref: https://github.com/denoland/deno_install
# TODO(everyone): Keep this script simple and easily auditable.
# TODO(mf): this should work on Linux and macOS. Not intended for Windows.
set -e
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m)
if [ "$arch" = "aarch64" ]; then
arch="arm64"
fi
if [ $# -eq 0 ]; then
goose_uri="https://github.com/pressly/goose/releases/latest/download/goose_${os}_${arch}"
else
goose_uri="https://github.com/pressly/goose/releases/download/${1}/goose_${os}_${arch}"
fi
goose_install="${GOOSE_INSTALL:-/usr/local}"
bin_dir="${goose_install}/bin"
exe="${bin_dir}/goose"
if [ ! -d "${bin_dir}" ]; then
mkdir -p "${bin_dir}"
fi
curl --silent --show-error --location --fail --location --output "${exe}" "$goose_uri"
chmod +x "${exe}"
echo "Goose was installed successfully to ${exe}"
if command -v goose >/dev/null; then
echo "Run 'goose --help' to get started"
fi
================================================
FILE: internal/controller/store.go
================================================
package controller
import (
"context"
"errors"
"github.com/pressly/goose/v3/database"
)
// A StoreController is used by the goose package to interact with a database. This type is a
// wrapper around the Store interface, but can be extended to include additional (optional) methods
// that are not part of the core Store interface.
type StoreController struct{ database.Store }
var _ database.StoreExtender = (*StoreController)(nil)
// NewStoreController returns a new StoreController that wraps the given Store.
//
// If the Store implements the following optional methods, the StoreController will call them as
// appropriate:
//
// - TableExists(context.Context, DBTxConn) (bool, error)
//
// If the Store does not implement a method, it will either return a [errors.ErrUnsupported] error
// or fall back to the default behavior.
func NewStoreController(store database.Store) *StoreController {
return &StoreController{store}
}
func (c *StoreController) TableExists(ctx context.Context, db database.DBTxConn) (bool, error) {
if t, ok := c.Store.(interface {
TableExists(ctx context.Context, db database.DBTxConn) (bool, error)
}); ok {
return t.TableExists(ctx, db)
}
return false, errors.ErrUnsupported
}
================================================
FILE: internal/dialects/clickhouse.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewClickhouse returns a new [dialect.Querier] for Clickhouse dialect.
func NewClickhouse() dialect.Querier {
return &clickhouse{}
}
type clickhouse struct{}
var _ dialect.Querier = (*clickhouse)(nil)
func (c *clickhouse) CreateTable(tableName string) string {
q := `CREATE TABLE IF NOT EXISTS %s (
version_id Int64,
is_applied UInt8,
date Date default now(),
tstamp DateTime default now()
)
ENGINE = MergeTree()
ORDER BY (date)`
return fmt.Sprintf(q, tableName)
}
func (c *clickhouse) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES ($1, $2)`
return fmt.Sprintf(q, tableName)
}
func (c *clickhouse) DeleteVersion(tableName string) string {
q := `ALTER TABLE %s DELETE WHERE version_id = $1 SETTINGS mutations_sync = 2`
return fmt.Sprintf(q, tableName)
}
func (c *clickhouse) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id = $1 ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (c *clickhouse) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied FROM %s ORDER BY version_id DESC`
return fmt.Sprintf(q, tableName)
}
func (c *clickhouse) GetLatestVersion(tableName string) string {
q := `SELECT max(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/dsql.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewAuroraDSQL returns a new [dialect.Querier] for Aurora DSQL dialect.
func NewAuroraDSQL() dialect.QuerierExtender {
return &dsql{}
}
type dsql struct{}
var _ dialect.QuerierExtender = (*dsql)(nil)
func (d *dsql) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
id integer PRIMARY KEY,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp NOT NULL DEFAULT now()
)`
return fmt.Sprintf(q, tableName)
}
func (d *dsql) InsertVersion(tableName string) string {
q := `INSERT INTO %s (id, version_id, is_applied)
VALUES (
COALESCE((SELECT MAX(id) FROM %s), 0) + 1,
$1,
$2
)`
return fmt.Sprintf(q, tableName, tableName)
}
func (d *dsql) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=$1`
return fmt.Sprintf(q, tableName)
}
func (d *dsql) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=$1 ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (d *dsql) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (d *dsql) GetLatestVersion(tableName string) string {
q := `SELECT max(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
func (d *dsql) TableExists(tableName string) string {
schemaName, tableName := parseTableIdentifier(tableName)
if schemaName != "" {
q := `SELECT EXISTS ( SELECT 1 FROM pg_tables WHERE schemaname = '%s' AND tablename = '%s' )`
return fmt.Sprintf(q, schemaName, tableName)
}
q := `SELECT EXISTS ( SELECT 1 FROM pg_tables WHERE (current_schema() IS NULL OR schemaname = current_schema()) AND tablename = '%s' )`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/mysql.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewMysql returns a new [dialect.Querier] for MySQL dialect.
func NewMysql() dialect.QuerierExtender {
return &mysql{}
}
type mysql struct{}
var _ dialect.QuerierExtender = (*mysql)(nil)
func (m *mysql) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp NULL default now(),
PRIMARY KEY(id)
)`
return fmt.Sprintf(q, tableName)
}
func (m *mysql) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES (?, ?)`
return fmt.Sprintf(q, tableName)
}
func (m *mysql) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=?`
return fmt.Sprintf(q, tableName)
}
func (m *mysql) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (m *mysql) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (m *mysql) GetLatestVersion(tableName string) string {
q := `SELECT MAX(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
func (m *mysql) TableExists(tableName string) string {
schemaName, tableName := parseTableIdentifier(tableName)
if schemaName != "" {
q := `SELECT EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = '%s' AND table_name = '%s' )`
return fmt.Sprintf(q, schemaName, tableName)
}
q := `SELECT EXISTS ( SELECT 1 FROM information_schema.tables WHERE (database() IS NULL OR table_schema = database()) AND table_name = '%s' )`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/postgres.go
================================================
package dialects
import (
"fmt"
"strings"
"github.com/pressly/goose/v3/database/dialect"
)
// NewPostgres returns a new [dialect.Querier] for PostgreSQL dialect.
func NewPostgres() dialect.QuerierExtender {
return &postgres{}
}
type postgres struct{}
var _ dialect.QuerierExtender = (*postgres)(nil)
func (p *postgres) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp NOT NULL DEFAULT now()
)`
return fmt.Sprintf(q, tableName)
}
func (p *postgres) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES ($1, $2)`
return fmt.Sprintf(q, tableName)
}
func (p *postgres) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=$1`
return fmt.Sprintf(q, tableName)
}
func (p *postgres) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=$1 ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (p *postgres) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (p *postgres) GetLatestVersion(tableName string) string {
q := `SELECT max(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
func (p *postgres) TableExists(tableName string) string {
schemaName, tableName := parseTableIdentifier(tableName)
if schemaName != "" {
q := `SELECT EXISTS ( SELECT 1 FROM pg_tables WHERE schemaname = '%s' AND tablename = '%s' )`
return fmt.Sprintf(q, schemaName, tableName)
}
q := `SELECT EXISTS ( SELECT 1 FROM pg_tables WHERE (current_schema() IS NULL OR schemaname = current_schema()) AND tablename = '%s' )`
return fmt.Sprintf(q, tableName)
}
func parseTableIdentifier(name string) (schema, table string) {
schema, table, found := strings.Cut(name, ".")
if !found {
return "", name
}
return schema, table
}
================================================
FILE: internal/dialects/redshift.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// Redshift returns a new [dialect.Querier] for Redshift dialect.
func NewRedshift() dialect.Querier {
return &redshift{}
}
type redshift struct{}
var _ dialect.Querier = (*redshift)(nil)
func (r *redshift) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
id integer NOT NULL identity(1, 1),
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp NULL default sysdate,
PRIMARY KEY(id)
)`
return fmt.Sprintf(q, tableName)
}
func (r *redshift) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES ($1, $2)`
return fmt.Sprintf(q, tableName)
}
func (r *redshift) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=$1`
return fmt.Sprintf(q, tableName)
}
func (r *redshift) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=$1 ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (r *redshift) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (r *redshift) GetLatestVersion(tableName string) string {
q := `SELECT max(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/spanner.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewSpanner returns a [dialect.Querier] for Spanner dialect.
func NewSpanner() dialect.Querier {
return &spanner{}
}
type spanner struct{}
var _ dialect.Querier = (*spanner)(nil)
func (s *spanner) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
version_id INT64 NOT NULL,
is_applied BOOL NOT NULL,
tstamp TIMESTAMP DEFAULT (CURRENT_TIMESTAMP()),
) PRIMARY KEY(version_id)`
return fmt.Sprintf(q, tableName)
}
func (s *spanner) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES (?, ?)`
return fmt.Sprintf(q, tableName)
}
func (s *spanner) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=?`
return fmt.Sprintf(q, tableName)
}
func (s *spanner) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (s *spanner) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY version_id DESC`
return fmt.Sprintf(q, tableName)
}
func (s *spanner) GetLatestVersion(tableName string) string {
q := `SELECT MAX(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/sqlite3.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewSqlite3 returns a [dialect.Querier] for SQLite3 dialect.
func NewSqlite3() dialect.Querier {
return &sqlite3{}
}
type sqlite3 struct{}
var _ dialect.Querier = (*sqlite3)(nil)
func (s *sqlite3) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
id INTEGER PRIMARY KEY AUTOINCREMENT,
version_id INTEGER NOT NULL,
is_applied INTEGER NOT NULL,
tstamp TIMESTAMP DEFAULT (datetime('now'))
)`
return fmt.Sprintf(q, tableName)
}
func (s *sqlite3) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES (?, ?)`
return fmt.Sprintf(q, tableName)
}
func (s *sqlite3) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=?`
return fmt.Sprintf(q, tableName)
}
func (s *sqlite3) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (s *sqlite3) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (s *sqlite3) GetLatestVersion(tableName string) string {
q := `SELECT MAX(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/sqlserver.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewSqlserver returns a [dialect.Querier] for SQL Server dialect.
func NewSqlserver() dialect.Querier {
return &sqlserver{}
}
type sqlserver struct{}
var _ dialect.Querier = (*sqlserver)(nil)
func (s *sqlserver) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
version_id BIGINT NOT NULL,
is_applied BIT NOT NULL,
tstamp DATETIME NULL DEFAULT CURRENT_TIMESTAMP
)`
return fmt.Sprintf(q, tableName)
}
func (s *sqlserver) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES (@p1, @p2)`
return fmt.Sprintf(q, tableName)
}
func (s *sqlserver) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=@p1`
return fmt.Sprintf(q, tableName)
}
func (s *sqlserver) GetMigrationByVersion(tableName string) string {
q := `SELECT TOP 1 tstamp, is_applied FROM %s WHERE version_id=@p1 ORDER BY tstamp DESC`
return fmt.Sprintf(q, tableName)
}
func (s *sqlserver) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied FROM %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (s *sqlserver) GetLatestVersion(tableName string) string {
q := `SELECT MAX(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/starrocks.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewStarrocks returns a [dialect.Querier] for StarRocks dialect.
func NewStarrocks() dialect.Querier {
return &starrocks{}
}
type starrocks struct{}
var _ dialect.Querier = (*starrocks)(nil)
func (m *starrocks) CreateTable(tableName string) string {
q := `CREATE TABLE IF NOT EXISTS %s (
id bigint NOT NULL AUTO_INCREMENT,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp datetime NULL default CURRENT_TIMESTAMP
)
PRIMARY KEY (id)
DISTRIBUTED BY HASH (id)
ORDER BY (id,version_id)`
return fmt.Sprintf(q, tableName)
}
func (m *starrocks) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES (?, ?)`
return fmt.Sprintf(q, tableName)
}
func (m *starrocks) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=?`
return fmt.Sprintf(q, tableName)
}
func (m *starrocks) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (m *starrocks) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (m *starrocks) GetLatestVersion(tableName string) string {
q := `SELECT MAX(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/tidb.go
================================================
package dialects
import (
"fmt"
"github.com/pressly/goose/v3/database/dialect"
)
// NewTidb returns a [dialect.Querier] for TiDB dialect.
func NewTidb() dialect.Querier {
return &Tidb{}
}
type Tidb struct{}
var _ dialect.Querier = (*Tidb)(nil)
func (t *Tidb) CreateTable(tableName string) string {
q := `CREATE TABLE %s (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp NULL default now(),
PRIMARY KEY(id)
)`
return fmt.Sprintf(q, tableName)
}
func (t *Tidb) InsertVersion(tableName string) string {
q := `INSERT INTO %s (version_id, is_applied) VALUES (?, ?)`
return fmt.Sprintf(q, tableName)
}
func (t *Tidb) DeleteVersion(tableName string) string {
q := `DELETE FROM %s WHERE version_id=?`
return fmt.Sprintf(q, tableName)
}
func (t *Tidb) GetMigrationByVersion(tableName string) string {
q := `SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1`
return fmt.Sprintf(q, tableName)
}
func (t *Tidb) ListMigrations(tableName string) string {
q := `SELECT version_id, is_applied from %s ORDER BY id DESC`
return fmt.Sprintf(q, tableName)
}
func (t *Tidb) GetLatestVersion(tableName string) string {
q := `SELECT MAX(version_id) FROM %s`
return fmt.Sprintf(q, tableName)
}
================================================
FILE: internal/dialects/turso.go
================================================
package dialects
import "github.com/pressly/goose/v3/database/dialect"
// NewTurso returns a [dialect.Querier] for Turso dialect.
func NewTurso() dialect.Querier {
return &turso{}
}
type turso struct {
sqlite3
}
var _ dialect.Quer
gitextract_en9629pt/ ├── .github/ │ ├── dependabot.yaml │ └── workflows/ │ ├── ci.yaml │ ├── integration.yaml │ ├── lint.yaml │ └── release.yaml ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yaml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── cmd/ │ └── goose/ │ ├── driver_clickhouse.go │ ├── driver_mssql.go │ ├── driver_mysql.go │ ├── driver_no_mysql.go │ ├── driver_postgres.go │ ├── driver_sqlite3.go │ ├── driver_turso.go │ ├── driver_vertica.go │ ├── driver_ydb.go │ ├── main.go │ └── main_test.go ├── create.go ├── create_test.go ├── database/ │ ├── dialect/ │ │ ├── querier.go │ │ └── querier_extended.go │ ├── dialects.go │ ├── doc.go │ ├── sql_extended.go │ ├── store.go │ ├── store_extended.go │ └── store_test.go ├── db.go ├── dialect.go ├── down.go ├── examples/ │ ├── README.md │ ├── go-migrations/ │ │ ├── 00001_create_users_table.sql │ │ ├── 00002_rename_root.go │ │ ├── 00003_add_user_no_tx.go │ │ ├── README.md │ │ └── main.go │ └── sql-migrations/ │ ├── 00001_create_users_table.sql │ ├── 00002_rename_root.sql │ ├── 00003_no_transaction.sql │ └── README.md ├── fix.go ├── fix_test.go ├── globals.go ├── globals_test.go ├── go.mod ├── go.sum ├── goose.go ├── goose_cli_test.go ├── goose_embed_test.go ├── helpers.go ├── helpers_test.go ├── install.sh ├── internal/ │ ├── controller/ │ │ └── store.go │ ├── dialects/ │ │ ├── clickhouse.go │ │ ├── dsql.go │ │ ├── mysql.go │ │ ├── postgres.go │ │ ├── redshift.go │ │ ├── spanner.go │ │ ├── sqlite3.go │ │ ├── sqlserver.go │ │ ├── starrocks.go │ │ ├── tidb.go │ │ ├── turso.go │ │ ├── vertica.go │ │ └── ydb.go │ ├── gooseutil/ │ │ ├── resolve.go │ │ └── resolve_test.go │ ├── legacystore/ │ │ └── legacystore.go │ ├── migrationstats/ │ │ ├── migration_go.go │ │ ├── migration_sql.go │ │ ├── migrationstats.go │ │ ├── migrationstats_test.go │ │ └── migrationstats_walker.go │ ├── sqlparser/ │ │ ├── parse.go │ │ ├── parse_test.go │ │ ├── parser.go │ │ ├── parser_test.go │ │ └── testdata/ │ │ ├── envsub/ │ │ │ ├── test01/ │ │ │ │ ├── 01.down.golden.sql │ │ │ │ ├── 01.up.golden.sql │ │ │ │ ├── 02.up.golden.sql │ │ │ │ ├── 03.up.golden.sql │ │ │ │ ├── 04.up.golden.sql │ │ │ │ └── input.sql │ │ │ ├── test02/ │ │ │ │ ├── 01.up.golden.sql │ │ │ │ ├── 02.up.golden.sql │ │ │ │ ├── 03.up.golden.sql │ │ │ │ └── input.sql │ │ │ └── test03/ │ │ │ ├── 01.up.golden.sql │ │ │ └── input.sql │ │ ├── invalid/ │ │ │ └── up/ │ │ │ ├── a.sql │ │ │ ├── b.sql │ │ │ ├── c.sql │ │ │ └── d.sql │ │ ├── valid-txn/ │ │ │ ├── 00001_create_users_table.sql │ │ │ ├── 00002_rename_root.sql │ │ │ └── 00003_no_transaction.sql │ │ └── valid-up/ │ │ ├── test01/ │ │ │ ├── 01.up.golden.sql │ │ │ ├── 02.up.golden.sql │ │ │ ├── 03.up.golden.sql │ │ │ └── input.sql │ │ ├── test02/ │ │ │ ├── 01.up.golden.sql │ │ │ └── input.sql │ │ ├── test03/ │ │ │ ├── 01.up.golden.sql │ │ │ └── input.sql │ │ ├── test04/ │ │ │ ├── 01.up.golden.sql │ │ │ ├── 02.up.golden.sql │ │ │ ├── 03.up.golden.sql │ │ │ └── input.sql │ │ ├── test05/ │ │ │ ├── 01.up.golden.sql │ │ │ ├── 02.up.golden.sql │ │ │ └── input.sql │ │ ├── test06/ │ │ │ ├── 01.up.golden.sql │ │ │ ├── 02.up.golden.sql │ │ │ ├── 03.up.golden.sql │ │ │ ├── 04.up.golden.sql │ │ │ ├── 05.up.golden.sql │ │ │ └── input.sql │ │ ├── test07/ │ │ │ ├── 01.up.golden.sql │ │ │ └── input.sql │ │ ├── test08/ │ │ │ ├── 01.up.golden.sql │ │ │ ├── 02.up.golden.sql │ │ │ ├── 03.up.golden.sql │ │ │ ├── 04.up.golden.sql │ │ │ ├── 05.up.golden.sql │ │ │ ├── 06.up.golden.sql │ │ │ └── input.sql │ │ └── test09/ │ │ ├── 01.up.golden.sql │ │ └── input.sql │ └── testing/ │ ├── go.mod │ ├── go.sum │ ├── integration/ │ │ ├── README.md │ │ ├── database_test.go │ │ ├── integration.go │ │ ├── locking/ │ │ │ ├── postgres_locking_test.go │ │ │ └── postgres_table_locking_test.go │ │ └── testdata/ │ │ └── migrations/ │ │ ├── clickhouse/ │ │ │ ├── 00001_a.sql │ │ │ ├── 00002_b.sql │ │ │ └── 00003_c.sql │ │ ├── clickhouse-remote/ │ │ │ └── 00001_a.sql │ │ ├── clickhouse-remote-backup/ │ │ │ └── taxi_zone_lookup.csv │ │ ├── mysql/ │ │ │ ├── 00001_table.sql │ │ │ ├── 00002_insert.sql │ │ │ ├── 00003_alter.sql │ │ │ ├── 00004_empty.sql │ │ │ ├── 00005_no_tx.sql │ │ │ └── 00006_complex.sql │ │ ├── postgres/ │ │ │ ├── 00001_table.sql │ │ │ ├── 00002_insert.sql │ │ │ ├── 00003_alter.sql │ │ │ ├── 00004_empty.sql │ │ │ ├── 00005_no_tx.sql │ │ │ └── 00006_complex.sql │ │ ├── spanner/ │ │ │ ├── 00001_table.sql │ │ │ ├── 00002_insert.sql │ │ │ ├── 00003_alter.sql │ │ │ ├── 00004_empty.sql │ │ │ ├── 00005_no_tx.sql │ │ │ └── 00006_view.sql │ │ ├── starrocks/ │ │ │ ├── 00001_a.sql │ │ │ ├── 00002_b.sql │ │ │ └── 00003_c.sql │ │ ├── turso/ │ │ │ ├── 00001_table.sql │ │ │ ├── 00002_insert.sql │ │ │ ├── 00003_alter.sql │ │ │ ├── 00004_empty.sql │ │ │ └── 00005_no_tx.sql │ │ └── ydb/ │ │ ├── 00001_a.sql │ │ ├── 00002_b.sql │ │ ├── 00003_c.sql │ │ ├── 00004_d.sql │ │ ├── 00005_e.sql │ │ ├── 00006_f.sql │ │ ├── 00007_g.sql │ │ └── 00008_h.sql │ └── testdb/ │ ├── clickhouse.go │ ├── container_healthcheck.go │ ├── mariadb.go │ ├── options.go │ ├── postgres.go │ ├── spanner.go │ ├── starrocks.go │ ├── testdb.go │ ├── turso.go │ └── ydb.go ├── lock/ │ ├── internal/ │ │ ├── store/ │ │ │ ├── postgres.go │ │ │ └── store.go │ │ └── table/ │ │ ├── config.go │ │ └── locker.go │ ├── locker.go │ ├── locktesting/ │ │ └── locktesting.go │ ├── postgres.go │ ├── session_locker_options.go │ ├── table_locker_options.go │ └── table_locker_options_test.go ├── log.go ├── migrate.go ├── migrate_test.go ├── migration.go ├── migration_sql.go ├── osfs.go ├── pkg/ │ └── dockermanage/ │ ├── doc.go │ ├── dockerpostgres/ │ │ ├── postgres.go │ │ └── postgres_test.go │ ├── manager.go │ └── options.go ├── provider.go ├── provider_collect.go ├── provider_collect_test.go ├── provider_errors.go ├── provider_options.go ├── provider_options_test.go ├── provider_run.go ├── provider_run_test.go ├── provider_test.go ├── provider_types.go ├── redo.go ├── register.go ├── reset.go ├── scripts/ │ └── release-notes.sh ├── status.go ├── testdata/ │ ├── migrations/ │ │ ├── 00001_users_table.sql │ │ ├── 00002_posts_table.sql │ │ ├── 00003_comments_table.sql │ │ ├── 00004_insert_data.sql │ │ └── 00005_posts_view.sql │ ├── no-versioning/ │ │ ├── migrations/ │ │ │ ├── 00001_a.sql │ │ │ ├── 00002_b.sql │ │ │ └── 00003_c.sql │ │ └── seed/ │ │ ├── 00001_a.sql │ │ └── 00002_b.sql │ └── testdata.go ├── tests/ │ └── gomigrations/ │ ├── error/ │ │ ├── gomigrations_error_test.go │ │ └── testdata/ │ │ ├── 001_up_no_tx.go │ │ ├── 002_ERROR_insert_no_tx.go │ │ ├── 003_truncate.go │ │ └── 004_ERROR_insert.go │ ├── register/ │ │ ├── register_test.go │ │ └── testdata/ │ │ ├── 001_addmigration.go │ │ ├── 002_addmigrationnotx.go │ │ ├── 003_addmigrationcontext.go │ │ └── 004_addmigrationnotxcontext.go │ └── success/ │ ├── gomigrations_success_test.go │ └── testdata/ │ ├── 001_up_down.go │ ├── 002_up_only.go │ ├── 003_down_only.go │ ├── 004_empty.go │ ├── 005_up_down_no_tx.go │ ├── 006_up_only_no_tx.go │ ├── 007_down_only_no_tx.go │ ├── 008_empty_no_tx.go │ ├── 009_up_down_ctx.go │ ├── 010_up_only_ctx.go │ ├── 011_down_only_ctx.go │ ├── 012_empty_ctx.go │ ├── 013_up_down_no_tx_ctx.go │ ├── 014_up_only_no_tx_ctx.go │ ├── 015_down_only_no_tx_ctx.go │ └── 016_empty_no_tx_ctx.go ├── up.go ├── up_test.go └── version.go
SYMBOL INDEX (890 symbols across 188 files)
FILE: cmd/goose/driver_mysql.go
function normalizeDBString (line 20) | func normalizeDBString(driver string, str string, certfile string, sslce...
constant tlsConfigKey (line 37) | tlsConfigKey = "custom"
function normalizeMySQLDSN (line 39) | func normalizeMySQLDSN(dsn string, tls bool) (string, error) {
function registerTLSConfig (line 51) | func registerTLSConfig(pemfile string, sslcert string, sslkey string) er...
FILE: cmd/goose/driver_no_mysql.go
function normalizeDBString (line 5) | func normalizeDBString(driver string, str string, certfile string, sslce...
FILE: cmd/goose/main.go
function main (line 48) | func main() {
function printDrivers (line 195) | func printDrivers() {
function mergeDrivers (line 208) | func mergeDrivers(drivers []string) []string {
function mergeArgs (line 231) | func mergeArgs(config *envConfig, args []string) []string {
function usage (line 244) | func usage() {
function gooseInit (line 347) | func gooseInit(dir string) error {
function gatherFilenames (line 365) | func gatherFilenames(filename string) ([]string, error) {
function printValidate (line 386) | func printValidate(filename string, verbose bool) error {
type envConfig (line 423) | type envConfig struct
method listEnvs (line 443) | func (c *envConfig) listEnvs() []envVar {
function loadEnvConfig (line 431) | func loadEnvConfig() *envConfig {
type envVar (line 453) | type envVar struct
function envOr (line 459) | func envOr(key, def string) string {
function firstNonEmpty (line 468) | func firstNonEmpty(values ...string) string {
FILE: cmd/goose/main_test.go
function TestFirstNonEmpty (line 7) | func TestFirstNonEmpty(t *testing.T) {
FILE: create.go
type tmplVars (line 13) | type tmplVars struct
function SetSequential (line 23) | func SetSequential(s bool) {
function CreateWithTemplate (line 28) | func CreateWithTemplate(db *sql.DB, dir string, tmpl *template.Template,...
function Create (line 84) | func Create(db *sql.DB, dir, name, migrationType string) error {
FILE: create_test.go
function TestSequential (line 12) | func TestSequential(t *testing.T) {
FILE: database/dialect/querier.go
type Querier (line 7) | type Querier interface
FILE: database/dialect/querier_extended.go
type QuerierExtender (line 15) | type QuerierExtender interface
FILE: database/dialects.go
type Dialect (line 14) | type Dialect
constant DialectCustom (line 17) | DialectCustom Dialect = ""
constant DialectClickHouse (line 18) | DialectClickHouse Dialect = "clickhouse"
constant DialectAuroraDSQL (line 19) | DialectAuroraDSQL Dialect = "dsql"
constant DialectMSSQL (line 20) | DialectMSSQL Dialect = "mssql"
constant DialectMySQL (line 21) | DialectMySQL Dialect = "mysql"
constant DialectPostgres (line 22) | DialectPostgres Dialect = "postgres"
constant DialectRedshift (line 23) | DialectRedshift Dialect = "redshift"
constant DialectSQLite3 (line 24) | DialectSQLite3 Dialect = "sqlite3"
constant DialectSpanner (line 25) | DialectSpanner Dialect = "spanner"
constant DialectStarrocks (line 26) | DialectStarrocks Dialect = "starrocks"
constant DialectTiDB (line 27) | DialectTiDB Dialect = "tidb"
constant DialectTurso (line 28) | DialectTurso Dialect = "turso"
constant DialectYdB (line 29) | DialectYdB Dialect = "ydb"
constant DialectVertica (line 32) | DialectVertica Dialect = "vertica"
function NewStore (line 36) | func NewStore(d Dialect, tableName string) (Store, error) {
function NewStoreFromQuerier (line 67) | func NewStoreFromQuerier(tableName string, querier dialect.Querier) (Sto...
type store (line 80) | type store struct
method Tablename (line 87) | func (s *store) Tablename() string {
method CreateVersionTable (line 91) | func (s *store) CreateVersionTable(ctx context.Context, db DBTxConn) e...
method Insert (line 99) | func (s *store) Insert(ctx context.Context, db DBTxConn, req InsertReq...
method Delete (line 107) | func (s *store) Delete(ctx context.Context, db DBTxConn, version int64...
method GetMigration (line 115) | func (s *store) GetMigration(
method GetLatestVersion (line 134) | func (s *store) GetLatestVersion(ctx context.Context, db DBTxConn) (in...
method ListMigrations (line 146) | func (s *store) ListMigrations(
method TableExists (line 180) | func (s *store) TableExists(ctx context.Context, db DBTxConn) (bool, e...
type queryController (line 196) | type queryController struct
method TableExists (line 209) | func (c *queryController) TableExists(tableName string) string {
function newQueryController (line 199) | func newQueryController(querier dialect.Querier) *queryController {
FILE: database/sql_extended.go
type DBTxConn (line 13) | type DBTxConn interface
FILE: database/store.go
type Store (line 26) | type Store interface
type InsertRequest (line 49) | type InsertRequest struct
type GetMigrationResult (line 58) | type GetMigrationResult struct
type ListMigrationsResult (line 63) | type ListMigrationsResult struct
FILE: database/store_extended.go
type StoreExtender (line 23) | type StoreExtender interface
FILE: database/store_test.go
function TestDialectStore (line 20) | func TestDialectStore(t *testing.T) {
function testStore (line 74) | func testStore(
function runTx (line 223) | func runTx(ctx context.Context, db *sql.DB, fn func(*sql.Tx) error) (ret...
function runConn (line 239) | func runConn(ctx context.Context, db *sql.DB, fn func(*sql.Conn) error) ...
FILE: db.go
function OpenDBWithDriver (line 10) | func OpenDBWithDriver(driver string, dbstring string) (*sql.DB, error) {
FILE: dialect.go
constant DialectCustom (line 14) | DialectCustom Dialect = database.DialectCustom
constant DialectClickHouse (line 15) | DialectClickHouse Dialect = database.DialectClickHouse
constant DialectMSSQL (line 16) | DialectMSSQL Dialect = database.DialectMSSQL
constant DialectMySQL (line 17) | DialectMySQL Dialect = database.DialectMySQL
constant DialectPostgres (line 18) | DialectPostgres Dialect = database.DialectPostgres
constant DialectRedshift (line 19) | DialectRedshift Dialect = database.DialectRedshift
constant DialectSQLite3 (line 20) | DialectSQLite3 Dialect = database.DialectSQLite3
constant DialectSpanner (line 21) | DialectSpanner Dialect = database.DialectSpanner
constant DialectStarrocks (line 22) | DialectStarrocks Dialect = database.DialectStarrocks
constant DialectTiDB (line 23) | DialectTiDB Dialect = database.DialectTiDB
constant DialectTurso (line 24) | DialectTurso Dialect = database.DialectTurso
constant DialectYdB (line 25) | DialectYdB Dialect = database.DialectYdB
constant DialectAuroraDSQL (line 28) | DialectAuroraDSQL Dialect = database.DialectAuroraDSQL
constant DialectVertica (line 31) | DialectVertica Dialect = database.DialectVertica
function init (line 34) | func init() {
function SetDialect (line 41) | func SetDialect(s string) error {
FILE: down.go
function Down (line 10) | func Down(db *sql.DB, dir string, opts ...OptionsFunc) error {
function DownContext (line 16) | func DownContext(ctx context.Context, db *sql.DB, dir string, opts ...Op...
function DownTo (line 45) | func DownTo(db *sql.DB, dir string, version int64, opts ...OptionsFunc) ...
function DownToContext (line 51) | func DownToContext(ctx context.Context, db *sql.DB, dir string, version ...
function downToNoVersioning (line 93) | func downToNoVersioning(ctx context.Context, db *sql.DB, migrations Migr...
FILE: examples/go-migrations/00001_create_users_table.sql
type users (line 2) | CREATE TABLE users (
FILE: examples/go-migrations/00002_rename_root.go
function init (line 10) | func init() {
function Up00002 (line 14) | func Up00002(ctx context.Context, tx *sql.Tx) error {
function Down00002 (line 19) | func Down00002(ctx context.Context, tx *sql.Tx) error {
FILE: examples/go-migrations/00003_add_user_no_tx.go
function init (line 11) | func init() {
function Up00003 (line 15) | func Up00003(ctx context.Context, db *sql.DB) error {
function getUserID (line 29) | func getUserID(db *sql.DB, username string) (int, error) {
function Down00003 (line 38) | func Down00003(ctx context.Context, db *sql.DB) error {
FILE: examples/go-migrations/main.go
function main (line 20) | func main() {
FILE: examples/sql-migrations/00001_create_users_table.sql
type users (line 2) | CREATE TABLE users (
FILE: examples/sql-migrations/00003_no_transaction.sql
type post (line 3) | CREATE TABLE post (
FILE: fix.go
constant seqVersionTemplate (line 10) | seqVersionTemplate = "%05v"
function Fix (line 12) | func Fix(dir string) error {
FILE: fix_test.go
function TestFix (line 12) | func TestFix(t *testing.T) {
FILE: globals.go
function ResetGlobalMigrations (line 16) | func ResetGlobalMigrations() {
function SetGlobalMigrations (line 25) | func SetGlobalMigrations(migrations ...*Migration) error {
function checkGoMigration (line 38) | func checkGoMigration(m *Migration) error {
function checkGoFunc (line 86) | func checkGoFunc(f *GoFunc) error {
FILE: globals_test.go
function TestNewGoMigration (line 11) | func TestNewGoMigration(t *testing.T) {
function TestTransactionMode (line 53) | func TestTransactionMode(t *testing.T) {
function TestLegacyFunctions (line 126) | func TestLegacyFunctions(t *testing.T) {
function TestGlobalRegister (line 190) | func TestGlobalRegister(t *testing.T) {
function TestCheckMigration (line 214) | func TestCheckMigration(t *testing.T) {
FILE: goose.go
constant VERSION (line 12) | VERSION = "v3.18.0"
function SetVerbose (line 26) | func SetVerbose(v bool) {
function SetBaseFS (line 33) | func SetBaseFS(fsys fs.FS) {
function Run (line 44) | func Run(command string, db *sql.DB, dir string, args ...string) error {
function RunContext (line 50) | func RunContext(ctx context.Context, command string, db *sql.DB, dir str...
function RunWithOptions (line 57) | func RunWithOptions(command string, db *sql.DB, dir string, args []strin...
function RunWithOptionsContext (line 63) | func RunWithOptionsContext(ctx context.Context, command string, db *sql....
function run (line 67) | func run(ctx context.Context, command string, db *sql.DB, dir string, ar...
FILE: goose_cli_test.go
constant gooseTestBinaryVersion (line 19) | gooseTestBinaryVersion = "v0.0.0"
function TestFullBinary (line 22) | func TestFullBinary(t *testing.T) {
function TestLiteBinary (line 30) | func TestLiteBinary(t *testing.T) {
type gooseBinary (line 162) | type gooseBinary struct
method run (line 166) | func (g gooseBinary) run(params ...string) (string, error) {
function buildGooseCLI (line 177) | func buildGooseCLI(t *testing.T, lite bool) gooseBinary {
function countSQLFiles (line 202) | func countSQLFiles(t *testing.T, dir string) int {
function createEmptyFile (line 209) | func createEmptyFile(t *testing.T, dir, name string) {
FILE: goose_embed_test.go
function TestEmbeddedMigrations (line 19) | func TestEmbeddedMigrations(t *testing.T) {
FILE: helpers.go
type camelSnakeStateMachine (line 10) | type camelSnakeStateMachine
method next (line 19) | func (s camelSnakeStateMachine) next(r rune) camelSnakeStateMachine {
constant idle (line 13) | idle camelSnakeStateMachine = iota
constant firstAlphaNum (line 14) | firstAlphaNum
constant alphaNum (line 15) | alphaNum
constant delimiter (line 16) | delimiter
function camelCase (line 43) | func camelCase(str string) string {
function snakeCase (line 61) | func snakeCase(str string) string {
function isAlphaNum (line 82) | func isAlphaNum(r rune) bool {
FILE: helpers_test.go
function TestCamelSnake (line 7) | func TestCamelSnake(t *testing.T) {
FILE: internal/controller/store.go
type StoreController (line 13) | type StoreController struct
method TableExists (line 30) | func (c *StoreController) TableExists(ctx context.Context, db database...
function NewStoreController (line 26) | func NewStoreController(store database.Store) *StoreController {
FILE: internal/dialects/clickhouse.go
function NewClickhouse (line 10) | func NewClickhouse() dialect.Querier {
type clickhouse (line 14) | type clickhouse struct
method CreateTable (line 18) | func (c *clickhouse) CreateTable(tableName string) string {
method InsertVersion (line 30) | func (c *clickhouse) InsertVersion(tableName string) string {
method DeleteVersion (line 35) | func (c *clickhouse) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 40) | func (c *clickhouse) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 45) | func (c *clickhouse) ListMigrations(tableName string) string {
method GetLatestVersion (line 50) | func (c *clickhouse) GetLatestVersion(tableName string) string {
FILE: internal/dialects/dsql.go
function NewAuroraDSQL (line 10) | func NewAuroraDSQL() dialect.QuerierExtender {
type dsql (line 14) | type dsql struct
method CreateTable (line 18) | func (d *dsql) CreateTable(tableName string) string {
method InsertVersion (line 28) | func (d *dsql) InsertVersion(tableName string) string {
method DeleteVersion (line 38) | func (d *dsql) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 43) | func (d *dsql) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 48) | func (d *dsql) ListMigrations(tableName string) string {
method GetLatestVersion (line 53) | func (d *dsql) GetLatestVersion(tableName string) string {
method TableExists (line 58) | func (d *dsql) TableExists(tableName string) string {
FILE: internal/dialects/mysql.go
function NewMysql (line 10) | func NewMysql() dialect.QuerierExtender {
type mysql (line 14) | type mysql struct
method CreateTable (line 18) | func (m *mysql) CreateTable(tableName string) string {
method InsertVersion (line 29) | func (m *mysql) InsertVersion(tableName string) string {
method DeleteVersion (line 34) | func (m *mysql) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 39) | func (m *mysql) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 44) | func (m *mysql) ListMigrations(tableName string) string {
method GetLatestVersion (line 49) | func (m *mysql) GetLatestVersion(tableName string) string {
method TableExists (line 54) | func (m *mysql) TableExists(tableName string) string {
FILE: internal/dialects/postgres.go
function NewPostgres (line 11) | func NewPostgres() dialect.QuerierExtender {
type postgres (line 15) | type postgres struct
method CreateTable (line 19) | func (p *postgres) CreateTable(tableName string) string {
method InsertVersion (line 29) | func (p *postgres) InsertVersion(tableName string) string {
method DeleteVersion (line 34) | func (p *postgres) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 39) | func (p *postgres) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 44) | func (p *postgres) ListMigrations(tableName string) string {
method GetLatestVersion (line 49) | func (p *postgres) GetLatestVersion(tableName string) string {
method TableExists (line 54) | func (p *postgres) TableExists(tableName string) string {
function parseTableIdentifier (line 64) | func parseTableIdentifier(name string) (schema, table string) {
FILE: internal/dialects/redshift.go
function NewRedshift (line 10) | func NewRedshift() dialect.Querier {
type redshift (line 14) | type redshift struct
method CreateTable (line 18) | func (r *redshift) CreateTable(tableName string) string {
method InsertVersion (line 29) | func (r *redshift) InsertVersion(tableName string) string {
method DeleteVersion (line 34) | func (r *redshift) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 39) | func (r *redshift) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 44) | func (r *redshift) ListMigrations(tableName string) string {
method GetLatestVersion (line 49) | func (r *redshift) GetLatestVersion(tableName string) string {
FILE: internal/dialects/spanner.go
function NewSpanner (line 10) | func NewSpanner() dialect.Querier {
type spanner (line 14) | type spanner struct
method CreateTable (line 18) | func (s *spanner) CreateTable(tableName string) string {
method InsertVersion (line 27) | func (s *spanner) InsertVersion(tableName string) string {
method DeleteVersion (line 32) | func (s *spanner) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 37) | func (s *spanner) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 42) | func (s *spanner) ListMigrations(tableName string) string {
method GetLatestVersion (line 47) | func (s *spanner) GetLatestVersion(tableName string) string {
FILE: internal/dialects/sqlite3.go
function NewSqlite3 (line 10) | func NewSqlite3() dialect.Querier {
type sqlite3 (line 14) | type sqlite3 struct
method CreateTable (line 18) | func (s *sqlite3) CreateTable(tableName string) string {
method InsertVersion (line 28) | func (s *sqlite3) InsertVersion(tableName string) string {
method DeleteVersion (line 33) | func (s *sqlite3) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 38) | func (s *sqlite3) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 43) | func (s *sqlite3) ListMigrations(tableName string) string {
method GetLatestVersion (line 48) | func (s *sqlite3) GetLatestVersion(tableName string) string {
FILE: internal/dialects/sqlserver.go
function NewSqlserver (line 10) | func NewSqlserver() dialect.Querier {
type sqlserver (line 14) | type sqlserver struct
method CreateTable (line 18) | func (s *sqlserver) CreateTable(tableName string) string {
method InsertVersion (line 28) | func (s *sqlserver) InsertVersion(tableName string) string {
method DeleteVersion (line 33) | func (s *sqlserver) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 38) | func (s *sqlserver) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 43) | func (s *sqlserver) ListMigrations(tableName string) string {
method GetLatestVersion (line 48) | func (s *sqlserver) GetLatestVersion(tableName string) string {
FILE: internal/dialects/starrocks.go
function NewStarrocks (line 10) | func NewStarrocks() dialect.Querier {
type starrocks (line 14) | type starrocks struct
method CreateTable (line 18) | func (m *starrocks) CreateTable(tableName string) string {
method InsertVersion (line 31) | func (m *starrocks) InsertVersion(tableName string) string {
method DeleteVersion (line 36) | func (m *starrocks) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 41) | func (m *starrocks) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 46) | func (m *starrocks) ListMigrations(tableName string) string {
method GetLatestVersion (line 51) | func (m *starrocks) GetLatestVersion(tableName string) string {
FILE: internal/dialects/tidb.go
function NewTidb (line 10) | func NewTidb() dialect.Querier {
type Tidb (line 14) | type Tidb struct
method CreateTable (line 18) | func (t *Tidb) CreateTable(tableName string) string {
method InsertVersion (line 29) | func (t *Tidb) InsertVersion(tableName string) string {
method DeleteVersion (line 34) | func (t *Tidb) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 39) | func (t *Tidb) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 44) | func (t *Tidb) ListMigrations(tableName string) string {
method GetLatestVersion (line 49) | func (t *Tidb) GetLatestVersion(tableName string) string {
FILE: internal/dialects/turso.go
function NewTurso (line 6) | func NewTurso() dialect.Querier {
type turso (line 10) | type turso struct
FILE: internal/dialects/vertica.go
function NewVertica (line 12) | func NewVertica() dialect.Querier {
type vertica (line 16) | type vertica struct
method CreateTable (line 20) | func (v *vertica) CreateTable(tableName string) string {
method InsertVersion (line 31) | func (v *vertica) InsertVersion(tableName string) string {
method DeleteVersion (line 36) | func (v *vertica) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 41) | func (v *vertica) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 46) | func (v *vertica) ListMigrations(tableName string) string {
method GetLatestVersion (line 51) | func (v *vertica) GetLatestVersion(tableName string) string {
FILE: internal/dialects/ydb.go
function NewYDB (line 10) | func NewYDB() dialect.Querier {
type ydb (line 14) | type ydb struct
method CreateTable (line 22) | func (c *ydb) CreateTable(tableName string) string {
method InsertVersion (line 34) | func (c *ydb) InsertVersion(tableName string) string {
method DeleteVersion (line 48) | func (c *ydb) DeleteVersion(tableName string) string {
method GetMigrationByVersion (line 54) | func (c *ydb) GetMigrationByVersion(tableName string) string {
method ListMigrations (line 60) | func (c *ydb) ListMigrations(tableName string) string {
method GetLatestVersion (line 68) | func (c *ydb) GetLatestVersion(tableName string) string {
function formatYDBTableName (line 18) | func formatYDBTableName(tableName string) string {
FILE: internal/gooseutil/resolve.go
function UpVersions (line 19) | func UpVersions(
function newMissingError (line 86) | func newMissingError(
FILE: internal/gooseutil/resolve_test.go
function TestResolveVersions (line 11) | func TestResolveVersions(t *testing.T) {
FILE: internal/legacystore/legacystore.go
type Store (line 23) | type Store interface
function NewStore (line 51) | func NewStore(d database.Dialect) (Store, error) {
type GetMigrationResult (line 84) | type GetMigrationResult struct
type ListMigrationsResult (line 89) | type ListMigrationsResult struct
type store (line 94) | type store struct
method CreateVersionTable (line 100) | func (s *store) CreateVersionTable(ctx context.Context, tx *sql.Tx, ta...
method InsertVersion (line 106) | func (s *store) InsertVersion(ctx context.Context, tx *sql.Tx, tableNa...
method InsertVersionNoTx (line 112) | func (s *store) InsertVersionNoTx(ctx context.Context, db *sql.DB, tab...
method DeleteVersion (line 118) | func (s *store) DeleteVersion(ctx context.Context, tx *sql.Tx, tableNa...
method DeleteVersionNoTx (line 124) | func (s *store) DeleteVersionNoTx(ctx context.Context, db *sql.DB, tab...
method GetMigration (line 130) | func (s *store) GetMigration(
method ListMigrations (line 149) | func (s *store) ListMigrations(ctx context.Context, db *sql.DB, tableN...
FILE: internal/migrationstats/migration_go.go
constant registerGoFuncName (line 14) | registerGoFuncName = "AddMigration"
constant registerGoFuncNameNoTx (line 15) | registerGoFuncNameNoTx = "AddMigrationNoTx"
constant registerGoFuncNameContext (line 16) | registerGoFuncNameContext = "AddMigrationContext"
constant registerGoFuncNameNoTxContext (line 17) | registerGoFuncNameNoTxContext = "AddMigrationNoTxContext"
type goMigration (line 20) | type goMigration struct
function parseGoFile (line 26) | func parseGoFile(r io.Reader) (*goMigration, error) {
function parseInitFunc (line 51) | func parseInitFunc(fd *ast.FuncDecl) (*goMigration, error) {
FILE: internal/migrationstats/migration_sql.go
type sqlMigration (line 11) | type sqlMigration struct
function parseSQLFile (line 16) | func parseSQLFile(r io.Reader, debug bool) (*sqlMigration, error) {
FILE: internal/migrationstats/migrationstats.go
type FileWalker (line 12) | type FileWalker interface
type Stats (line 18) | type Stats struct
function GatherStats (line 33) | func GatherStats(fw FileWalker, debug bool) ([]*Stats, error) {
function nilAsNumber (line 73) | func nilAsNumber(s string) int {
FILE: internal/migrationstats/migrationstats_test.go
function TestParsingGoMigrations (line 12) | func TestParsingGoMigrations(t *testing.T) {
function TestGoMigrationStats (line 43) | func TestGoMigrationStats(t *testing.T) {
function checkGoStats (line 75) | func checkGoStats(t *testing.T, stats *Stats, filename string, version i...
function TestParsingGoMigrationsError (line 84) | func TestParsingGoMigrationsError(t *testing.T) {
FILE: internal/migrationstats/migrationstats_walker.go
function NewFileWalker (line 12) | func NewFileWalker(filenames ...string) FileWalker {
type fileWalker (line 18) | type fileWalker struct
method Walk (line 24) | func (f *fileWalker) Walk(fn func(filename string, r io.Reader) error)...
function walk (line 37) | func walk(filename string, fn func(filename string, r io.Reader) error) ...
FILE: internal/sqlparser/parse.go
type ParsedSQL (line 11) | type ParsedSQL struct
function ParseAllFromFS (line 16) | func ParseAllFromFS(fsys fs.FS, filename string, debug bool) (*ParsedSQL...
function parse (line 46) | func parse(fsys fs.FS, filename string, direction Direction, debug bool)...
FILE: internal/sqlparser/parse_test.go
function TestParseAllFromFS (line 12) | func TestParseAllFromFS(t *testing.T) {
function assertParsedSQL (line 69) | func assertParsedSQL(t *testing.T, got *sqlparser.ParsedSQL, useTx bool,...
function newFile (line 77) | func newFile(data string) *fstest.MapFile {
FILE: internal/sqlparser/parser.go
type Direction (line 17) | type Direction
method String (line 31) | func (d Direction) String() string {
method ToBool (line 35) | func (d Direction) ToBool() bool {
constant DirectionUp (line 20) | DirectionUp Direction = "up"
constant DirectionDown (line 21) | DirectionDown Direction = "down"
function FromBool (line 24) | func FromBool(b bool) Direction {
type parserState (line 39) | type parserState
constant start (line 42) | start parserState = iota
constant gooseUp (line 43) | gooseUp
constant gooseStatementBeginUp (line 44) | gooseStatementBeginUp
constant gooseStatementEndUp (line 45) | gooseStatementEndUp
constant gooseDown (line 46) | gooseDown
constant gooseStatementBeginDown (line 47) | gooseStatementBeginDown
constant gooseStatementEndDown (line 48) | gooseStatementEndDown
type stateMachine (line 51) | type stateMachine struct
method get (line 63) | func (s *stateMachine) get() parserState {
method set (line 67) | func (s *stateMachine) set(new parserState) {
method print (line 77) | func (s *stateMachine) print(msg string, args ...any) {
function newStateMachine (line 56) | func newStateMachine(begin parserState, verbose bool) *stateMachine {
constant grayColor (line 73) | grayColor = "\033[90m"
constant resetColor (line 74) | resetColor = "\033[00m"
constant scanBufSize (line 84) | scanBufSize = 4 * 1024 * 1024
function ParseSQLMigration (line 103) | func ParseSQLMigration(r io.Reader, direction Direction, debug bool) (st...
type annotation (line 289) | type annotation
constant annotationUp (line 292) | annotationUp annotation = "Up"
constant annotationDown (line 293) | annotationDown annotation = "Down"
constant annotationStatementBegin (line 294) | annotationStatementBegin annotation = "StatementBegin"
constant annotationStatementEnd (line 295) | annotationStatementEnd annotation = "StatementEnd"
constant annotationNoTransaction (line 296) | annotationNoTransaction annotation = "NO TRANSACTION"
constant annotationEnvsubOn (line 297) | annotationEnvsubOn annotation = "ENVSUB ON"
constant annotationEnvsubOff (line 298) | annotationEnvsubOff annotation = "ENVSUB OFF"
function extractAnnotation (line 319) | func extractAnnotation(line string) (annotation, error) {
function missingSemicolonError (line 353) | func missingSemicolonError(state parserState, direction Direction, s str...
type envWrapper (line 361) | type envWrapper struct
method Get (line 365) | func (e *envWrapper) Get(key string) (string, bool) {
function cleanupStatement (line 369) | func cleanupStatement(input string) string {
function endsWithSemicolon (line 375) | func endsWithSemicolon(line string) bool {
FILE: internal/sqlparser/parser_test.go
function TestMain (line 18) | func TestMain(m *testing.M) {
function TestSemicolons (line 23) | func TestSemicolons(t *testing.T) {
function TestSplitStatements (line 48) | func TestSplitStatements(t *testing.T) {
function TestInvalidUp (line 89) | func TestInvalidUp(t *testing.T) {
function TestUseTransactions (line 105) | func TestUseTransactions(t *testing.T) {
function TestParsingErrors (line 135) | func TestParsingErrors(t *testing.T) {
function TestValidUp (line 373) | func TestValidUp(t *testing.T) {
function testValid (line 409) | func testValid(t *testing.T, dir string, count int, direction Direction) {
function compareStatements (line 421) | func compareStatements(t *testing.T, dir string, statements []string, di...
function isCIEnvironment (line 461) | func isCIEnvironment() bool {
function TestEnvsub (line 466) | func TestEnvsub(t *testing.T) {
function TestEnvsubError (line 493) | func TestEnvsubError(t *testing.T) {
function Test_extractAnnotation (line 511) | func Test_extractAnnotation(t *testing.T) {
FILE: internal/sqlparser/testdata/envsub/test01/01.up.golden.sql
type us_east_post (line 1) | CREATE TABLE us_east_post (
FILE: internal/sqlparser/testdata/envsub/test01/input.sql
type GOOSE_ENV_REGION (line 3) | CREATE TABLE ${GOOSE_ENV_REGION}post (
FILE: internal/sqlparser/testdata/envsub/test02/01.up.golden.sql
type post (line 1) | CREATE TABLE post (
FILE: internal/sqlparser/testdata/envsub/test02/02.up.golden.sql
type post (line 1) | CREATE TABLE post (
FILE: internal/sqlparser/testdata/envsub/test02/input.sql
type post (line 4) | CREATE TABLE post (
type post (line 14) | CREATE TABLE post (
FILE: internal/sqlparser/testdata/envsub/test03/01.up.golden.sql
type post (line 1) | CREATE TABLE post (
FILE: internal/sqlparser/testdata/envsub/test03/input.sql
type post (line 2) | CREATE TABLE post (
FILE: internal/sqlparser/testdata/valid-txn/00001_create_users_table.sql
type users (line 2) | CREATE TABLE users (
FILE: internal/sqlparser/testdata/valid-txn/00003_no_transaction.sql
type post (line 3) | CREATE TABLE post (
FILE: internal/sqlparser/testdata/valid-up/test01/01.up.golden.sql
type emp (line 1) | CREATE TABLE emp (
FILE: internal/sqlparser/testdata/valid-up/test01/02.up.golden.sql
function emp_stamp (line 1) | CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
FILE: internal/sqlparser/testdata/valid-up/test01/input.sql
type emp (line 2) | CREATE TABLE emp (
function emp_stamp (line 10) | CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
FILE: internal/sqlparser/testdata/valid-up/test02/01.up.golden.sql
type emp (line 1) | CREATE TABLE emp (
type emp_audit (line 6) | CREATE TABLE emp_audit(
function process_emp_audit (line 14) | CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_a...
FILE: internal/sqlparser/testdata/valid-up/test02/input.sql
type emp (line 3) | CREATE TABLE emp (
type emp_audit (line 8) | CREATE TABLE emp_audit(
function process_emp_audit (line 16) | CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_a...
FILE: internal/sqlparser/testdata/valid-up/test03/01.up.golden.sql
function cs_update_referrer_type_proc (line 1) | CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '
FILE: internal/sqlparser/testdata/valid-up/test03/input.sql
function cs_update_referrer_type_proc (line 3) | CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '
FILE: internal/sqlparser/testdata/valid-up/test04/01.up.golden.sql
type ssh_keys (line 1) | CREATE TABLE ssh_keys (
FILE: internal/sqlparser/testdata/valid-up/test04/input.sql
type ssh_keys (line 3) | CREATE TABLE ssh_keys (
FILE: internal/sqlparser/testdata/valid-up/test05/01.up.golden.sql
type ssh_keys (line 1) | CREATE TABLE ssh_keys (
FILE: internal/sqlparser/testdata/valid-up/test05/02.up.golden.sql
type ssh_keys_backup (line 1) | CREATE TABLE ssh_keys_backup (
FILE: internal/sqlparser/testdata/valid-up/test05/input.sql
type ssh_keys (line 3) | CREATE TABLE ssh_keys (
type ssh_keys_backup (line 14) | CREATE TABLE ssh_keys_backup (
FILE: internal/sqlparser/testdata/valid-up/test06/01.up.golden.sql
type article (line 1) | CREATE TABLE article (
FILE: internal/sqlparser/testdata/valid-up/test06/04.up.golden.sql
function do_something (line 1) | CREATE FUNCTION do_something(sql TEXT) RETURNS INTEGER AS $$
FILE: internal/sqlparser/testdata/valid-up/test06/input.sql
type article (line 3) | CREATE TABLE article (
function do_something (line 33) | CREATE FUNCTION do_something(sql TEXT) RETURNS INTEGER AS $$
FILE: internal/sqlparser/testdata/valid-up/test07/01.up.golden.sql
type public (line 1) | CREATE INDEX ON public.users (user_id)
FILE: internal/sqlparser/testdata/valid-up/test07/input.sql
type public (line 4) | CREATE INDEX ON public.users (user_id)
FILE: internal/sqlparser/testdata/valid-up/test08/01.up.golden.sql
type `table_a` (line 1) | CREATE TABLE `table_a` (
FILE: internal/sqlparser/testdata/valid-up/test08/02.up.golden.sql
type `table_b` (line 1) | CREATE TABLE `table_b` (
FILE: internal/sqlparser/testdata/valid-up/test08/03.up.golden.sql
type `table_c` (line 1) | CREATE TABLE `table_c` (
FILE: internal/sqlparser/testdata/valid-up/test08/input.sql
type `table_a` (line 3) | CREATE TABLE `table_a` (
type `table_b` (line 9) | CREATE TABLE `table_b` (
type `table_c` (line 15) | CREATE TABLE `table_c` (
FILE: internal/sqlparser/testdata/valid-up/test09/01.up.golden.sql
type t (line 1) | create table t ( id int )
FILE: internal/sqlparser/testdata/valid-up/test09/input.sql
type t (line 3) | create table t ( id int )
FILE: internal/testing/integration/database_test.go
function TestPostgres (line 13) | func TestPostgres(t *testing.T) {
function TestSpanner (line 24) | func TestSpanner(t *testing.T) {
function TestClickhouse (line 35) | func TestClickhouse(t *testing.T) {
function TestClickhouseRemote (line 87) | func TestClickhouseRemote(t *testing.T) {
function TestMySQL (line 103) | func TestMySQL(t *testing.T) {
function TestTurso (line 114) | func TestTurso(t *testing.T) {
function TestYDB (line 125) | func TestYDB(t *testing.T) {
function TestStarrocks (line 136) | func TestStarrocks(t *testing.T) {
FILE: internal/testing/integration/integration.go
type collected (line 16) | type collected struct
function collectMigrations (line 21) | func collectMigrations(t *testing.T, dir string) []collected {
function testDatabase (line 39) | func testDatabase(t *testing.T, dialect database.Dialect, db *sql.DB, mi...
FILE: internal/testing/integration/locking/postgres_locking_test.go
function TestPostgresSessionLocker (line 24) | func TestPostgresSessionLocker(t *testing.T) {
function TestPostgresProviderLocking (line 171) | func TestPostgresProviderLocking(t *testing.T) {
function TestPostgresPending (line 411) | func TestPostgresPending(t *testing.T) {
function existsPgLock (line 552) | func existsPgLock(ctx context.Context, db *sql.DB, lockID int64) (bool, ...
FILE: internal/testing/integration/locking/postgres_table_locking_test.go
function TestConcurrentTableLocking (line 19) | func TestConcurrentTableLocking(t *testing.T) {
function TestSequentialTableLocking (line 49) | func TestSequentialTableLocking(t *testing.T) {
function TestLockerImplementations (line 106) | func TestLockerImplementations(t *testing.T) {
function TestPostgresTableLockerIntegration (line 229) | func TestPostgresTableLockerIntegration(t *testing.T) {
FILE: internal/testing/integration/testdata/migrations/clickhouse/00001_a.sql
type trips (line 2) | CREATE TABLE IF NOT EXISTS trips
FILE: internal/testing/integration/testdata/migrations/clickhouse/00002_b.sql
type clickstream (line 2) | CREATE TABLE IF NOT EXISTS clickstream (
FILE: internal/testing/integration/testdata/migrations/mysql/00001_table.sql
type owners (line 3) | CREATE TABLE owners (
type repos (line 9) | CREATE TABLE IF NOT EXISTS repos (
FILE: internal/testing/integration/testdata/migrations/mysql/00005_no_tx.sql
type owners_owner_name_idx (line 4) | CREATE UNIQUE INDEX owners_owner_name_idx ON owners(owner_name)
FILE: internal/testing/integration/testdata/migrations/postgres/00001_table.sql
type owners (line 5) | CREATE TABLE owners (
type repos (line 11) | CREATE TABLE IF NOT EXISTS repos (
FILE: internal/testing/integration/testdata/migrations/postgres/00005_no_tx.sql
type owners (line 4) | CREATE UNIQUE INDEX CONCURRENTLY ON owners(owner_name)
FILE: internal/testing/integration/testdata/migrations/postgres/00006_complex.sql
function insert_repository (line 3) | CREATE OR REPLACE FUNCTION insert_repository(
FILE: internal/testing/integration/testdata/migrations/spanner/00005_no_tx.sql
type owners_owner_name_idx (line 5) | CREATE UNIQUE NULL_FILTERED INDEX owners_owner_name_idx ON owners (owner...
FILE: internal/testing/integration/testdata/migrations/spanner/00006_view.sql
type view_owners (line 5) | CREATE VIEW view_owners
FILE: internal/testing/integration/testdata/migrations/starrocks/00002_b.sql
type testing (line 3) | CREATE TABLE testing.test_migrations_1 (
type testing (line 14) | CREATE TABLE testing.test_migrations_2 (
FILE: internal/testing/integration/testdata/migrations/turso/00001_table.sql
type owners (line 3) | CREATE TABLE owners (
type repos (line 9) | CREATE TABLE IF NOT EXISTS repos (
FILE: internal/testing/integration/testdata/migrations/turso/00005_no_tx.sql
type idx_owners_owner_name (line 4) | CREATE UNIQUE INDEX IF NOT EXISTS idx_owners_owner_name ON owners(owner_...
FILE: internal/testing/integration/testdata/migrations/ydb/00001_a.sql
type owners (line 3) | CREATE TABLE owners (
type repos (line 9) | CREATE TABLE repos (
FILE: internal/testing/integration/testdata/migrations/ydb/00006_f.sql
type stargazers (line 3) | CREATE TABLE stargazers (
FILE: internal/testing/integration/testdata/migrations/ydb/00007_g.sql
type issues (line 3) | CREATE TABLE issues (
FILE: internal/testing/testdb/clickhouse.go
constant CLICKHOUSE_IMAGE (line 18) | CLICKHOUSE_IMAGE = "clickhouse/clickhouse-server"
constant CLICKHOUSE_VERSION (line 19) | CLICKHOUSE_VERSION = "24-alpine"
constant CLICKHOUSE_DB (line 21) | CLICKHOUSE_DB = "clickdb"
constant CLICKHOUSE_USER (line 22) | CLICKHOUSE_USER = "clickuser"
constant CLICKHOUSE_PASSWORD (line 23) | CLICKHOUSE_PASSWORD = "password1"
constant CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT (line 24) | CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT = "1"
function newClickHouse (line 27) | func newClickHouse(opts ...OptionsFunc) (*sql.DB, func(), error) {
function clickHouseOpenDB (line 92) | func clickHouseOpenDB(address string, tlsConfig *tls.Config, debug bool)...
FILE: internal/testing/testdb/container_healthcheck.go
function containerWaitHealthy (line 12) | func containerWaitHealthy(ctx context.Context, pool *dockertest.Pool, id...
function containerHealthStatus (line 31) | func containerHealthStatus(ctx context.Context, pool *dockertest.Pool, i...
FILE: internal/testing/testdb/mariadb.go
constant MARIADB_IMAGE (line 17) | MARIADB_IMAGE = "mariadb"
constant MARIADB_VERSION (line 18) | MARIADB_VERSION = "11"
constant MARIADB_DB (line 20) | MARIADB_DB = "testdb"
constant MARIADB_USER (line 21) | MARIADB_USER = "tester"
constant MARIADB_PASSWORD (line 22) | MARIADB_PASSWORD = "password1"
function newMariaDB (line 25) | func newMariaDB(opts ...OptionsFunc) (*sql.DB, func(), error) {
FILE: internal/testing/testdb/options.go
type options (line 3) | type options struct
type OptionsFunc (line 8) | type OptionsFunc
function WithBindPort (line 10) | func WithBindPort(n int) OptionsFunc {
function WithDebug (line 14) | func WithDebug(b bool) OptionsFunc {
FILE: internal/testing/testdb/postgres.go
constant POSTGRES_IMAGE (line 16) | POSTGRES_IMAGE = "postgres"
constant POSTGRES_VERSION (line 17) | POSTGRES_VERSION = "16-alpine"
constant POSTGRES_DB (line 19) | POSTGRES_DB = "testdb"
constant POSTGRES_USER (line 20) | POSTGRES_USER = "postgres"
constant POSTGRES_PASSWORD (line 21) | POSTGRES_PASSWORD = "password1"
function newPostgres (line 24) | func newPostgres(opts ...OptionsFunc) (*sql.DB, func(), error) {
FILE: internal/testing/testdb/spanner.go
constant SPANNER_IMAGE (line 23) | SPANNER_IMAGE = "gcr.io/cloud-spanner-emulator/emulator"
constant SPANNER_VERSION (line 24) | SPANNER_VERSION = "latest"
constant SPANNER_PROJECT (line 26) | SPANNER_PROJECT = "test-project"
constant SPANNER_INSTANCE (line 27) | SPANNER_INSTANCE = "test-instance"
constant SPANNER_DATABASE (line 28) | SPANNER_DATABASE = "test-db"
function newSpanner (line 31) | func newSpanner(opts ...OptionsFunc) (*sql.DB, func(), error) {
function createSpannerResources (line 104) | func createSpannerResources(ctx context.Context) error {
FILE: internal/testing/testdb/starrocks.go
constant STARROCKS_IMAGE (line 17) | STARROCKS_IMAGE = "starrocks/allin1-ubuntu"
constant STARROCKS_VERSION (line 18) | STARROCKS_VERSION = "3.5.11"
constant STARROCKS_USER (line 20) | STARROCKS_USER = "root"
constant STARROCKS_INIT_DB (line 21) | STARROCKS_INIT_DB = "migrations"
function newStarrocks (line 24) | func newStarrocks(opts ...OptionsFunc) (*sql.DB, func(), error) {
FILE: internal/testing/testdb/testdb.go
function NewClickHouse (line 6) | func NewClickHouse(options ...OptionsFunc) (db *sql.DB, cleanup func(), ...
function NewPostgres (line 11) | func NewPostgres(options ...OptionsFunc) (db *sql.DB, cleanup func(), er...
function NewSpanner (line 16) | func NewSpanner(options ...OptionsFunc) (db *sql.DB, cleanup func(), err...
function NewMariaDB (line 21) | func NewMariaDB(options ...OptionsFunc) (db *sql.DB, cleanup func(), err...
function NewYdb (line 26) | func NewYdb(options ...OptionsFunc) (db *sql.DB, cleanup func(), err err...
function NewStarrocks (line 31) | func NewStarrocks(options ...OptionsFunc) (db *sql.DB, cleanup func(), e...
FILE: internal/testing/testdb/turso.go
constant TURSO_IMAGE (line 16) | TURSO_IMAGE = "ghcr.io/tursodatabase/libsql-server"
constant TURSO_VERSION (line 17) | TURSO_VERSION = "v0.24.7"
constant TURSO_PORT (line 18) | TURSO_PORT = "8080"
function NewTurso (line 22) | func NewTurso(options ...OptionsFunc) (db *sql.DB, cleanup func(), err e...
function newTurso (line 26) | func newTurso(opts ...OptionsFunc) (*sql.DB, func(), error) {
function tursoOpenDB (line 86) | func tursoOpenDB(container *dockertest.Resource) (*sql.DB, error) {
FILE: internal/testing/testdb/ydb.go
constant YDB_IMAGE (line 21) | YDB_IMAGE = "ghcr.io/ydb-platform/local-ydb"
constant YDB_VERSION (line 22) | YDB_VERSION = "24.1"
constant YDB_PORT (line 23) | YDB_PORT = "2136"
constant YDB_UI_PORT (line 24) | YDB_UI_PORT = "8765"
constant YDB_DATABASE (line 25) | YDB_DATABASE = "local"
function newYdb (line 28) | func newYdb(opts ...OptionsFunc) (*sql.DB, func(), error) {
FILE: lock/internal/store/postgres.go
function NewPostgres (line 15) | func NewPostgres(tableName string) (LockStore, error) {
type postgresStore (line 26) | type postgresStore struct
method TableExists (line 30) | func (s *postgresStore) TableExists(
method CreateLockTable (line 53) | func (s *postgresStore) CreateLockTable(
method AcquireLock (line 84) | func (s *postgresStore) AcquireLock(
method ReleaseLock (line 134) | func (s *postgresStore) ReleaseLock(
method UpdateLease (line 175) | func (s *postgresStore) UpdateLease(
method CheckLockStatus (line 212) | func (s *postgresStore) CheckLockStatus(
method CleanupStaleLocks (line 238) | func (s *postgresStore) CleanupStaleLocks(ctx context.Context, db *sql...
function formatDurationAsInterval (line 272) | func formatDurationAsInterval(d time.Duration) string {
function parseTableIdentifier (line 276) | func parseTableIdentifier(name string) (schema, table string) {
FILE: lock/internal/store/store.go
type LockStore (line 10) | type LockStore interface
type LockStatus (line 30) | type LockStatus struct
type AcquireLockResult (line 38) | type AcquireLockResult struct
type ReleaseLockResult (line 44) | type ReleaseLockResult struct
type UpdateLeaseResult (line 49) | type UpdateLeaseResult struct
FILE: lock/internal/table/config.go
type Config (line 9) | type Config struct
type ProbeConfig (line 25) | type ProbeConfig struct
FILE: lock/internal/table/locker.go
type RetryPolicyFunc (line 21) | type RetryPolicyFunc
type Locker (line 25) | type Locker struct
method Lock (line 81) | func (l *Locker) Lock(ctx context.Context, db *sql.DB) error {
method Unlock (line 134) | func (l *Locker) Unlock(ctx context.Context, db *sql.DB) error {
method startHeartbeat (line 173) | func (l *Locker) startHeartbeat(parentCtx context.Context, db *sql.DB) {
method stopHeartbeat (line 213) | func (l *Locker) stopHeartbeat() {
method shouldRetry (line 224) | func (l *Locker) shouldRetry(err error) bool {
function New (line 46) | func New(lockStore store.LockStore, cfg Config) *Locker {
function randomHex (line 231) | func randomHex(n int) string {
FILE: lock/locker.go
type SessionLocker (line 20) | type SessionLocker interface
type Locker (line 29) | type Locker interface
FILE: lock/locktesting/locktesting.go
function TestProviderLocking (line 27) | func TestProviderLocking(
function TestConcurrentLocking (line 118) | func TestConcurrentLocking(
FILE: lock/postgres.go
function NewPostgresTableLocker (line 33) | func NewPostgresTableLocker(options ...TableLockerOption) (Locker, error) {
function NewPostgresSessionLocker (line 71) | func NewPostgresSessionLocker(opts ...SessionLockerOption) (SessionLocke...
type postgresSessionLocker (line 101) | type postgresSessionLocker struct
method SessionLock (line 109) | func (l *postgresSessionLocker) SessionLock(ctx context.Context, conn ...
method SessionUnlock (line 127) | func (l *postgresSessionLocker) SessionUnlock(ctx context.Context, con...
FILE: lock/session_locker_options.go
constant DefaultLockID (line 13) | DefaultLockID int64 = 4097083626
type SessionLockerOption (line 17) | type SessionLockerOption interface
function WithLockID (line 24) | func WithLockID(lockID int64) SessionLockerOption {
function WithLockTimeout (line 38) | func WithLockTimeout(period, failureThreshold uint64) SessionLockerOption {
function WithUnlockTimeout (line 61) | func WithUnlockTimeout(period, failureThreshold uint64) SessionLockerOpt...
type sessionLockerConfig (line 77) | type sessionLockerConfig struct
type probe (line 85) | type probe struct
type sessionLockerConfigFunc (line 94) | type sessionLockerConfigFunc
method apply (line 96) | func (f sessionLockerConfigFunc) apply(cfg *sessionLockerConfig) error {
FILE: lock/table_locker_options.go
constant DefaultLockTableName (line 14) | DefaultLockTableName = "goose_lock"
type TableLockerOption (line 18) | type TableLockerOption interface
function WithTableName (line 23) | func WithTableName(tableName string) TableLockerOption {
function WithTableLockID (line 35) | func WithTableLockID(lockID int64) TableLockerOption {
function WithTableLeaseDuration (line 47) | func WithTableLeaseDuration(duration time.Duration) TableLockerOption {
function WithTableHeartbeatInterval (line 59) | func WithTableHeartbeatInterval(interval time.Duration) TableLockerOption {
function WithTableLockTimeout (line 70) | func WithTableLockTimeout(intervalDuration time.Duration, failureThresho...
function WithTableUnlockTimeout (line 87) | func WithTableUnlockTimeout(intervalDuration time.Duration, failureThres...
function WithTableLogger (line 105) | func WithTableLogger(logger *slog.Logger) TableLockerOption {
function WithTableRetryPolicy (line 125) | func WithTableRetryPolicy(retryPolicy func(error) bool) TableLockerOption {
type tableLockerConfigFunc (line 132) | type tableLockerConfigFunc
method apply (line 134) | func (f tableLockerConfigFunc) apply(cfg *table.Config) error {
FILE: lock/table_locker_options_test.go
function TestTableLockerOptions (line 10) | func TestTableLockerOptions(t *testing.T) {
FILE: log.go
type Logger (line 10) | type Logger interface
function SetLogger (line 16) | func SetLogger(l Logger) {
type stdLogger (line 21) | type stdLogger struct
method Fatalf (line 23) | func (*stdLogger) Fatalf(format string, v ...any) { std.Fatalf(format,...
method Printf (line 24) | func (*stdLogger) Printf(format string, v ...any) { std.Printf(format,...
function NopLogger (line 27) | func NopLogger() Logger {
type nopLogger (line 31) | type nopLogger struct
method Fatalf (line 35) | func (*nopLogger) Fatalf(format string, v ...any) {}
method Printf (line 36) | func (*nopLogger) Printf(format string, v ...any) {}
FILE: migrate.go
type Migrations (line 30) | type Migrations
method Len (line 33) | func (ms Migrations) Len() int { return len(ms) }
method Swap (line 34) | func (ms Migrations) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
method Less (line 35) | func (ms Migrations) Less(i, j int) bool {
method Current (line 43) | func (ms Migrations) Current(current int64) (*Migration, error) {
method Next (line 54) | func (ms Migrations) Next(current int64) (*Migration, error) {
method Previous (line 65) | func (ms Migrations) Previous(current int64) (*Migration, error) {
method Last (line 76) | func (ms Migrations) Last() (*Migration, error) {
method versioned (line 85) | func (ms Migrations) versioned() (Migrations, error) {
method timestamped (line 102) | func (ms Migrations) timestamped() (Migrations, error) {
method String (line 121) | func (ms Migrations) String() string {
function collectMigrationsFS (line 129) | func collectMigrationsFS(
function CollectMigrations (line 175) | func CollectMigrations(dirpath string, current, target int64) (Migration...
function sortAndConnectMigrations (line 179) | func sortAndConnectMigrations(migrations Migrations) Migrations {
function versionFilter (line 196) | func versionFilter(v, current, target int64) bool {
function EnsureDBVersion (line 208) | func EnsureDBVersion(db *sql.DB) (int64, error) {
function EnsureDBVersionContext (line 215) | func EnsureDBVersionContext(ctx context.Context, db *sql.DB) (int64, err...
function createVersionTable (line 255) | func createVersionTable(ctx context.Context, db *sql.DB) error {
function GetDBVersion (line 272) | func GetDBVersion(db *sql.DB) (int64, error) {
function GetDBVersionContext (line 278) | func GetDBVersionContext(ctx context.Context, db *sql.DB) (int64, error) {
function collectGoMigrations (line 299) | func collectGoMigrations(
FILE: migrate_test.go
function TestMigrationSort (line 13) | func TestMigrationSort(t *testing.T) {
function newMigration (line 31) | func newMigration(v int64, src string) *Migration {
function validateMigrationSort (line 35) | func validateMigrationSort(t *testing.T, ms Migrations, sorted []int64) {
function TestCollectMigrations (line 67) | func TestCollectMigrations(t *testing.T) {
function TestVersionFilter (line 225) | func TestVersionFilter(t *testing.T) {
function createEmptyFile (line 255) | func createEmptyFile(t *testing.T, dir, name string) {
function clearMap (line 263) | func clearMap(m map[int64]*Migration) {
FILE: migration.go
function NewGoMigration (line 21) | func NewGoMigration(version int64, up, down *GoFunc) *Migration {
type Migration (line 85) | type Migration struct
method String (line 182) | func (m *Migration) String() string {
method Up (line 187) | func (m *Migration) Up(db *sql.DB) error {
method UpContext (line 193) | func (m *Migration) UpContext(ctx context.Context, db *sql.DB) error {
method Down (line 201) | func (m *Migration) Down(db *sql.DB) error {
method DownContext (line 207) | func (m *Migration) DownContext(ctx context.Context, db *sql.DB) error {
method run (line 214) | func (m *Migration) run(ctx context.Context, db *sql.DB, direction boo...
method ref (line 396) | func (m *Migration) ref() string {
type sqlMigration (line 121) | type sqlMigration struct
type GoFunc (line 135) | type GoFunc struct
type TransactionMode (line 155) | type TransactionMode
method String (line 162) | func (m TransactionMode) String() string {
constant TransactionEnabled (line 158) | TransactionEnabled TransactionMode = iota + 1
constant TransactionDisabled (line 159) | TransactionDisabled
type MigrationRecord (line 176) | type MigrationRecord struct
function runGoMigrationNoTx (line 291) | func runGoMigrationNoTx(
function runGoMigration (line 311) | func runGoMigration(
function insertOrDeleteVersion (line 345) | func insertOrDeleteVersion(ctx context.Context, tx *sql.Tx, version int6...
function insertOrDeleteVersionNoTx (line 352) | func insertOrDeleteVersionNoTx(ctx context.Context, db *sql.DB, version ...
function NumericComponent (line 363) | func NumericComponent(filename string) (int64, error) {
function truncateDuration (line 382) | func truncateDuration(d time.Duration) time.Duration {
FILE: migration_sql.go
function runSQLMigration (line 18) | func runSQLMigration(
constant grayColor (line 93) | grayColor = "\033[90m"
constant resetColor (line 94) | resetColor = "\033[00m"
function verboseInfo (line 97) | func verboseInfo(s string, args ...any) {
function clearStatement (line 112) | func clearStatement(s string) string {
FILE: osfs.go
type osFS (line 10) | type osFS struct
method Open (line 12) | func (osFS) Open(name string) (fs.File, error) { return os.Open(filepa...
method ReadDir (line 14) | func (osFS) ReadDir(name string) ([]fs.DirEntry, error) { return os.Re...
method Stat (line 16) | func (osFS) Stat(name string) (fs.FileInfo, error) { return os.Stat(fi...
method ReadFile (line 18) | func (osFS) ReadFile(name string) ([]byte, error) { return os.ReadFile...
method Glob (line 20) | func (osFS) Glob(pattern string) ([]string, error) { return filepath.G...
type noopFS (line 22) | type noopFS struct
method Open (line 26) | func (f noopFS) Open(name string) (fs.File, error) {
FILE: pkg/dockermanage/dockerpostgres/postgres.go
constant DefaultImage (line 18) | DefaultImage = "postgres:16-alpine"
constant DefaultDatabase (line 21) | DefaultDatabase = "testdb"
constant DefaultUser (line 24) | DefaultUser = "postgres"
constant DefaultPassword (line 27) | DefaultPassword = "password1"
constant defaultContainerPort (line 29) | defaultContainerPort = "5432/tcp"
type Option (line 33) | type Option interface
type optionFunc (line 37) | type optionFunc
method apply (line 39) | func (f optionFunc) apply(cfg *config) error {
type config (line 43) | type config struct
function defaultConfig (line 52) | func defaultConfig() *config {
function WithImage (line 65) | func WithImage(image string) Option {
function WithDatabase (line 77) | func WithDatabase(database string) Option {
function WithUser (line 89) | func WithUser(user string) Option {
function WithPassword (line 101) | func WithPassword(password string) Option {
function WithHostPort (line 112) | func WithHostPort(port int) Option {
function WithLabel (line 123) | func WithLabel(key, value string) Option {
function WithLabels (line 135) | func WithLabels(labels map[string]string) Option {
type Instance (line 149) | type Instance struct
method DSN (line 157) | func (i *Instance) DSN() string {
function Start (line 172) | func Start(ctx context.Context, manager *dockermanage.Manager, options ....
function TCPReady (line 222) | func TCPReady(ctx context.Context, c *dockermanage.Container) error {
FILE: pkg/dockermanage/dockerpostgres/postgres_test.go
function newManager (line 18) | func newManager(t *testing.T) *dockermanage.Manager {
function TestStartAndConnect (line 28) | func TestStartAndConnect(t *testing.T) {
function TestStartAndStop (line 54) | func TestStartAndStop(t *testing.T) {
function TestManagedLabel (line 67) | func TestManagedLabel(t *testing.T) {
function TestExecPgDump (line 88) | func TestExecPgDump(t *testing.T) {
FILE: pkg/dockermanage/manager.go
constant defaultReadinessTimeout (line 24) | defaultReadinessTimeout = 30 * time.Second
constant defaultReadinessDelay (line 25) | defaultReadinessDelay = 500 * time.Millisecond
type Container (line 29) | type Container struct
type ReadinessFunc (line 38) | type ReadinessFunc
type Manager (line 41) | type Manager struct
method Start (line 68) | func (m *Manager) Start(ctx context.Context, options ...Option) (_ *Co...
method Stop (line 179) | func (m *Manager) Stop(ctx context.Context, containerID string) error {
method Remove (line 188) | func (m *Manager) Remove(ctx context.Context, containerID string) error {
method WaitReady (line 215) | func (m *Manager) WaitReady(ctx context.Context, container *Container,...
method ListManaged (line 252) | func (m *Manager) ListManaged(ctx context.Context) ([]string, error) {
method StopManaged (line 268) | func (m *Manager) StopManaged(ctx context.Context) error {
method RemoveManaged (line 287) | func (m *Manager) RemoveManaged(ctx context.Context) error {
method Exec (line 319) | func (m *Manager) Exec(ctx context.Context, containerID string, opts E...
method Close (line 366) | func (m *Manager) Close() error {
method pullImageIfNotExists (line 405) | func (m *Manager) pullImageIfNotExists(ctx context.Context, imageName ...
function NewManager (line 47) | func NewManager(logger *slog.Logger) (*Manager, error) {
function newManagerWithClient (line 57) | func newManagerWithClient(dockerClient *client.Client, logger *slog.Logg...
function resolveBoundPort (line 157) | func resolveBoundPort(containerJSON container.InspectResponse, container...
type WaitOption (line 197) | type WaitOption
type waitConfig (line 199) | type waitConfig struct
function WithTimeout (line 205) | func WithTimeout(d time.Duration) WaitOption {
function WithDelay (line 210) | func WithDelay(d time.Duration) WaitOption {
type ExecOptions (line 306) | type ExecOptions struct
type ExecResult (line 314) | type ExecResult struct
constant streamStdout (line 373) | streamStdout byte = 1
constant streamStderr (line 374) | streamStderr byte = 2
constant streamHeaderSize (line 375) | streamHeaderSize = 8
function demuxDockerStream (line 380) | func demuxDockerStream(r io.Reader, stdout, stderr io.Writer) error {
FILE: pkg/dockermanage/options.go
constant DefaultHostIP (line 16) | DefaultHostIP = "127.0.0.1"
constant ManagedLabelKey (line 20) | ManagedLabelKey = "pressly.goose"
type Option (line 24) | type Option interface
type optionFunc (line 28) | type optionFunc
method apply (line 30) | func (f optionFunc) apply(cfg *config) error {
type config (line 34) | type config struct
function defaultConfig (line 46) | func defaultConfig() *config {
function WithName (line 57) | func WithName(name string) Option {
function WithImage (line 69) | func WithImage(image string) Option {
function WithContainerPort (line 81) | func WithContainerPort(port string) Option {
function WithContainerPortTCP (line 93) | func WithContainerPortTCP(port int) Option {
function WithHostIP (line 108) | func WithHostIP(hostIP string) Option {
function WithHostPort (line 120) | func WithHostPort(port int) Option {
function WithEnv (line 131) | func WithEnv(key, value string) Option {
function WithEnvVars (line 146) | func WithEnvVars(envVars []string) Option {
function WithAutoRemove (line 154) | func WithAutoRemove(autoRemove bool) Option {
function WithPullProgress (line 162) | func WithPullProgress(w io.Writer) Option {
function WithLabel (line 170) | func WithLabel(key, value string) Option {
function WithLabels (line 182) | func WithLabels(labels map[string]string) Option {
FILE: provider.go
type Provider (line 25) | type Provider struct
method Status (line 161) | func (p *Provider) Status(ctx context.Context) ([]*MigrationStatus, er...
method HasPending (line 170) | func (p *Provider) HasPending(ctx context.Context) (bool, error) {
method GetVersions (line 178) | func (p *Provider) GetVersions(ctx context.Context) (current, target i...
method GetDBVersion (line 185) | func (p *Provider) GetDBVersion(ctx context.Context) (int64, error) {
method ListSources (line 195) | func (p *Provider) ListSources() []*Source {
method Ping (line 208) | func (p *Provider) Ping(ctx context.Context) error {
method Close (line 213) | func (p *Provider) Close() error {
method ApplyVersion (line 223) | func (p *Provider) ApplyVersion(ctx context.Context, version int64, di...
method Up (line 244) | func (p *Provider) Up(ctx context.Context) ([]*MigrationResult, error) {
method UpByOne (line 257) | func (p *Provider) UpByOne(ctx context.Context) (*MigrationResult, err...
method UpTo (line 291) | func (p *Provider) UpTo(ctx context.Context, version int64) ([]*Migrat...
method Down (line 308) | func (p *Provider) Down(ctx context.Context) (*MigrationResult, error) {
method DownTo (line 338) | func (p *Provider) DownTo(ctx context.Context, version int64) ([]*Migr...
method up (line 347) | func (p *Provider) up(
method down (line 412) | func (p *Provider) down(
method apply (line 468) | func (p *Provider) apply(
method getVersions (line 518) | func (p *Provider) getVersions(ctx context.Context) (current, target i...
method hasPending (line 545) | func (p *Provider) hasPending(ctx context.Context) (_ bool, retErr err...
method status (line 609) | func (p *Provider) status(ctx context.Context) (_ []*MigrationStatus, ...
method getDBMaxVersion (line 648) | func (p *Provider) getDBMaxVersion(ctx context.Context, conn *sql.Conn...
function NewProvider (line 58) | func NewProvider(dialect Dialect, db *sql.DB, fsys fs.FS, opts ...Provid...
function newProvider (line 108) | func newProvider(
function getVersionsFromMigrations (line 591) | func getVersionsFromMigrations(in []*Migration) []int64 {
function getVersionsFromListMigrations (line 600) | func getVersionsFromListMigrations(in []*database.ListMigrationsResult) ...
FILE: provider_collect.go
type fileSources (line 13) | type fileSources struct
function collectFilesystemSources (line 27) | func collectFilesystemSources(
function newSQLMigration (line 103) | func newSQLMigration(source Source) *Migration {
function merge (line 116) | func merge(sources *fileSources, registered map[int64]*Migration) ([]*Mi...
function unregisteredError (line 177) | func unregisteredError(unregistered []string) error {
FILE: provider_collect_test.go
function TestCollectFileSources (line 11) | func TestCollectFileSources(t *testing.T) {
function TestMerge (line 184) | func TestMerge(t *testing.T) {
function assertMigration (line 289) | func assertMigration(t *testing.T, got *Migration, want Source) {
function newSQLOnlyFS (line 305) | func newSQLOnlyFS() fstest.MapFS {
function newSource (line 314) | func newSource(t MigrationType, fullpath string, version int64) Source {
FILE: provider_errors.go
type PartialError (line 29) | type PartialError struct
method Error (line 39) | func (e *PartialError) Error() string {
method Unwrap (line 46) | func (e *PartialError) Unwrap() error {
FILE: provider_options.go
constant DefaultTablename (line 15) | DefaultTablename = "goose_db_version"
type ProviderOption (line 19) | type ProviderOption interface
function WithStore (line 32) | func WithStore(store database.Store) ProviderOption {
function WithTableName (line 52) | func WithTableName(name string) ProviderOption {
function WithVerbose (line 63) | func WithVerbose(b bool) ProviderOption {
function WithSessionLocker (line 74) | func WithSessionLocker(locker lock.SessionLocker) ProviderOption {
function WithLocker (line 98) | func WithLocker(locker lock.Locker) ProviderOption {
function WithExcludeNames (line 120) | func WithExcludeNames(excludes []string) ProviderOption {
function WithExcludeVersions (line 134) | func WithExcludeVersions(versions []int64) ProviderOption {
function WithGoMigrations (line 153) | func WithGoMigrations(migrations ...*Migration) ProviderOption {
function WithDisableGlobalRegistry (line 170) | func WithDisableGlobalRegistry(b bool) ProviderOption {
function WithAllowOutofOrder (line 184) | func WithAllowOutofOrder(b bool) ProviderOption {
function WithDisableVersioning (line 195) | func WithDisableVersioning(b bool) ProviderOption {
function WithLogger (line 204) | func WithLogger(l Logger) ProviderOption {
function WithSlog (line 225) | func WithSlog(logger *slog.Logger) ProviderOption {
function WithIsolateDDL (line 240) | func WithIsolateDDL(b bool) ProviderOption {
type config (line 247) | type config struct
type configFunc (line 276) | type configFunc
method apply (line 278) | func (f configFunc) apply(cfg *config) error {
FILE: provider_options_test.go
function TestNewProvider (line 15) | func TestNewProvider(t *testing.T) {
FILE: provider_run.go
method prepareMigration (line 26) | func (p *Provider) prepareMigration(fsys fs.FS, m *Migration, direction ...
method logf (line 72) | func (p *Provider) logf(ctx context.Context, legacyMsg string, slogMsg s...
method runMigrations (line 96) | func (p *Provider) runMigrations(
method runIndividually (line 203) | func (p *Provider) runIndividually(
method maybeInsertOrDelete (line 243) | func (p *Provider) maybeInsertOrDelete(
function beginTx (line 261) | func beginTx(ctx context.Context, conn *sql.Conn, fn func(tx *sql.Tx) er...
method initialize (line 277) | func (p *Provider) initialize(ctx context.Context, useLocker bool) (*sql...
method ensureVersionTable (line 338) | func (p *Provider) ensureVersionTable(
method tryEnsureVersionTable (line 358) | func (p *Provider) tryEnsureVersionTable(ctx context.Context, conn *sql....
method getMigration (line 416) | func (p *Provider) getMigration(version int64) (*Migration, error) {
function useTx (line 427) | func useTx(m *Migration, direction bool) (bool, error) {
function isEmpty (line 448) | func isEmpty(m *Migration, direction bool) bool {
method runMigration (line 466) | func (p *Provider) runMigration(ctx context.Context, db database.DBTxCon...
method runGo (line 478) | func (p *Provider) runGo(ctx context.Context, db database.DBTxConn, m *M...
method runSQL (line 510) | func (p *Provider) runSQL(ctx context.Context, db database.DBTxConn, m *...
FILE: provider_run_test.go
function TestProviderRun (line 22) | func TestProviderRun(t *testing.T) {
function TestConcurrentProvider (line 376) | func TestConcurrentProvider(t *testing.T) {
function TestNoVersioning (line 471) | func TestNoVersioning(t *testing.T) {
function TestAllowMissing (line 552) | func TestAllowMissing(t *testing.T) {
function TestSQLiteSharedCache (line 693) | func TestSQLiteSharedCache(t *testing.T) {
function TestGoMigrationPanic (line 737) | func TestGoMigrationPanic(t *testing.T) {
function TestCustomStoreTableExists (line 764) | func TestCustomStoreTableExists(t *testing.T) {
function TestProviderApply (line 779) | func TestProviderApply(t *testing.T) {
function TestPending (line 793) | func TestPending(t *testing.T) {
type customStoreSQLite3 (line 867) | type customStoreSQLite3 struct
method TableExists (line 869) | func (s *customStoreSQLite3) TableExists(ctx context.Context, db datab...
function getGooseVersionCount (line 878) | func getGooseVersionCount(db *sql.DB, gooseTable string) (int64, error) {
function TestGoOnly (line 888) | func TestGoOnly(t *testing.T) {
function newDBFn (line 1010) | func newDBFn(query string) func(context.Context, *sql.DB) error {
function newTxFn (line 1017) | func newTxFn(query string) func(context.Context, *sql.Tx) error {
function tableExists (line 1024) | func tableExists(t *testing.T, db *sql.DB, table string) bool {
constant charset (line 1033) | charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
function randomAlphaNumeric (line 1036) | func randomAlphaNumeric(length int) string {
function newProviderWithDB (line 1044) | func newProviderWithDB(t *testing.T, opts ...goose.ProviderOption) (*goo...
function newDB (line 1056) | func newDB(t *testing.T) *sql.DB {
function getMaxVersionID (line 1064) | func getMaxVersionID(db *sql.DB, gooseTable string) (int64, error) {
function getTableNames (line 1074) | func getTableNames(db *sql.DB) ([]string, error) {
function assertStatus (line 1094) | func assertStatus(
function assertResult (line 1107) | func assertResult(
function assertSource (line 1123) | func assertSource(t *testing.T, got *goose.Source, typ goose.MigrationTy...
function newSource (line 1130) | func newSource(t goose.MigrationType, fullpath string, version int64) *g...
function newMapFile (line 1138) | func newMapFile(data string) *fstest.MapFile {
function newFsys (line 1144) | func newFsys() fstest.MapFS {
FILE: provider_test.go
function TestProvider (line 15) | func TestProvider(t *testing.T) {
function TestPartialErrorUnwrap (line 78) | func TestPartialErrorUnwrap(t *testing.T) {
FILE: provider_types.go
type MigrationType (line 10) | type MigrationType
constant TypeGo (line 13) | TypeGo MigrationType = "go"
constant TypeSQL (line 14) | TypeSQL MigrationType = "sql"
type Source (line 21) | type Source struct
type MigrationResult (line 28) | type MigrationResult struct
method String (line 50) | func (m *MigrationResult) String() string {
type State (line 72) | type State
constant StatePending (line 76) | StatePending State = "pending"
constant StateApplied (line 79) | StateApplied State = "applied"
type MigrationStatus (line 87) | type MigrationStatus struct
FILE: redo.go
function Redo (line 9) | func Redo(db *sql.DB, dir string, opts ...OptionsFunc) error {
function RedoContext (line 15) | func RedoContext(ctx context.Context, db *sql.DB, dir string, opts ...Op...
FILE: register.go
type GoMigrationContext (line 12) | type GoMigrationContext
function AddMigrationContext (line 15) | func AddMigrationContext(up, down GoMigrationContext) {
function AddNamedMigrationContext (line 21) | func AddNamedMigrationContext(filename string, up, down GoMigrationConte...
type GoMigrationNoTxContext (line 34) | type GoMigrationNoTxContext
function AddMigrationNoTxContext (line 37) | func AddMigrationNoTxContext(up, down GoMigrationNoTxContext) {
function AddNamedMigrationNoTxContext (line 43) | func AddNamedMigrationNoTxContext(filename string, up, down GoMigrationN...
function register (line 54) | func register(filename string, useTx bool, up, down *GoFunc) error {
function withContext (line 75) | func withContext[T any](fn func(T) error) func(context.Context, T) error {
function withoutContext (line 86) | func withoutContext[T any](fn func(context.Context, T) error) func(T) er...
type GoMigration (line 98) | type GoMigration
type GoMigrationNoTx (line 103) | type GoMigrationNoTx
function AddMigration (line 108) | func AddMigration(up, down GoMigration) {
function AddNamedMigration (line 116) | func AddNamedMigration(filename string, up, down GoMigration) {
function AddMigrationNoTx (line 123) | func AddMigrationNoTx(up, down GoMigrationNoTx) {
function AddNamedMigrationNoTx (line 131) | func AddNamedMigrationNoTx(filename string, up, down GoMigrationNoTx) {
FILE: reset.go
function Reset (line 11) | func Reset(db *sql.DB, dir string, opts ...OptionsFunc) error {
function ResetContext (line 17) | func ResetContext(ctx context.Context, db *sql.DB, dir string, opts ...O...
function dbMigrationsStatus (line 48) | func dbMigrationsStatus(ctx context.Context, db *sql.DB) (map[int64]bool...
FILE: status.go
function Status (line 13) | func Status(db *sql.DB, dir string, opts ...OptionsFunc) error {
function StatusContext (line 19) | func StatusContext(ctx context.Context, db *sql.DB, dir string, opts ......
function printMigrationStatus (line 53) | func printMigrationStatus(ctx context.Context, db *sql.DB, version int64...
FILE: testdata/migrations/00001_users_table.sql
type users (line 2) | CREATE TABLE users (
FILE: testdata/migrations/00002_posts_table.sql
type posts (line 3) | CREATE TABLE posts (
FILE: testdata/migrations/00003_comments_table.sql
type comments (line 2) | CREATE TABLE comments (
FILE: testdata/migrations/00005_posts_view.sql
type posts_view (line 4) | CREATE VIEW posts_view AS
FILE: testdata/no-versioning/migrations/00001_a.sql
type owners (line 2) | CREATE TABLE owners (
FILE: testdata/testdata.go
function MustMigrationsFS (line 12) | func MustMigrationsFS() fs.FS {
FILE: tests/gomigrations/error/gomigrations_error_test.go
function TestGoMigrationByOne (line 14) | func TestGoMigrationByOne(t *testing.T) {
FILE: tests/gomigrations/error/testdata/001_up_no_tx.go
function init (line 9) | func init() {
function up001 (line 13) | func up001(db *sql.DB) error {
FILE: tests/gomigrations/error/testdata/002_ERROR_insert_no_tx.go
function init (line 10) | func init() {
function up002 (line 14) | func up002(db *sql.DB) error {
FILE: tests/gomigrations/error/testdata/003_truncate.go
function init (line 9) | func init() {
function up003 (line 13) | func up003(tx *sql.Tx) error {
FILE: tests/gomigrations/error/testdata/004_ERROR_insert.go
function init (line 10) | func init() {
function up004 (line 14) | func up004(tx *sql.Tx) error {
FILE: tests/gomigrations/register/register_test.go
function TestAddFunctions (line 13) | func TestAddFunctions(t *testing.T) {
function checkMigration (line 52) | func checkMigration(t *testing.T, got *goose.Migration, want *goose.Migr...
function checkFunctions (line 63) | func checkFunctions(t *testing.T, m *goose.Migration) {
FILE: tests/gomigrations/register/testdata/001_addmigration.go
function init (line 9) | func init() {
FILE: tests/gomigrations/register/testdata/002_addmigrationnotx.go
function init (line 9) | func init() {
FILE: tests/gomigrations/register/testdata/003_addmigrationcontext.go
function init (line 10) | func init() {
FILE: tests/gomigrations/register/testdata/004_addmigrationnotxcontext.go
function init (line 10) | func init() {
FILE: tests/gomigrations/success/gomigrations_success_test.go
function TestGoMigrationByOne (line 15) | func TestGoMigrationByOne(t *testing.T) {
function ListTables (line 87) | func ListTables(db *sql.DB) ([]string, error) {
FILE: tests/gomigrations/success/testdata/001_up_down.go
function init (line 12) | func init() {
function up001 (line 16) | func up001(tx *sql.Tx) error {
function down001 (line 20) | func down001(tx *sql.Tx) error {
function createTable (line 24) | func createTable(db database.DBTxConn, name string) error {
function dropTable (line 29) | func dropTable(db database.DBTxConn, name string) error {
FILE: tests/gomigrations/success/testdata/002_up_only.go
function init (line 9) | func init() {
function up002 (line 13) | func up002(tx *sql.Tx) error {
FILE: tests/gomigrations/success/testdata/003_down_only.go
function init (line 9) | func init() {
function down003 (line 13) | func down003(tx *sql.Tx) error {
FILE: tests/gomigrations/success/testdata/004_empty.go
function init (line 7) | func init() {
FILE: tests/gomigrations/success/testdata/005_up_down_no_tx.go
function init (line 9) | func init() {
function up005 (line 13) | func up005(db *sql.DB) error {
function down005 (line 17) | func down005(db *sql.DB) error {
FILE: tests/gomigrations/success/testdata/006_up_only_no_tx.go
function init (line 9) | func init() {
function up006 (line 13) | func up006(db *sql.DB) error {
FILE: tests/gomigrations/success/testdata/007_down_only_no_tx.go
function init (line 9) | func init() {
function down007 (line 13) | func down007(db *sql.DB) error {
FILE: tests/gomigrations/success/testdata/008_empty_no_tx.go
function init (line 7) | func init() {
FILE: tests/gomigrations/success/testdata/009_up_down_ctx.go
function init (line 10) | func init() {
function up009 (line 14) | func up009(ctx context.Context, tx *sql.Tx) error {
function down009 (line 18) | func down009(ctx context.Context, tx *sql.Tx) error {
FILE: tests/gomigrations/success/testdata/010_up_only_ctx.go
function init (line 10) | func init() {
function up010 (line 14) | func up010(ctx context.Context, tx *sql.Tx) error {
FILE: tests/gomigrations/success/testdata/011_down_only_ctx.go
function init (line 10) | func init() {
function down011 (line 14) | func down011(ctx context.Context, tx *sql.Tx) error {
FILE: tests/gomigrations/success/testdata/012_empty_ctx.go
function init (line 7) | func init() {
FILE: tests/gomigrations/success/testdata/013_up_down_no_tx_ctx.go
function init (line 10) | func init() {
function up013 (line 14) | func up013(ctx context.Context, db *sql.DB) error {
function down013 (line 18) | func down013(ctx context.Context, db *sql.DB) error {
FILE: tests/gomigrations/success/testdata/014_up_only_no_tx_ctx.go
function init (line 10) | func init() {
function up014 (line 14) | func up014(ctx context.Context, db *sql.DB) error {
FILE: tests/gomigrations/success/testdata/015_down_only_no_tx_ctx.go
function init (line 10) | func init() {
function down015 (line 14) | func down015(ctx context.Context, db *sql.DB) error {
FILE: tests/gomigrations/success/testdata/016_empty_no_tx_ctx.go
function init (line 7) | func init() {
FILE: up.go
type options (line 11) | type options struct
type OptionsFunc (line 17) | type OptionsFunc
function WithAllowMissing (line 19) | func WithAllowMissing() OptionsFunc {
function WithNoVersioning (line 23) | func WithNoVersioning() OptionsFunc {
function WithNoColor (line 27) | func WithNoColor(b bool) OptionsFunc {
function withApplyUpByOne (line 31) | func withApplyUpByOne() OptionsFunc {
function UpTo (line 36) | func UpTo(db *sql.DB, dir string, version int64, opts ...OptionsFunc) er...
function UpToContext (line 41) | func UpToContext(ctx context.Context, db *sql.DB, dir string, version in...
function upToNoVersioning (line 144) | func upToNoVersioning(ctx context.Context, db *sql.DB, migrations Migrat...
function Up (line 161) | func Up(db *sql.DB, dir string, opts ...OptionsFunc) error {
function UpContext (line 167) | func UpContext(ctx context.Context, db *sql.DB, dir string, opts ...Opti...
function UpByOne (line 172) | func UpByOne(db *sql.DB, dir string, opts ...OptionsFunc) error {
function UpByOneContext (line 178) | func UpByOneContext(ctx context.Context, db *sql.DB, dir string, opts .....
function listAllDBVersions (line 184) | func listAllDBVersions(ctx context.Context, db *sql.DB) (Migrations, err...
function findMissingMigrations (line 206) | func findMissingMigrations(knownMigrations, newMigrations Migrations, db...
FILE: up_test.go
function TestFindMissingMigrations (line 7) | func TestFindMissingMigrations(t *testing.T) {
FILE: version.go
function Version (line 10) | func Version(db *sql.DB, dir string, opts ...OptionsFunc) error {
function VersionContext (line 16) | func VersionContext(ctx context.Context, db *sql.DB, dir string, opts .....
function TableName (line 45) | func TableName() string {
function SetTableName (line 50) | func SetTableName(n string) {
Condensed preview — 268 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (725K chars).
[
{
"path": ".github/dependabot.yaml",
"chars": 474,
"preview": "version: 2\n\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"weekly\"\n "
},
{
"path": ".github/workflows/ci.yaml",
"chars": 1790,
"preview": "name: Goose CI\n\non:\n push:\n branches:\n - main\n pull_request:\n workflow_dispatch:\n\nconcurrency:\n group: ${{ g"
},
{
"path": ".github/workflows/integration.yaml",
"chars": 863,
"preview": "name: Goose integration tests\n\non:\n push:\n branches:\n - main\n pull_request:\n workflow_dispatch:\n\nconcurrency:"
},
{
"path": ".github/workflows/lint.yaml",
"chars": 1534,
"preview": "name: golangci\n\non:\n push:\n branches:\n - main\n pull_request:\n workflow_dispatch:\n\nconcurrency:\n group: ${{ g"
},
{
"path": ".github/workflows/release.yaml",
"chars": 823,
"preview": "name: goreleaser\non:\n push:\n tags:\n - '*'\npermissions:\n contents: write\njobs:\n goreleaser:\n runs-on: ubunt"
},
{
"path": ".gitignore",
"chars": 189,
"preview": ".idea\n.vscode\n.DS_Store\n*.swp\n*.test\n\n# Files output by tests\n/bin\n\n# Coverage files\ncoverage.out\ncoverage.html\n\n# Local"
},
{
"path": ".golangci.yaml",
"chars": 494,
"preview": "version: \"2\"\nlinters:\n default: none\n enable:\n - errcheck\n - govet\n - ineffassign\n - misspell\n - static"
},
{
"path": ".goreleaser.yaml",
"chars": 901,
"preview": "# yaml-language-server: $schema=https://goreleaser.com/static/schema.json\n#\n# See https://goreleaser.com/customization/ "
},
{
"path": "CHANGELOG.md",
"chars": 14319,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "LICENSE",
"chars": 1197,
"preview": "MIT License\n\nOriginal work Copyright (c) 2012 Liam Staskawicz\nModified work Copyright (c) 2016 Vojtech Vitek\nModified wo"
},
{
"path": "Makefile",
"chars": 5017,
"preview": "GO_TEST_FLAGS ?= -race -count=1 -v -timeout=5m -json\n\n# These are the default values for the test database. They can be "
},
{
"path": "README.md",
"chars": 16387,
"preview": "# goose\n\n<img align=\"right\" width=\"125\" src=\"assets/goose_logo.png\">\n\n[\n"
},
{
"path": "cmd/goose/driver_mssql.go",
"chars": 84,
"preview": "//go:build !no_mssql\n\npackage main\n\nimport (\n\t_ \"github.com/microsoft/go-mssqldb\"\n)\n"
},
{
"path": "cmd/goose/driver_mysql.go",
"chars": 1786,
"preview": "//go:build !no_mysql\n\npackage main\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/go-sql-drive"
},
{
"path": "cmd/goose/driver_no_mysql.go",
"chars": 156,
"preview": "//go:build no_mysql\n\npackage main\n\nfunc normalizeDBString(driver string, str string, certfile string, sslcert string, ss"
},
{
"path": "cmd/goose/driver_postgres.go",
"chars": 86,
"preview": "//go:build !no_postgres\n\npackage main\n\nimport (\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n)\n"
},
{
"path": "cmd/goose/driver_sqlite3.go",
"chars": 96,
"preview": "//go:build !no_sqlite3 && !(windows && arm64)\n\npackage main\n\nimport (\n\t_ \"modernc.org/sqlite\"\n)\n"
},
{
"path": "cmd/goose/driver_turso.go",
"chars": 102,
"preview": "//go:build !no_libsql\n\npackage main\n\nimport (\n\t_ \"github.com/tursodatabase/libsql-client-go/libsql\"\n)\n"
},
{
"path": "cmd/goose/driver_vertica.go",
"chars": 88,
"preview": "//go:build !no_vertica\n\npackage main\n\nimport (\n\t_ \"github.com/vertica/vertica-sql-go\"\n)\n"
},
{
"path": "cmd/goose/driver_ydb.go",
"chars": 88,
"preview": "//go:build !no_ydb\n\npackage main\n\nimport (\n\t_ \"github.com/ydb-platform/ydb-go-sdk/v3\"\n)\n"
},
{
"path": "cmd/goose/main.go",
"chars": 13358,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runt"
},
{
"path": "cmd/goose/main_test.go",
"chars": 1348,
"preview": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestFirstNonEmpty(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinp"
},
{
"path": "create.go",
"chars": 2688,
"preview": "package goose\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"text/template\"\n\t\"time\"\n)\n\ntype tmplVar"
},
{
"path": "create_test.go",
"chars": 1189,
"preview": "package goose\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSequential(t *testing.T) {\n\tt."
},
{
"path": "database/dialect/querier.go",
"chars": 1276,
"preview": "package dialect\n\n// Querier is the interface that wraps the basic methods to create a dialect specific query.\n//\n// It i"
},
{
"path": "database/dialect/querier_extended.go",
"chars": 882,
"preview": "package dialect\n\n// QuerierExtender extends the [Querier] interface with optional database-specific optimizations.\n// Wh"
},
{
"path": "database/dialects.go",
"chars": 6400,
"preview": "package database\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n"
},
{
"path": "database/doc.go",
"chars": 615,
"preview": "// Package database defines a generic [Store] interface for goose to use when interacting with the\n// database. It is me"
},
{
"path": "database/sql_extended.go",
"chars": 660,
"preview": "package database\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n)\n\n// DBTxConn is a thin interface for common methods that is sati"
},
{
"path": "database/store.go",
"chars": 2775,
"preview": "package database\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n)\n\nvar (\n\t// ErrVersionNotFound must be returned by [GetMigratio"
},
{
"path": "database/store_extended.go",
"chars": 1532,
"preview": "package database\n\nimport \"context\"\n\n// StoreExtender is an extension of the Store interface that provides optional optim"
},
{
"path": "database/store_test.go",
"chars": 7235,
"preview": "package database_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/pressly/g"
},
{
"path": "db.go",
"chars": 1631,
"preview": "package goose\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n)\n\n// OpenDBWithDriver creates a connection to a database, and modifies g"
},
{
"path": "dialect.go",
"chars": 1955,
"preview": "package goose\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database\"\n\t\"github.com/pressly/goose/v3/internal/legacysto"
},
{
"path": "down.go",
"chars": 2931,
"preview": "package goose\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n)\n\n// Down rolls back a single migration from the current vers"
},
{
"path": "examples/README.md",
"chars": 73,
"preview": "# 1. [SQL migrations](sql-migrations)\n# 2. [Go migrations](go-migrations)"
},
{
"path": "examples/go-migrations/00001_create_users_table.sql",
"chars": 252,
"preview": "-- +goose Up\nCREATE TABLE users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n username TEXT,\n name TEXT,\n surnam"
},
{
"path": "examples/go-migrations/00002_rename_root.go",
"chars": 470,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"github.com/pressly/goose/v3\"\n)\n\nfunc init() {\n\tgoose.AddMigrationCo"
},
{
"path": "examples/go-migrations/00003_add_user_no_tx.go",
"chars": 950,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\n\t\"github.com/pressly/goose/v3\"\n)\n\nfunc init() {\n\tgoose.AddM"
},
{
"path": "examples/go-migrations/README.md",
"chars": 1314,
"preview": "# SQL + Go migrations\n\n## This example: Custom goose binary with built-in Go migrations\n\n```bash\n$ go build -o goose-cus"
},
{
"path": "examples/go-migrations/main.go",
"chars": 1027,
"preview": "// This is custom goose binary with sqlite3 support only.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"log\"\n\t\"os\"\n\n\t\"git"
},
{
"path": "examples/sql-migrations/00001_create_users_table.sql",
"chars": 243,
"preview": "-- +goose Up\nCREATE TABLE users (\n id int NOT NULL PRIMARY KEY,\n username text,\n name text,\n surname text\n);"
},
{
"path": "examples/sql-migrations/00002_rename_root.sql",
"chars": 239,
"preview": "-- +goose Up\n-- +goose StatementBegin\nUPDATE users SET username='admin' WHERE username='root';\n-- +goose StatementEnd\n\n-"
},
{
"path": "examples/sql-migrations/00003_no_transaction.sql",
"chars": 166,
"preview": "-- +goose NO TRANSACTION\n-- +goose Up\nCREATE TABLE post (\n id int NOT NULL,\n title text,\n body text,\n PRIMAR"
},
{
"path": "examples/sql-migrations/README.md",
"chars": 770,
"preview": "# SQL migrations only\n\nSee [this example](../go-migrations) for Go migrations.\n\n```bash\n$ go install github.com/pressly/"
},
{
"path": "fix.go",
"chars": 1104,
"preview": "package goose\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nconst seqVersionTemplate = \"%05v\"\n\nfunc Fix(dir stri"
},
{
"path": "fix_test.go",
"chars": 1985,
"preview": "package goose\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestFix(t *testing.T) {\n\tt.Paralle"
},
{
"path": "globals.go",
"chars": 3323,
"preview": "package goose\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"path/filepath\"\n)\n\nvar (\n\tregisteredGoMigrations = make(map[int64]*Migration)\n"
},
{
"path": "globals_test.go",
"chars": 11483,
"preview": "package goose\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNewGoM"
},
{
"path": "go.mod",
"chars": 4083,
"preview": "module github.com/pressly/goose/v3\n\ngo 1.25.0\n\nrequire (\n\tgithub.com/ClickHouse/clickhouse-go/v2 v2.43.0\n\tgithub.com/con"
},
{
"path": "go.sum",
"chars": 38359,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1"
},
{
"path": "goose.go",
"chars": 3822,
"preview": "package goose\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"strconv\"\n)\n\n// Deprecated: VERSION will no longer b"
},
{
"path": "goose_cli_test.go",
"chars": 6385,
"preview": "package goose_test\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretc"
},
{
"path": "goose_embed_test.go",
"chars": 1584,
"preview": "package goose_test\n\nimport (\n\t\"database/sql\"\n\t\"embed\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/pressly/g"
},
{
"path": "helpers.go",
"chars": 1785,
"preview": "package goose\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\ntype camelSnakeStateMachine int\n\nconst ( // "
},
{
"path": "helpers_test.go",
"chars": 688,
"preview": "package goose\n\nimport (\n\t\"testing\"\n)\n\nfunc TestCamelSnake(t *testing.T) {\n\tt.Parallel()\n\n\ttt := []struct {\n\t\tin strin"
},
{
"path": "install.sh",
"chars": 1029,
"preview": "#!/bin/sh\n# Adapted from the Deno installer: Copyright 2019 the Deno authors. All rights reserved. MIT license.\n# Ref: h"
},
{
"path": "internal/controller/store.go",
"chars": 1229,
"preview": "package controller\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/pressly/goose/v3/database\"\n)\n\n// A StoreController is us"
},
{
"path": "internal/dialects/clickhouse.go",
"chars": 1429,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewClickhouse returns a new [di"
},
{
"path": "internal/dialects/dsql.go",
"chars": 1881,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewAuroraDSQL returns a new [di"
},
{
"path": "internal/dialects/mysql.go",
"chars": 1827,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewMysql returns a new [dialect"
},
{
"path": "internal/dialects/postgres.go",
"chars": 2022,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewPostgres returns "
},
{
"path": "internal/dialects/redshift.go",
"chars": 1356,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// Redshift returns a new [dialect"
},
{
"path": "internal/dialects/spanner.go",
"chars": 1320,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewSpanner returns a [dialect.Q"
},
{
"path": "internal/dialects/sqlite3.go",
"chars": 1328,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewSqlite3 returns a [dialect.Q"
},
{
"path": "internal/dialects/sqlserver.go",
"chars": 1363,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewSqlserver returns a [dialect"
},
{
"path": "internal/dialects/starrocks.go",
"chars": 1436,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewStarrocks returns a [dialect"
},
{
"path": "internal/dialects/tidb.go",
"chars": 1316,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewTidb returns a [dialect.Quer"
},
{
"path": "internal/dialects/turso.go",
"chars": 256,
"preview": "package dialects\n\nimport \"github.com/pressly/goose/v3/database/dialect\"\n\n// NewTurso returns a [dialect.Querier] for Tur"
},
{
"path": "internal/dialects/vertica.go",
"chars": 1421,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewVertica returns a new [diale"
},
{
"path": "internal/dialects/ydb.go",
"chars": 1854,
"preview": "package dialects\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pressly/goose/v3/database/dialect\"\n)\n\n// NewYDB returns a new [dialect.Q"
},
{
"path": "internal/gooseutil/resolve.go",
"chars": 3524,
"preview": "// Package gooseutil provides utility functions we want to keep internal to the package. It's\n// intended to be a collec"
},
{
"path": "internal/gooseutil/resolve_test.go",
"chars": 7535,
"preview": "package gooseutil\n\nimport (\n\t\"math\"\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestResolveVers"
},
{
"path": "internal/legacystore/legacystore.go",
"chars": 5453,
"preview": "package legacystore\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/pressly/goose/v3/database\"\n\t\"githu"
},
{
"path": "internal/migrationstats/migration_go.go",
"chars": 3488,
"preview": "package migrationstats\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"strings\"\n)\n\nconst (\n\tregist"
},
{
"path": "internal/migrationstats/migration_sql.go",
"chars": 943,
"preview": "package migrationstats\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/pressly/goose/v3/internal/sqlparser\"\n)\n\ntype sqlMig"
},
{
"path": "internal/migrationstats/migrationstats.go",
"chars": 1877,
"preview": "package migrationstats\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"path/filepath\"\n\n\t\"github.com/pressly/goose/v3\"\n)\n\n// FileWalker walks al"
},
{
"path": "internal/migrationstats/migrationstats_test.go",
"chars": 5124,
"preview": "package migrationstats\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n"
},
{
"path": "internal/migrationstats/migrationstats_walker.go",
"chars": 863,
"preview": "package migrationstats\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\n// NewFileWalker returns a new FileWalker for the given"
},
{
"path": "internal/sqlparser/parse.go",
"chars": 1469,
"preview": "package sqlparser\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\n\t\"go.uber.org/multierr\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\ntype ParsedSQL str"
},
{
"path": "internal/sqlparser/parse_test.go",
"chars": 2123,
"preview": "package sqlparser_test\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"testing/fstest\"\n\n\t\"github.com/pressly/goose/v3/internal/sqlparser\"\n\t"
},
{
"path": "internal/sqlparser/parser.go",
"chars": 11471,
"preview": "package sqlparser\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/mfrid"
},
{
"path": "internal/sqlparser/parser_test.go",
"chars": 14008,
"preview": "package sqlparser\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testif"
},
{
"path": "internal/sqlparser/testdata/envsub/test01/01.down.golden.sql",
"chars": 39,
"preview": "DROP TABLE us_east_post; -- 1st stmt"
},
{
"path": "internal/sqlparser/testdata/envsub/test01/01.up.golden.sql",
"chars": 123,
"preview": "CREATE TABLE us_east_post (\n\t\tid int NOT NULL,\n\t\ttitle text,\n\t\tbody text,\n\t\tPRIMARY KEY(id)\n); -- 1st s"
},
{
"path": "internal/sqlparser/testdata/envsub/test01/02.up.golden.sql",
"chars": 31,
"preview": "SELECT 2; -- 2nd stmt"
},
{
"path": "internal/sqlparser/testdata/envsub/test01/03.up.golden.sql",
"chars": 31,
"preview": "SELECT 3; SELECT 3; -- 3rd stmt"
},
{
"path": "internal/sqlparser/testdata/envsub/test01/04.up.golden.sql",
"chars": 31,
"preview": "SELECT 4; -- 4th stmt"
},
{
"path": "internal/sqlparser/testdata/envsub/test01/input.sql",
"chars": 354,
"preview": "-- +goose ENVSUB ON\n-- +goose Up\nCREATE TABLE ${GOOSE_ENV_REGION}post (\n\t\tid int NOT NULL,\n\t\ttitle text,\n\t\tbody text,\n\t\t"
},
{
"path": "internal/sqlparser/testdata/envsub/test02/01.up.golden.sql",
"chars": 116,
"preview": "CREATE TABLE post (\n\tid int NOT NULL,\n\ttitle text,\n\tfoo text,\n\tfootitle3 text,\n\tdefaulttitle4 text,\n\ttitle5 text,\n);"
},
{
"path": "internal/sqlparser/testdata/envsub/test02/02.up.golden.sql",
"chars": 198,
"preview": "CREATE TABLE post (\n\tid int NOT NULL,\n\ttitle text,\n\t$GOOSE_ENV_NAME text,\n\t${GOOSE_ENV_NAME}title3 text,\n\t${ANOTHER_VAR:"
},
{
"path": "internal/sqlparser/testdata/envsub/test02/03.up.golden.sql",
"chars": 142,
"preview": "CREATE OR REPLACE FUNCTION test_func()\nRETURNS void AS $$\nBEGIN\n\tRAISE NOTICE 'foo $GOOSE_ENV_NAME $GOOSE_ENV_NAME';\nEND"
},
{
"path": "internal/sqlparser/testdata/envsub/test02/input.sql",
"chars": 703,
"preview": "-- +goose Up\n\n-- +goose ENVSUB ON\nCREATE TABLE post (\n\tid int NOT NULL,\n\ttitle text,\n\t$GOOSE_ENV_NAME text,\n\t${GOOSE_ENV"
},
{
"path": "internal/sqlparser/testdata/envsub/test03/01.up.golden.sql",
"chars": 168,
"preview": "CREATE TABLE post (\n\tid int NOT NULL,\n\ttitle text,\n\t$NAME text,\n\t${NAME}title3 text,\n\t${ANOTHER_VAR:-default}title4 text"
},
{
"path": "internal/sqlparser/testdata/envsub/test03/input.sql",
"chars": 182,
"preview": "-- +goose Up\nCREATE TABLE post (\n\tid int NOT NULL,\n\ttitle text,\n\t$NAME text,\n\t${NAME}title3 text,\n\t${ANOTHER_VAR:-defaul"
},
{
"path": "internal/sqlparser/testdata/invalid/up/a.sql",
"chars": 84,
"preview": "-- +goose Up\nSELECT * FROM foo;\nSELECT * FROM bar\n-- +goose Down\nSELECT * FROM baz;\n"
},
{
"path": "internal/sqlparser/testdata/invalid/up/b.sql",
"chars": 65,
"preview": "-- +goose Up\nSELECT * FROM bar\n-- +goose Down\nSELECT * FROM baz;\n"
},
{
"path": "internal/sqlparser/testdata/invalid/up/c.sql",
"chars": 46,
"preview": "-- +goose Up\nSELECT * FROM bar\n-- +goose Down\n"
},
{
"path": "internal/sqlparser/testdata/invalid/up/d.sql",
"chars": 31,
"preview": "-- +goose Up\nSELECT * FROM bar\n"
},
{
"path": "internal/sqlparser/testdata/valid-txn/00001_create_users_table.sql",
"chars": 243,
"preview": "-- +goose Up\nCREATE TABLE users (\n id int NOT NULL PRIMARY KEY,\n username text,\n name text,\n surname text\n);"
},
{
"path": "internal/sqlparser/testdata/valid-txn/00002_rename_root.sql",
"chars": 239,
"preview": "-- +goose Up\n-- +goose StatementBegin\nUPDATE users SET username='admin' WHERE username='root';\n-- +goose StatementEnd\n\n-"
},
{
"path": "internal/sqlparser/testdata/valid-txn/00003_no_transaction.sql",
"chars": 166,
"preview": "-- +goose NO TRANSACTION\n-- +goose Up\nCREATE TABLE post (\n id int NOT NULL,\n title text,\n body text,\n PRIMAR"
},
{
"path": "internal/sqlparser/testdata/valid-up/test01/01.up.golden.sql",
"chars": 103,
"preview": "CREATE TABLE emp (\n empname text,\n salary integer,\n last_date timestamp,\n last_user text\n);"
},
{
"path": "internal/sqlparser/testdata/valid-up/test01/02.up.golden.sql",
"chars": 717,
"preview": "CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$\n BEGIN\n -- Check that empname and salary are given\n"
},
{
"path": "internal/sqlparser/testdata/valid-up/test01/03.up.golden.sql",
"chars": 102,
"preview": "CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp\n FOR EACH ROW EXECUTE FUNCTION emp_stamp();"
},
{
"path": "internal/sqlparser/testdata/valid-up/test01/input.sql",
"chars": 1004,
"preview": "-- +goose UP\nCREATE TABLE emp (\n empname text,\n salary integer,\n last_date timestamp,\n last_user text\n);\n\n--"
},
{
"path": "internal/sqlparser/testdata/valid-up/test02/01.up.golden.sql",
"chars": 1131,
"preview": "CREATE TABLE emp (\n empname text NOT NULL,\n salary integer\n);\n\nCREATE TABLE emp_audit(\n op"
},
{
"path": "internal/sqlparser/testdata/valid-up/test02/input.sql",
"chars": 1209,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE emp (\n empname text NOT NULL,\n salary inte"
},
{
"path": "internal/sqlparser/testdata/valid-up/test03/01.up.golden.sql",
"chars": 1309,
"preview": "CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '\nDECLARE\n referrer_keys RECORD; -- Declare a gene"
},
{
"path": "internal/sqlparser/testdata/valid-up/test03/input.sql",
"chars": 1387,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '\nDECLARE\n re"
},
{
"path": "internal/sqlparser/testdata/valid-up/test04/01.up.golden.sql",
"chars": 72,
"preview": "CREATE TABLE ssh_keys (\n id integer NOT NULL,\n \"publicKey\" text\n);"
},
{
"path": "internal/sqlparser/testdata/valid-up/test04/02.up.golden.sql",
"chars": 479,
"preview": "INSERT INTO ssh_keys (id, \"publicKey\") VALUES (1, '-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAqAo9QORIXMPMa/qv8908Z2sH2+"
},
{
"path": "internal/sqlparser/testdata/valid-up/test04/03.up.golden.sql",
"chars": 1423,
"preview": "INSERT INTO ssh_keys (id, \"publicKey\") VALUES (2, '-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAA"
},
{
"path": "internal/sqlparser/testdata/valid-up/test04/input.sql",
"chars": 2057,
"preview": "-- +goose Up\n\nCREATE TABLE ssh_keys (\n id integer NOT NULL,\n \"publicKey\" text\n);\n\n-- +goose StatementBegin\nINSERT "
},
{
"path": "internal/sqlparser/testdata/valid-up/test05/01.up.golden.sql",
"chars": 96,
"preview": "CREATE TABLE ssh_keys (\n id integer NOT NULL,\n \"publicKey\" text\n-- insert comment there\n);"
},
{
"path": "internal/sqlparser/testdata/valid-up/test05/02.up.golden.sql",
"chars": 134,
"preview": "CREATE TABLE ssh_keys_backup (\n id integer NOT NULL,\n -- insert comment here\n \"publicKey\" text\n -- insert co"
},
{
"path": "internal/sqlparser/testdata/valid-up/test05/input.sql",
"chars": 353,
"preview": "-- +goose Up\n\nCREATE TABLE ssh_keys (\n id integer NOT NULL,\n \"publicKey\" text\n-- insert comment there\n);\n-- insert"
},
{
"path": "internal/sqlparser/testdata/valid-up/test06/01.up.golden.sql",
"chars": 62,
"preview": "CREATE TABLE article (\n id text,\n content text);"
},
{
"path": "internal/sqlparser/testdata/valid-up/test06/02.up.golden.sql",
"chars": 111,
"preview": "INSERT INTO article (id, content) VALUES ('id_0001', E'# My markdown doc\n\nfirst paragraph\n\nsecond paragraph');"
},
{
"path": "internal/sqlparser/testdata/valid-up/test06/03.up.golden.sql",
"chars": 169,
"preview": "INSERT INTO article (id, content) VALUES ('id_0002', E'# My second \n\nmarkdown doc\n\nfirst paragraph\n\n-- with a comment\n "
},
{
"path": "internal/sqlparser/testdata/valid-up/test06/04.up.golden.sql",
"chars": 330,
"preview": "CREATE FUNCTION do_something(sql TEXT) RETURNS INTEGER AS $$\nBEGIN\n -- initiate technology\n PERFORM something_or_other"
},
{
"path": "internal/sqlparser/testdata/valid-up/test06/05.up.golden.sql",
"chars": 202,
"preview": "INSERT INTO post (id, title, body)\nVALUES ('id_01', 'my_title', '\nthis is an insert statement including empty lines.\n\nem"
},
{
"path": "internal/sqlparser/testdata/valid-up/test06/input.sql",
"chars": 1073,
"preview": "-- +goose Up\n\nCREATE TABLE article (\n id text,\n content text);\n\nINSERT INTO article (id, content) VALUES ("
},
{
"path": "internal/sqlparser/testdata/valid-up/test07/01.up.golden.sql",
"chars": 39,
"preview": "CREATE INDEX ON public.users (user_id);"
},
{
"path": "internal/sqlparser/testdata/valid-up/test07/input.sql",
"chars": 210,
"preview": " \n-- +goose Up\n-- +goose StatementBegin\nCREATE INDEX ON public.users (user_id);\n-- +goose StatementEnd\n\n-- +goose Dow"
},
{
"path": "internal/sqlparser/testdata/valid-up/test08/01.up.golden.sql",
"chars": 185,
"preview": "CREATE TABLE `table_a` (\n `column_1` DATETIME DEFAULT NOW(),\n `column_2` DATETIME DEFAULT NOW(),\n `column_3` DA"
},
{
"path": "internal/sqlparser/testdata/valid-up/test08/02.up.golden.sql",
"chars": 185,
"preview": "CREATE TABLE `table_b` (\n `column_1` DATETIME DEFAULT NOW(),\n `column_2` DATETIME DEFAULT NOW(),\n `column_3` DA"
},
{
"path": "internal/sqlparser/testdata/valid-up/test08/03.up.golden.sql",
"chars": 185,
"preview": "CREATE TABLE `table_c` (\n `column_1` DATETIME DEFAULT NOW(),\n `column_2` DATETIME DEFAULT NOW(),\n `column_3` DA"
},
{
"path": "internal/sqlparser/testdata/valid-up/test08/04.up.golden.sql",
"chars": 66,
"preview": "/*!80031 ALTER TABLE `table_a` MODIFY `column_1` TEXT NOT NULL */;"
},
{
"path": "internal/sqlparser/testdata/valid-up/test08/05.up.golden.sql",
"chars": 66,
"preview": "/*!80031 ALTER TABLE `table_b` MODIFY `column_2` TEXT NOT NULL */;"
},
{
"path": "internal/sqlparser/testdata/valid-up/test08/06.up.golden.sql",
"chars": 66,
"preview": "/*!80033 ALTER TABLE `table_c` MODIFY `column_3` TEXT NOT NULL */;"
},
{
"path": "internal/sqlparser/testdata/valid-up/test08/input.sql",
"chars": 776,
"preview": "-- +goose Up\n\nCREATE TABLE `table_a` (\n `column_1` DATETIME DEFAULT NOW(),\n `column_2` DATETIME DEFAULT NOW(),\n "
},
{
"path": "internal/sqlparser/testdata/valid-up/test09/01.up.golden.sql",
"chars": 160,
"preview": "create table t ( id int );\nupdate rows set value = now() -- missing semicolon. valid statement because wrapped in goose "
},
{
"path": "internal/sqlparser/testdata/valid-up/test09/input.sql",
"chars": 262,
"preview": "-- +goose Up\n-- +goose StatementBegin\ncreate table t ( id int );\nupdate rows set value = now() -- missing semicolon. val"
},
{
"path": "internal/testing/go.mod",
"chars": 5979,
"preview": "module github.com/pressly/goose/v3/internal/testing\n\ngo 1.25.0\n\nrequire (\n\tcloud.google.com/go/spanner v1.88.0\n\tgithub.c"
},
{
"path": "internal/testing/go.sum",
"chars": 42679,
"preview": "cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=\ncel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ"
},
{
"path": "internal/testing/integration/README.md",
"chars": 2401,
"preview": "# Integration tests\n\nThis directory contains integration tests for the [pressly/goose/v3][goose_module] Go module. An\nin"
},
{
"path": "internal/testing/integration/database_test.go",
"chars": 4028,
"preview": "package integration\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/pressly/goose/v3\"\n\t\"github.com/pressly/goose/v3/database\""
},
{
"path": "internal/testing/integration/integration.go",
"chars": 2468,
"preview": "package integration\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/press"
},
{
"path": "internal/testing/integration/locking/postgres_locking_test.go",
"chars": 16465,
"preview": "package locking_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash/crc32\"\n\t\"math/rand/v2\"\n\t\"os\"\n\t\"sort\"\n\t"
},
{
"path": "internal/testing/integration/locking/postgres_table_locking_test.go",
"chars": 9492,
"preview": "package locking_test\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n\t\"math/rand/v2\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/pressl"
},
{
"path": "internal/testing/integration/testdata/migrations/clickhouse/00001_a.sql",
"chars": 1745,
"preview": "-- +goose Up\nCREATE TABLE IF NOT EXISTS trips\n(\n `trip_id` UInt32,\n `vendor_id` Enum8('1' = 1, '2' = 2, '3' = 3, '"
},
{
"path": "internal/testing/integration/testdata/migrations/clickhouse/00002_b.sql",
"chars": 283,
"preview": "-- +goose Up\nCREATE TABLE IF NOT EXISTS clickstream (\n customer_id String, \n time_stamp Date, \n click_event_typ"
},
{
"path": "internal/testing/integration/testdata/migrations/clickhouse/00003_c.sql",
"chars": 353,
"preview": "-- +goose Up\nINSERT INTO clickstream VALUES ('customer1', '2021-10-02', 'add_to_cart', 'US', 568239 ); \n\nINSERT INTO cli"
},
{
"path": "internal/testing/integration/testdata/migrations/clickhouse-remote/00001_a.sql",
"chars": 407,
"preview": "-- +goose Up\nCREATE DICTIONARY taxi_zone_dictionary (\n LocationID UInt16 DEFAULT 0,\n Borough String,\n Zone Stri"
},
{
"path": "internal/testing/integration/testdata/migrations/clickhouse-remote-backup/taxi_zone_lookup.csv",
"chars": 12322,
"preview": "\"LocationID\",\"Borough\",\"Zone\",\"service_zone\"\r\n1,\"EWR\",\"Newark Airport\",\"EWR\"\r\n2,\"Queens\",\"Jamaica Bay\",\"Boro Zone\"\r\n3,\"B"
},
{
"path": "internal/testing/integration/testdata/migrations/mysql/00001_table.sql",
"chars": 643,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE owners (\n owner_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n "
},
{
"path": "internal/testing/integration/testdata/migrations/mysql/00002_insert.sql",
"chars": 566,
"preview": "-- +goose Up\n-- +goose StatementBegin\nINSERT INTO owners (owner_name, owner_type)\nVALUES\n ('lucas', 'user'),\n ('space'"
},
{
"path": "internal/testing/integration/testdata/migrations/mysql/00003_alter.sql",
"chars": 385,
"preview": "-- +goose Up\n-- +goose StatementBegin\nALTER TABLE repos \n ADD COLUMN IF NOT EXISTS homepage_url TEXT;\n\nALTER TABLE re"
},
{
"path": "internal/testing/integration/testdata/migrations/mysql/00004_empty.sql",
"chars": 13,
"preview": "-- +goose Up\n"
},
{
"path": "internal/testing/integration/testdata/migrations/mysql/00005_no_tx.sql",
"chars": 175,
"preview": "-- +goose NO TRANSACTION\n\n-- +goose Up\nCREATE UNIQUE INDEX owners_owner_name_idx ON owners(owner_name);\n\n-- +goose Down\n"
},
{
"path": "internal/testing/integration/testdata/migrations/mysql/00006_complex.sql",
"chars": 960,
"preview": "-- +goose up\n\n-- +goose statementbegin\nCREATE OR REPLACE PROCEDURE insert_repository(\n IN p_repo_full_name VARCHAR(25"
},
{
"path": "internal/testing/integration/testdata/migrations/postgres/00001_table.sql",
"chars": 600,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TYPE owner_type as ENUM('user', 'organization');\n\nCREATE TABLE owners (\n "
},
{
"path": "internal/testing/integration/testdata/migrations/postgres/00002_insert.sql",
"chars": 454,
"preview": "-- +goose Up\n-- +goose StatementBegin\nINSERT INTO owners(owner_name, owner_type) \n VALUES ('lucas', 'user'), ('space'"
},
{
"path": "internal/testing/integration/testdata/migrations/postgres/00003_alter.sql",
"chars": 336,
"preview": "-- +goose Up\n-- +goose StatementBegin\nALTER TABLE repos \n ADD COLUMN IF NOT EXISTS homepage_url text,\n ADD COLUMN "
},
{
"path": "internal/testing/integration/testdata/migrations/postgres/00004_empty.sql",
"chars": 13,
"preview": "-- +goose Up\n"
},
{
"path": "internal/testing/integration/testdata/migrations/postgres/00005_no_tx.sql",
"chars": 155,
"preview": "-- +goose NO TRANSACTION\n\n-- +goose Up\nCREATE UNIQUE INDEX CONCURRENTLY ON owners(owner_name);\n\n-- +goose Down\nDROP INDE"
},
{
"path": "internal/testing/integration/testdata/migrations/postgres/00006_complex.sql",
"chars": 1008,
"preview": "-- +goose up\n-- +goose statementbegin\nCREATE OR REPLACE FUNCTION insert_repository(\n p_repo_full_name TEXT,\n p_own"
},
{
"path": "internal/testing/integration/testdata/migrations/spanner/00001_table.sql",
"chars": 316,
"preview": "-- +goose NO TRANSACTION\n-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE owners (\n owner_id INT64 NOT NULL,\n ow"
},
{
"path": "internal/testing/integration/testdata/migrations/spanner/00002_insert.sql",
"chars": 346,
"preview": "-- +goose Up\n-- +goose StatementBegin\nINSERT INTO owners (owner_id, owner_name, owner_type) VALUES\n (1, 'lucas', 'user'"
},
{
"path": "internal/testing/integration/testdata/migrations/spanner/00003_alter.sql",
"chars": 249,
"preview": "-- +goose NO TRANSACTION\n-- +goose Up\n-- +goose StatementBegin\nALTER TABLE owners ADD COLUMN homepage_url STRING(255)\n--"
},
{
"path": "internal/testing/integration/testdata/migrations/spanner/00004_empty.sql",
"chars": 47,
"preview": "-- +goose Up\n-- no-op\n\n-- +goose Down\n-- no-op\n"
},
{
"path": "internal/testing/integration/testdata/migrations/spanner/00005_no_tx.sql",
"chars": 263,
"preview": "-- +goose NO TRANSACTION\n\n-- +goose Up\n-- +goose StatementBegin\nCREATE UNIQUE NULL_FILTERED INDEX owners_owner_name_idx "
},
{
"path": "internal/testing/integration/testdata/migrations/spanner/00006_view.sql",
"chars": 300,
"preview": "-- +goose NO TRANSACTION\n\n-- +goose Up\n-- +goose StatementBegin\nCREATE VIEW view_owners\nSQL SECURITY INVOKER AS\nSELECT\n "
},
{
"path": "internal/testing/integration/testdata/migrations/starrocks/00001_a.sql",
"chars": 193,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE SCHEMA IF NOT EXISTS testing;\n-- +goose StatementEnd\n\n-- +goose Down\n-- +go"
},
{
"path": "internal/testing/integration/testdata/migrations/starrocks/00002_b.sql",
"chars": 857,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE testing.test_migrations_1 (\n\t\tversion_id bigint NOT NULL,\n\t\tid bigint"
},
{
"path": "internal/testing/integration/testdata/migrations/starrocks/00003_c.sql",
"chars": 525,
"preview": "-- +goose Up\n-- +goose StatementBegin\nINSERT INTO testing.test_migrations_1 (version_id, is_applied) VALUES (1, true);\n-"
},
{
"path": "internal/testing/integration/testdata/migrations/turso/00001_table.sql",
"chars": 561,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE owners (\n owner_id INTEGER PRIMARY KEY AUTOINCREMENT,\n owner_na"
},
{
"path": "internal/testing/integration/testdata/migrations/turso/00002_insert.sql",
"chars": 528,
"preview": "-- +goose Up\n-- +goose StatementBegin\nINSERT INTO owners(owner_name, owner_type) \nVALUES \n ('lucas', 'user'),\n ('s"
},
{
"path": "internal/testing/integration/testdata/migrations/turso/00003_alter.sql",
"chars": 322,
"preview": "-- +goose Up\n-- +goose StatementBegin\nALTER TABLE repos \nADD COLUMN homepage_url TEXT;\n\nALTER TABLE repos \nADD COLUMN is"
},
{
"path": "internal/testing/integration/testdata/migrations/turso/00004_empty.sql",
"chars": 13,
"preview": "-- +goose Up\n"
},
{
"path": "internal/testing/integration/testdata/migrations/turso/00005_no_tx.sql",
"chars": 180,
"preview": "-- +goose NO TRANSACTION\n\n-- +goose Up\nCREATE UNIQUE INDEX IF NOT EXISTS idx_owners_owner_name ON owners(owner_name);\n\n\n"
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00001_a.sql",
"chars": 398,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE owners (\n owner_id Uint64,\n owner_name Utf8,\n owner_type Utf"
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00002_b.sql",
"chars": 257,
"preview": "-- +goose Up\n-- +goose StatementBegin\nINSERT INTO owners(owner_id, owner_name, owner_type)\nVALUES (1, 'lucas', 'user'), "
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00003_c.sql",
"chars": 464,
"preview": "\n-- +goose Up\n-- +goose StatementBegin\nINSERT INTO owners(owner_id, owner_name, owner_type)\nVALUES (3, 'james', 'user'),"
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00004_d.sql",
"chars": 286,
"preview": "-- +goose Up\n-- +goose StatementBegin\nALTER TABLE repos\n ADD COLUMN homepage_url Utf8,\n ADD COLUMN is_private Bool"
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00005_e.sql",
"chars": 295,
"preview": "-- +goose Up\n-- +goose StatementBegin\n-- NOTE: intentionally left blank to verify migration logic.\nSELECT 'up SQL query'"
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00006_f.sql",
"chars": 359,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE stargazers (\n stargazer_repo_id Uint64,\n stargazer_owner_id UIn"
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00007_g.sql",
"chars": 332,
"preview": "-- +goose Up\n-- +goose StatementBegin\nCREATE TABLE issues (\n issue_id Uint64,\n issue_created_by Uint64,\n issue_"
},
{
"path": "internal/testing/integration/testdata/migrations/ydb/00008_h.sql",
"chars": 239,
"preview": "-- +goose Up\n-- +goose StatementBegin\nALTER TABLE stargazers DROP COLUMN stargazer_location;\n-- +goose StatementEnd\n\n-- "
},
{
"path": "internal/testing/testdb/clickhouse.go",
"chars": 3257,
"preview": "package testdb\n\nimport (\n\t\"crypto/tls\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ClickHouse/clickho"
},
{
"path": "internal/testing/testdb/container_healthcheck.go",
"chars": 884,
"preview": "package testdb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/ory/dockertest/v3\"\n\t\"github.com/ory/dockertest/v3/docker/types"
},
{
"path": "internal/testing/testdb/mariadb.go",
"chars": 2690,
"preview": "package testdb\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"strconv\"\n\t\"time\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github."
},
{
"path": "internal/testing/testdb/options.go",
"chars": 271,
"preview": "package testdb\n\ntype options struct {\n\tbindPort int\n\tdebug bool\n}\n\ntype OptionsFunc func(o *options)\n\nfunc WithBindPo"
},
{
"path": "internal/testing/testdb/postgres.go",
"chars": 2485,
"preview": "package testdb\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"strconv\"\n\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n\t\"github.com/ory/"
},
{
"path": "internal/testing/testdb/spanner.go",
"chars": 3942,
"preview": "package testdb\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t_ \"github.com/googleapis/go"
},
{
"path": "internal/testing/testdb/starrocks.go",
"chars": 2731,
"preview": "package testdb\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"strconv\"\n\t\"time\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github."
},
{
"path": "internal/testing/testdb/testdb.go",
"chars": 1358,
"preview": "package testdb\n\nimport \"database/sql\"\n\n// NewClickHouse starts a ClickHouse docker container. Returns db connection and "
},
{
"path": "internal/testing/testdb/turso.go",
"chars": 2664,
"preview": "package testdb\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"strconv\"\n\n\t\"github.com/ory/dockertest/v3\"\n\t\"github.com/ory/dock"
},
{
"path": "internal/testing/testdb/ydb.go",
"chars": 3637,
"preview": "package testdb\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ory/dockertest/"
},
{
"path": "lock/internal/store/postgres.go",
"chars": 7430,
"preview": "package store\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/multierr\"\n)\n\n// Ne"
},
{
"path": "lock/internal/store/store.go",
"chars": 1958,
"preview": "package store\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"time\"\n)\n\n// LockStore defines the interface for storing and managin"
},
{
"path": "lock/internal/table/config.go",
"chars": 570,
"preview": "package table\n\nimport (\n\t\"log/slog\"\n\t\"time\"\n)\n\n// Config holds configuration for table locker.\ntype Config struct {\n\tTab"
},
{
"path": "lock/internal/table/locker.go",
"chars": 7275,
"preview": "package table\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"sync"
},
{
"path": "lock/locker.go",
"chars": 1145,
"preview": "// Package lock defines the Locker interface and implements the locking logic.\npackage lock\n\nimport (\n\t\"context\"\n\t\"datab"
},
{
"path": "lock/locktesting/locktesting.go",
"chars": 6193,
"preview": "package locktesting\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/pressly/goose/v"
},
{
"path": "lock/postgres.go",
"chars": 5507,
"preview": "package lock\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/pressly/goose/v3/lock/internal/"
},
{
"path": "lock/session_locker_options.go",
"chars": 3108,
"preview": "package lock\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\nconst (\n\t// DefaultLockID is the id used to lock the database for migrations"
},
{
"path": "lock/table_locker_options.go",
"chars": 4340,
"preview": "package lock\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"time\"\n\n\t\"github.com/pressly/goose/v3/lock/internal/table\"\n)\n\nconst"
}
]
// ... and 68 more files (download for full content)
About this extraction
This page contains the full source code of the pressly/goose GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 268 files (642.6 KB), approximately 212.3k tokens, and a symbol index with 890 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.