Skip to content
This repository was archived by the owner on Nov 12, 2025. It is now read-only.

Commit ddbc0e4

Browse files
committed
"Rebase" off the parent, as best as one can when this repo isn't an actual fork.
1 parent c378583 commit ddbc0e4

File tree

9 files changed

+335
-201
lines changed

9 files changed

+335
-201
lines changed

.circleci/config.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ jobs:
4040
key: go-mod-v1-{{ arch }}-{{ checksum "go.sum" }}
4141
paths:
4242
- "/go/pkg/mod"
43-
- run: go get github.com/mattn/goveralls
44-
- run: goveralls -service=circle-ci -coverprofile /tmp/coverage/combined.txt
4543

4644
"golang-1_16":
4745
<<: *template

.github/workflows/ci.yaml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,6 @@ jobs:
3333
- name: Run test
3434
run: make test COVERAGE_DIR=/tmp/coverage
3535

36-
- name: Send goveralls coverage
37-
uses: shogo82148/actions-goveralls@v1
38-
with:
39-
path-to-profile: /tmp/coverage/combined.txt
40-
flag-name: Go-${{ matrix.go }}
41-
parallel: true
42-
43-
check-coverage:
44-
name: Check coverage
45-
needs: [test]
46-
runs-on: ubuntu-latest
47-
steps:
48-
- uses: shogo82148/actions-goveralls@v1
49-
with:
50-
parallel-finished: true
51-
5236
goreleaser:
5337
name: Release a new version
5438
needs: [lint, test]

.travis.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,11 @@ before_install:
3737
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0
3838
- echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}"
3939

40-
install:
41-
- go get github.com/mattn/goveralls
42-
4340
script:
4441
- golangci-lint run
4542
- make test COVERAGE_DIR=/tmp/coverage
4643

4744
after_success:
48-
- goveralls -service=travis-ci -coverprofile /tmp/coverage/combined.txt
4945
- make list-external-deps > dependency_tree.txt && cat dependency_tree.txt
5046
- make build-cli
5147
- gem install --no-document fpm

Makefile

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
SOURCE ?= file go_bindata github github_ee bitbucket aws_s3 google_cloud_storage godoc_vfs gitlab
2-
DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb yugabytedb clickhouse mongodb sqlserver firebird neo4j pgx pgx5 rqlite
2+
DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb yugabytedb clickhouse mongodb sqlserver firebird neo4j pgx pgx5 rqlite duckdb
33
DATABASE_TEST ?= $(DATABASE) sqlite sqlite3 sqlcipher
44
VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-)
55
TEST_FLAGS ?=
66
REPO_OWNER ?= $(shell cd .. && basename "$$(pwd)")
77
COVERAGE_DIR ?= .coverage
88

99
build:
10-
CGO_ENABLED=0 go build -ldflags='-X main.Version=$(VERSION)' -tags '$(DATABASE) $(SOURCE)' ./cmd/migrate
10+
CGO_ENABLED=1 go build -ldflags='-X main.Version=$(VERSION)' -tags '$(DATABASE) $(SOURCE)' ./cmd/migrate
1111

1212
build-docker:
13-
CGO_ENABLED=0 go build -a -o build/migrate.linux-386 -ldflags="-s -w -X main.Version=${VERSION}" -tags "$(DATABASE) $(SOURCE)" ./cmd/migrate
13+
CGO_ENABLED=1 go build -a -o build/migrate.linux-386 -ldflags="-s -w -X main.Version=${VERSION}" -tags "$(DATABASE) $(SOURCE)" ./cmd/migrate
1414

