Skip to content

Commit

Permalink
Merge remote-tracking branch 'tobischo/feature/add-snowflake-dialect-…
Browse files Browse the repository at this point in the history
…support'
  • Loading branch information
nelsam committed Oct 18, 2022
2 parents f7ee81c + b24d00a commit 2db0f5e
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 0 deletions.
152 changes: 152 additions & 0 deletions dialect_snowflake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2012 James Cooper. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gorp

import (
"fmt"
"reflect"
"strings"
)

type SnowflakeDialect struct {
suffix string
LowercaseFields bool
}

func (d SnowflakeDialect) QuerySuffix() string { return ";" }

func (d SnowflakeDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
switch val.Kind() {
case reflect.Ptr:
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
case reflect.Bool:
return "boolean"
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32:

if isAutoIncr {
return "serial"
}
return "integer"
case reflect.Int64, reflect.Uint64:
if isAutoIncr {
return "bigserial"
}
return "bigint"
case reflect.Float64:
return "double precision"
case reflect.Float32:
return "real"
case reflect.Slice:
if val.Elem().Kind() == reflect.Uint8 {
return "binary"
}
}

switch val.Name() {
case "NullInt64":
return "bigint"
case "NullFloat64":
return "double precision"
case "NullBool":
return "boolean"
case "Time", "NullTime":
return "timestamp with time zone"
}

if maxsize > 0 {
return fmt.Sprintf("varchar(%d)", maxsize)
} else {
return "text"
}

}

// Returns empty string
func (d SnowflakeDialect) AutoIncrStr() string {
return ""
}

func (d SnowflakeDialect) AutoIncrBindValue() string {
return "default"
}

func (d SnowflakeDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
return ""
}

// Returns suffix
func (d SnowflakeDialect) CreateTableSuffix() string {
return d.suffix
}

func (d SnowflakeDialect) CreateIndexSuffix() string {
return ""
}

func (d SnowflakeDialect) DropIndexSuffix() string {
return ""
}

func (d SnowflakeDialect) TruncateClause() string {
return "truncate"
}

// Returns "$(i+1)"
func (d SnowflakeDialect) BindVar(i int) string {
return "?"
}

func (d SnowflakeDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error {
rows, err := exec.Query(insertSql, params...)
if err != nil {
return err
}
defer rows.Close()

if !rows.Next() {
return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err())
}
if err := rows.Scan(target); err != nil {
return err
}
if rows.Next() {
return fmt.Errorf("more than two serial value returned for insert: %s", insertSql)
}
return rows.Err()
}

func (d SnowflakeDialect) QuoteField(f string) string {
if d.LowercaseFields {
return `"` + strings.ToLower(f) + `"`
}
return `"` + f + `"`
}

func (d SnowflakeDialect) QuotedTableForQuery(schema string, table string) string {
if strings.TrimSpace(schema) == "" {
return d.QuoteField(table)
}

return schema + "." + d.QuoteField(table)
}

func (d SnowflakeDialect) IfSchemaNotExists(command, schema string) string {
return fmt.Sprintf("%s if not exists", command)
}

func (d SnowflakeDialect) IfTableExists(command, schema, table string) string {
return fmt.Sprintf("%s if exists", command)
}

func (d SnowflakeDialect) IfTableNotExists(command, schema, table string) string {
return fmt.Sprintf("%s if not exists", command)
}
143 changes: 143 additions & 0 deletions dialect_snowflake_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright 2012 James Cooper. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

//go:build !integration
// +build !integration

package gorp_test

import (
"database/sql"
"reflect"
"testing"
"time"

"github.com/go-gorp/gorp/v3"
"github.com/poy/onpar"
"github.com/poy/onpar/expect"
"github.com/poy/onpar/matchers"
)

