From 9b85b8ee6a2e00e833dd3a74c92a018320998f13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net>
Date: Thu, 7 Aug 2014 11:42:30 -0500
Subject: [PATCH] SQLite: Adding support for db.Result.Group().

---
 sqlite/_dumps/structs.sql |  8 ++++++
 sqlite/database.go        |  1 +
 sqlite/database_test.go   | 56 +++++++++++++++++++++++++++++++++++++++
 sqlite/layout.go          |  8 ++++++
 sqlite/result.go          | 22 +++++++++++++++
 5 files changed, 95 insertions(+)

diff --git a/sqlite/_dumps/structs.sql b/sqlite/_dumps/structs.sql
index 20778822..8d586592 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 59e7523a..96153241 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 438f81aa..57a527e1 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 bb750efe..9aba6427 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 79f14063..8e50383a 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.
-- 
GitLab