Repository: genjidb/genji (go)

Issue:
COUNT(*) with empty set returns nothing
For example, using genji CLI you can reproduce this behavior in the following way:

```sql
CREATE TABLE foo;
SELECT COUNT(*) FROM foo;
```
And it returns literally nothing.

Contrary, if you insert something in table, then you will get proper count of items:

```sql
CREATE TABLE bar;
INSERT INTO bar VALUES {"bar":true};
SELECT COUNT(*) FROM bar;
```

Prints:

```json
{
  "COUNT(*)": 1
}
```

I expect that `SELECT COUNT(*)` for `foo` should return:

```json
{
  "COUNT(*)": 0
}
```

UPD:

I digged code a little bit. The reason (as I understand) is how the aggregates created in document/iterator.go:285.
If there are no documents in stream then no groups witll be created and ther for loop on document/iterator.go:321 will be just passed --- so no field from aggregates will be added to the result.


Pull request:
Fix aggregation result for empty input
Fixes #284 

Patch:
diff --git a/document/iterator.go b/document/iterator.go
index 13dba74f..c1c9cd1f 100644
--- a/document/iterator.go
+++ b/document/iterator.go
@@ -291,6 +291,18 @@ func (s Stream) Aggregate(aggregatorBuilders ...AggregatorBuilder) Stream {
 		var b bytes.Buffer
 
 		enc := NewValueEncoder(&b)
+
+		mkGroup := func(g Value) []Aggregator {
+			groupKey := b.String()
+			groupKeys = append(groupKeys, groupKey)
+			aggs := make([]Aggregator, len(aggregatorBuilders))
+			for i, builder := range aggregatorBuilders {
+				aggs[i] = builder.Aggregator(g)
+			}
+			aggregates[groupKey] = aggs
+			return aggs
+		}
+
 		err := s.Iterate(func(d Document) error {
 			group := nullValue
 
@@ -309,13 +321,7 @@ func (s Stream) Aggregate(aggregatorBuilders ...AggregatorBuilder) Stream {
 			// because of compiler optimizations.
 			aggs, ok := aggregates[string(b.Bytes())]
 			if !ok {
-				groupKey := b.String()
-				groupKeys = append(groupKeys, groupKey)
-				aggs = make([]Aggregator, len(aggregatorBuilders))
-				for i, builder := range aggregatorBuilders {
-					aggs[i] = builder.Aggregator(group)
-				}
-				aggregates[groupKey] = aggs
+				aggs = mkGroup(group)
 			}
 
 			for _, agg := range aggs {
@@ -331,6 +337,11 @@ func (s Stream) Aggregate(aggregatorBuilders ...AggregatorBuilder) Stream {
 			return err
 		}
 
+		if len(groupKeys) == 0 {
+			// create one group by default for the null value
+			mkGroup(nullValue)
+		}
+
 		for _, groupKey := range groupKeys {
 			fb := NewFieldBuffer()
 			aggs := aggregates[groupKey]
diff --git a/sql/query/expr/function.go b/sql/query/expr/function.go
index efd88e04..cc831000 100644
--- a/sql/query/expr/function.go
+++ b/sql/query/expr/function.go
@@ -334,7 +334,11 @@ func (m *MinAggregator) Add(d document.Document) error {
 
 // Aggregate adds a field to the given buffer with the minimum value.
 func (m *MinAggregator) Aggregate(fb *document.FieldBuffer) error {
-	fb.Add(m.Fn.String(), m.Min)
+	if m.Min.Type == 0 {
+		fb.Add(m.Fn.String(), document.NewNullValue())
+	} else {
+		fb.Add(m.Fn.String(), m.Min)
+	}
 	return nil
 }
 
@@ -434,7 +438,11 @@ func (m *MaxAggregator) Add(d document.Document) error {
 
 // Aggregate adds a field to the given buffer with the maximum value.
 func (m *MaxAggregator) Aggregate(fb *document.FieldBuffer) error {
-	fb.Add(m.Fn.String(), m.Max)
+	if m.Max.Type == 0 {
+		fb.Add(m.Fn.String(), document.NewNullValue())
+	} else {
+		fb.Add(m.Fn.String(), m.Max)
+	}
 	return nil
 }
 


Test patch:
diff --git a/sql/query/select_test.go b/sql/query/select_test.go
index c814435f..e62391fd 100644
--- a/sql/query/select_test.go
+++ b/sql/query/select_test.go
@@ -242,6 +242,23 @@ func TestSelectStmt(t *testing.T) {
 
 		require.JSONEq(t, `{"MAX(a)": [1, 2, 3]}`, string(enc))
 	})
+
+	t.Run("empty table with aggregators", func(t *testing.T) {
+		db, err := genji.Open(":memory:")
+		require.NoError(t, err)
+		defer db.Close()
+
+		err = db.Exec("CREATE TABLE test;")
+		require.NoError(t, err)
+
+		d, err := db.QueryDocument("SELECT MAX(a), MIN(b), COUNT(*), SUM(id) FROM test")
+		require.NoError(t, err)
+
+		enc, err := json.Marshal(d)
+		require.NoError(t, err)
+
+		require.JSONEq(t, `{"MAX(a)": null, "MIN(b)": null, "COUNT(*)": 0, "SUM(id)": null}`, string(enc))
+	})
 }
 
 func TestDistinct(t *testing.T) {