From 4fe0d90eb8ee6239f34773cfae7561cc418ffe78 Mon Sep 17 00:00:00 2001 From: "kirill.lokhmatov" Date: Mon, 19 Dec 2022 15:21:16 +0200 Subject: [PATCH 1/7] do not produce ignore for on conflict updates --- sqlgen/insert_sql_generator.go | 6 +++++- sqlgen/insert_sql_generator_test.go | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/sqlgen/insert_sql_generator.go b/sqlgen/insert_sql_generator.go index 1c6105b9..ba50a69b 100644 --- a/sqlgen/insert_sql_generator.go +++ b/sqlgen/insert_sql_generator.go @@ -72,7 +72,11 @@ 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 + if o != nil { + _, isUpdate = o.(exp.ConflictUpdateExpression) + } + if isg.DialectOptions().SupportsInsertIgnoreSyntax && !isUpdate { b.Write(isg.DialectOptions().InsertIgnoreClause) } else { b.Write(isg.DialectOptions().InsertClause) diff --git a/sqlgen/insert_sql_generator_test.go b/sqlgen/insert_sql_generator_test.go index 95479c56..777d7bae 100644 --- a/sqlgen/insert_sql_generator_test.go +++ b/sqlgen/insert_sql_generator_test.go @@ -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"}, }, From 75cc71b81dd1821e14d79976456289532261fe25 Mon Sep 17 00:00:00 2001 From: "kirill.lokhmatov" Date: Mon, 19 Dec 2022 15:36:36 +0200 Subject: [PATCH 2/7] update fork header --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 95494c18..b2ded596 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/doug-martin/goqu/v9 +module github.com/kirill-lokhmatov/goqu/v9 go 1.12 From 452c6639faff2b96fec469a1891b5defce36f63c Mon Sep 17 00:00:00 2001 From: "kirill.lokhmatov" Date: Tue, 7 Mar 2023 14:54:39 +0200 Subject: [PATCH 3/7] add hex bytes type --- sqlgen/expression_sql_generator.go | 34 ++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/sqlgen/expression_sql_generator.go b/sqlgen/expression_sql_generator.go index 82ce15c5..c2e27df7 100644 --- a/sqlgen/expression_sql_generator.go +++ b/sqlgen/expression_sql_generator.go @@ -2,6 +2,7 @@ package sqlgen import ( "database/sql/driver" + "encoding/hex" "reflect" "strconv" "time" @@ -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) } @@ -135,6 +140,8 @@ func (esg *expressionSQLGenerator) reflectSQL(b sb.SQLBuilder, val interface{}) esg.literalNil(b) case util.IsSlice(valKind): switch t := val.(type) { + case HexBytes: + esg.literalHexBytes(b, t) case []byte: esg.literalBytes(b, t) case []exp.CommonTableExpression: @@ -344,6 +351,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() { @@ -534,8 +557,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() @@ -555,7 +579,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()) @@ -619,7 +644,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()) From 0a31f5f5c3aa31b58a21d0020a05637566ce36d3 Mon Sep 17 00:00:00 2001 From: "kirill.lokhmatov" Date: Tue, 7 Mar 2023 15:14:42 +0200 Subject: [PATCH 4/7] add hex bytes type --- sqlgen/expression_sql_generator.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sqlgen/expression_sql_generator.go b/sqlgen/expression_sql_generator.go index c2e27df7..0ac89ceb 100644 --- a/sqlgen/expression_sql_generator.go +++ b/sqlgen/expression_sql_generator.go @@ -140,8 +140,6 @@ func (esg *expressionSQLGenerator) reflectSQL(b sb.SQLBuilder, val interface{}) esg.literalNil(b) case util.IsSlice(valKind): switch t := val.(type) { - case HexBytes: - esg.literalHexBytes(b, t) case []byte: esg.literalBytes(b, t) case []exp.CommonTableExpression: @@ -160,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)) + } } } From 1154bbe693b3c794914b7683642620e2e9c4c0ee Mon Sep 17 00:00:00 2001 From: "kirill.lokhmatov" Date: Tue, 7 Mar 2023 20:22:06 +0200 Subject: [PATCH 5/7] update gosum --- go.mod | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index b2ded596..bb7bb19e 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,12 @@ -module github.com/kirill-lokhmatov/goqu/v9 +module github.com/doug-martin/goqu/v9 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 -) +) \ No newline at end of file From cc7b7a5e9cf741fe6a2316064442165914bc3c11 Mon Sep 17 00:00:00 2001 From: "kirill.lokhmatov" Date: Mon, 13 Mar 2023 23:13:39 +0200 Subject: [PATCH 6/7] turn on cte support --- dialect/mysql/mysql.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dialect/mysql/mysql.go b/dialect/mysql/mysql.go index d5566e49..ada571e7 100644 --- a/dialect/mysql/mysql.go +++ b/dialect/mysql/mysql.go @@ -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 } From 28eab4164f74ae7b26e9d80a72e4f73a0763f3c6 Mon Sep 17 00:00:00 2001 From: "kirill.lokhmatov" Date: Fri, 10 Nov 2023 12:22:01 +0200 Subject: [PATCH 7/7] add ignore override --- exp/exp.go | 3 +++ sqlgen/insert_sql_generator.go | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/exp/exp.go b/exp/exp.go index 240a96a6..60834d3d 100644 --- a/exp/exp.go +++ b/exp/exp.go @@ -275,6 +275,9 @@ type ( Expression Action() ConflictAction } + ConflictNoIgnore interface { + NoIgnore() + } ConflictUpdateExpression interface { ConflictExpression TargetColumn() string diff --git a/sqlgen/insert_sql_generator.go b/sqlgen/insert_sql_generator.go index ba50a69b..467e0514 100644 --- a/sqlgen/insert_sql_generator.go +++ b/sqlgen/insert_sql_generator.go @@ -73,10 +73,13 @@ func (isg *insertSQLGenerator) Generate( // Adds the correct fragment to being an INSERT statement func (isg *insertSQLGenerator) InsertBeginSQL(b sb.SQLBuilder, o exp.ConflictExpression) { isUpdate := false + forceNoIgnore := false + if o != nil { _, isUpdate = o.(exp.ConflictUpdateExpression) + _, forceNoIgnore = o.(exp.ConflictNoIgnore) } - if isg.DialectOptions().SupportsInsertIgnoreSyntax && !isUpdate { + if isg.DialectOptions().SupportsInsertIgnoreSyntax && !isUpdate && !forceNoIgnore { b.Write(isg.DialectOptions().InsertIgnoreClause) } else { b.Write(isg.DialectOptions().InsertClause)