Skip to content

Commit

Permalink
Adds vertica driver support (#393)
Browse files Browse the repository at this point in the history
  • Loading branch information
bobpace authored Dec 5, 2022
1 parent 73e53f9 commit b582641
Show file tree
Hide file tree
Showing 15 changed files with 357 additions and 1 deletion.
13 changes: 13 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,16 @@ jobs:
go-version: "1.18"
- name: Run clickhouse test
run: make test-clickhouse
test-vertica:
name: Run vertica tests
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: "1.18"
- name: Run vertica test
run: make test-vertica
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ test-e2e-mysql:
test-clickhouse:
go test -timeout=10m -count=1 -race -v ./tests/clickhouse -test.short

test-vertica:
go test -count=1 -v ./tests/vertica

docker-cleanup:
docker stop -t=0 $$(docker ps --filter="label=goose_test" -aq)

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Drivers:
redshift
tidb
clickhouse
vertica
Examples:
goose sqlite3 ./foo.db status
Expand All @@ -74,6 +75,8 @@ Examples:
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 vertica "vertica://user:password@localhost:5433/dbname?connection_load_balance=1" status
Options:
Expand Down
8 changes: 8 additions & 0 deletions cmd/goose/driver_vertica.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !no_vertica
// +build !no_vertica

package main

import (
_ "github.com/vertica/vertica-sql-go"
)
2 changes: 2 additions & 0 deletions cmd/goose/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ Drivers:
redshift
tidb
clickhouse
vertica
Examples:
goose sqlite3 ./foo.db status
Expand All @@ -211,6 +212,7 @@ Examples:
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 vertica "vertica://user:password@localhost:5433/dbname?connection_load_balance=1" status
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose status
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose create init sql
Expand Down
2 changes: 1 addition & 1 deletion db.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func OpenDBWithDriver(driver string, dbstring string) (*sql.DB, error) {
}

switch driver {
case "postgres", "pgx", "sqlite3", "sqlite", "mysql", "sqlserver", "clickhouse":
case "postgres", "pgx", "sqlite3", "sqlite", "mysql", "sqlserver", "clickhouse", "vertica":
return sql.Open(driver, dbstring)
default:
return nil, fmt.Errorf("unsupported driver %s", driver)
Expand Down
40 changes: 40 additions & 0 deletions dialect.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func SetDialect(d string) error {
dialect = &TiDBDialect{}
case "clickhouse":
dialect = &ClickHouseDialect{}
case "vertica":
dialect = &VerticaDialect{}
default:
return fmt.Errorf("%q: unknown dialect", d)
}
Expand Down Expand Up @@ -322,3 +324,41 @@ func (m ClickHouseDialect) migrationSQL() string {
func (m ClickHouseDialect) deleteVersionSQL() string {
return fmt.Sprintf("ALTER TABLE %s DELETE WHERE version_id = $1", TableName())
}

////////////////////////////
// Vertica
////////////////////////////

// VerticaDialect struct.
type VerticaDialect struct{}

func (v VerticaDialect) createVersionTableSQL() string {
return fmt.Sprintf(`CREATE TABLE %s (
id identity(1,1) NOT NULL,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp NULL default now(),
PRIMARY KEY(id)
);`, TableName())
}

func (v VerticaDialect) insertVersionSQL() string {
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (?, ?);", TableName())
}

func (v VerticaDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
if err != nil {
return nil, err
}

return rows, err
}

func (m VerticaDialect) migrationSQL() string {
return fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1", TableName())
}

func (v VerticaDialect) deleteVersionSQL() string {
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/go-sql-driver/mysql v1.6.0
github.com/jackc/pgx/v4 v4.17.2
github.com/ory/dockertest/v3 v3.9.1
github.com/vertica/vertica-sql-go v1.2.2
github.com/ziutek/mymysql v1.5.4
modernc.org/sqlite v1.19.2
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vertica/vertica-sql-go v1.2.2 h1:woI501lizEoqONmO5B7a5DNsLTQTsT0HnD1JM7SiNhk=
github.com/vertica/vertica-sql-go v1.2.2/go.mod h1:fGr44VWdEvL+f+Qt5LkKLOT7GoxaWdoUCnPBU9h6t04=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
Expand Down
5 changes: 5 additions & 0 deletions internal/testdb/testdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ func NewPostgres(options ...OptionsFunc) (db *sql.DB, cleanup func(), err error)
func NewMariaDB(options ...OptionsFunc) (db *sql.DB, cleanup func(), err error) {
return newMariaDB(options...)
}

// NewVertica starts a Vertica docker container. Returns a db connection and a docker cleanup function.
func NewVertica(options ...OptionsFunc) (db *sql.DB, cleanup func(), err error) {
return newVertica(options...)
}
96 changes: 96 additions & 0 deletions internal/testdb/vertica.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package testdb

import (
"database/sql"
"fmt"
"log"
"strconv"
"time"

"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
_ "github.com/vertica/vertica-sql-go"
)

const (
// https://hub.docker.com/r/vertica/vertica-ce
VERTICA_IMAGE = "vertica/vertica-ce"
VERTICA_VERSION = "12.0.0-0"
VERTICA_DB = "testdb"
)

func newVertica(opts ...OptionsFunc) (*sql.DB, func(), error) {
option := &options{}
for _, f := range opts {
f(option)
}
// Uses a sensible default on windows (tcp/http) and linux/osx (socket).
pool, err := dockertest.NewPool("")
if err != nil {
return nil, nil, fmt.Errorf("failed to connect to docker: %v", err)
}
options := &dockertest.RunOptions{
Repository: VERTICA_IMAGE,
Tag: VERTICA_VERSION,
Env: []string{
"VERTICA_DB_NAME=" + VERTICA_DB,
"VMART_ETL_SCRIPT=", // Don't install VMART data inside container.
},
Labels: map[string]string{"goose_test": "1"},
PortBindings: make(map[docker.Port][]docker.PortBinding),
// Prevent package installation for faster container startup.
Mounts: []string{"/tmp/empty:/opt/vertica/packages"},
}
if option.bindPort > 0 {
options.PortBindings[docker.Port("5433/tcp")] = []docker.PortBinding{
{HostPort: strconv.Itoa(option.bindPort)},
}
}
container, err := pool.RunWithOptions(
options,
func(config *docker.HostConfig) {
// Set AutoRemove to true so that stopped container goes away by itself.
config.AutoRemove = true
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
},
)
if err != nil {
return nil, nil, fmt.Errorf("failed to create docker container: %v", err)
}
cleanup := func() {
if option.debug {
// User must manually delete the Docker container.
return
}
if err := pool.Purge(container); err != nil {
log.Printf("failed to purge resource: %v", err)
}
}
verticaInfo := fmt.Sprintf("vertica://%s:%s@%s:%s/%s",
"dbadmin",
"",
"localhost",
container.GetPort("5433/tcp"), // Fetch port dynamically assigned to container
VERTICA_DB,
)

var db *sql.DB
// Give vertica a head start since the container takes a little bit to start up.
time.Sleep(time.Second * 15)

// Exponential backoff-retry, because the application in the container
// might not be ready to accept connections yet.
if err := pool.Retry(
func() error {
var err error
db, err = sql.Open("vertica", verticaInfo)
if err != nil {
return err
}
return db.Ping()
},
); err != nil {
return nil, cleanup, fmt.Errorf("could not connect to docker database: %v", err)
}
return db, cleanup, nil
}
9 changes: 9 additions & 0 deletions tests/vertica/testdata/migrations/00001_a.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
CREATE SCHEMA IF NOT EXISTS testing;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP SCHEMA IF EXISTS testing;
-- +goose StatementEnd
44 changes: 44 additions & 0 deletions tests/vertica/testdata/migrations/00002_b.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS testing.dim_test_scd
(
test_key BIGINT NOT NULL,
test_id UUID NOT NULL,
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
is_current BOOLEAN NOT NULL
DEFAULT (valid_to = '9999/12/31'),
external_id VARCHAR(100)
) UNSEGMENTED ALL NODES;
-- +goose StatementEnd
-- +goose StatementBegin
CREATE PROJECTION IF NOT EXISTS testing.dim_test_scd_proj_is_current AS
SELECT test_key,
test_id,
valid_from,
valid_to,
is_current,
external_id
FROM testing.dim_test_scd
ORDER BY is_current, test_id
SEGMENTED BY HASH(test_key) ALL NODES;
-- +goose StatementEnd
-- +goose StatementBegin
CREATE OR REPLACE VIEW testing.Test AS
SELECT test_key,
test_id,
external_id
FROM testing.dim_test_scd
WHERE is_current = true;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP VIEW IF EXISTS testing.Test;
-- +goose StatementEnd
-- +goose StatementBegin
DROP PROJECTION IF EXISTS testing.dim_test_scd_proj_is_current;
-- +goose StatementEnd
-- +goose StatementBegin
DROP TABLE IF EXISTS testing.dim_test_scd;
-- +goose StatementEnd
15 changes: 15 additions & 0 deletions tests/vertica/testdata/migrations/00003_c.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO testing.dim_test_scd VALUES (1, '575a0dd4-bd97-44ac-aae0-987090181da8', '2021-10-02', '2021-10-03', false, '123');
-- +goose StatementEnd
-- +goose StatementBegin
INSERT INTO testing.dim_test_scd VALUES (2, '575a0dd4-bd97-44ac-aae0-987090181da8', '2021-10-03', '2021-10-04', false, '456');
-- +goose StatementEnd
-- +goose StatementBegin
INSERT INTO testing.dim_test_scd VALUES (3, '575a0dd4-bd97-44ac-aae0-987090181da8', '2021-10-04', '9999-12-31', true, '789');
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DELETE FROM testing.dim_test_scd where test_id = '575a0dd4-bd97-44ac-aae0-987090181da8';
-- +goose StatementEnd
Loading

0 comments on commit b582641

Please sign in to comment.