From 1d402b32ffcc30d66ffa02885e3b3ed32e9b0b11 Mon Sep 17 00:00:00 2001
From: Nicholas Duffy <duffn@outlook.com>
Date: Wed, 3 May 2017 17:41:20 -0600
Subject: [PATCH] Add Redshift driver support

Redshift can be accessed and `up`/`down` SQL executed with the `pq`
library by default, however, the `createVersionTableSql` of the `PostgresDialect`
is not compatible with Redshift due to the `serial` datatype and `now()` default on.

This PR creates a new Redshift dialect. The dialect still uses the `pq` library and
only updates the SQL in `createVersionTableSql` to be compatible with Redshift.

Closes #32.
---
 README.md                         |  1 +
 cmd/goose/main.go                 |  8 ++++++--
 dialect.go                        | 31 +++++++++++++++++++++++++++++++
 example/migrations-go/cmd/main.go |  7 ++++++-
 4 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 1832072..e140701 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ Examples:
     goose postgres "user=postgres dbname=postgres sslmode=disable" up
     goose mysql "user:password@/dbname" down
     goose sqlite3 ./foo.db status
+    goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" create init sql
 
 Options:
   -dir string
diff --git a/cmd/goose/main.go b/cmd/goose/main.go
index 94527a7..407b889 100644
--- a/cmd/goose/main.go
+++ b/cmd/goose/main.go
@@ -46,7 +46,7 @@ func main() {
 	driver, dbstring, command := args[0], args[1], args[2]
 
 	switch driver {
-	case "postgres", "mysql", "sqlite3":
+	case "postgres", "mysql", "sqlite3", "redshift":
 		if err := goose.SetDialect(driver); err != nil {
 			log.Fatal(err)
 		}
@@ -60,6 +60,10 @@ func main() {
 	default:
 	}
 
+	if driver == "redshift" {
+		driver = "postgres"
+	}
+
 	db, err := sql.Open(driver, dbstring)
 	if err != nil {
 		log.Fatalf("-dbstring=%q: %v\n", dbstring, err)
@@ -88,7 +92,7 @@ Examples:
     goose postgres "user=postgres dbname=postgres sslmode=disable" up
     goose mysql "user:password@/dbname" down
     goose sqlite3 ./foo.db status
-    goose postgres "user=postgres dbname=postgres sslmode=disable" create init sql
+    goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" create init sql
 
 Options:
 `
diff --git a/dialect.go b/dialect.go
index 536152b..8237849 100644
--- a/dialect.go
+++ b/dialect.go
@@ -27,6 +27,8 @@ func SetDialect(d string) error {
 		dialect = &MySqlDialect{}
 	case "sqlite3":
 		dialect = &Sqlite3Dialect{}
+	case "redshift":
+		dialect = &RedshiftDialect{}
 	default:
 		return fmt.Errorf("%q: unknown dialect", d)
 	}
@@ -119,3 +121,32 @@ func (m Sqlite3Dialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
 
 	return rows, err
 }
+
+////////////////////////////
+// Redshift
+////////////////////////////
+
+type RedshiftDialect struct{}
+
+func (rs RedshiftDialect) createVersionTableSql() string {
+	return `CREATE TABLE goose_db_version (
+            	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)
+            );`
+}
+
+func (rs RedshiftDialect) insertVersionSql() string {
+	return "INSERT INTO goose_db_version (version_id, is_applied) VALUES ($1, $2);"
+}
+
+func (rs RedshiftDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
+	rows, err := db.Query("SELECT version_id, is_applied from goose_db_version ORDER BY id DESC")
+	if err != nil {
+		return nil, err
+	}
+
+	return rows, err
+}
diff --git a/example/migrations-go/cmd/main.go b/example/migrations-go/cmd/main.go
index 797b471..a7d3ce9 100644
--- a/example/migrations-go/cmd/main.go
+++ b/example/migrations-go/cmd/main.go
@@ -40,7 +40,7 @@ func main() {
 	driver, dbstring, command := args[0], args[1], args[2]
 
 	switch driver {
-	case "postgres", "mysql", "sqlite3":
+	case "postgres", "mysql", "sqlite3", "redshift":
 		if err := goose.SetDialect(driver); err != nil {
 			log.Fatal(err)
 		}
@@ -54,6 +54,10 @@ func main() {
 	default:
 	}
 
+	if driver == "redshift" {
+		driver = "postgres"
+	}
+
 	db, err := sql.Open(driver, dbstring)
 	if err != nil {
 		log.Fatalf("-dbstring=%q: %v\n", dbstring, err)
@@ -77,6 +81,7 @@ Examples:
     goose postgres "user=postgres dbname=postgres sslmode=disable" up
     goose mysql "user:password@/dbname" down
     goose sqlite3 ./foo.db status
+    goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" create init sql
 
 Options:
 `