Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

do not produce ignore for on conflict updates #373

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dialect/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func DialectOptions() *goqu.SQLDialectOptions {

func DialectOptionsV8() *goqu.SQLDialectOptions {
opts := DialectOptions()
opts.SupportsWithCTE = true
opts.SupportsWithCTERecursive = true
opts.SupportsWindowFunction = true
return opts
}
Expand Down
3 changes: 3 additions & 0 deletions exp/exp.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ type (
Expression
Action() ConflictAction
}
ConflictNoIgnore interface {
NoIgnore()
}
ConflictUpdateExpression interface {
ConflictExpression
TargetColumn() string
Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ go 1.12

require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denisenkom/go-mssqldb v0.10.0
github.com/go-sql-driver/mysql v1.6.0
github.com/lib/pq v1.10.1
github.com/mattn/go-sqlite3 v1.14.7
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 // indirect
)
)
39 changes: 34 additions & 5 deletions sqlgen/expression_sql_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sqlgen

import (
"database/sql/driver"
"encoding/hex"
"reflect"
"strconv"
"time"
Expand Down Expand Up @@ -41,6 +42,10 @@ var (
ErrEmptyCaseWhens = errors.New(`when conditions not found for case statement`)
)

type HexBytes struct {
Buf []byte
}

func errUnsupportedExpressionType(e exp.Expression) error {
return errors.New("unsupported expression type %T", e)
}
Expand Down Expand Up @@ -153,7 +158,12 @@ func (esg *expressionSQLGenerator) reflectSQL(b sb.SQLBuilder, val interface{})
case util.IsBool(valKind):
esg.Generate(b, v.Bool())
default:
b.SetError(errors.NewEncodeError(val))
switch t := val.(type) {
case HexBytes:
esg.literalHexBytes(b, t)
default:
b.SetError(errors.NewEncodeError(val))
}
}
}

Expand Down Expand Up @@ -344,6 +354,22 @@ func (esg *expressionSQLGenerator) literalString(b sb.SQLBuilder, s string) {
b.WriteRunes(esg.dialectOptions.StringQuote)
}

func (esg *expressionSQLGenerator) literalHexBytes(b sb.SQLBuilder, hbs HexBytes) {
if b.IsPrepared() {
esg.placeHolderSQL(b, hbs)
return
}
if hbs.Buf == nil {
b.WriteStrings("NULL")
return
}

b.WriteStrings("0x")
bs := hbs.Buf
b.WriteStrings(hex.EncodeToString(bs))
bs = bs[len(bs):]
}

// Generates SQL for a slice of bytes
func (esg *expressionSQLGenerator) literalBytes(b sb.SQLBuilder, bs []byte) {
if b.IsPrepared() {
Expand Down Expand Up @@ -534,8 +560,9 @@ func (esg *expressionSQLGenerator) updateExpressionSQL(b sb.SQLBuilder, update e
}

// Generates SQL for a LiteralExpression
// L("a + b") -> a + b
// L("a = ?", 1) -> a = 1
//
// L("a + b") -> a + b
// L("a = ?", 1) -> a = 1
func (esg *expressionSQLGenerator) literalExpressionSQL(b sb.SQLBuilder, literal exp.LiteralExpression) {
l := literal.Literal()
args := literal.Args()
Expand All @@ -555,7 +582,8 @@ func (esg *expressionSQLGenerator) literalExpressionSQL(b sb.SQLBuilder, literal
}

// Generates SQL for a SQLFunctionExpression
// COUNT(I("a")) -> COUNT("a")
//
// COUNT(I("a")) -> COUNT("a")
func (esg *expressionSQLGenerator) sqlFunctionExpressionSQL(b sb.SQLBuilder, sqlFunc exp.SQLFunctionExpression) {
b.WriteStrings(sqlFunc.Name())
esg.Generate(b, sqlFunc.Args())
Expand Down Expand Up @@ -619,7 +647,8 @@ func (esg *expressionSQLGenerator) windowExpressionSQL(b sb.SQLBuilder, we exp.W
}

// Generates SQL for a CastExpression
// I("a").Cast("NUMERIC") -> CAST("a" AS NUMERIC)
//
// I("a").Cast("NUMERIC") -> CAST("a" AS NUMERIC)
func (esg *expressionSQLGenerator) castExpressionSQL(b sb.SQLBuilder, cast exp.CastExpression) {
b.Write(esg.dialectOptions.CastFragment).WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, cast.Casted())
Expand Down
9 changes: 8 additions & 1 deletion sqlgen/insert_sql_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,14 @@ func (isg *insertSQLGenerator) Generate(

// Adds the correct fragment to being an INSERT statement
func (isg *insertSQLGenerator) InsertBeginSQL(b sb.SQLBuilder, o exp.ConflictExpression) {
if isg.DialectOptions().SupportsInsertIgnoreSyntax && o != nil {
isUpdate := false
forceNoIgnore := false

if o != nil {
_, isUpdate = o.(exp.ConflictUpdateExpression)
_, forceNoIgnore = o.(exp.ConflictNoIgnore)
}
if isg.DialectOptions().SupportsInsertIgnoreSyntax && !isUpdate && !forceNoIgnore {
b.Write(isg.DialectOptions().InsertIgnoreClause)
} else {
b.Write(isg.DialectOptions().InsertClause)
Expand Down
12 changes: 6 additions & 6 deletions sqlgen/insert_sql_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,33 +333,33 @@ func (igs *insertSQLGeneratorSuite) TestGenerate_onConflict() {

insertTestCase{
clause: icDu,
sql: `insert ignore into "test" ("a") VALUES ('a1') on conflict (test) do update set "a"='b'`,
sql: `INSERT INTO "test" ("a") VALUES ('a1') on conflict (test) do update set "a"='b'`,
},
insertTestCase{
clause: icDu,
sql: `insert ignore into "test" ("a") VALUES (?) on conflict (test) do update set "a"=?`,
sql: `INSERT INTO "test" ("a") VALUES (?) on conflict (test) do update set "a"=?`,
isPrepared: true,
args: []interface{}{"a1", "b"},
},

insertTestCase{
clause: icDoc,
sql: `insert ignore into "test" ("a") VALUES ('a1') on conflict on constraint test do update set "a"='b'`,
sql: `INSERT INTO "test" ("a") VALUES ('a1') on conflict on constraint test do update set "a"='b'`,
},
insertTestCase{
clause: icDoc,
sql: `insert ignore into "test" ("a") VALUES (?) on conflict on constraint test do update set "a"=?`,
sql: `INSERT INTO "test" ("a") VALUES (?) on conflict on constraint test do update set "a"=?`,
isPrepared: true,
args: []interface{}{"a1", "b"},
},

insertTestCase{
clause: icDuw,
sql: `insert ignore into "test" ("a") VALUES ('a1') on conflict (test) do update set "a"='b' WHERE ("foo" IS TRUE)`,
sql: `INSERT INTO "test" ("a") VALUES ('a1') on conflict (test) do update set "a"='b' WHERE ("foo" IS TRUE)`,
},
insertTestCase{
clause: icDuw,
sql: `insert ignore into "test" ("a") VALUES (?) on conflict (test) do update set "a"=? WHERE ("foo" IS TRUE)`,
sql: `INSERT INTO "test" ("a") VALUES (?) on conflict (test) do update set "a"=? WHERE ("foo" IS TRUE)`,
isPrepared: true,
args: []interface{}{"a1", "b"},
},
Expand Down