diff --git a/sqlite/_dumps/structs.sql b/sqlite/_dumps/structs.sql index 207788225b2d8c7084a5e5c408c7846f68702096..8d586592d159ebc9e2f9bcd1913f5b049148ee2f 100644 --- a/sqlite/_dumps/structs.sql +++ b/sqlite/_dumps/structs.sql @@ -52,4 +52,12 @@ CREATE TABLE data_types ( _time text ); +DROP TABLE IF EXISTS stats_test; + +CREATE TABLE stats_test ( + id integer primary key, + numeric integer, + value integer +); + COMMIT; diff --git a/sqlite/database.go b/sqlite/database.go index 59e7523af8b8a9d0dfa573a462d81dc2fca0d3e3..961532413648d8bc5005078a53da0391823f6058 100644 --- a/sqlite/database.go +++ b/sqlite/database.go @@ -105,6 +105,7 @@ func init() { sqlDropDatabaseLayout, sqlDropTableLayout, sqlSelectCountLayout, + sqlGroupByLayout, } db.Register(Adapter, &source{}) diff --git a/sqlite/database_test.go b/sqlite/database_test.go index 438f81aa0d174216adeb2b24666ef6733b41ab6b..57a527e180aca74a75ebcd492199148f1dd4d185 100644 --- a/sqlite/database_test.go +++ b/sqlite/database_test.go @@ -30,6 +30,7 @@ package sqlite import ( "database/sql" + "math/rand" "os" "reflect" "strings" @@ -309,6 +310,61 @@ func TestNullableFields(t *testing.T) { } +func TestGroup(t *testing.T) { + + var err error + var sess db.Database + var stats db.Collection + + if sess, err = db.Open(Adapter, settings); err != nil { + t.Fatal(err) + } + + type stats_t struct { + Numeric int `db:"numeric"` + Value int `db:"value"` + } + + defer sess.Close() + + if stats, err = sess.Collection("stats_test"); err != nil { + t.Fatal(err) + } + + // Truncating table. + if err = stats.Truncate(); err != nil { + t.Fatal(err) + } + + // Adding row append. + for i := 0; i < 1000; i++ { + numeric, value := rand.Intn(10), rand.Intn(100) + if _, err = stats.Append(stats_t{numeric, value}); err != nil { + t.Fatal(err) + } + } + + // db.Func{"COUNT", 1}, + // db.Func{"SUM", `value`}, + + // Testing GROUP BY + res := stats.Find().Select( + `numeric`, + db.Raw{`COUNT(1) AS counter`}, + db.Raw{`SUM(value) AS total`}, + ).Group(`numeric`) + + var results []map[string]interface{} + + if err = res.All(&results); err != nil { + t.Fatal(err) + } + + if len(results) != 10 { + t.Fatalf(`Expecting exactly 10 results, this could fail, but it's very unlikely to happen.`) + } +} + // Attempts to count all rows in our newly defined set. func TestResultCount(t *testing.T) { var err error diff --git a/sqlite/layout.go b/sqlite/layout.go index bb750efef05196a716a419b7017f8ccc1686010c..9aba64270f092afb5e14be938da32325124d0b95 100644 --- a/sqlite/layout.go +++ b/sqlite/layout.go @@ -65,6 +65,8 @@ const ( {{.Where}} + {{.GroupBy}} + {{.OrderBy}} {{if .Limit}} @@ -122,5 +124,11 @@ const ( DROP TABLE {{.Table}} ` + sqlGroupByLayout = ` + {{if .GroupColumns}} + GROUP BY {{.GroupColumns}} + {{end}} + ` + sqlNull = `NULL` ) diff --git a/sqlite/result.go b/sqlite/result.go index 79f14063fa0560456b65bb450e79d9eedbf793d1..8e50383af968d29dc3c0e8197d7539821ba511a6 100644 --- a/sqlite/result.go +++ b/sqlite/result.go @@ -43,6 +43,7 @@ type result struct { columns sqlgen.Columns where sqlgen.Where orderBy sqlgen.OrderBy + groupBy sqlgen.GroupBy arguments []interface{} } @@ -59,6 +60,7 @@ func (self *result) setCursor() error { Offset: self.offset, Where: self.where, OrderBy: self.orderBy, + GroupBy: self.groupBy, }, self.arguments...) } return err @@ -83,6 +85,26 @@ func (self *result) Skip(n uint) db.Result { return self } +// Used to group results that have the same value in the same column or +// columns. +func (self *result) Group(fields ...interface{}) db.Result { + + groupByColumns := make(sqlgen.GroupBy, 0, len(fields)) + + l := len(fields) + for i := 0; i < l; i++ { + switch value := fields[i].(type) { + // Maybe other types? + default: + groupByColumns = append(groupByColumns, sqlgen.Column{value}) + } + } + + self.groupBy = groupByColumns + + return self +} + // Determines sorting of results according to the provided names. Fields may be // prefixed by - (minus) which means descending order, ascending order would be // used otherwise.