From 2b8df579df94d6642fa79e5daa28ff1f92210af7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net>
Date: Wed, 21 Sep 2016 12:23:41 -0500
Subject: [PATCH] Add column schema guessing based on map/struct values.

---
 internal/sqladapter/testing/adapter.go.tpl | 36 ++++++++++++++++++++++
 lib/sqlbuilder/builder_test.go             | 14 +++++++++
 lib/sqlbuilder/insert.go                   | 27 +++++++++-------
 3 files changed, 66 insertions(+), 11 deletions(-)

diff --git a/internal/sqladapter/testing/adapter.go.tpl b/internal/sqladapter/testing/adapter.go.tpl
index c8f05b1f..e015ff45 100644
--- a/internal/sqladapter/testing/adapter.go.tpl
+++ b/internal/sqladapter/testing/adapter.go.tpl
@@ -1115,6 +1115,42 @@ func TestBatchInsert(t *testing.T) {
 	}
 }
 
+func TestBatchInsertNoColumns(t *testing.T) {
+	sess := mustOpen()
+	defer sess.Close()
+
+	for batchSize := 0; batchSize < 17; batchSize++ {
+		err := sess.Collection("artist").Truncate()
+		assert.NoError(t, err)
+
+		batch := sess.InsertInto("artist").Batch(batchSize)
+
+		totalItems := int(rand.Int31n(21))
+
+		go func() {
+			defer batch.Done()
+			for i := 0; i < totalItems; i++ {
+				value := struct{Name string `db:"name"`}{fmt.Sprintf("artist-%d", i)}
+				batch.Values(value)
+			}
+		}()
+
+		err = batch.Wait()
+		assert.NoError(t, err)
+		assert.NoError(t, batch.Err())
+
+		c, err := sess.Collection("artist").Find().Count()
+		assert.NoError(t, err)
+		assert.Equal(t, uint64(totalItems), c)
+
+		for i := 0; i < totalItems; i++ {
+			c, err := sess.Collection("artist").Find(db.Cond{"name": fmt.Sprintf("artist-%d", i)}).Count()
+			assert.NoError(t, err)
+			assert.Equal(t, uint64(1), c)
+		}
+	}
+}
+
 func TestBatchInsertReturningKeys(t *testing.T) {
 	if Adapter != "postgresql" {
 		t.Skip("Currently not supported.")
diff --git a/lib/sqlbuilder/builder_test.go b/lib/sqlbuilder/builder_test.go
index 0be57152..00e01e43 100644
--- a/lib/sqlbuilder/builder_test.go
+++ b/lib/sqlbuilder/builder_test.go
@@ -539,6 +539,20 @@ func TestInsert(t *testing.T) {
 		}{12, "Chavela Vargas"}).String(),
 	)
 
+	assert.Equal(
+		`INSERT INTO "artist" ("id", "name") VALUES ($1, $2), ($3, $4), ($5, $6)`,
+		b.InsertInto("artist").Values(struct {
+			ID   int    `db:"id"`
+			Name string `db:"name"`
+		}{12, "Chavela Vargas"}).Values(struct {
+			ID   int    `db:"id"`
+			Name string `db:"name"`
+		}{13, "Alondra de la Parra"}).Values(struct {
+			ID   int    `db:"id"`
+			Name string `db:"name"`
+		}{14, "Haruki Murakami"}).String(),
+	)
+
 	assert.Equal(
 		`INSERT INTO "artist" ("name", "id") VALUES ($1, $2)`,
 		b.InsertInto("artist").Columns("name", "id").Values("Chavela Vargas", 12).String(),
diff --git a/lib/sqlbuilder/insert.go b/lib/sqlbuilder/insert.go
index d86991a7..775b13e7 100644
--- a/lib/sqlbuilder/insert.go
+++ b/lib/sqlbuilder/insert.go
@@ -65,18 +65,23 @@ func (qi *inserter) Columns(columns ...string) Inserter {
 }
 
 func (qi *inserter) Values(values ...interface{}) Inserter {
-	if len(qi.columns) == 0 && len(values) == 1 {
-		ff, vv, _ := Map(values[0])
-
-		columns, vals, arguments, _ := qi.builder.t.ToColumnsValuesAndArguments(ff, vv)
-
-		qi.arguments = append(qi.arguments, arguments...)
-		qi.values = append(qi.values, vals)
-
-		for _, c := range columns.Columns {
-			qi.columns = append(qi.columns, c)
+	if len(values) == 1 {
+		ff, vv, err := Map(values[0])
+		if err == nil {
+			columns, vals, arguments, _ := qi.builder.t.ToColumnsValuesAndArguments(ff, vv)
+
+			qi.arguments = append(qi.arguments, arguments...)
+			qi.values = append(qi.values, vals)
+			if len(qi.columns) == 0 {
+				for _, c := range columns.Columns {
+					qi.columns = append(qi.columns, c)
+				}
+			}
+			return qi
 		}
-	} else if len(qi.columns) == 0 || len(values) == len(qi.columns) {
+	}
+
+	if len(qi.columns) == 0 || len(values) == len(qi.columns) {
 		qi.arguments = append(qi.arguments, values...)
 
 		l := len(values)
-- 
GitLab