Skip to content

Commit 32e1abf

Browse files
author
Miguel Molina
authored
Merge pull request #144 from erizocosmico/feature/sql-debug
add methods to enable debug of SQL statements
2 parents 671d20a + 1371110 commit 32e1abf

File tree

5 files changed

+306
-1
lines changed

5 files changed

+306
-1
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Support for arrays of all basic Go types and all JSON and arrays operators is pr
3434
* [Query with relationships](#query-with-relationships)
3535
* [Querying JSON](#querying-json)
3636
* [Transactions](#transactions)
37+
* [Debug SQL queries](#debug-sql-queries)
3738
* [Benchmarks](#benchmarks)
3839
* [Acknowledgements](#acknowledgements)
3940
* [Contributing](#contributing)
@@ -602,6 +603,26 @@ store.Transaction(func(s *UserStore) error {
602603
* `time.Time` and `url.URL` need to be used as is. That is, you can not use a type `Foo` being `type Foo time.Time`. `time.Time` and `url.URL` are types that are treated in a special way, if you do that, it would be the same as saying `type Foo struct { ... }` and kallax would no longer be able to identify the correct type.
603604
* Multidimensional arrays or slices are **not supported** except inside a JSON field.
604605

606+
## Debug SQL queries
607+
608+
It is possible to debug the SQL queries being executed with kallax. To do that, you just need to call the `Debug` method of a store. This returns a new store with debugging enabled.
609+
610+
```go
611+
store.Debug().Find(myQuery)
612+
```
613+
614+
This will log to stdout using `log.Printf` `kallax: Query: THE QUERY SQL STATEMENT, args: [arg1 arg2]`.
615+
616+
You can use a custom logger (any function with a type `func(string, ...interface{})` using the `DebugWith` method instead.
617+
618+
```go
619+
func myLogger(message string, args ...interface{}) {
620+
myloglib.Debugf("%s, args: %v", message, args)
621+
}
622+
623+
store.DebugWith(myLogger).Find(myQuery)
624+
```
625+
605626
## Benchmarks
606627

607628
Here are some benchmarks against [GORM](https://github.com/jinzhu/gorm) and `database/sql`, which is one of the most popular ORMs for Go. In the future we might add benchmarks for some more complex cases and other available ORMs.

generator/templates/model.tgo

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ func (s *{{.StoreName}}) SetGenericStore(store *kallax.Store) {
106106
s.Store = store
107107
}
108108

109+
// Debug returns a new store that will print all SQL statements to stdout using
110+
// the log.Printf function.
111+
func (s *{{.StoreName}}) Debug() *{{.StoreName}} {
112+
return &{{.StoreName}}{s.Store.Debug()}
113+
}
114+
115+
// DebugWith returns a new store that will print all SQL statements using the
116+
// given logger function.
117+
func (s *{{.StoreName}}) DebugWith(logger kallax.LoggerFunc) *{{.StoreName}} {
118+
return &{{.StoreName}}{s.Store.DebugWith(logger)}
119+
}
120+
109121
{{if .HasNonInverses}}
110122
func (s *{{.StoreName}}) relationshipRecords(record *{{.Name}}) []kallax.RecordWithSchema {
111123
var records []kallax.RecordWithSchema

store.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"database/sql"
55
"errors"
66
"fmt"
7+
"log"
78

89
"github.com/Masterminds/squirrel"
910
"github.com/lann/builder"
@@ -51,6 +52,40 @@ func StoreFrom(to, from GenericStorer) {
5152
to.SetGenericStore(from.GenericStore())
5253
}
5354

55+
// LoggerFunc is a function that takes a log message with some arguments and
56+
// logs it.
57+
type LoggerFunc func(string, ...interface{})
58+
59+
// debugProxy is a database proxy that logs all SQL statements executed.
60+
type debugProxy struct {
61+
logger LoggerFunc
62+
proxy squirrel.DBProxy
63+
}
64+
65+
func defaultLogger(message string, args ...interface{}) {
66+
log.Printf("%s, args: %v", message, args)
67+
}
68+
69+
func (p *debugProxy) Exec(query string, args ...interface{}) (sql.Result, error) {
70+
p.logger(fmt.Sprintf("kallax: Exec: %s", query), args...)
71+
return p.proxy.Exec(query, args...)
72+
}
73+
74+
func (p *debugProxy) Query(query string, args ...interface{}) (*sql.Rows, error) {
75+
p.logger(fmt.Sprintf("kallax: Query: %s", query), args...)
76+
return p.proxy.Query(query, args...)
77+
}
78+
79+
func (p *debugProxy) QueryRow(query string, args ...interface{}) squirrel.RowScanner {
80+
p.logger(fmt.Sprintf("kallax: QueryRow: %s", query), args...)
81+
return p.proxy.QueryRow(query, args...)
82+
}
83+
84+
func (p *debugProxy) Prepare(query string) (*sql.Stmt, error) {
85+
p.logger(fmt.Sprintf("kallax: Prepare: %s", query))
86+
return p.proxy.Prepare(query)
87+
}
88+
5489
// Store is a structure capable of retrieving records from a concrete table in
5590
// the database.
5691
type Store struct {
@@ -79,6 +114,22 @@ func newStoreWithTransaction(tx *sql.Tx) *Store {
79114
}
80115
}
81116

117+
// Debug returns a new store that will print all SQL statements to stdout using
118+
// the log.Printf function.
119+
func (s *Store) Debug() *Store {
120+
return s.DebugWith(defaultLogger)
121+
}
122+
123+
// DebugWith returns a new store that will print all SQL statements using the
124+
// given logger function.
125+
func (s *Store) DebugWith(logger LoggerFunc) *Store {
126+
return &Store{
127+
builder: s.builder,
128+
db: s.db,
129+
proxy: &debugProxy{logger, s.proxy},
130+
}
131+
}
132+
82133
// Insert insert the given record in the table, returns error if no-new
83134
// record is given. The record id is set if it's empty.
84135
func (s *Store) Insert(schema Schema, record Record) error {

store_test.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,27 @@ func (s *StoreSuite) TestMustFind() {
236236
})
237237

238238
s.Panics(func() {
239-
s.errStore.MustFind(q)
239+
s.errStore.Debug().MustFind(q)
240240
})
241241
}
242242

243+
func (s *StoreSuite) TestDebugWith() {
244+
var queries []string
245+
var logger = func(q string, args ...interface{}) {
246+
queries = append(queries, q)
247+
}
248+
s.store.DebugWith(logger).RawQuery("SELECT 1 + 1")
249+
s.store.DebugWith(logger).RawExec("UPDATE foo SET bar = 1")
250+
251+
s.Equal(
252+
queries,
253+
[]string{
254+
"kallax: Query: SELECT 1 + 1",
255+
"kallax: Exec: UPDATE foo SET bar = 1",
256+
},
257+
)
258+
}
259+
243260
func (s *StoreSuite) assertFound(rs ResultSet, expected ...string) {
244261
var names []string
245262
for rs.Next() {

0 commit comments

Comments
 (0)