1515
build-cli: clean
1616
-mkdir ./cli/build
17-
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o ../../cli/build/migrate.linux-amd64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
18-
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -a -o ../../cli/build/migrate.linux-armv7 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
19-
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -a -o ../../cli/build/migrate.linux-arm64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
20-
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -a -o ../../cli/build/migrate.darwin-amd64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
21-
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -a -o ../../cli/build/migrate.windows-386.exe -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
22-
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -a -o ../../cli/build/migrate.windows-amd64.exe -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
17+
cd ./cmd/migrate && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -a -o ../../cli/build/migrate.linux-amd64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
18+
cd ./cmd/migrate && CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 go build -a -o ../../cli/build/migrate.linux-armv7 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
19+
cd ./cmd/migrate && CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -a -o ../../cli/build/migrate.linux-arm64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
20+
cd ./cmd/migrate && CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -a -o ../../cli/build/migrate.darwin-amd64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
21+
cd ./cmd/migrate && CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -a -o ../../cli/build/migrate.windows-386.exe -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
22+
cd ./cmd/migrate && CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -a -o ../../cli/build/migrate.windows-amd64.exe -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
2323
cd ./cli/build && find . -name 'migrate*' | xargs -I{} tar czf {}.tar.gz {}
2424
cd ./cli/build && shasum -a 256 * > sha256sum.txt
2525
cat ./cli/build/sha256sum.txt

