From 6f4136878a9c71b78045e8bfb8919529642353dc Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Mon, 12 Dec 2022 01:30:13 +0900 Subject: [PATCH 1/6] made to pass connection instead of copying store --- database.yml | 2 ++ dialect.go | 14 +++++++------- dialect_cockroach.go | 32 ++++++++++++++++---------------- dialect_common.go | 42 +++++++++++++++++++++--------------------- dialect_mysql.go | 28 ++++++++++++++-------------- dialect_postgresql.go | 32 ++++++++++++++++---------------- dialect_sqlite.go | 32 ++++++++++++++++---------------- executors.go | 12 ++++++------ finders.go | 6 +++--- 9 files changed, 101 insertions(+), 99 deletions(-) diff --git a/database.yml b/database.yml index d8c1567e8..75edb7e19 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 0e49b168a..013ab92a4 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 041c308ae..80bd6d5ef 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 867bdb6bd..50cb1caa9 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 5ab26487f..0a233889a 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 912195e41..25fcdcdaf 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 6f38fbd6f..54b92d898 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 df165ed3b..501593ab1 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 81ebb7462..2afa0b474 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 } From fe5ea37fabd54537c99cf042c59d11a89a84e939 Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Fri, 16 Dec 2022 22:17:40 +0900 Subject: [PATCH 2/6] fixed issue of unwanted creation of transaction bug and clean up --- logger.go | 55 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/logger.go b/logger.go index 8a24224ed..98dbe18fe 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 "" +} From 35967190380a343ea0e588a7bd94e14d11307a92 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Mon, 2 Jan 2023 16:39:39 +0100 Subject: [PATCH 3/6] Add `AfterEagerFind()` See https://github.com/gobuffalo/pop/issues/557 Closes https://github.com/gobuffalo/pop/issues/476 Signed-off-by: aeneasr <3372410+aeneasr@users.noreply.github.com> --- callbacks.go | 22 +++++++++-- callbacks_test.go | 13 ++++++- finders.go | 37 +++++++++++++------ pop_test.go | 6 +++ .../20181104135800_callbacks_users.up.fizz | 1 + 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/callbacks.go b/callbacks.go index b841df642..81c86c7f9 100644 --- a/callbacks.go +++ b/callbacks.go @@ -12,12 +12,24 @@ type AfterFindable interface { AfterFind(*Connection) error } -func (m *Model) afterFind(c *Connection) error { - if x, ok := m.Value.(AfterFindable); ok { +// AfterEagerFindable callback will be called after a record, or records, +// has been retrieved from the database and their associations have been +// eagerly loaded. +type AfterEagerFindable interface { + AfterEagerFind(*Connection) error +} + +func (m *Model) afterFind(c *Connection, eager bool) error { + if x, ok := m.Value.(AfterFindable); ok && !eager { if err := x.AfterFind(c); err != nil { return err } } + if x, ok := m.Value.(AfterEagerFindable); ok && eager { + if err := x.AfterEagerFind(c); err != nil { + return err + } + } // if the "model" is a slice/array we want // to loop through each of the elements in the collection @@ -34,9 +46,13 @@ func (m *Model) afterFind(c *Connection) error { wg.Go(func() error { y := rv.Index(i) y = y.Addr() - if x, ok := y.Interface().(AfterFindable); ok { + if x, ok := y.Interface().(AfterFindable); ok && !eager { return x.AfterFind(c) } + + if x, ok := y.Interface().(AfterEagerFindable); ok && eager { + return x.AfterEagerFind(c) + } return nil }) }(i) diff --git a/callbacks_test.go b/callbacks_test.go index 02ecd4072..ac527c9a4 100644 --- a/callbacks_test.go +++ b/callbacks_test.go @@ -45,6 +45,10 @@ func Test_Callbacks(t *testing.T) { r.Equal("AF", user.AfterF) r.NoError(tx.Find(user, user.ID)) r.Equal("AfterFind", user.AfterF) + r.Empty(user.AfterEF) + + r.NoError(tx.Eager().Find(user, user.ID)) + r.Equal("AfterEagerFind", user.AfterEF) r.NoError(tx.Destroy(user)) @@ -70,11 +74,16 @@ func Test_Callbacks_on_Slice(t *testing.T) { users := CallbacksUsers{} r.NoError(tx.All(&users)) - r.Len(users, 2) - for _, u := range users { r.Equal("AfterFind", u.AfterF) + r.Empty(u.AfterEF) + } + + r.NoError(tx.Eager().All(&users)) + r.Len(users, 2) + for _, u := range users { + r.Equal("AfterEagerFind", u.AfterEF) } }) } diff --git a/finders.go b/finders.go index 2afa0b474..d59cc9e85 100644 --- a/finders.go +++ b/finders.go @@ -66,13 +66,14 @@ func (c *Connection) First(model interface{}) error { // // q.Where("name = ?", "mark").First(&User{}) func (q *Query) First(model interface{}) error { + var m *Model err := q.Connection.timeFunc("First", func() error { q.Limit(1) - m := NewModel(model, q.Connection.Context()) + m = NewModel(model, q.Connection.Context()) if err := q.Connection.Dialect.SelectOne(q.Connection, m, *q); err != nil { return err } - return m.afterFind(q.Connection) + return m.afterFind(q.Connection, false) }) if err != nil { @@ -80,10 +81,14 @@ func (q *Query) First(model interface{}) error { } if q.eager { - err = q.eagerAssociations(model) + err := q.eagerAssociations(model) q.disableEager() - return err + if err != nil { + return err + } + return m.afterFind(q.Connection, true) } + return nil } @@ -98,14 +103,15 @@ func (c *Connection) Last(model interface{}) error { // // q.Where("name = ?", "mark").Last(&User{}) func (q *Query) Last(model interface{}) error { + var m *Model err := q.Connection.timeFunc("Last", func() error { q.Limit(1) q.Order("created_at DESC, id DESC") - m := NewModel(model, q.Connection.Context()) + m = NewModel(model, q.Connection.Context()) if err := q.Connection.Dialect.SelectOne(q.Connection, m, *q); err != nil { return err } - return m.afterFind(q.Connection) + return m.afterFind(q.Connection, false) }) if err != nil { @@ -115,7 +121,10 @@ func (q *Query) Last(model interface{}) error { if q.eager { err = q.eagerAssociations(model) q.disableEager() - return err + if err != nil { + return err + } + return m.afterFind(q.Connection, true) } return nil @@ -132,17 +141,20 @@ func (c *Connection) All(models interface{}) error { // // q.Where("name = ?", "mark").All(&[]User{}) func (q *Query) All(models interface{}) error { + var m *Model err := q.Connection.timeFunc("All", func() error { - m := NewModel(models, q.Connection.Context()) + m = NewModel(models, q.Connection.Context()) err := q.Connection.Dialect.SelectMany(q.Connection, m, *q) if err != nil { return err } + err = q.paginateModel(models) if err != nil { return err } - return m.afterFind(q.Connection) + + return m.afterFind(q.Connection, false) }) if err != nil { @@ -152,7 +164,10 @@ func (q *Query) All(models interface{}) error { if q.eager { err = q.eagerAssociations(models) q.disableEager() - return err + if err != nil { + return err + } + return m.afterFind(q.Connection, true) } return nil @@ -301,7 +316,7 @@ func (q *Query) eagerDefaultAssociations(model interface{}) error { // Exists returns true/false if a record exists in the database that matches // the query. // -// q.Where("name = ?", "mark").Exists(&User{}) +// q.Where("name = ?", "mark").Exists(&User{}) func (q *Query) Exists(model interface{}) (bool, error) { tmpQuery := Q(q.Connection) q.Clone(tmpQuery) // avoid meddling with original query diff --git a/pop_test.go b/pop_test.go index 95fd8648d..1382c39aa 100644 --- a/pop_test.go +++ b/pop_test.go @@ -364,6 +364,7 @@ type CallbacksUser struct { AfterU string `db:"after_u"` AfterD string `db:"after_d"` AfterF string `db:"after_f"` + AfterEF string `db:"after_ef"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } @@ -420,6 +421,11 @@ func (u *CallbacksUser) AfterFind(tx *Connection) error { return nil } +func (u *CallbacksUser) AfterEagerFind(tx *Connection) error { + u.AfterEF = "AfterEagerFind" + return nil +} + type Label struct { ID string `db:"id"` } diff --git a/testdata/migrations/20181104135800_callbacks_users.up.fizz b/testdata/migrations/20181104135800_callbacks_users.up.fizz index bc07aba90..b0e5ae8fd 100644 --- a/testdata/migrations/20181104135800_callbacks_users.up.fizz +++ b/testdata/migrations/20181104135800_callbacks_users.up.fizz @@ -9,6 +9,7 @@ create_table("callbacks_users") { t.Column("after_u", "string", {}) t.Column("after_d", "string", {}) t.Column("after_f", "string", {}) + t.Column("after_ef", "string", {}) t.Column("before_v", "string", {}) t.Timestamps() } \ No newline at end of file From 1082c83ddaa2920b3759bd78efef716f96620b23 Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Fri, 13 Jan 2023 23:42:56 +0900 Subject: [PATCH 4/6] readability: more if depths, but show exclusive blocks explicitly --- callbacks.go | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/callbacks.go b/callbacks.go index 81c86c7f9..7b443a3a8 100644 --- a/callbacks.go +++ b/callbacks.go @@ -20,14 +20,17 @@ type AfterEagerFindable interface { } func (m *Model) afterFind(c *Connection, eager bool) error { - if x, ok := m.Value.(AfterFindable); ok && !eager { - if err := x.AfterFind(c); err != nil { - return err + if eager { + if x, ok := m.Value.(AfterEagerFindable); ok { + if err := x.AfterEagerFind(c); err != nil { + return err + } } - } - if x, ok := m.Value.(AfterEagerFindable); ok && eager { - if err := x.AfterEagerFind(c); err != nil { - return err + } else { + if x, ok := m.Value.(AfterFindable); ok { + if err := x.AfterFind(c); err != nil { + return err + } } } @@ -46,13 +49,17 @@ func (m *Model) afterFind(c *Connection, eager bool) error { wg.Go(func() error { y := rv.Index(i) y = y.Addr() - if x, ok := y.Interface().(AfterFindable); ok && !eager { - return x.AfterFind(c) - } - if x, ok := y.Interface().(AfterEagerFindable); ok && eager { - return x.AfterEagerFind(c) + if eager { + if x, ok := y.Interface().(AfterEagerFindable); ok { + return x.AfterEagerFind(c) + } + } else { + if x, ok := y.Interface().(AfterFindable); ok { + return x.AfterFind(c) + } } + return nil }) }(i) From 89774fa47021f0717e6e5d09e719f27d27fc1ee1 Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Fri, 13 Jan 2023 23:58:13 +0900 Subject: [PATCH 5/6] limit invoking go routines when only the model is AfterFindable (fixes #799) --- callbacks.go | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/callbacks.go b/callbacks.go index 7b443a3a8..8173ebafc 100644 --- a/callbacks.go +++ b/callbacks.go @@ -45,24 +45,21 @@ func (m *Model) afterFind(c *Connection, eager bool) error { wg := &errgroup.Group{} for i := 0; i < rv.Len(); i++ { - func(i int) { - wg.Go(func() error { - y := rv.Index(i) - y = y.Addr() - - if eager { - if x, ok := y.Interface().(AfterEagerFindable); ok { - return x.AfterEagerFind(c) - } - } else { - if x, ok := y.Interface().(AfterFindable); ok { - return x.AfterFind(c) - } - } - - return nil - }) - }(i) + elem := rv.Index(i).Addr() + + if eager { + if x, ok := elem.Interface().(AfterEagerFindable); ok { + wg.Go(func() error { + return x.AfterEagerFind(c) + }) + } + } else { + if x, ok := elem.Interface().(AfterFindable); ok { + wg.Go(func() error { + return x.AfterFind(c) + }) + } + } } return wg.Wait() From fbb24cf549d29625b23e88c4d7f4a4ab0e95e44d Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Wed, 18 Jan 2023 21:52:20 +0900 Subject: [PATCH 6/6] versoin bump --- go.mod | 6 +++--- go.sum | 9 ++++++--- soda/cmd/version.go | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 72e1bb89f..bcdfe51f6 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,15 @@ go 1.16 require ( github.com/fatih/color v1.13.0 - github.com/go-sql-driver/mysql v1.6.0 + github.com/go-sql-driver/mysql v1.7.0 github.com/gobuffalo/attrs v1.0.3 github.com/gobuffalo/envy v1.10.2 github.com/gobuffalo/fizz v1.14.4 - github.com/gobuffalo/flect v0.3.0 + github.com/gobuffalo/flect v1.0.0 github.com/gobuffalo/genny/v2 v2.1.0 github.com/gobuffalo/logger v1.0.7 github.com/gobuffalo/nulls v0.4.2 - github.com/gobuffalo/plush/v4 v4.1.16 + github.com/gobuffalo/plush/v4 v4.1.18 github.com/gobuffalo/validate/v3 v3.3.3 github.com/gofrs/uuid v4.3.1+incompatible github.com/jackc/pgconn v1.13.0 diff --git a/go.sum b/go.sum index 0c302a63c..771e506dc 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,9 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 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.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/attrs v1.0.3 h1:LPXvtWcVpt6KJh31cK4MDyWlgehutmHpvjaWvHDRI4o= github.com/gobuffalo/attrs v1.0.3/go.mod h1:KvDJCE0avbufqS0Bw3UV7RQynESY0jjod+572ctX4t8= @@ -27,8 +28,9 @@ github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4 github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= github.com/gobuffalo/fizz v1.14.4 h1:8uume7joF6niTNWN582IQ2jhGTUoa9g1fiV/tIoGdBs= github.com/gobuffalo/fizz v1.14.4/go.mod h1:9/2fGNXNeIFOXEEgTPJwiK63e44RjG+Nc4hfMm1ArGM= -github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk= github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE= +github.com/gobuffalo/flect v1.0.0 h1:eBFmskjXZgAOagiTXJH25Nt5sdFwNRcb8DKZsIsAUQI= +github.com/gobuffalo/flect v1.0.0/go.mod h1:l9V6xSb4BlXwsxEMj3FVEub2nkdQjWhPvD8XTTlHPQc= github.com/gobuffalo/genny/v2 v2.1.0 h1:cCRBbqzo3GfNvj3UetD16zRgUvWFEyyl0qTqquuIqOM= github.com/gobuffalo/genny/v2 v2.1.0/go.mod h1:4yoTNk4bYuP3BMM6uQKYPvtP6WsXFGm2w2EFYZdRls8= github.com/gobuffalo/github_flavored_markdown v1.1.3 h1:rSMPtx9ePkFB22vJ+dH+m/EUBS8doQ3S8LeEXcdwZHk= @@ -41,8 +43,9 @@ github.com/gobuffalo/nulls v0.4.2 h1:GAqBR29R3oPY+WCC7JL9KKk9erchaNuV6unsOSZGQkw github.com/gobuffalo/nulls v0.4.2/go.mod h1:EElw2zmBYafU2R9W4Ii1ByIj177wA/pc0JdjtD0EsH8= github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= -github.com/gobuffalo/plush/v4 v4.1.16 h1:Y6jVVTLdg1BxRXDIbTJz+J8QRzEAtv5ZwYpGdIFR7VU= github.com/gobuffalo/plush/v4 v4.1.16/go.mod h1:6t7swVsarJ8qSLw1qyAH/KbrcSTwdun2ASEQkOznakg= +github.com/gobuffalo/plush/v4 v4.1.18 h1:bnPjdMTEUQHqj9TNX2Ck3mxEXYZa+0nrFMNM07kpX9g= +github.com/gobuffalo/plush/v4 v4.1.18/go.mod h1:xi2tJIhFI4UdzIL8sxZtzGYOd2xbBpcFbLZlIPGGZhU= github.com/gobuffalo/tags/v3 v3.1.4 h1:X/ydLLPhgXV4h04Hp2xlbI2oc5MDaa7eub6zw8oHjsM= github.com/gobuffalo/tags/v3 v3.1.4/go.mod h1:ArRNo3ErlHO8BtdA0REaZxijuWnWzF6PUXngmMXd2I0= github.com/gobuffalo/validate/v3 v3.3.3 h1:o7wkIGSvZBYBd6ChQoLxkz2y1pfmhbI4jNJYh6PuNJ4= diff --git a/soda/cmd/version.go b/soda/cmd/version.go index f0cca9f10..0f91d587f 100644 --- a/soda/cmd/version.go +++ b/soda/cmd/version.go @@ -2,4 +2,4 @@ package cmd // Version defines the current Pop version. // this version will also be used by soda(also buffalo-pop) -const Version = "v6.0.7" +const Version = "v6.1.1"