From 0d250f5b41248cecc5ad32fbfc4a1a34259843c9 Mon Sep 17 00:00:00 2001 From: bill matlock Date: Mon, 18 Mar 2024 16:38:28 -0400 Subject: [PATCH 01/14] ON CONFLICT DO NOTHING without a conflict target is valid SQL as far as Postgres is concerned. --- postgres/clause.go | 2 +- postgres/clause_test.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/postgres/clause.go b/postgres/clause.go index 0953e26..b5b121e 100644 --- a/postgres/clause.go +++ b/postgres/clause.go @@ -44,7 +44,7 @@ func (o *onConflictClause) DO_UPDATE(action conflictAction) InsertStatement { } func (o *onConflictClause) Serialize(statementType jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) { - if len(o.indexExpressions) == 0 && o.constraint == "" { + if len(o.indexExpressions) == 0 && o.constraint == "" && o.do != jet.Keyword("DO NOTHING") { return } diff --git a/postgres/clause_test.go b/postgres/clause_test.go index 28be315..54fe476 100644 --- a/postgres/clause_test.go +++ b/postgres/clause_test.go @@ -8,7 +8,8 @@ func TestOnConflict(t *testing.T) { onConflict := &onConflictClause{} onConflict.DO_NOTHING() - assertClauseSerialize(t, onConflict, "") + assertClauseSerialize(t, onConflict, ` +ON CONFLICT DO NOTHING`) onConflict = &onConflictClause{indexExpressions: ColumnList{table1ColBool}} onConflict.DO_NOTHING() From 39de87671e642a911b95b0da201bccf3f48af864 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 24 Mar 2024 13:02:23 +0100 Subject: [PATCH 02/14] Additional tests for 'ON CONFLICT DO NOTHING without conflict target does not appear in generated SQL' bug. --- cmd/jet/version.go | 2 +- postgres/clause.go | 3 ++- sqlite/on_conflict_clause.go | 3 ++- tests/postgres/insert_test.go | 33 +++++++++++++++++++++++++++++++++ tests/sqlite/insert_test.go | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/cmd/jet/version.go b/cmd/jet/version.go index 051263b..3300008 100644 --- a/cmd/jet/version.go +++ b/cmd/jet/version.go @@ -1,3 +1,3 @@ package main -const version = "v2.10.1" +const version = "v2.11.1" diff --git a/postgres/clause.go b/postgres/clause.go index b5b121e..3025c5b 100644 --- a/postgres/clause.go +++ b/postgres/clause.go @@ -2,6 +2,7 @@ package postgres import ( "github.com/go-jet/jet/v2/internal/jet" + "github.com/go-jet/jet/v2/internal/utils/is" ) type onConflict interface { @@ -44,7 +45,7 @@ func (o *onConflictClause) DO_UPDATE(action conflictAction) InsertStatement { } func (o *onConflictClause) Serialize(statementType jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) { - if len(o.indexExpressions) == 0 && o.constraint == "" && o.do != jet.Keyword("DO NOTHING") { + if is.Nil(o.do) { return } diff --git a/sqlite/on_conflict_clause.go b/sqlite/on_conflict_clause.go index 1e2ec8f..e867a6f 100644 --- a/sqlite/on_conflict_clause.go +++ b/sqlite/on_conflict_clause.go @@ -2,6 +2,7 @@ package sqlite import ( "github.com/go-jet/jet/v2/internal/jet" + "github.com/go-jet/jet/v2/internal/utils/is" ) type onConflict interface { @@ -37,7 +38,7 @@ func (o *onConflictClause) DO_UPDATE(action conflictAction) InsertStatement { } func (o *onConflictClause) Serialize(statementType jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) { - if len(o.indexExpressions) == 0 && o.do == nil { + if is.Nil(o.do) { return } diff --git a/tests/postgres/insert_test.go b/tests/postgres/insert_test.go index e34405e..a079091 100644 --- a/tests/postgres/insert_test.go +++ b/tests/postgres/insert_test.go @@ -87,6 +87,24 @@ func TestInsertOnConflict(t *testing.T) { t.Run("do nothing", func(t *testing.T) { employee := model.Employee{EmployeeID: rand.Int31()} + stmt := Employee.INSERT(Employee.AllColumns). + MODEL(employee). + MODEL(employee). + ON_CONFLICT().DO_NOTHING() + + testutils.AssertStatementSql(t, stmt, ` +INSERT INTO test_sample.employee (employee_id, first_name, last_name, employment_date, manager_id) +VALUES ($1, $2, $3, $4, $5), + ($6, $7, $8, $9, $10) +ON CONFLICT DO NOTHING; +`) + testutils.AssertExecAndRollback(t, stmt, db, 1) + requireLogged(t, stmt) + }) + + t.Run("do nothing with index", func(t *testing.T) { + employee := model.Employee{EmployeeID: rand.Int31()} + stmt := Employee.INSERT(Employee.AllColumns). MODEL(employee). MODEL(employee). @@ -207,6 +225,21 @@ ON CONFLICT (id) WHERE (id * 2) > 10 DO UPDATE testutils.AssertExecAndRollback(t, stmt, db, 1) }) + + t.Run("nil action removes ON CONFLICT clause", func(t *testing.T) { + employee := model.Employee{EmployeeID: rand.Int31()} + + stmt := Employee.INSERT(Employee.AllColumns). + MODEL(employee). + ON_CONFLICT().DO_UPDATE(nil) + + testutils.AssertStatementSql(t, stmt, ` +INSERT INTO test_sample.employee (employee_id, first_name, last_name, employment_date, manager_id) +VALUES ($1, $2, $3, $4, $5); +`) + testutils.AssertExecAndRollback(t, stmt, db, 1) + requireLogged(t, stmt) + }) } func TestInsertModelObject(t *testing.T) { diff --git a/tests/sqlite/insert_test.go b/tests/sqlite/insert_test.go index e1b3a54..776d546 100644 --- a/tests/sqlite/insert_test.go +++ b/tests/sqlite/insert_test.go @@ -267,6 +267,24 @@ func TestInsertOnConflict(t *testing.T) { t.Run("do nothing", func(t *testing.T) { link := model.Link{ID: rand.Int31()} + stmt := Link.INSERT(Link.AllColumns). + MODEL(link). + MODEL(link). + ON_CONFLICT().DO_NOTHING() + + testutils.AssertStatementSql(t, stmt, ` +INSERT INTO link (id, url, name, description) +VALUES (?, ?, ?, ?), + (?, ?, ?, ?) +ON CONFLICT DO NOTHING; +`) + testutils.AssertExecAndRollback(t, stmt, sampleDB, 1) + requireLogged(t, stmt) + }) + + t.Run("do nothing with index", func(t *testing.T) { + link := model.Link{ID: rand.Int31()} + stmt := Link.INSERT(Link.AllColumns). MODEL(link). MODEL(link). @@ -341,6 +359,21 @@ ON CONFLICT (id) WHERE (id * 2) > 10 DO UPDATE testutils.AssertExecAndRollback(t, stmt, sampleDB) requireLogged(t, stmt) }) + + t.Run("nil action removes ON CONFLICT clause", func(t *testing.T) { + link := model.Link{ID: rand.Int31()} + + stmt := Link.INSERT(Link.AllColumns). + MODEL(link). + ON_CONFLICT().DO_UPDATE(nil) + + testutils.AssertStatementSql(t, stmt, ` +INSERT INTO link (id, url, name, description) +VALUES (?, ?, ?, ?); +`) + testutils.AssertExecAndRollback(t, stmt, sampleDB, 1) + requireLogged(t, stmt) + }) } func TestInsertContextDeadlineExceeded(t *testing.T) { From c6fccc33481294988e402a40387bb965ee94b78e Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 24 Mar 2024 13:12:07 +0100 Subject: [PATCH 03/14] Update dependencies. --- go.mod | 12 ++++++------ go.sum | 23 ++++++++++++----------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 0ed2466..093ac08 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,18 @@ go 1.18 require ( github.com/go-sql-driver/mysql v1.7.1 github.com/google/uuid v1.6.0 - github.com/jackc/pgconn v1.14.1 - github.com/lib/pq v1.10.8 + github.com/jackc/pgconn v1.14.3 + github.com/lib/pq v1.10.9 github.com/mattn/go-sqlite3 v1.14.17 ) require ( - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/jackc/pgtype v1.14.0 github.com/jackc/pgx/v4 v4.18.1 github.com/pkg/profile v1.7.0 github.com/shopspring/decimal v1.3.1 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.9.0 github.com/volatiletech/null/v8 v8.1.2 gopkg.in/guregu/null.v4 v4.0.0 ) @@ -30,13 +30,13 @@ require ( github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.2 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/randomize v0.0.1 // indirect github.com/volatiletech/strmangle v0.0.1 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 18fce9c..7535da7 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -43,8 +43,8 @@ github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfG github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= -github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= -github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -60,8 +60,9 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= @@ -94,8 +95,8 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.8 h1:3fdt97i/cwSU83+E0hZTC/Xpc9mTZxc6UWSCRcSbxiE= -github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -133,8 +134,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/volatiletech/inflect v0.0.1 h1:2a6FcMQyhmPZcLa+uet3VJ8gLn/9svWhJxJYwvE8KsU= github.com/volatiletech/inflect v0.0.1/go.mod h1:IBti31tG6phkHitLlr5j7shC5SOo//x0AjDzaJU1PLA= github.com/volatiletech/null/v8 v8.1.2 h1:kiTiX1PpwvuugKwfvUNX/SU/5A2KGZMXfGD0DUHdKEI= @@ -167,8 +168,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= From 1fd423bf8bf66beac916775ac03192e669f078d1 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 24 Mar 2024 13:25:08 +0100 Subject: [PATCH 04/14] Update dependencies. --- go.mod | 7 ++++--- go.sum | 28 ++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 093ac08..f416202 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/go-jet/jet/v2 go 1.18 require ( - github.com/go-sql-driver/mysql v1.7.1 + github.com/go-sql-driver/mysql v1.8.0 github.com/google/uuid v1.6.0 github.com/jackc/pgconn v1.14.3 github.com/lib/pq v1.10.9 @@ -12,8 +12,8 @@ require ( require ( github.com/google/go-cmp v0.6.0 - github.com/jackc/pgtype v1.14.0 - github.com/jackc/pgx/v4 v4.18.1 + github.com/jackc/pgtype v1.14.3 + github.com/jackc/pgx/v4 v4.18.3 github.com/pkg/profile v1.7.0 github.com/shopspring/decimal v1.3.1 github.com/stretchr/testify v1.9.0 @@ -22,6 +22,7 @@ require ( ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/friendsofgo/errors v0.9.2 // indirect diff --git a/go.sum b/go.sum index 7535da7..5d26683 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= @@ -18,8 +20,8 @@ github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzj github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= +github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= @@ -42,7 +44,6 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= @@ -60,7 +61,6 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= @@ -70,14 +70,16 @@ github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01C github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus= +github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= -github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= +github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -167,13 +169,14 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -181,8 +184,11 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -199,10 +205,14 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -210,6 +220,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -222,6 +233,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 1d310624d8bdeb06381d6d9ca508f15d6c5364c1 Mon Sep 17 00:00:00 2001 From: Karl Blomster Date: Tue, 26 Mar 2024 15:49:17 +0100 Subject: [PATCH 05/14] Export BinaryOperator function --- internal/jet/operators.go | 4 ++++ mysql/expressions.go | 3 +++ postgres/expressions.go | 3 +++ sqlite/expressions.go | 3 +++ 4 files changed, 13 insertions(+) diff --git a/internal/jet/operators.go b/internal/jet/operators.go index bf1dedf..c453c3e 100644 --- a/internal/jet/operators.go +++ b/internal/jet/operators.go @@ -188,3 +188,7 @@ func (c *caseOperatorImpl) serialize(statement StatementType, out *SQLBuilder, o func DISTINCT(expr Expression) Expression { return newPrefixOperatorExpression(expr, "DISTINCT") } + +func BinaryOperator(lhs Expression, rhs Expression, operator string) Expression { + return NewBinaryOperatorExpression(lhs, rhs, operator) +} diff --git a/mysql/expressions.go b/mysql/expressions.go index 9fa95e6..b3f4ce5 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -93,3 +93,6 @@ var Func = jet.Func // NewEnumValue creates new named enum value var NewEnumValue = jet.NewEnumValue + +// BinaryOperator can be used to use custom or unsupported operators that take two operands. +var BinaryOperator = jet.BinaryOperator diff --git a/postgres/expressions.go b/postgres/expressions.go index b964534..759f055 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -142,3 +142,6 @@ var Func = jet.Func // NewEnumValue creates new named enum value var NewEnumValue = jet.NewEnumValue + +// BinaryOperator can be used to use custom or unsupported operators that take two operands. +var BinaryOperator = jet.BinaryOperator diff --git a/sqlite/expressions.go b/sqlite/expressions.go index f1b0a0f..93bb7b7 100644 --- a/sqlite/expressions.go +++ b/sqlite/expressions.go @@ -96,3 +96,6 @@ var Func = jet.Func // NewEnumValue creates new named enum value var NewEnumValue = jet.NewEnumValue + +// BinaryOperator can be used to use custom or unsupported operators that take two operands. +var BinaryOperator = jet.BinaryOperator From 523f780b02a0a9e026848af2515bc91be2037889 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 21:49:42 +0000 Subject: [PATCH 06/14] Bump github.com/go-sql-driver/mysql from 1.8.0 to 1.8.1 Bumps [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) from 1.8.0 to 1.8.1. - [Release notes](https://github.com/go-sql-driver/mysql/releases) - [Changelog](https://github.com/go-sql-driver/mysql/blob/v1.8.1/CHANGELOG.md) - [Commits](https://github.com/go-sql-driver/mysql/compare/v1.8.0...v1.8.1) --- updated-dependencies: - dependency-name: github.com/go-sql-driver/mysql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f416202..09d58fa 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/go-jet/jet/v2 go 1.18 require ( - github.com/go-sql-driver/mysql v1.8.0 + github.com/go-sql-driver/mysql v1.8.1 github.com/google/uuid v1.6.0 github.com/jackc/pgconn v1.14.3 github.com/lib/pq v1.10.9 diff --git a/go.sum b/go.sum index 5d26683..0169b72 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzj github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= -github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= From 172a822d9614076c039e2d62dba0972c1e3950b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 21:16:21 +0000 Subject: [PATCH 07/14] Bump github.com/shopspring/decimal from 1.3.1 to 1.4.0 Bumps [github.com/shopspring/decimal](https://github.com/shopspring/decimal) from 1.3.1 to 1.4.0. - [Release notes](https://github.com/shopspring/decimal/releases) - [Changelog](https://github.com/shopspring/decimal/blob/master/CHANGELOG.md) - [Commits](https://github.com/shopspring/decimal/compare/v1.3.1...v1.4.0) --- updated-dependencies: - dependency-name: github.com/shopspring/decimal dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f416202..4a49f73 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/jackc/pgtype v1.14.3 github.com/jackc/pgx/v4 v4.18.3 github.com/pkg/profile v1.7.0 - github.com/shopspring/decimal v1.3.1 + github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.9.0 github.com/volatiletech/null/v8 v8.1.2 gopkg.in/guregu/null.v4 v4.0.0 diff --git a/go.sum b/go.sum index 5d26683..f48ca66 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 093f895db2e8bb220305f8c09dab1952134579e7 Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 3 Jul 2024 11:56:59 +0200 Subject: [PATCH 08/14] Fix failing test. --- cmd/jet/version.go | 2 +- tests/postgres/range_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/jet/version.go b/cmd/jet/version.go index 3300008..4241ec7 100644 --- a/cmd/jet/version.go +++ b/cmd/jet/version.go @@ -1,3 +1,3 @@ package main -const version = "v2.11.1" +const version = "v2.11.0" diff --git a/tests/postgres/range_test.go b/tests/postgres/range_test.go index b4bb712..3d10bb8 100644 --- a/tests/postgres/range_test.go +++ b/tests/postgres/range_test.go @@ -341,14 +341,14 @@ RETURNING sample_ranges.date_range AS "sample_ranges.date_range", SampleRanges.Int8Range.SET(INT8_RANGE(Int64(-1200), Int64(7800))), ). WHERE( - SampleRanges.TimestampzRange.LOWER_BOUND().GT(NOW()), + SampleRanges.TimestampzRange.LOWER_BOUND().GT(Timestampz(2024, 2, 27, 0, 0, 0, 0, "UTC")), ) testutils.AssertDebugStatementSql(t, stmt, ` UPDATE test_sample.sample_ranges SET int4_range = int4range(-12::integer, 78::integer), int8_range = int8range(-1200::bigint, 7800::bigint) -WHERE LOWER(sample_ranges.timestampz_range) > NOW(); +WHERE LOWER(sample_ranges.timestampz_range) > '2024-02-27 00:00:00 UTC'::timestamp with time zone; `) testutils.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) { From f48970d124136456f346900c57ad708f710c7cc8 Mon Sep 17 00:00:00 2001 From: Mathieu Kooiman Date: Fri, 12 Jul 2024 14:09:17 +0200 Subject: [PATCH 09/14] refactor: expose NewCustomExpression for dialects to use --- internal/jet/expression.go | 2 +- internal/jet/func_expression.go | 2 +- internal/jet/group_by_clause.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/jet/expression.go b/internal/jet/expression.go index d62920c..aedb555 100644 --- a/internal/jet/expression.go +++ b/internal/jet/expression.go @@ -278,7 +278,7 @@ type customExpression struct { parts []Serializer } -func newCustomExpression(parts ...Serializer) Expression { +func NewCustomExpression(parts ...Serializer) Expression { ret := customExpression{ parts: parts, } diff --git a/internal/jet/func_expression.go b/internal/jet/func_expression.go index 6036b8d..43c7ec3 100644 --- a/internal/jet/func_expression.go +++ b/internal/jet/func_expression.go @@ -548,7 +548,7 @@ func TO_TIMESTAMP(timestampzStr, format StringExpression) TimestampzExpression { // EXTRACT extracts time component from time expression func EXTRACT(field string, from Expression) Expression { - return newCustomExpression(Token("EXTRACT("), Token(field), Token("FROM"), from, Token(")")) + return NewCustomExpression(Token("EXTRACT("), Token(field), Token("FROM"), from, Token(")")) } // CURRENT_DATE returns current date diff --git a/internal/jet/group_by_clause.go b/internal/jet/group_by_clause.go index 5f0c4b1..59e244b 100644 --- a/internal/jet/group_by_clause.go +++ b/internal/jet/group_by_clause.go @@ -35,7 +35,7 @@ func GROUPING(expressions ...Expression) IntegerExpression { // WITH_ROLLUP operator is used with the GROUP BY clause to generate all prefixes of a group of columns including the empty list. // It creates extra rows in the result set that represent the subtotal values for each combination of columns. func WITH_ROLLUP(expressions ...Expression) GroupByClause { - return newCustomExpression( + return NewCustomExpression( parametersSerializer(expressions), Token("WITH ROLLUP"), ) } From 3ec0e2cabdc27fb68132508b5c369e01deea236e Mon Sep 17 00:00:00 2001 From: Mathieu Kooiman Date: Sat, 13 Jul 2024 17:04:33 +0200 Subject: [PATCH 10/14] refactor: NewCustomExpression -> CustomExpression --- internal/jet/expression.go | 2 +- internal/jet/func_expression.go | 2 +- internal/jet/group_by_clause.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/jet/expression.go b/internal/jet/expression.go index aedb555..05b1797 100644 --- a/internal/jet/expression.go +++ b/internal/jet/expression.go @@ -278,7 +278,7 @@ type customExpression struct { parts []Serializer } -func NewCustomExpression(parts ...Serializer) Expression { +func CustomExpression(parts ...Serializer) Expression { ret := customExpression{ parts: parts, } diff --git a/internal/jet/func_expression.go b/internal/jet/func_expression.go index 43c7ec3..7e49880 100644 --- a/internal/jet/func_expression.go +++ b/internal/jet/func_expression.go @@ -548,7 +548,7 @@ func TO_TIMESTAMP(timestampzStr, format StringExpression) TimestampzExpression { // EXTRACT extracts time component from time expression func EXTRACT(field string, from Expression) Expression { - return NewCustomExpression(Token("EXTRACT("), Token(field), Token("FROM"), from, Token(")")) + return CustomExpression(Token("EXTRACT("), Token(field), Token("FROM"), from, Token(")")) } // CURRENT_DATE returns current date diff --git a/internal/jet/group_by_clause.go b/internal/jet/group_by_clause.go index 59e244b..017bb4e 100644 --- a/internal/jet/group_by_clause.go +++ b/internal/jet/group_by_clause.go @@ -35,7 +35,7 @@ func GROUPING(expressions ...Expression) IntegerExpression { // WITH_ROLLUP operator is used with the GROUP BY clause to generate all prefixes of a group of columns including the empty list. // It creates extra rows in the result set that represent the subtotal values for each combination of columns. func WITH_ROLLUP(expressions ...Expression) GroupByClause { - return NewCustomExpression( + return CustomExpression( parametersSerializer(expressions), Token("WITH ROLLUP"), ) } From 6cabfcdc1a4b00d51b26cf3a31bc8c91d8f18441 Mon Sep 17 00:00:00 2001 From: Mathieu Kooiman Date: Sat, 13 Jul 2024 17:10:26 +0200 Subject: [PATCH 11/14] feat: expose CustomExpression+Token in mysql/postgres/sqlite --- mysql/expressions.go | 6 ++++++ postgres/expressions.go | 6 ++++++ sqlite/expressions.go | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/mysql/expressions.go b/mysql/expressions.go index b3f4ce5..53b1fa7 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -70,6 +70,12 @@ var DateTimeExp = jet.TimestampExp // Does not add sql cast to generated sql builder output. var TimestampExp = jet.TimestampExp +// CustomExpression is used to define custom expressions. +var CustomExpression = jet.CustomExpression + +// Token is used to define custom token in a custom expression. +type Token = jet.Token + // RawArgs is type used to pass optional arguments to Raw method type RawArgs = map[string]interface{} diff --git a/postgres/expressions.go b/postgres/expressions.go index 759f055..9872910 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -111,6 +111,12 @@ var ( TstzRangeExp = jet.TstzRangeExp ) +// CustomExpression is used to define custom expressions. +var CustomExpression = jet.CustomExpression + +// Token is used to define custom token in a custom expression. +type Token = jet.Token + // RawArgs is type used to pass optional arguments to Raw method type RawArgs = map[string]interface{} diff --git a/sqlite/expressions.go b/sqlite/expressions.go index 93bb7b7..42ccc96 100644 --- a/sqlite/expressions.go +++ b/sqlite/expressions.go @@ -73,6 +73,12 @@ var DateTimeExp = jet.TimestampExp // Does not add sql cast to generated sql builder output. var TimestampExp = jet.TimestampExp +// CustomExpression is used to define custom expressions. +var CustomExpression = jet.CustomExpression + +// Token is used to define custom token in a custom expression. +type Token = jet.Token + // RawArgs is type used to pass optional arguments to Raw method type RawArgs = map[string]interface{} From fb66fbd31cef88aa4eb0874f23012283819410bd Mon Sep 17 00:00:00 2001 From: Yosyp Buchma Date: Sat, 20 Jul 2024 16:49:49 +0200 Subject: [PATCH 12/14] fix ProjectionList{}.As method (issue #364) --- internal/jet/projection.go | 4 ++++ internal/jet/projection_test.go | 13 ++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/jet/projection.go b/internal/jet/projection.go index 1b1c625..3139d3f 100644 --- a/internal/jet/projection.go +++ b/internal/jet/projection.go @@ -45,6 +45,10 @@ func (pl ProjectionList) As(tableAlias string) ProjectionList { newProjectionList = append(newProjectionList, p.As(tableAlias)) case ColumnExpression: newProjectionList = append(newProjectionList, newAlias(p, tableAlias+"."+p.Name())) + case ColumnList: + for _, c := range p { + newProjectionList = append(newProjectionList, newAlias(c, tableAlias+"."+c.Name())) + } case *alias: newAlias := *p _, columnName := extractTableAndColumnName(newAlias.alias) diff --git a/internal/jet/projection_test.go b/internal/jet/projection_test.go index 7728e15..96727d5 100644 --- a/internal/jet/projection_test.go +++ b/internal/jet/projection_test.go @@ -12,6 +12,7 @@ func TestProjectionAs(t *testing.T) { AVG(table1ColInt).AS("avg"), AVG(table1ColInt).AS("t.avg"), }, + ColumnList{table2Col3, table2Col4}, } aliasedProjectionList := projectionList.As("new_alias.*") @@ -22,7 +23,9 @@ SUM(table1.col_int) AS "new_alias.sum", SUM(table1.col_int) AS "new_alias.sum", table1.col_bool AS "new_alias.col_bool", AVG(table1.col_int) AS "new_alias.avg", -AVG(table1.col_int) AS "new_alias.avg"`) +AVG(table1.col_int) AS "new_alias.avg", +table2.col3 AS "new_alias.col3", +table2.col4 AS "new_alias.col4"`) subQueryProjections := projectionList.fromImpl(NewSelectTable(nil, "subQuery")) @@ -32,7 +35,9 @@ AVG(table1.col_int) AS "new_alias.avg"`) "subQuery"."table.sum" AS "table.sum", "subQuery"."table1.col_bool" AS "table1.col_bool", "subQuery".avg AS "avg", -"subQuery"."t.avg" AS "t.avg"`) +"subQuery"."t.avg" AS "t.avg", +"subQuery"."table2.col3" AS "table2.col3", +"subQuery"."table2.col4" AS "table2.col4"`) aliasedSubQueryProjectionList := subQueryProjections.(ProjectionList).As("subAlias") @@ -42,5 +47,7 @@ AVG(table1.col_int) AS "new_alias.avg"`) "subQuery"."table.sum" AS "subAlias.sum", "subQuery"."table1.col_bool" AS "subAlias.col_bool", "subQuery".avg AS "subAlias.avg", -"subQuery"."t.avg" AS "subAlias.avg"`) +"subQuery"."t.avg" AS "subAlias.avg", +"subQuery"."table2.col3" AS "subAlias.col3", +"subQuery"."table2.col4" AS "subAlias.col4"`) } From c599fca5ead3869c1edbde799958e5b724af35d9 Mon Sep 17 00:00:00 2001 From: Yosyp Buchma Date: Thu, 25 Jul 2024 17:09:52 +0200 Subject: [PATCH 13/14] New method: func (ColumnList) As(tableAlias string) ProjectionList --- internal/jet/column_list.go | 17 +++++++++++++++++ internal/jet/projection.go | 20 +++++++++++--------- internal/jet/projection_test.go | 12 ++++++++++++ 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/internal/jet/column_list.go b/internal/jet/column_list.go index a4a0b66..4834871 100644 --- a/internal/jet/column_list.go +++ b/internal/jet/column_list.go @@ -1,5 +1,7 @@ package jet +import "strings" + // ColumnList is a helper type to support list of columns as single projection type ColumnList []ColumnExpression @@ -39,6 +41,21 @@ func (cl ColumnList) Except(excludedColumns ...Column) ColumnList { return ret } +// As will create new projection list where each column is wrapped with a new table alias. +// tableAlias should be in the form 'name' or 'name.*', or it can also be an empty string. +// For instance: If projection list has a column 'Artist.Name', and tableAlias is 'Musician.*', returned projection list will +// have a column wrapped in alias 'Musician.Name'. If tableAlias is empty string, it removes existing table alias ('Artist.Name' becomes 'Name'). +func (cl ColumnList) As(tableAlias string) ProjectionList { + if tableAlias > "" { + tableAlias = strings.TrimRight(tableAlias, ".*") + "." + } + ret := make(ProjectionList, 0, len(cl)) + for _, c := range cl { + ret = append(ret, c.AS(tableAlias+c.Name())) + } + return ret +} + func (cl ColumnList) fromImpl(subQuery SelectTable) Projection { newProjectionList := ProjectionList{} diff --git a/internal/jet/projection.go b/internal/jet/projection.go index 3139d3f..09f1c2b 100644 --- a/internal/jet/projection.go +++ b/internal/jet/projection.go @@ -31,11 +31,15 @@ func (pl ProjectionList) serializeForProjection(statement StatementType, out *SQ } // As will create new projection list where each column is wrapped with a new table alias. -// tableAlias should be in the form 'name' or 'name.*'. +// tableAlias should be in the form 'name' or 'name.*', or it can be an empty string, which will remove existing table alias. // For instance: If projection list has a column 'Artist.Name', and tableAlias is 'Musician.*', returned projection list will -// have a column wrapped in alias 'Musician.Name'. +// have a column wrapped in alias 'Musician.Name'. If tableAlias is empty string, it removes existing table alias ('Artist.Name' becomes 'Name'). func (pl ProjectionList) As(tableAlias string) ProjectionList { - tableAlias = strings.TrimRight(tableAlias, ".*") + tableAliasWithDot := "" + if tableAlias != "" { + tableAlias = strings.TrimRight(tableAlias, ".*") + tableAliasWithDot = tableAlias + "." + } newProjectionList := ProjectionList{} @@ -43,16 +47,14 @@ func (pl ProjectionList) As(tableAlias string) ProjectionList { switch p := projection.(type) { case ProjectionList: newProjectionList = append(newProjectionList, p.As(tableAlias)) - case ColumnExpression: - newProjectionList = append(newProjectionList, newAlias(p, tableAlias+"."+p.Name())) case ColumnList: - for _, c := range p { - newProjectionList = append(newProjectionList, newAlias(c, tableAlias+"."+c.Name())) - } + newProjectionList = append(newProjectionList, p.As(tableAlias)) + case ColumnExpression: + newProjectionList = append(newProjectionList, newAlias(p, tableAliasWithDot+p.Name())) case *alias: newAlias := *p _, columnName := extractTableAndColumnName(newAlias.alias) - newAlias.alias = tableAlias + "." + columnName + newAlias.alias = tableAliasWithDot + columnName newProjectionList = append(newProjectionList, &newAlias) } } diff --git a/internal/jet/projection_test.go b/internal/jet/projection_test.go index 96727d5..0370b43 100644 --- a/internal/jet/projection_test.go +++ b/internal/jet/projection_test.go @@ -27,6 +27,18 @@ AVG(table1.col_int) AS "new_alias.avg", table2.col3 AS "new_alias.col3", table2.col4 AS "new_alias.col4"`) + aliasedProjectionList = projectionList.As("") + + assertProjectionSerialize(t, aliasedProjectionList, + `table1.col3 AS "col3", +SUM(table1.col_int) AS "sum", +SUM(table1.col_int) AS "sum", +table1.col_bool AS "col_bool", +AVG(table1.col_int) AS "avg", +AVG(table1.col_int) AS "avg", +table2.col3 AS "col3", +table2.col4 AS "col4"`) + subQueryProjections := projectionList.fromImpl(NewSelectTable(nil, "subQuery")) assertProjectionSerialize(t, subQueryProjections, From 583ecba4a1bbd09366647fa47bb44adb0c130717 Mon Sep 17 00:00:00 2001 From: Yosyp Buchma Date: Fri, 26 Jul 2024 14:51:45 +0200 Subject: [PATCH 14/14] refactor: extracted common logic for column aliasing --- internal/jet/column_list.go | 7 +------ internal/jet/projection.go | 12 ++---------- internal/jet/utils.go | 17 +++++++++++++++-- internal/jet/utils_test.go | 10 +++++++++- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/internal/jet/column_list.go b/internal/jet/column_list.go index 4834871..a07b9ba 100644 --- a/internal/jet/column_list.go +++ b/internal/jet/column_list.go @@ -1,7 +1,5 @@ package jet -import "strings" - // ColumnList is a helper type to support list of columns as single projection type ColumnList []ColumnExpression @@ -46,12 +44,9 @@ func (cl ColumnList) Except(excludedColumns ...Column) ColumnList { // For instance: If projection list has a column 'Artist.Name', and tableAlias is 'Musician.*', returned projection list will // have a column wrapped in alias 'Musician.Name'. If tableAlias is empty string, it removes existing table alias ('Artist.Name' becomes 'Name'). func (cl ColumnList) As(tableAlias string) ProjectionList { - if tableAlias > "" { - tableAlias = strings.TrimRight(tableAlias, ".*") + "." - } ret := make(ProjectionList, 0, len(cl)) for _, c := range cl { - ret = append(ret, c.AS(tableAlias+c.Name())) + ret = append(ret, c.AS(joinAlias(tableAlias, c.Name()))) } return ret } diff --git a/internal/jet/projection.go b/internal/jet/projection.go index 09f1c2b..3b2ccd8 100644 --- a/internal/jet/projection.go +++ b/internal/jet/projection.go @@ -1,7 +1,5 @@ package jet -import "strings" - // Projection is interface for all projection types. Types that can be part of, for instance SELECT clause. type Projection interface { serializeForProjection(statement StatementType, out *SQLBuilder) @@ -35,12 +33,6 @@ func (pl ProjectionList) serializeForProjection(statement StatementType, out *SQ // For instance: If projection list has a column 'Artist.Name', and tableAlias is 'Musician.*', returned projection list will // have a column wrapped in alias 'Musician.Name'. If tableAlias is empty string, it removes existing table alias ('Artist.Name' becomes 'Name'). func (pl ProjectionList) As(tableAlias string) ProjectionList { - tableAliasWithDot := "" - if tableAlias != "" { - tableAlias = strings.TrimRight(tableAlias, ".*") - tableAliasWithDot = tableAlias + "." - } - newProjectionList := ProjectionList{} for _, projection := range pl { @@ -50,11 +42,11 @@ func (pl ProjectionList) As(tableAlias string) ProjectionList { case ColumnList: newProjectionList = append(newProjectionList, p.As(tableAlias)) case ColumnExpression: - newProjectionList = append(newProjectionList, newAlias(p, tableAliasWithDot+p.Name())) + newProjectionList = append(newProjectionList, newAlias(p, joinAlias(tableAlias, p.Name()))) case *alias: newAlias := *p _, columnName := extractTableAndColumnName(newAlias.alias) - newAlias.alias = tableAliasWithDot + columnName + newAlias.alias = joinAlias(tableAlias, columnName) newProjectionList = append(newProjectionList, &newAlias) } } diff --git a/internal/jet/utils.go b/internal/jet/utils.go index fe29a09..466f2a5 100644 --- a/internal/jet/utils.go +++ b/internal/jet/utils.go @@ -1,10 +1,11 @@ package jet import ( - "github.com/go-jet/jet/v2/internal/utils/dbidentifier" - "github.com/go-jet/jet/v2/internal/utils/must" "reflect" "strings" + + "github.com/go-jet/jet/v2/internal/utils/dbidentifier" + "github.com/go-jet/jet/v2/internal/utils/must" ) // SerializeClauseList func @@ -278,3 +279,15 @@ func serializeToDefaultDebugString(expr Serializer) string { expr.serialize(SelectStatementType, &out) return out.Buff.String() } + +// joinAlias examples: +// +// joinAlias("foo", "bar") // "foo.bar" +// joinAlias("foo.*", "bar") // "foo.bar" +// joinAlias("", "bar") // "bar" +func joinAlias(tableAlias, columnAlias string) string { + if tableAlias == "" { + return columnAlias + } + return strings.TrimRight(tableAlias, ".*") + "." + columnAlias +} diff --git a/internal/jet/utils_test.go b/internal/jet/utils_test.go index b907760..86feff1 100644 --- a/internal/jet/utils_test.go +++ b/internal/jet/utils_test.go @@ -1,8 +1,9 @@ package jet import ( - "github.com/stretchr/testify/require" "testing" + + "github.com/stretchr/testify/require" ) func TestOptionalOrDefaultString(t *testing.T) { @@ -17,3 +18,10 @@ func TestOptionalOrDefaultExpression(t *testing.T) { require.Equal(t, OptionalOrDefaultExpression(defaultExpression), defaultExpression) require.Equal(t, OptionalOrDefaultExpression(defaultExpression, optionalExpression), optionalExpression) } + +func TestJoinAlias(t *testing.T) { + require.Equal(t, joinAlias("", ""), "") + require.Equal(t, joinAlias("foo", "bar"), "foo.bar") + require.Equal(t, joinAlias("foo.*", "bar"), "foo.bar") + require.Equal(t, joinAlias("", "bar"), "bar") +}