From 7660bdd8b55458d29905f2401dfc7b5657f25ac6 Mon Sep 17 00:00:00 2001 From: go-jet Date: Fri, 2 Aug 2019 11:08:24 +0200 Subject: [PATCH] MySQL update statement tests. --- clause.go | 2 +- dialects.go | 60 +++++++ internal/testutils/test_utils.go | 23 ++- tests/init/data/mysql/test_sample.sql | 14 ++ tests/mysql/cast_test.go | 8 +- tests/mysql/select_test.go | 126 +++++++-------- tests/mysql/update_test.go | 224 ++++++++++++++++++++++++++ tests/postgres/delete_test.go | 4 +- tests/postgres/insert_test.go | 10 +- tests/postgres/update_test.go | 10 +- tests/postgres/util_test.go | 2 +- update_statement.go | 38 +---- 12 files changed, 406 insertions(+), 115 deletions(-) create mode 100644 tests/mysql/update_test.go diff --git a/clause.go b/clause.go index afdf343..dfd2474 100644 --- a/clause.go +++ b/clause.go @@ -131,7 +131,7 @@ func (q *sqlBuilder) writeReturning(statement statementType, returning []project } if !q.dialect.SupportsReturning { - panic(q.dialect.Name + " dialect does not support RETURNING.") + panic("jet: " + q.dialect.Name + " dialect does not support RETURNING.") } q.newLine() diff --git a/dialects.go b/dialects.go index 9e60cf9..ac3c996 100644 --- a/dialects.go +++ b/dialects.go @@ -21,6 +21,7 @@ func newPostgresDialect() Dialect { return "$" + strconv.Itoa(ord) } postgresDialect.SupportsReturning = true + postgresDialect.UpdateAssigment = postgresUpdateAssigment return postgresDialect } @@ -40,6 +41,7 @@ func newMySQLDialect() Dialect { } mySQLDialect.SupportsReturning = false + mySQLDialect.UpdateAssigment = mysqlUpdateAssigment return mySQLDialect } @@ -52,6 +54,7 @@ type Dialect struct { AliasQuoteChar byte IdentifierQuoteChar byte ArgumentPlaceholder queryPlaceholderFunc + UpdateAssigment func(columns []column, values []clause, out *sqlBuilder) (err error) SupportsReturning bool } @@ -60,6 +63,63 @@ func (d *Dialect) serializeOverride(operator string) serializeOverride { return d.SerializeOverrides[operator] } +func mysqlUpdateAssigment(columns []column, values []clause, out *sqlBuilder) (err error) { + + if len(columns) != len(values) { + return errors.New("jet: mismatch in numers of columns and values") + } + + for i, column := range columns { + if i > 0 { + out.writeString(", ") + } + + out.writeString(column.Name()) + + out.writeString(" = ") + + if err = values[i].serialize(updateStatement, out); err != nil { + return err + } + } + + return nil +} + +func postgresUpdateAssigment(columns []column, values []clause, out *sqlBuilder) (err error) { + if len(columns) > 1 { + out.writeString("(") + } + + err = serializeColumnNames(columns, out) + + if err != nil { + return + } + + if len(columns) > 1 { + out.writeString(")") + } + + out.writeString("=") + + if len(values) > 1 { + out.writeString("(") + } + + err = serializeClauseList(updateStatement, values, out) + + if err != nil { + return + } + + if len(values) > 1 { + out.writeString(")") + } + + return +} + type queryPlaceholderFunc func(ord int) string func newDialect(name, packageName string) Dialect { diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index c75bf8a..3032070 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "github.com/go-jet/jet" + "github.com/go-jet/jet/execution" "gotest.tools/assert" "io/ioutil" "runtime" @@ -28,6 +29,24 @@ func JsonSave(v interface{}, path string) { } } +func AssertExec(t *testing.T, stmt jet.Statement, db execution.DB, rowsAffected ...int64) { + res, err := stmt.Exec(db) + + assert.NilError(t, err) + rows, err := res.RowsAffected() + assert.NilError(t, err) + + if len(rowsAffected) > 0 { + assert.Equal(t, rows, rowsAffected[0]) + } +} + +func AssertExecErr(t *testing.T, stmt jet.Statement, db execution.DB, errorStr string) { + _, err := stmt.Exec(db) + + assert.Error(t, err, errorStr) +} + func AssertJSON(t *testing.T, expectedJSON string, data interface{}) { jsonData, err := json.MarshalIndent(data, "", "\t") assert.NilError(t, err) @@ -46,8 +65,8 @@ func AssertJSONFile(t *testing.T, data interface{}, jsonFilePath string) { jsonData, err := json.MarshalIndent(data, "", "\t") assert.NilError(t, err) - //assert.Assert(t, string(fileJSONData) == string(jsonData)) - assert.DeepEqual(t, string(fileJSONData), string(jsonData)) + assert.Assert(t, string(fileJSONData) == string(jsonData)) + //assert.DeepEqual(t, string(fileJSONData), string(jsonData)) } func AssertStatementSql(t *testing.T, query jet.Statement, expectedQuery string, expectedArgs ...interface{}) { diff --git a/tests/init/data/mysql/test_sample.sql b/tests/init/data/mysql/test_sample.sql index 7883802..a64d3d1 100644 --- a/tests/init/data/mysql/test_sample.sql +++ b/tests/init/data/mysql/test_sample.sql @@ -138,4 +138,18 @@ CREATE TABLE IF NOT EXISTS test_sample.link ( ); INSERT INTO test_sample.link (ID, url, name, description) VALUES +(0, 'http://www.youtube.com', 'Youtube' , ''); + +-- Link2 table -------------------- + +DROP TABLE IF EXISTS test_sample.link2; + +CREATE TABLE IF NOT EXISTS test_sample.link2 ( + id int PRIMARY KEY AUTO_INCREMENT, + url VARCHAR (255) NOT NULL, + name VARCHAR (255) NOT NULL, + description VARCHAR (255) +); + +INSERT INTO test_sample.link2 (ID, url, name, description) VALUES (0, 'http://www.youtube.com', 'Youtube' , ''); \ No newline at end of file diff --git a/tests/mysql/cast_test.go b/tests/mysql/cast_test.go index bfc8630..07570a0 100644 --- a/tests/mysql/cast_test.go +++ b/tests/mysql/cast_test.go @@ -13,7 +13,7 @@ func TestCast(t *testing.T) { query := SELECT( CAST(String("test")).AS("CHAR CHARACTER SET utf8").AS("result.AS1"), - CAST(String("2011-02-02")).AS_DATE().AS("result.date"), + CAST(String("2011-02-02")).AS_DATE().AS("result.date1"), CAST(String("14:06:10")).AS_TIME().AS("result.time"), CAST(String("2011-02-02 14:06:10")).AS_DATETIME().AS("result.datetime"), @@ -27,7 +27,7 @@ func TestCast(t *testing.T) { testutils.AssertStatementSql(t, query, ` SELECT CAST(? AS CHAR CHARACTER SET utf8) AS "result.AS1", - CAST(? AS DATE) AS "result.date", + CAST(? AS DATE) AS "result.date1", CAST(? AS TIME) AS "result.time", CAST(? AS DATETIME) AS "result.datetime", CAST(? AS CHAR) AS "result.char1", @@ -41,7 +41,7 @@ FROM test_sample.all_types; type Result struct { As1 string - Date time.Time + Date1 time.Time Time time.Time DateTime time.Time Char1 string @@ -59,7 +59,7 @@ FROM test_sample.all_types; assert.DeepEqual(t, dest, Result{ As1: "test", - Date: *testutils.Date("2011-02-02"), + Date1: *testutils.Date("2011-02-02"), Time: *testutils.TimeWithoutTimeZone("14:06:10"), DateTime: *testutils.TimestampWithoutTimeZone("2011-02-02 14:06:10", 0), Char1: "150", diff --git a/tests/mysql/select_test.go b/tests/mysql/select_test.go index 1c23630..c693ca6 100644 --- a/tests/mysql/select_test.go +++ b/tests/mysql/select_test.go @@ -137,69 +137,6 @@ ORDER BY payment.customer_id, SUM(payment.amount) ASC; testutils.AssertJSONFile(t, dest, "mysql/testdata/customer_payment_sum.json") } -func getRowLockTestData() map[SelectLock]string { - return map[SelectLock]string{ - UPDATE(): "UPDATE", - SHARE(): "SHARE", - } -} - -func TestRowLock(t *testing.T) { - expectedSQL := ` -SELECT * -FROM dvds.address -LIMIT 3 -OFFSET 1 -FOR` - query := Address. - SELECT(STAR). - LIMIT(3). - OFFSET(1) - - for lockType, lockTypeStr := range getRowLockTestData() { - query.FOR(lockType) - - expectedQuery := expectedSQL + " " + lockTypeStr + ";\n" - testutils.AssertDebugStatementSql(t, query, expectedQuery, int64(3), int64(1)) - - tx, _ := db.Begin() - - _, err := query.Exec(tx) - assert.NilError(t, err) - - err = tx.Rollback() - assert.NilError(t, err) - } - - for lockType, lockTypeStr := range getRowLockTestData() { - query.FOR(lockType.NOWAIT()) - - testutils.AssertDebugStatementSql(t, query, expectedSQL+" "+lockTypeStr+" NOWAIT;\n", int64(3), int64(1)) - - tx, _ := db.Begin() - - _, err := query.Exec(tx) - assert.NilError(t, err) - - err = tx.Rollback() - assert.NilError(t, err) - } - - for lockType, lockTypeStr := range getRowLockTestData() { - query.FOR(lockType.SKIP_LOCKED()) - - testutils.AssertDebugStatementSql(t, query, expectedSQL+" "+lockTypeStr+" SKIP LOCKED;\n", int64(3), int64(1)) - - tx, _ := db.Begin() - - _, err := query.Exec(tx) - assert.NilError(t, err) - - err = tx.Rollback() - assert.NilError(t, err) - } -} - func TestSubQuery(t *testing.T) { rRatingFilms := Film. @@ -385,3 +322,66 @@ LIMIT ?; testutils.AssertJSONFile(t, dest, "./mysql/testdata/lang_film_actor_inventory_rental.json") } } + +func getRowLockTestData() map[SelectLock]string { + return map[SelectLock]string{ + UPDATE(): "UPDATE", + SHARE(): "SHARE", + } +} + +func TestRowLock(t *testing.T) { + expectedSQL := ` +SELECT * +FROM dvds.address +LIMIT 3 +OFFSET 1 +FOR` + query := Address. + SELECT(STAR). + LIMIT(3). + OFFSET(1) + + for lockType, lockTypeStr := range getRowLockTestData() { + query.FOR(lockType) + + expectedQuery := expectedSQL + " " + lockTypeStr + ";\n" + testutils.AssertDebugStatementSql(t, query, expectedQuery, int64(3), int64(1)) + + tx, _ := db.Begin() + + _, err := query.Exec(tx) + assert.NilError(t, err) + + err = tx.Rollback() + assert.NilError(t, err) + } + + for lockType, lockTypeStr := range getRowLockTestData() { + query.FOR(lockType.NOWAIT()) + + testutils.AssertDebugStatementSql(t, query, expectedSQL+" "+lockTypeStr+" NOWAIT;\n", int64(3), int64(1)) + + tx, _ := db.Begin() + + _, err := query.Exec(tx) + assert.NilError(t, err) + + err = tx.Rollback() + assert.NilError(t, err) + } + + for lockType, lockTypeStr := range getRowLockTestData() { + query.FOR(lockType.SKIP_LOCKED()) + + testutils.AssertDebugStatementSql(t, query, expectedSQL+" "+lockTypeStr+" SKIP LOCKED;\n", int64(3), int64(1)) + + tx, _ := db.Begin() + + _, err := query.Exec(tx) + assert.NilError(t, err) + + err = tx.Rollback() + assert.NilError(t, err) + } +} diff --git a/tests/mysql/update_test.go b/tests/mysql/update_test.go new file mode 100644 index 0000000..7abd7f3 --- /dev/null +++ b/tests/mysql/update_test.go @@ -0,0 +1,224 @@ +package mysql + +import ( + "context" + . "github.com/go-jet/jet" + "github.com/go-jet/jet/internal/testutils" + "github.com/go-jet/jet/tests/.gentestdata/mysql/test_sample/model" + . "github.com/go-jet/jet/tests/.gentestdata/mysql/test_sample/table" + "gotest.tools/assert" + "testing" + "time" +) + +func TestUpdateValues(t *testing.T) { + setupLinkTableForUpdateTest(t) + + query := Link. + UPDATE(Link.Name, Link.URL). + SET("Bong", "http://bong.com"). + WHERE(Link.Name.EQ(String("Bing"))) + + var expectedSQL = ` +UPDATE test_sample.link +SET name = 'Bong', url = 'http://bong.com' +WHERE link.name = 'Bing'; +` + + testutils.AssertDebugStatementSql(t, query, expectedSQL, "Bong", "http://bong.com", "Bing") + + testutils.AssertExec(t, query, db) + + links := []model.Link{} + + err := Link. + SELECT(Link.AllColumns). + WHERE(Link.Name.EQ(String("Bong"))). + Query(db, &links) + + assert.NilError(t, err) + assert.Equal(t, len(links), 1) + assert.DeepEqual(t, links[0], model.Link{ + ID: 204, + URL: "http://bong.com", + Name: "Bong", + }) +} + +func TestUpdateWithSubQueries(t *testing.T) { + setupLinkTableForUpdateTest(t) + + query := Link. + UPDATE(Link.Name, Link.URL). + SET( + SELECT(String("Bong")), + SELECT(Link2.URL). + FROM(Link2). + WHERE(Link2.Name.EQ(String("Youtube"))), + ). + WHERE(Link.Name.EQ(String("Bing"))) + + expectedSQL := ` +UPDATE test_sample.link +SET name = ( + SELECT ? +), url = ( + SELECT link2.url AS "link2.url" + FROM test_sample.link2 + WHERE link2.name = ? +) +WHERE link.name = ?; +` + testutils.AssertStatementSql(t, query, expectedSQL, "Bong", "Youtube", "Bing") + + testutils.AssertExec(t, query, db) +} + +func TestUpdateAndReturning(t *testing.T) { + defer func() { + r := recover() + assert.Equal(t, r, "jet: MySQL dialect does not support RETURNING.") + }() + + stmt := Link. + UPDATE(Link.Name, Link.URL). + SET("DuckDuckGo", "http://www.duckduckgo.com"). + WHERE(Link.Name.EQ(String("Ask"))). + RETURNING(Link.AllColumns) + + stmt.Query(db, &struct{}{}) +} + +func TestUpdateWithModelData(t *testing.T) { + setupLinkTableForUpdateTest(t) + + link := model.Link{ + ID: 201, + URL: "http://www.duckduckgo.com", + Name: "DuckDuckGo", + } + + stmt := Link. + UPDATE(Link.AllColumns). + MODEL(link). + WHERE(Link.ID.EQ(Int(int64(link.ID)))) + + expectedSQL := ` +UPDATE test_sample.link +SET id = ?, url = ?, name = ?, description = ? +WHERE link.id = ?; +` + testutils.AssertStatementSql(t, stmt, expectedSQL, int32(201), "http://www.duckduckgo.com", "DuckDuckGo", nil, int64(201)) + + testutils.AssertExec(t, stmt, db) +} + +func TestUpdateWithModelDataAndPredefinedColumnList(t *testing.T) { + + setupLinkTableForUpdateTest(t) + + link := model.Link{ + ID: 201, + URL: "http://www.duckduckgo.com", + Name: "DuckDuckGo", + } + + updateColumnList := ColumnList{Link.Description, Link.Name, Link.URL} + + stmt := Link. + UPDATE(updateColumnList). + MODEL(link). + WHERE(Link.ID.EQ(Int(int64(link.ID)))) + + var expectedSQL = ` +UPDATE test_sample.link +SET description = NULL, name = 'DuckDuckGo', url = 'http://www.duckduckgo.com' +WHERE link.id = 201; +` + //fmt.Println(stmt.DebugSql()) + + testutils.AssertDebugStatementSql(t, stmt, expectedSQL, nil, "DuckDuckGo", "http://www.duckduckgo.com", int64(201)) + + testutils.AssertExec(t, stmt, db) +} + +func TestUpdateWithInvalidModelData(t *testing.T) { + defer func() { + r := recover() + + assert.Equal(t, r, "missing struct field for column : id") + }() + + setupLinkTableForUpdateTest(t) + + link := struct { + Ident int + URL string + Name string + Description *string + Rel *string + }{ + Ident: 201, + URL: "http://www.duckduckgo.com", + Name: "DuckDuckGo", + } + + stmt := Link. + UPDATE(Link.AllColumns). + MODEL(link). + WHERE(Link.ID.EQ(Int(int64(link.Ident)))) + + stmt.Sql() +} + +func TestUpdateQueryContext(t *testing.T) { + setupLinkTableForUpdateTest(t) + + updateStmt := Link. + UPDATE(Link.Name, Link.URL). + SET("Bong", "http://bong.com"). + WHERE(Link.Name.EQ(String("Bing"))) + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond) + defer cancel() + + time.Sleep(10 * time.Millisecond) + + dest := []model.Link{} + err := updateStmt.QueryContext(ctx, db, &dest) + + assert.Error(t, err, "context deadline exceeded") +} + +func TestUpdateExecContext(t *testing.T) { + setupLinkTableForUpdateTest(t) + + updateStmt := Link. + UPDATE(Link.Name, Link.URL). + SET("Bong", "http://bong.com"). + WHERE(Link.Name.EQ(String("Bing"))) + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond) + defer cancel() + + time.Sleep(10 * time.Millisecond) + + _, err := updateStmt.ExecContext(ctx, db) + + assert.Error(t, err, "context deadline exceeded") +} + +func setupLinkTableForUpdateTest(t *testing.T) { + + cleanUpLinkTable(t) + + _, err := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description). + VALUES(200, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT). + VALUES(201, "http://www.ask.com", "Ask", DEFAULT). + VALUES(202, "http://www.ask.com", "Ask", DEFAULT). + VALUES(203, "http://www.yahoo.com", "Yahoo", DEFAULT). + VALUES(204, "http://www.bing.com", "Bing", DEFAULT). + Exec(db) + + assert.NilError(t, err) +} diff --git a/tests/postgres/delete_test.go b/tests/postgres/delete_test.go index c17f927..10ecb23 100644 --- a/tests/postgres/delete_test.go +++ b/tests/postgres/delete_test.go @@ -23,7 +23,7 @@ WHERE link.name IN ('Gmail', 'Outlook'); WHERE(Link.Name.IN(String("Gmail"), String("Outlook"))) testutils.AssertDebugStatementSql(t, deleteStmt, expectedSQL, "Gmail", "Outlook") - assertExec(t, deleteStmt, 2) + AssertExec(t, deleteStmt, 2) } func TestDeleteWithWhereAndReturning(t *testing.T) { @@ -61,7 +61,7 @@ func initForDeleteTest(t *testing.T) { VALUES("www.gmail.com", "Gmail", "Email service developed by Google"). VALUES("www.outlook.live.com", "Outlook", "Email service developed by Microsoft") - assertExec(t, stmt, 2) + AssertExec(t, stmt, 2) } func TestDeleteQueryContext(t *testing.T) { diff --git a/tests/postgres/insert_test.go b/tests/postgres/insert_test.go index 22ca26f..cf344e9 100644 --- a/tests/postgres/insert_test.go +++ b/tests/postgres/insert_test.go @@ -88,7 +88,7 @@ INSERT INTO test_sample.link VALUES testutils.AssertDebugStatementSql(t, stmt, expectedSQL, 100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial") - assertExec(t, stmt, 1) + AssertExec(t, stmt, 1) } func TestInsertModelObject(t *testing.T) { @@ -109,7 +109,7 @@ INSERT INTO test_sample.link (url, name) VALUES testutils.AssertDebugStatementSql(t, query, expectedSQL, "http://www.duckduckgo.com", "Duck Duck go") - assertExec(t, query, 1) + AssertExec(t, query, 1) } func TestInsertModelObjectEmptyColumnList(t *testing.T) { @@ -131,7 +131,7 @@ INSERT INTO test_sample.link VALUES testutils.AssertDebugStatementSql(t, query, expectedSQL, int32(1000), "http://www.duckduckgo.com", "Duck Duck go", nil) - assertExec(t, query, 1) + AssertExec(t, query, 1) } func TestInsertModelsObject(t *testing.T) { @@ -166,7 +166,7 @@ INSERT INTO test_sample.link (url, name) VALUES "http://www.google.com", "Google", "http://www.yahoo.com", "Yahoo") - assertExec(t, stmt, 3) + AssertExec(t, stmt, 3) } func TestInsertUsingMutableColumns(t *testing.T) { @@ -200,7 +200,7 @@ INSERT INTO test_sample.link (url, name, description) VALUES "http://www.google.com", "Google", nil, "http://www.yahoo.com", "Yahoo", nil) - assertExec(t, stmt, 4) + AssertExec(t, stmt, 4) } func TestInsertQuery(t *testing.T) { diff --git a/tests/postgres/update_test.go b/tests/postgres/update_test.go index 314f5d6..08d483f 100644 --- a/tests/postgres/update_test.go +++ b/tests/postgres/update_test.go @@ -26,7 +26,7 @@ WHERE link.name = 'Bing'; ` testutils.AssertDebugStatementSql(t, query, expectedSQL, "Bong", "http://bong.com", "Bing") - assertExec(t, query, 1) + AssertExec(t, query, 1) links := []model.Link{} @@ -71,7 +71,7 @@ WHERE link.name = 'Bing'; testutils.AssertDebugStatementSql(t, query, expectedSQL, "Bong", "Bing", "Bing") - assertExec(t, query, 1) + AssertExec(t, query, 1) } func TestUpdateAndReturning(t *testing.T) { @@ -129,7 +129,7 @@ WHERE link.id = 0; ` testutils.AssertDebugStatementSql(t, stmt, expectedSQL, int64(0), int64(0)) - assertExec(t, stmt, 1) + AssertExec(t, stmt, 1) } func TestUpdateWithInvalidSelect(t *testing.T) { @@ -178,7 +178,7 @@ WHERE link.id = 201; ` testutils.AssertDebugStatementSql(t, stmt, expectedSQL, int32(201), "http://www.duckduckgo.com", "DuckDuckGo", nil, int64(201)) - assertExec(t, stmt, 1) + AssertExec(t, stmt, 1) } func TestUpdateWithModelDataAndPredefinedColumnList(t *testing.T) { @@ -205,7 +205,7 @@ WHERE link.id = 201; ` testutils.AssertDebugStatementSql(t, stmt, expectedSQL, nil, "DuckDuckGo", "http://www.duckduckgo.com", int64(201)) - assertExec(t, stmt, 1) + AssertExec(t, stmt, 1) } func TestUpdateWithInvalidModelData(t *testing.T) { diff --git a/tests/postgres/util_test.go b/tests/postgres/util_test.go index 8da7989..1041eca 100644 --- a/tests/postgres/util_test.go +++ b/tests/postgres/util_test.go @@ -9,7 +9,7 @@ import ( "testing" ) -func assertExec(t *testing.T, stmt jet.Statement, rowsAffected int64) { +func AssertExec(t *testing.T, stmt jet.Statement, rowsAffected int64) { res, err := stmt.Exec(db) assert.NilError(t, err) diff --git a/update_statement.go b/update_statement.go index ee3bfae..5855ee4 100644 --- a/update_statement.go +++ b/update_statement.go @@ -23,26 +23,26 @@ func newUpdateStatement(table WritableTable, columns []column) UpdateStatement { return &updateStatementImpl{ table: table, columns: columns, - row: make([]clause, 0, len(columns)), + values: make([]clause, 0, len(columns)), } } type updateStatementImpl struct { table WritableTable columns []column - row []clause + values []clause where BoolExpression returning []projection } func (u *updateStatementImpl) SET(value interface{}, values ...interface{}) UpdateStatement { - u.row = unwindRowFromValues(value, values) + u.values = unwindRowFromValues(value, values) return u } func (u *updateStatementImpl) MODEL(data interface{}) UpdateStatement { - u.row = unwindRowFromModel(u.columns, data) + u.values = unwindRowFromModel(u.columns, data) return u } @@ -82,43 +82,17 @@ func (u *updateStatementImpl) Sql(dialect ...Dialect) (query string, args []inte return "", nil, errors.New("jet: no columns selected") } - if len(u.row) == 0 { + if len(u.values) == 0 { return "", nil, errors.New("jet: no values to updated") } out.newLine() out.writeString("SET") - if len(u.columns) > 1 { - out.writeString("(") - } - - err = serializeColumnNames(u.columns, out) - - if err != nil { + if err = out.dialect.UpdateAssigment(u.columns, u.values, out); err != nil { return } - if len(u.columns) > 1 { - out.writeString(")") - } - - out.writeString("=") - - if len(u.row) > 1 { - out.writeString("(") - } - - err = serializeClauseList(updateStatement, u.row, out) - - if err != nil { - return - } - - if len(u.row) > 1 { - out.writeString(")") - } - if u.where == nil { return "", nil, errors.New("jet: WHERE clause not set") }