diff --git a/bench_all.sh b/bench_all.sh new file mode 100755 index 0000000..1825b6b --- /dev/null +++ b/bench_all.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +#set -e + +export GORP_TEST_DSN="gorptest/gorptest/" +export GORP_TEST_DIALECT="mysql" +go test -bench . -benchmem + +export GORP_TEST_DSN="user=$USER dbname=gorptest sslmode=disable" +export GORP_TEST_DIALECT="postgres" +go test -bench . -benchmem + +export GORP_TEST_DSN="/tmp/gorptest.bin" +export GORP_TEST_DIALECT="sqlite" +go test -bench . -benchmem + +# PASS +# BenchmarkNativeCrud 2000 872061 ns/op 5364 B/op 106 allocs/op +# BenchmarkGorpCrud 2000 918331 ns/op 7257 B/op 159 allocs/op +# ok _/Users/jmoiron/dev/go/gorp 4.413s +# PASS +# BenchmarkNativeCrud 1000 1468223 ns/op 8833 B/op 335 allocs/op +# BenchmarkGorpCrud 1000 1485000 ns/op 11585 B/op 390 allocs/op +# ok _/Users/jmoiron/dev/go/gorp 3.664s +# PASS +# BenchmarkNativeCrud 1000 1761324 ns/op 1446 B/op 51 allocs/op +# BenchmarkGorpCrud 1000 1733306 ns/op 2942 B/op 99 allocs/op +# ok _/Users/jmoiron/dev/go/gorp 3.977s +# + diff --git a/dialect.go b/dialect.go index d14dfaa..f2fc9a2 100644 --- a/dialect.go +++ b/dialect.go @@ -288,3 +288,18 @@ func (m MySQLDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params func (d MySQLDialect) QuoteField(f string) string { return "`" + f + "`" } + +// Formats the bindvars in the query string (these are '?') for the dialect. +func ReBind(query string, dialect Dialect) string { + + binder := dialect.BindVar(0) + if binder == "?" { + return query + } + + for i, j := 0, strings.Index(query, "?"); j >= 0; i++ { + query = strings.Replace(query, "?", dialect.BindVar(i), 1) + j = strings.Index(query, "?") + } + return query +} diff --git a/gorp.go b/gorp.go index 249941e..9c5c855 100644 --- a/gorp.go +++ b/gorp.go @@ -573,6 +573,8 @@ func (m *DbMap) Select(i interface{}, query string, args ...interface{}) ([]inte return hookedselect(m, m, i, query, args...) } +// FIXME: this comment is wrong, query preparation is commented out + // Exec runs an arbitrary SQL statement. args represent the bind parameters. // This is equivalent to running: Prepare(), Exec() using database/sql func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) { diff --git a/gorp_test.go b/gorp_test.go index 2558512..3e29874 100644 --- a/gorp_test.go +++ b/gorp_test.go @@ -564,6 +564,8 @@ func TestWithStringPk(t *testing.T) { } func BenchmarkNativeCrud(b *testing.B) { + var err error + b.StopTimer() dbmap := initDbMapBench() defer dbmap.DropTables() @@ -574,24 +576,44 @@ func BenchmarkNativeCrud(b *testing.B) { update := "update invoice_test set Created=?, Updated=?, Memo=?, PersonId=? where Id=?" delete := "delete from invoice_test where Id=?" + suffix := dbmap.Dialect.AutoIncrInsertSuffix(&ColumnMap{ColumnName: "Id"}) + insert = ReBind(insert, dbmap.Dialect) + suffix + sel = ReBind(sel, dbmap.Dialect) + update = ReBind(update, dbmap.Dialect) + delete = ReBind(delete, dbmap.Dialect) + inv := &Invoice{0, 100, 200, "my memo", 0, false} for i := 0; i < b.N; i++ { - res, err := dbmap.Db.Exec(insert, inv.Created, inv.Updated, - inv.Memo, inv.PersonId) - if err != nil { - panic(err) - } + if len(suffix) == 0 { + res, err := dbmap.Db.Exec(insert, inv.Created, inv.Updated, inv.Memo, inv.PersonId) + if err != nil { + panic(err) + } + + newid, err := res.LastInsertId() + if err != nil { + panic(err) + } + inv.Id = newid + } else { + rows, err := dbmap.Db.Query(insert, inv.Created, inv.Updated, inv.Memo, inv.PersonId) + if err != nil { + panic(err) + } + + if rows.Next() { + err = rows.Scan(&inv.Id) + if err != nil { + panic(err) + } + } + rows.Close() - newid, err := res.LastInsertId() - if err != nil { - panic(err) } - inv.Id = newid row := dbmap.Db.QueryRow(sel, inv.Id) - err = row.Scan(&inv.Id, &inv.Created, &inv.Updated, &inv.Memo, - &inv.PersonId) + err = row.Scan(&inv.Id, &inv.Created, &inv.Updated, &inv.Memo, &inv.PersonId) if err != nil { panic(err) } @@ -619,6 +641,7 @@ func BenchmarkGorpCrud(b *testing.B) { b.StopTimer() dbmap := initDbMapBench() defer dbmap.DropTables() + //dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds)) b.StartTimer() inv := &Invoice{0, 100, 200, "my memo", 0, true}