README.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
[![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/golang-migrate/migrate/ci.yaml?branch=master)](https://github.com/golang-migrate/migrate/actions/workflows/ci.yaml?query=branch%3Amaster)
2-
[![GoDoc](https://pkg.go.dev/badge/github.com/golang-migrate/migrate)](https://pkg.go.dev/github.com/golang-migrate/migrate/v4)
3-
[![Coverage Status](https://img.shields.io/coveralls/github/golang-migrate/migrate/master.svg)](https://coveralls.io/github/golang-migrate/migrate?branch=master)
4-
[![packagecloud.io](https://img.shields.io/badge/deb-packagecloud.io-844fec.svg)](https://packagecloud.io/golang-migrate/migrate?filter=debs)
5-
[![Docker Pulls](https://img.shields.io/docker/pulls/migrate/migrate.svg)](https://hub.docker.com/r/migrate/migrate/)
6-
![Supported Go Versions](https://img.shields.io/badge/Go-1.22%2C%201.23-lightgrey.svg)
7-
[![GitHub Release](https://img.shields.io/github/release/golang-migrate/migrate.svg)](https://github.com/golang-migrate/migrate/releases)
8-
[![Go Report Card](https://goreportcard.com/badge/github.com/golang-migrate/migrate/v4)](https://goreportcard.com/report/github.com/golang-migrate/migrate/v4)
1+
# NOTE
2+
# NOTE: This is a Kubecost internal fork of https://github.com/golang-migrate/migrate for implementing DuckDB migration support. If this project goes well and our internal fork is adopted, we should strongly consider contributed back to upstream.
3+
# NOTE
4+
# Forked on 2023-07-12
95

106
# migrate
117

database/duckdb/duckdb.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package duckdb
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"io"
7+
nurl "net/url"
8+
"strings"
9+
10+
"go.uber.org/atomic"
11+
12+
"github.com/golang-migrate/migrate/v4"
13+
"github.com/golang-migrate/migrate/v4/database"
14+
"github.com/hashicorp/go-multierror"
15+
_ "github.com/marcboeker/go-duckdb"
16+
)
17+
18+
func init() {
19+
database.Register("duckdb", &DuckDB{})
20+
}
21+
22+
const MigrationTable = "gmg_schema_migrations"
23+
24+
type DuckDB struct {
25+
db *sql.DB
26+
isLocked atomic.Bool
27+
}
28+
29+
func (d *DuckDB) Open(url string) (database.Driver, error) {
30+
purl, err := nurl.Parse(url)
31+
if err != nil {
32+
return nil, fmt.Errorf("parsing url: %w", err)
33+
}
34+
dbfile := strings.Replace(migrate.FilterCustomQuery(purl).String(), "duckdb://", "", 1)
35+
db, err := sql.Open("duckdb", dbfile)
36+
if err != nil {
37+
return nil, fmt.Errorf("opening '%s': %w", dbfile, err)
38+
}
39+
40+
if err := db.Ping(); err != nil {
41+
return nil, fmt.Errorf("pinging: %w", err)
42+
}
43+
d.db = db
44+
45+
if err := d.ensureVersionTable(); err != nil {
46+
return nil, fmt.Errorf("ensuring version table: %w", err)
47+
}
48+
49+
return d, nil
50+
}
51+
52+
func (d *DuckDB) Close() error {
53+
return d.db.Close()
54+
}
55+
56+
func (d *DuckDB) Lock() error {
57+
if !d.isLocked.CAS(false, true) {
58+
return database.ErrLocked
59+
}
60+
return nil
61+
}
62+
63+
func (d *DuckDB) Unlock() error {
64+
if !d.isLocked.CAS(true, false) {
65+
return database.ErrNotLocked
66+
}
67+
return nil
68+
}
69+
70+
func (d *DuckDB) Drop() error {
71+
// FIXME: implement
72+
return fmt.Errorf("drop unimplemented because of duckdb size problems and not enough time during prototyping")
73+
}
74+
75+
func (d *DuckDB) SetVersion(version int, dirty bool) error {
76+
tx, err := d.db.Begin()
77+
if err != nil {
78+
return &database.Error{OrigErr: err, Err: "transaction start failed"}
79+
}
80+
81+
query := "DELETE FROM " + MigrationTable
82+
if _, err := tx.Exec(query); err != nil {
83+
return &database.Error{OrigErr: err, Query: []byte(query)}
84+
}
85+
86+
// Also re-write the schema version for nil dirty versions to prevent
87+
// empty schema version for failed down migration on the first migration
88+
// See: https://github.com/golang-migrate/migrate/issues/330
89+
//
90+
// NOTE: Copied from sqlite implementation, unsure if this is necessary for
91+
// duckdb
92+
if version >= 0 || (version == database.NilVersion && dirty) {
93+
query := fmt.Sprintf(`INSERT INTO %s (version, dirty) VALUES (?, ?)`, MigrationTable)
94+
if _, err := tx.Exec(query, version, dirty); err != nil {
95+
if errRollback := tx.Rollback(); errRollback != nil {
96+
err = multierror.Append(err, errRollback)
97+
}
98+
return &database.Error{OrigErr: err, Query: []byte(query)}
99+
}
100+
}
101+
102+
if err := tx.Commit(); err != nil {
103+
return &database.Error{OrigErr: err, Err: "transaction commit failed"}
104+
}
105+
106+
return nil
107+
}
108+
109+
func (m *DuckDB) Version() (version int, dirty bool, err error) {
110+
query := "SELECT version, dirty FROM " + MigrationTable + " LIMIT 1"
111+
err = m.db.QueryRow(query).Scan(&version, &dirty)
112+
if err != nil {
113+
return database.NilVersion, false, nil
114+
}
115+
return version, dirty, nil
116+
}
117+
118+
func (d *DuckDB) Run(migration io.Reader) error {
119+
migr, err := io.ReadAll(migration)
120+
if err != nil {
121+
return fmt.Errorf("reading migration: %w", err)
122+
}
123+
query := string(migr[:])
124+
125+
tx, err := d.db.Begin()
126+
if err != nil {
127+
return &database.Error{OrigErr: err, Err: "transaction start failed"}
128+
}
129+
if _, err := tx.Exec(query); err != nil {
130+
if errRollback := tx.Rollback(); errRollback != nil {
131+
err = multierror.Append(err, errRollback)
132+
}
133+
return &database.Error{OrigErr: err, Query: []byte(query)}
134+
}
135+
if err := tx.Commit(); err != nil {
136+
return &database.Error{OrigErr: err, Err: "transaction commit failed"}
137+
}
138+
return nil
139+
}
140+
141+
// ensureVersionTable checks if versions table exists and, if not, creates it.
142+
// Note that this function locks the database, which deviates from the usual
143+
// convention of "caller locks" in the Sqlite type.
144+
func (d *DuckDB) ensureVersionTable() (err error) {
145+
if err = d.Lock(); err != nil {
146+
return err
147+
}
148+
149+
defer func() {
150+
if e := d.Unlock(); e != nil {
151+
if err == nil {
152+
err = e
153+
} else {
154+
err = multierror.Append(err, e)
155+
}
156+
}
157+
}()
158+
159+
query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s (version BIGINT, dirty BOOLEAN);`, MigrationTable)
160+
161+
if _, err := d.db.Exec(query); err != nil {
162+
return fmt.Errorf("creating version table via '%s': %w", query, err)
163+
}
164+
return nil
165+
}

0 commit comments

Comments
 (0)