From 6c6ccaf0b31ee9892629cf88ef7be084f7201fd8 Mon Sep 17 00:00:00 2001 From: Gregory Eremin Date: Sat, 7 Jul 2018 14:11:23 +0200 Subject: [PATCH] Move stuff around --- dbc/bench/dbc_test.go | 11 +++++++++++ {sqldb => dbc}/callback.go | 2 +- {sqldb => dbc}/chain_test.go | 4 ++-- {sqldb => dbc}/conn.go | 27 ++++++++++++++++++--------- {sqldb => dbc}/exec_result.go | 2 +- sqldb/caller.go => dbc/query.go | 16 ++++++++-------- {sqldb => dbc}/result_test.go | 14 +++++++------- sqldb/query_result.go => dbc/rows.go | 28 ++++++++++++++-------------- {sqldb => dbc}/sqldb_test.go | 23 +++++++++++++++-------- dbc/tools/populate_big_table.go | 25 +++++++++++++++++++++++++ 10 files changed, 102 insertions(+), 50 deletions(-) create mode 100644 dbc/bench/dbc_test.go rename {sqldb => dbc}/callback.go (98%) rename {sqldb => dbc}/chain_test.go (90%) rename {sqldb => dbc}/conn.go (73%) rename {sqldb => dbc}/exec_result.go (98%) rename sqldb/caller.go => dbc/query.go (85%) rename {sqldb => dbc}/result_test.go (76%) rename sqldb/query_result.go => dbc/rows.go (86%) rename {sqldb => dbc}/sqldb_test.go (77%) create mode 100644 dbc/tools/populate_big_table.go diff --git a/dbc/bench/dbc_test.go b/dbc/bench/dbc_test.go new file mode 100644 index 0000000..709af70 --- /dev/null +++ b/dbc/bench/dbc_test.go @@ -0,0 +1,11 @@ +package bench + +import ( + "testing" +) + +func BenchLoadSliceOfStructs(b *testing.B) { + for i := 0; i < b.N; i++ { + + } +} diff --git a/sqldb/callback.go b/dbc/callback.go similarity index 98% rename from sqldb/callback.go rename to dbc/callback.go index 78cb2d8..4a9fa85 100644 --- a/sqldb/callback.go +++ b/dbc/callback.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "context" diff --git a/sqldb/chain_test.go b/dbc/chain_test.go similarity index 90% rename from sqldb/chain_test.go rename to dbc/chain_test.go index 38408aa..5c147b8 100644 --- a/sqldb/chain_test.go +++ b/dbc/chain_test.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "context" @@ -7,7 +7,7 @@ import ( func TestCallChain(t *testing.T) { ctx := context.Background() - mustT(t, conn. + mustExec(t, conn. Exec(ctx, "INSERT INTO sqldb_test (id, name) VALUES (3, 'Fred')").Then(). Exec(ctx, "UPDATE sqldb_test SET name = 'Wilson' WHERE id = 3").Then(). Exec(ctx, "DELETE FROM sqldb_test WHERE id = 3"), diff --git a/sqldb/conn.go b/dbc/conn.go similarity index 73% rename from sqldb/conn.go rename to dbc/conn.go index 8f66607..34fec07 100644 --- a/sqldb/conn.go +++ b/dbc/conn.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "context" @@ -10,10 +10,19 @@ import ( // Conn represents database connection. type Conn interface { connOrTx + // Begin executes a transaction. Begin(context.Context, func(Tx) error) error + // BeginCustom executes a transaction with provided options. + BeginCustom(context.Context, func(Tx) error, *sql.TxOptions) error + // Close closes the connection. Close() error + // DB returns the underlying DB object. DB() *sql.DB + // Before adds a callback function that would be called before a query is + // executed. Before(BeforeCallback) + // After adds a callback function that would be called after a query was + // executed. After(AfterCallback) } @@ -58,9 +67,15 @@ func Connect(ctx context.Context, f Flavor, dsn string) (Conn, error) { }, nil } -// Begin executes a transaction. func (c *dbWrapper) Begin(ctx context.Context, fn func(tx Tx) error) error { - tx, err := c.conn.BeginTx(ctx, &sql.TxOptions{}) + return c.BeginCustom(ctx, fn, nil) +} + +func (c *dbWrapper) BeginCustom(ctx context.Context, fn func(tx Tx) error, opts *sql.TxOptions) error { + if opts == nil { + opts = &sql.TxOptions{} + } + tx, err := c.conn.BeginTx(ctx, opts) if err != nil { return err } @@ -71,24 +86,18 @@ func (c *dbWrapper) Begin(ctx context.Context, fn func(tx Tx) error) error { return err } -// DB returns the underlying DB object. func (c *dbWrapper) DB() *sql.DB { return c.conn } -// Close closes the connection. func (c *dbWrapper) Close() error { return c.conn.Close() } -// Before adds a callback function that would be called before a query is -// executed. func (c *dbWrapper) Before(cb BeforeCallback) { c.cb.addBefore(cb) } -// After adds a callback function that would be called after a query was -// executed. func (c *dbWrapper) After(cb AfterCallback) { c.cb.addAfter(cb) } diff --git a/sqldb/exec_result.go b/dbc/exec_result.go similarity index 98% rename from sqldb/exec_result.go rename to dbc/exec_result.go index 9deebd5..97b62fa 100644 --- a/sqldb/exec_result.go +++ b/dbc/exec_result.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "context" diff --git a/sqldb/caller.go b/dbc/query.go similarity index 85% rename from sqldb/caller.go rename to dbc/query.go index 4211e41..8c265e7 100644 --- a/sqldb/caller.go +++ b/dbc/query.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "context" @@ -20,8 +20,8 @@ type executer interface { type queryPerformer interface { // Query executes a query and returns a result object that can later be used // to retrieve values. - Query(ctx context.Context, query string, args ...interface{}) QueryResult - QueryNamed(ctx context.Context, query string, arg interface{}) QueryResult + Query(ctx context.Context, query string, args ...interface{}) Rows + QueryNamed(ctx context.Context, query string, arg interface{}) Rows } type stdConnOrTx interface { @@ -51,17 +51,17 @@ func (c *caller) ExecNamed(ctx context.Context, query string, arg interface{}) E return nil } -func (c *caller) Query(ctx context.Context, query string, args ...interface{}) QueryResult { +func (c *caller) Query(ctx context.Context, query string, args ...interface{}) Rows { c.cb.callBefore(ctx, query) startedAt := time.Now() - rows, err := c.db.QueryContext(ctx, query, args...) + r, err := c.db.QueryContext(ctx, query, args...) c.cb.callAfter(ctx, query, time.Since(startedAt), err) - return &queryResult{ + return &rows{ err: err, - rows: rows, + rows: r, } } -func (c *caller) QueryNamed(ctx context.Context, query string, arg interface{}) QueryResult { +func (c *caller) QueryNamed(ctx context.Context, query string, arg interface{}) Rows { return nil } diff --git a/sqldb/result_test.go b/dbc/result_test.go similarity index 76% rename from sqldb/result_test.go rename to dbc/result_test.go index 48f1ee4..6b5768b 100644 --- a/sqldb/result_test.go +++ b/dbc/result_test.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "context" @@ -11,7 +11,7 @@ func TestLoadSingleValue(t *testing.T) { ctx := context.Background() exp := int(1) var out int - mustT(t, conn.Query(ctx, "SELECT 1").Load(&out)) + mustQuery(t, conn.Query(ctx, "SELECT 1").Load(&out)) if exp != out { t.Errorf("Value doesn't match: expected %d, got %d", exp, out) } @@ -21,7 +21,7 @@ func TestLoadSlice(t *testing.T) { ctx := context.Background() exp := []int{1, 2} var out []int - mustT(t, conn.Query(ctx, "SELECT id FROM sqldb_test").Load(&out)) + mustQuery(t, conn.Query(ctx, "SELECT id FROM sqldb_test").Load(&out)) if !cmp.Equal(exp, out) { t.Errorf("Values dont't match: %s", cmp.Diff(exp, out)) } @@ -31,7 +31,7 @@ func TestLoadMap(t *testing.T) { ctx := context.Background() exp := map[string]interface{}{"id": int64(1), "name": "Alice"} var out map[string]interface{} - mustT(t, conn.Query(ctx, "SELECT * FROM sqldb_test WHERE id = 1").Load(&out)) + mustQuery(t, conn.Query(ctx, "SELECT * FROM sqldb_test WHERE id = 1").Load(&out)) if !cmp.Equal(exp, out) { t.Errorf("Record doesn't match: %s", cmp.Diff(exp, out)) } @@ -44,7 +44,7 @@ func TestLoadSliceOfMaps(t *testing.T) { {"id": int64(2), "name": "Bob"}, } var out []map[string]interface{} - mustT(t, conn.Query(ctx, "SELECT * FROM sqldb_test ORDER BY id ASC").Load(&out)) + mustQuery(t, conn.Query(ctx, "SELECT * FROM sqldb_test ORDER BY id ASC").Load(&out)) if !cmp.Equal(exp, out) { t.Errorf("Records don't match: %s", cmp.Diff(exp, out)) } @@ -54,7 +54,7 @@ func TestLoadStruct(t *testing.T) { ctx := context.Background() exp := record{ID: 1, Name: "Alice"} var out record - mustT(t, conn.Query(ctx, "SELECT * FROM sqldb_test WHERE id = 1").Load(&out)) + mustQuery(t, conn.Query(ctx, "SELECT * FROM sqldb_test WHERE id = 1").Load(&out)) if !cmp.Equal(exp, out) { t.Errorf("Record doesn't match: %s", cmp.Diff(exp, out)) } @@ -67,7 +67,7 @@ func TestLoadSliceOfStructs(t *testing.T) { {ID: 2, Name: "Bob"}, } var out []record - mustT(t, conn.Query(ctx, "SELECT * FROM sqldb_test").Load(&out)) + mustQuery(t, conn.Query(ctx, "SELECT * FROM sqldb_test").Load(&out)) if !cmp.Equal(exp, out) { t.Errorf("Records don't match: %s", cmp.Diff(exp, out)) } diff --git a/sqldb/query_result.go b/dbc/rows.go similarity index 86% rename from sqldb/query_result.go rename to dbc/rows.go index 6340cac..568c7d7 100644 --- a/sqldb/query_result.go +++ b/dbc/rows.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "database/sql" @@ -8,8 +8,8 @@ import ( "github.com/localhots/gobelt/reflect2" ) -// QueryResult ... -type QueryResult interface { +// Rows ... +type Rows interface { Error() error Load(dest interface{}) error Rows() *sql.Rows @@ -17,20 +17,20 @@ type QueryResult interface { const tagName = "db" -type queryResult struct { +type rows struct { err error rows *sql.Rows } -func (r *queryResult) Rows() *sql.Rows { +func (r *rows) Rows() *sql.Rows { return r.rows } -func (r *queryResult) Error() error { +func (r *rows) Error() error { return r.err } -func (r *queryResult) Load(dest interface{}) error { +func (r *rows) Load(dest interface{}) error { if r.err != nil { return r.err } @@ -67,13 +67,13 @@ func (r *queryResult) Load(dest interface{}) error { return nil } -func (r *queryResult) loadValue(dest interface{}) { +func (r *rows) loadValue(dest interface{}) { if r.rows.Next() { r.err = r.rows.Scan(dest) } } -func (r *queryResult) loadSlice(typ reflect.Type, dest interface{}) { +func (r *rows) loadSlice(typ reflect.Type, dest interface{}) { vSlice := reflect.MakeSlice(typ, 0, 0) for r.rows.Next() { val := reflect.New(typ.Elem()) @@ -86,7 +86,7 @@ func (r *queryResult) loadSlice(typ reflect.Type, dest interface{}) { reflect.ValueOf(dest).Elem().Set(vSlice) } -func (r *queryResult) loadMap(dest *map[string]interface{}) { +func (r *rows) loadMap(dest *map[string]interface{}) { if !r.rows.Next() { return } @@ -127,7 +127,7 @@ func (r *queryResult) loadMap(dest *map[string]interface{}) { } } -func (r *queryResult) loadSliceOfMaps(dest *[]map[string]interface{}) { +func (r *rows) loadSliceOfMaps(dest *[]map[string]interface{}) { cols, err := r.rows.Columns() if err != nil { r.err = err @@ -168,7 +168,7 @@ func (r *queryResult) loadSliceOfMaps(dest *[]map[string]interface{}) { } } -func (r *queryResult) loadStruct(typ reflect.Type, dest interface{}) { +func (r *rows) loadStruct(typ reflect.Type, dest interface{}) { if !r.rows.Next() { return } @@ -204,7 +204,7 @@ func (r *queryResult) loadStruct(typ reflect.Type, dest interface{}) { } } -func (r *queryResult) loadSliceOfStructs(typ reflect.Type, dest interface{}) { +func (r *rows) loadSliceOfStructs(typ reflect.Type, dest interface{}) { cols, err := r.rows.Columns() if err != nil { r.err = err @@ -243,7 +243,7 @@ func (r *queryResult) loadSliceOfStructs(typ reflect.Type, dest interface{}) { } } -func (r *queryResult) withError(err error) *queryResult { +func (r *rows) withError(err error) Rows { r.err = err return r } diff --git a/sqldb/sqldb_test.go b/dbc/sqldb_test.go similarity index 77% rename from sqldb/sqldb_test.go rename to dbc/sqldb_test.go index 9aae7c2..e8f7b60 100644 --- a/sqldb/sqldb_test.go +++ b/dbc/sqldb_test.go @@ -1,4 +1,4 @@ -package sqldb +package dbc import ( "context" @@ -16,7 +16,7 @@ type record struct { Name string `db:"name"` } -var conn *Conn +var conn Conn func TestMain(m *testing.M) { dsn := flag.String("dsn", "", "Database source name") @@ -35,17 +35,17 @@ func TestMain(m *testing.M) { } log.Println("Seeding database") - must(conn.Exec(ctx, ` + mustExecMain(conn.Exec(ctx, ` DROP TABLE IF EXISTS sqldb_test `)) - must(conn.Exec(ctx, ` + mustExecMain(conn.Exec(ctx, ` CREATE TABLE sqldb_test ( id int(11) UNSIGNED NOT NULL, name VARCHAR(10) DEFAULT '', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=ascii `)) - must(conn.Exec(ctx, ` + mustExecMain(conn.Exec(ctx, ` INSERT INTO sqldb_test (id, name) VALUES (1, "Alice"), @@ -61,17 +61,24 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } -func mustT(t *testing.T, r Result) Result { +func mustExec(t *testing.T, r ExecResult) ExecResult { t.Helper() if r.Error() != nil { - t.Fatalf("Query failed: %v", r.Error()) + t.Fatalf("Exec failed: %v", r.Error()) } return r } -func must(r Result) Result { +func mustExecMain(r ExecResult) ExecResult { if r.Error() != nil { log.Fatalf("Query failed: %v\n", r.Error()) } return r } + +func mustQuery(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("Query failed: %v", err) + } +} diff --git a/dbc/tools/populate_big_table.go b/dbc/tools/populate_big_table.go new file mode 100644 index 0000000..75d0548 --- /dev/null +++ b/dbc/tools/populate_big_table.go @@ -0,0 +1,25 @@ +package main + +import ( + "context" + "flag" + + "github.com/localhots/gobelt/dbc" + "github.com/localhots/gobelt/log" +) + +func main() { + dsn := flag.String("dsn", "", "Database source name") + flag.Parse() + + ctx := context.Background() + conn, err := dbc.Connect(ctx, dbc.MySQL, *dsn) + if err != nil { + log.Fatal(ctx, "Failed to establish database conneciton", log.F{ + "dsn": dsn, + "error": err, + }) + } + + _ = conn +}