func TestSnowflakeDialect(t *testing.T) {
o := onpar.New()
defer o.Run(t)

o.BeforeEach(func(t *testing.T) (expect.Expectation, gorp.SnowflakeDialect) {
return expect.New(t), gorp.SnowflakeDialect{
LowercaseFields: false,
}
})

o.Group("ToSqlType", func() {
tests := []struct {
name string
value interface{}
maxSize int
autoIncr bool
expected string
}{
{"bool", true, 0, false, "boolean"},
{"int8", int8(1), 0, false, "integer"},
{"uint8", uint8(1), 0, false, "integer"},
{"int16", int16(1), 0, false, "integer"},
{"uint16", uint16(1), 0, false, "integer"},
{"int32", int32(1), 0, false, "integer"},
{"int (treated as int32)", int(1), 0, false, "integer"},
{"uint32", uint32(1), 0, false, "integer"},
{"uint (treated as uint32)", uint(1), 0, false, "integer"},
{"int64", int64(1), 0, false, "bigint"},
{"uint64", uint64(1), 0, false, "bigint"},
{"float32", float32(1), 0, false, "real"},
{"float64", float64(1), 0, false, "double precision"},
{"[]uint8", []uint8{1}, 0, false, "bytea"},
{"NullInt64", sql.NullInt64{}, 0, false, "bigint"},
{"NullFloat64", sql.NullFloat64{}, 0, false, "double precision"},
{"NullBool", sql.NullBool{}, 0, false, "boolean"},
{"Time", time.Time{}, 0, false, "timestamp with time zone"},
{"default-size string", "", 0, false, "text"},
{"sized string", "", 50, false, "varchar(50)"},
{"large string", "", 1024, false, "varchar(1024)"},
}
for _, t := range tests {
o.Spec(t.name, func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
typ := reflect.TypeOf(t.value)
sqlType := dialect.ToSqlType(typ, t.maxSize, t.autoIncr)
expect(sqlType).To(matchers.Equal(t.expected))
})
}
})

o.Spec("AutoIncrStr", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.AutoIncrStr()).To(matchers.Equal(""))
})

o.Spec("AutoIncrBindValue", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.AutoIncrBindValue()).To(matchers.Equal("default"))
})

o.Spec("AutoIncrInsertSuffix", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.AutoIncrInsertSuffix(nil)).To(matchers.Equal(""))
})

o.Spec("CreateTableSuffix", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.CreateTableSuffix()).To(matchers.Equal(""))
})

o.Spec("CreateIndexSuffix", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.CreateIndexSuffix()).To(matchers.Equal(""))
})

o.Spec("DropIndexSuffix", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.DropIndexSuffix()).To(matchers.Equal(""))
})

o.Spec("TruncateClause", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.TruncateClause()).To(matchers.Equal("truncate"))
})

o.Spec("BindVar", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.BindVar(0)).To(matchers.Equal("?"))
expect(dialect.BindVar(4)).To(matchers.Equal("?"))
})

o.Group("QuoteField", func() {
o.Spec("By default, case is preserved", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.QuoteField("Foo")).To(matchers.Equal(`"Foo"`))
expect(dialect.QuoteField("bar")).To(matchers.Equal(`"bar"`))
})

o.Group("With LowercaseFields set to true", func() {
o.BeforeEach(func(expect expect.Expectation, dialect gorp.SnowflakeDialect) (expect.Expectation, gorp.SnowflakeDialect) {
dialect.LowercaseFields = true
return expect, dialect
})

o.Spec("fields are lowercased", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.QuoteField("Foo")).To(matchers.Equal(`"foo"`))
})
})
})

o.Group("QuotedTableForQuery", func() {
o.Spec("using the default schema", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.QuotedTableForQuery("", "foo")).To(matchers.Equal(`"foo"`))
})

o.Spec("with a supplied schema", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.QuotedTableForQuery("foo", "bar")).To(matchers.Equal(`foo."bar"`))
})
})

o.Spec("IfSchemaNotExists", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.IfSchemaNotExists("foo", "bar")).To(matchers.Equal("foo if not exists"))
})

o.Spec("IfTableExists", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.IfTableExists("foo", "bar", "baz")).To(matchers.Equal("foo if exists"))
})

o.Spec("IfTableNotExists", func(expect expect.Expectation, dialect gorp.SnowflakeDialect) {
expect(dialect.IfTableNotExists("foo", "bar", "baz")).To(matchers.Equal("foo if not exists"))
})
}

0 comments on commit 2db0f5e

Please sign in to comment.