diff --git a/database.yml b/database.yml index d8c1567e..75edb7e1 100644 --- a/database.yml +++ b/database.yml @@ -30,4 +30,6 @@ cockroach_ssl: sqlite: dialect: "sqlite3" database: "tmp/test.sqlite" + options: + lock: true diff --git a/dialect.go b/dialect.go index 0e49b168..013ab92a 100644 --- a/dialect.go +++ b/dialect.go @@ -8,13 +8,13 @@ import ( ) type crudable interface { - SelectOne(store, *Model, Query) error - SelectMany(store, *Model, Query) error - Create(store, *Model, columns.Columns) error - Update(store, *Model, columns.Columns) error - UpdateQuery(store, *Model, columns.Columns, Query) (int64, error) - Destroy(store, *Model) error - Delete(store, *Model, Query) error + SelectOne(*Connection, *Model, Query) error + SelectMany(*Connection, *Model, Query) error + Create(*Connection, *Model, columns.Columns) error + Update(*Connection, *Model, columns.Columns) error + UpdateQuery(*Connection, *Model, columns.Columns, Query) (int64, error) + Destroy(*Connection, *Model) error + Delete(*Connection, *Model, Query) error } type fizzable interface { diff --git a/dialect_cockroach.go b/dialect_cockroach.go index 041c308a..80bd6d5e 100644 --- a/dialect_cockroach.go +++ b/dialect_cockroach.go @@ -66,7 +66,7 @@ func (p *cockroach) Details() *ConnectionDetails { return p.ConnectionDetails } -func (p *cockroach) Create(s store, model *Model, cols columns.Columns) error { +func (p *cockroach) Create(c *Connection, model *Model, cols columns.Columns) error { keyType, err := model.PrimaryKeyType() if err != nil { return err @@ -81,8 +81,8 @@ func (p *cockroach) Create(s store, model *Model, cols columns.Columns) error { } else { query = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES returning %s", p.Quote(model.TableName()), model.IDField()) } - txlog(logging.SQL, s, query, model.Value) - stmt, err := s.PrepareNamed(query) + txlog(logging.SQL, c, query, model.Value) + stmt, err := c.Store.PrepareNamed(query) if err != nil { return err } @@ -100,33 +100,33 @@ func (p *cockroach) Create(s store, model *Model, cols columns.Columns) error { } return nil } - return genericCreate(s, model, cols, p) + return genericCreate(c, model, cols, p) } -func (p *cockroach) Update(s store, model *Model, cols columns.Columns) error { - return genericUpdate(s, model, cols, p) +func (p *cockroach) Update(c *Connection, model *Model, cols columns.Columns) error { + return genericUpdate(c, model, cols, p) } -func (p *cockroach) UpdateQuery(s store, model *Model, cols columns.Columns, query Query) (int64, error) { - return genericUpdateQuery(s, model, cols, p, query, sqlx.DOLLAR) +func (p *cockroach) UpdateQuery(c *Connection, model *Model, cols columns.Columns, query Query) (int64, error) { + return genericUpdateQuery(c, model, cols, p, query, sqlx.DOLLAR) } -func (p *cockroach) Destroy(s store, model *Model) error { +func (p *cockroach) Destroy(c *Connection, model *Model) error { stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", p.Quote(model.TableName()), model.Alias(), model.WhereID())) - _, err := genericExec(s, stmt, model.ID()) + _, err := genericExec(c, stmt, model.ID()) return err } -func (p *cockroach) Delete(s store, model *Model, query Query) error { - return genericDelete(s, model, query) +func (p *cockroach) Delete(c *Connection, model *Model, query Query) error { + return genericDelete(c, model, query) } -func (p *cockroach) SelectOne(s store, model *Model, query Query) error { - return genericSelectOne(s, model, query) +func (p *cockroach) SelectOne(c *Connection, model *Model, query Query) error { + return genericSelectOne(c, model, query) } -func (p *cockroach) SelectMany(s store, models *Model, query Query) error { - return genericSelectMany(s, models, query) +func (p *cockroach) SelectMany(c *Connection, models *Model, query Query) error { + return genericSelectMany(c, models, query) } func (p *cockroach) CreateDB() error { diff --git a/dialect_common.go b/dialect_common.go index 867bdb6b..50cb1caa 100644 --- a/dialect_common.go +++ b/dialect_common.go @@ -42,7 +42,7 @@ func (commonDialect) Quote(key string) string { return strings.Join(parts, ".") } -func genericCreate(s store, model *Model, cols columns.Columns, quoter quotable) error { +func genericCreate(c *Connection, model *Model, cols columns.Columns, quoter quotable) error { keyType, err := model.PrimaryKeyType() if err != nil { return err @@ -53,8 +53,8 @@ func genericCreate(s store, model *Model, cols columns.Columns, quoter quotable) cols.Remove(model.IDField()) w := cols.Writeable() query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", quoter.Quote(model.TableName()), w.QuotedString(quoter), w.SymbolizedString()) - txlog(logging.SQL, s, query, model.Value) - res, err := s.NamedExec(query, model.Value) + txlog(logging.SQL, c, query, model.Value) + res, err := c.Store.NamedExec(query, model.Value) if err != nil { return err } @@ -81,8 +81,8 @@ func genericCreate(s store, model *Model, cols columns.Columns, quoter quotable) w := cols.Writeable() w.Add(model.IDField()) query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", quoter.Quote(model.TableName()), w.QuotedString(quoter), w.SymbolizedString()) - txlog(logging.SQL, s, query, model.Value) - stmt, err := s.PrepareNamed(query) + txlog(logging.SQL, c, query, model.Value) + stmt, err := c.Store.PrepareNamed(query) if err != nil { return err } @@ -101,17 +101,17 @@ func genericCreate(s store, model *Model, cols columns.Columns, quoter quotable) return fmt.Errorf("can not use %s as a primary key type!", keyType) } -func genericUpdate(s store, model *Model, cols columns.Columns, quoter quotable) error { +func genericUpdate(c *Connection, model *Model, cols columns.Columns, quoter quotable) error { stmt := fmt.Sprintf("UPDATE %s AS %s SET %s WHERE %s", quoter.Quote(model.TableName()), model.Alias(), cols.Writeable().QuotedUpdateString(quoter), model.WhereNamedID()) - txlog(logging.SQL, s, stmt, model.ID()) - _, err := s.NamedExec(stmt, model.Value) + txlog(logging.SQL, c, stmt, model.ID()) + _, err := c.Store.NamedExec(stmt, model.Value) if err != nil { return err } return nil } -func genericUpdateQuery(s store, model *Model, cols columns.Columns, quoter quotable, query Query, bindType int) (int64, error) { +func genericUpdateQuery(c *Connection, model *Model, cols columns.Columns, quoter quotable, query Query, bindType int) (int64, error) { q := fmt.Sprintf("UPDATE %s AS %s SET %s", quoter.Quote(model.TableName()), model.Alias(), cols.Writeable().QuotedUpdateString(quoter)) q, updateArgs, err := sqlx.Named(q, model.Value) @@ -124,7 +124,7 @@ func genericUpdateQuery(s store, model *Model, cols columns.Columns, quoter quot q = sqlx.Rebind(bindType, q) - result, err := genericExec(s, q, append(updateArgs, sb.args...)...) + result, err := genericExec(c, q, append(updateArgs, sb.args...)...) if err != nil { return 0, err } @@ -137,41 +137,41 @@ func genericUpdateQuery(s store, model *Model, cols columns.Columns, quoter quot return n, err } -func genericDestroy(s store, model *Model, quoter quotable) error { +func genericDestroy(c *Connection, model *Model, quoter quotable) error { stmt := fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", quoter.Quote(model.TableName()), model.Alias(), model.WhereID()) - _, err := genericExec(s, stmt, model.ID()) + _, err := genericExec(c, stmt, model.ID()) if err != nil { return err } return nil } -func genericDelete(s store, model *Model, query Query) error { +func genericDelete(c *Connection, model *Model, query Query) error { sqlQuery, args := query.ToSQL(model) - _, err := genericExec(s, sqlQuery, args...) + _, err := genericExec(c, sqlQuery, args...) return err } -func genericExec(s store, stmt string, args ...interface{}) (sql.Result, error) { - txlog(logging.SQL, s, stmt, args...) - res, err := s.Exec(stmt, args...) +func genericExec(c *Connection, stmt string, args ...interface{}) (sql.Result, error) { + txlog(logging.SQL, c, stmt, args...) + res, err := c.Store.Exec(stmt, args...) return res, err } -func genericSelectOne(s store, model *Model, query Query) error { +func genericSelectOne(c *Connection, model *Model, query Query) error { sqlQuery, args := query.ToSQL(model) txlog(logging.SQL, query.Connection, sqlQuery, args...) - err := s.Get(model.Value, sqlQuery, args...) + err := c.Store.Get(model.Value, sqlQuery, args...) if err != nil { return err } return nil } -func genericSelectMany(s store, models *Model, query Query) error { +func genericSelectMany(c *Connection, models *Model, query Query) error { sqlQuery, args := query.ToSQL(models) txlog(logging.SQL, query.Connection, sqlQuery, args...) - err := s.Select(models.Value, sqlQuery, args...) + err := c.Store.Select(models.Value, sqlQuery, args...) if err != nil { return err } diff --git a/dialect_mysql.go b/dialect_mysql.go index 5ab26487..0a233889 100644 --- a/dialect_mysql.go +++ b/dialect_mysql.go @@ -82,31 +82,31 @@ func (m *mysql) MigrationURL() string { return m.URL() } -func (m *mysql) Create(s store, model *Model, cols columns.Columns) error { - if err := genericCreate(s, model, cols, m); err != nil { +func (m *mysql) Create(c *Connection, model *Model, cols columns.Columns) error { + if err := genericCreate(c, model, cols, m); err != nil { return fmt.Errorf("mysql create: %w", err) } return nil } -func (m *mysql) Update(s store, model *Model, cols columns.Columns) error { - if err := genericUpdate(s, model, cols, m); err != nil { +func (m *mysql) Update(c *Connection, model *Model, cols columns.Columns) error { + if err := genericUpdate(c, model, cols, m); err != nil { return fmt.Errorf("mysql update: %w", err) } return nil } -func (m *mysql) UpdateQuery(s store, model *Model, cols columns.Columns, query Query) (int64, error) { - if n, err := genericUpdateQuery(s, model, cols, m, query, sqlx.QUESTION); err != nil { +func (m *mysql) UpdateQuery(c *Connection, model *Model, cols columns.Columns, query Query) (int64, error) { + if n, err := genericUpdateQuery(c, model, cols, m, query, sqlx.QUESTION); err != nil { return n, fmt.Errorf("mysql update query: %w", err) } else { return n, nil } } -func (m *mysql) Destroy(s store, model *Model) error { +func (m *mysql) Destroy(c *Connection, model *Model) error { stmt := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", m.Quote(model.TableName()), model.IDField()) - _, err := genericExec(s, stmt, model.ID()) + _, err := genericExec(c, stmt, model.ID()) if err != nil { return fmt.Errorf("mysql destroy: %w", err) } @@ -115,26 +115,26 @@ func (m *mysql) Destroy(s store, model *Model) error { var asRegex = regexp.MustCompile(`\sAS\s\S+`) // exactly " AS non-spaces" -func (m *mysql) Delete(s store, model *Model, query Query) error { +func (m *mysql) Delete(c *Connection, model *Model, query Query) error { sqlQuery, args := query.ToSQL(model) // * MySQL does not support table alias for DELETE syntax until 8.0. // * Do not generate SQL manually if they may have `WHERE IN`. // * Spaces are intentionally added to make it easy to see on the log. sqlQuery = asRegex.ReplaceAllString(sqlQuery, " ") - _, err := genericExec(s, sqlQuery, args...) + _, err := genericExec(c, sqlQuery, args...) return err } -func (m *mysql) SelectOne(s store, model *Model, query Query) error { - if err := genericSelectOne(s, model, query); err != nil { +func (m *mysql) SelectOne(c *Connection, model *Model, query Query) error { + if err := genericSelectOne(c, model, query); err != nil { return fmt.Errorf("mysql select one: %w", err) } return nil } -func (m *mysql) SelectMany(s store, models *Model, query Query) error { - if err := genericSelectMany(s, models, query); err != nil { +func (m *mysql) SelectMany(c *Connection, models *Model, query Query) error { + if err := genericSelectMany(c, models, query); err != nil { return fmt.Errorf("mysql select many: %w", err) } return nil diff --git a/dialect_postgresql.go b/dialect_postgresql.go index 912195e4..25fcdcda 100644 --- a/dialect_postgresql.go +++ b/dialect_postgresql.go @@ -50,7 +50,7 @@ func (p *postgresql) Details() *ConnectionDetails { return p.ConnectionDetails } -func (p *postgresql) Create(s store, model *Model, cols columns.Columns) error { +func (p *postgresql) Create(c *Connection, model *Model, cols columns.Columns) error { keyType, err := model.PrimaryKeyType() if err != nil { return err @@ -65,8 +65,8 @@ func (p *postgresql) Create(s store, model *Model, cols columns.Columns) error { } else { query = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES returning %s", p.Quote(model.TableName()), model.IDField()) } - txlog(logging.SQL, s, query, model.Value) - stmt, err := s.PrepareNamed(query) + txlog(logging.SQL, c, query, model.Value) + stmt, err := c.Store.PrepareNamed(query) if err != nil { return err } @@ -84,36 +84,36 @@ func (p *postgresql) Create(s store, model *Model, cols columns.Columns) error { } return nil } - return genericCreate(s, model, cols, p) + return genericCreate(c, model, cols, p) } -func (p *postgresql) Update(s store, model *Model, cols columns.Columns) error { - return genericUpdate(s, model, cols, p) +func (p *postgresql) Update(c *Connection, model *Model, cols columns.Columns) error { + return genericUpdate(c, model, cols, p) } -func (p *postgresql) UpdateQuery(s store, model *Model, cols columns.Columns, query Query) (int64, error) { - return genericUpdateQuery(s, model, cols, p, query, sqlx.DOLLAR) +func (p *postgresql) UpdateQuery(c *Connection, model *Model, cols columns.Columns, query Query) (int64, error) { + return genericUpdateQuery(c, model, cols, p, query, sqlx.DOLLAR) } -func (p *postgresql) Destroy(s store, model *Model) error { +func (p *postgresql) Destroy(c *Connection, model *Model) error { stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", p.Quote(model.TableName()), model.Alias(), model.WhereID())) - _, err := genericExec(s, stmt, model.ID()) + _, err := genericExec(c, stmt, model.ID()) if err != nil { return err } return nil } -func (p *postgresql) Delete(s store, model *Model, query Query) error { - return genericDelete(s, model, query) +func (p *postgresql) Delete(c *Connection, model *Model, query Query) error { + return genericDelete(c, model, query) } -func (p *postgresql) SelectOne(s store, model *Model, query Query) error { - return genericSelectOne(s, model, query) +func (p *postgresql) SelectOne(c *Connection, model *Model, query Query) error { + return genericSelectOne(c, model, query) } -func (p *postgresql) SelectMany(s store, models *Model, query Query) error { - return genericSelectMany(s, models, query) +func (p *postgresql) SelectMany(c *Connection, models *Model, query Query) error { + return genericSelectMany(c, models, query) } func (p *postgresql) CreateDB() error { diff --git a/dialect_sqlite.go b/dialect_sqlite.go index 6f38fbd6..54b92d89 100644 --- a/dialect_sqlite.go +++ b/dialect_sqlite.go @@ -70,7 +70,7 @@ func (m *sqlite) MigrationURL() string { return m.ConnectionDetails.URL } -func (m *sqlite) Create(s store, model *Model, cols columns.Columns) error { +func (m *sqlite) Create(c *Connection, model *Model, cols columns.Columns) error { return m.locker(m.smGil, func() error { keyType, err := model.PrimaryKeyType() if err != nil { @@ -87,8 +87,8 @@ func (m *sqlite) Create(s store, model *Model, cols columns.Columns) error { } else { query = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", m.Quote(model.TableName())) } - txlog(logging.SQL, s, query, model.Value) - res, err := s.NamedExec(query, model.Value) + txlog(logging.SQL, c, query, model.Value) + res, err := c.Store.NamedExec(query, model.Value) if err != nil { return err } @@ -101,26 +101,26 @@ func (m *sqlite) Create(s store, model *Model, cols columns.Columns) error { } return nil } - if err := genericCreate(s, model, cols, m); err != nil { + if err := genericCreate(c, model, cols, m); err != nil { return fmt.Errorf("sqlite create: %w", err) } return nil }) } -func (m *sqlite) Update(s store, model *Model, cols columns.Columns) error { +func (m *sqlite) Update(c *Connection, model *Model, cols columns.Columns) error { return m.locker(m.smGil, func() error { - if err := genericUpdate(s, model, cols, m); err != nil { + if err := genericUpdate(c, model, cols, m); err != nil { return fmt.Errorf("sqlite update: %w", err) } return nil }) } -func (m *sqlite) UpdateQuery(s store, model *Model, cols columns.Columns, query Query) (int64, error) { +func (m *sqlite) UpdateQuery(c *Connection, model *Model, cols columns.Columns, query Query) (int64, error) { rowsAffected := int64(0) err := m.locker(m.smGil, func() error { - if n, err := genericUpdateQuery(s, model, cols, m, query, sqlx.QUESTION); err != nil { + if n, err := genericUpdateQuery(c, model, cols, m, query, sqlx.QUESTION); err != nil { rowsAffected = n return fmt.Errorf("sqlite update query: %w", err) } else { @@ -131,31 +131,31 @@ func (m *sqlite) UpdateQuery(s store, model *Model, cols columns.Columns, query return rowsAffected, err } -func (m *sqlite) Destroy(s store, model *Model) error { +func (m *sqlite) Destroy(c *Connection, model *Model) error { return m.locker(m.smGil, func() error { - if err := genericDestroy(s, model, m); err != nil { + if err := genericDestroy(c, model, m); err != nil { return fmt.Errorf("sqlite destroy: %w", err) } return nil }) } -func (m *sqlite) Delete(s store, model *Model, query Query) error { - return genericDelete(s, model, query) +func (m *sqlite) Delete(c *Connection, model *Model, query Query) error { + return genericDelete(c, model, query) } -func (m *sqlite) SelectOne(s store, model *Model, query Query) error { +func (m *sqlite) SelectOne(c *Connection, model *Model, query Query) error { return m.locker(m.smGil, func() error { - if err := genericSelectOne(s, model, query); err != nil { + if err := genericSelectOne(c, model, query); err != nil { return fmt.Errorf("sqlite select one: %w", err) } return nil }) } -func (m *sqlite) SelectMany(s store, models *Model, query Query) error { +func (m *sqlite) SelectMany(c *Connection, models *Model, query Query) error { return m.locker(m.smGil, func() error { - if err := genericSelectMany(s, models, query); err != nil { + if err := genericSelectMany(c, models, query); err != nil { return fmt.Errorf("sqlite select many: %w", err) } return nil diff --git a/executors.go b/executors.go index df165ed3..501593ab 100644 --- a/executors.go +++ b/executors.go @@ -255,7 +255,7 @@ func (c *Connection) Create(model interface{}, excludeColumns ...string) error { m.setUpdatedAt(now) m.setCreatedAt(now) - if err = c.Dialect.Create(c.Store, m, cols); err != nil { + if err = c.Dialect.Create(c, m, cols); err != nil { return err } @@ -372,7 +372,7 @@ func (c *Connection) Update(model interface{}, excludeColumns ...string) error { now := nowFunc().Truncate(time.Microsecond) m.setUpdatedAt(now) - if err = c.Dialect.Update(c.Store, m, cols); err != nil { + if err = c.Dialect.Update(c, m, cols); err != nil { return err } if err = m.afterUpdate(c); err != nil { @@ -410,7 +410,7 @@ func (q *Query) UpdateQuery(model interface{}, columnNames ...string) (int64, er now := nowFunc().Truncate(time.Microsecond) sm.setUpdatedAt(now) - return q.Connection.Dialect.UpdateQuery(q.Connection.Store, sm, cols, *q) + return q.Connection.Dialect.UpdateQuery(q.Connection, sm, cols, *q) } // UpdateColumns writes changes from an entry to the database, including only the given columns @@ -446,7 +446,7 @@ func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) err now := nowFunc().Truncate(time.Microsecond) m.setUpdatedAt(now) - if err = c.Dialect.Update(c.Store, m, cols); err != nil { + if err = c.Dialect.Update(c, m, cols); err != nil { return err } if err = m.afterUpdate(c); err != nil { @@ -470,7 +470,7 @@ func (c *Connection) Destroy(model interface{}) error { if err = m.beforeDestroy(c); err != nil { return err } - if err = c.Dialect.Destroy(c.Store, m); err != nil { + if err = c.Dialect.Destroy(c, m); err != nil { return err } @@ -484,7 +484,7 @@ func (q *Query) Delete(model interface{}) error { return q.Connection.timeFunc("Delete", func() error { m := NewModel(model, q.Connection.Context()) - err := q.Connection.Dialect.Delete(q.Connection.Store, m, *q) + err := q.Connection.Dialect.Delete(q.Connection, m, *q) if err != nil { return err } diff --git a/finders.go b/finders.go index 81ebb746..2afa0b47 100644 --- a/finders.go +++ b/finders.go @@ -69,7 +69,7 @@ func (q *Query) First(model interface{}) error { err := q.Connection.timeFunc("First", func() error { q.Limit(1) m := NewModel(model, q.Connection.Context()) - if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil { + if err := q.Connection.Dialect.SelectOne(q.Connection, m, *q); err != nil { return err } return m.afterFind(q.Connection) @@ -102,7 +102,7 @@ func (q *Query) Last(model interface{}) error { q.Limit(1) q.Order("created_at DESC, id DESC") m := NewModel(model, q.Connection.Context()) - if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil { + if err := q.Connection.Dialect.SelectOne(q.Connection, m, *q); err != nil { return err } return m.afterFind(q.Connection) @@ -134,7 +134,7 @@ func (c *Connection) All(models interface{}) error { func (q *Query) All(models interface{}) error { err := q.Connection.timeFunc("All", func() error { m := NewModel(models, q.Connection.Context()) - err := q.Connection.Dialect.SelectMany(q.Connection.Store, m, *q) + err := q.Connection.Dialect.SelectMany(q.Connection, m, *q) if err != nil { return err } diff --git a/logger.go b/logger.go index 8a24224e..98dbe18f 100644 --- a/logger.go +++ b/logger.go @@ -50,30 +50,32 @@ var txlog = func(lvl logging.Level, anon interface{}, s string, args ...interfac } else { s = fmt.Sprintf("%s - %s", lvl, s) } - } else { - s = fmt.Sprintf(s, args...) - s = fmt.Sprintf("%s - %s", lvl, s) - } - connID := "" - txID := 0 - switch typed := anon.(type) { - case *Connection: - connID = typed.ID - if typed.TX != nil { - txID = typed.TX.ID - } - case *Tx: - txID = typed.ID - case store: - tx, err := typed.Transaction() - if err == nil { - txID = tx.ID + connID := "" + txID := 0 + extra := "" + switch typed := anon.(type) { + case *Connection: + connID = typed.ID + if typed.TX != nil { + txID = typed.TX.ID + } + + extra = printStats(&typed.Store) + case *Tx: + txID = typed.ID + case store: + if t, ok := typed.(*Tx); ok { + txID = t.ID + } + + extra = printStats(&typed) } - } - if connID != "" || txID != 0 { - s = fmt.Sprintf("%s (conn=%v, tx=%v)", s, connID, txID) + s = fmt.Sprintf("%s (conn=%v, tx=%v%v)", s, connID, txID, extra) + } else { + s = fmt.Sprintf(s, args...) + s = fmt.Sprintf("%s - %s", lvl, s) } if Color { @@ -82,3 +84,14 @@ var txlog = func(lvl logging.Level, anon interface{}, s string, args ...interfac defaultStdLogger.Println(s) } + +// printStats returns a string represent connection pool information from +// the given store. +func printStats(s *store) string { + if db, ok := (*s).(*dB); ok { + s := db.Stats() + return fmt.Sprintf(", maxconn: %d, openconn: %d, in-use: %d, idle: %d", s.MaxOpenConnections, s.OpenConnections, s.InUse, s.Idle) + } + + return "" +}