From afc967174ba02925519d3a32ed52004d3543340a Mon Sep 17 00:00:00 2001
From: Jonathan Duck <jonathan@ec2software.com>
Date: Tue, 27 Aug 2019 13:23:39 -0700
Subject: [PATCH] Fixed column order (#430)

* Add test to demonstrate INSERT fails with a suffix on quoted column name #429

* Ensure columns are sorted the same way each time.
---
 columns/columns.go      | 30 +++++++++++++++++-------------
 columns/columns_test.go | 21 +++++++++++++++++++++
 2 files changed, 38 insertions(+), 13 deletions(-)

diff --git a/columns/columns.go b/columns/columns.go
index 823d6455..8ee8f15c 100644
--- a/columns/columns.go
+++ b/columns/columns.go
@@ -118,37 +118,41 @@ func (c Columns) Readable() *ReadableColumns {
 	return w
 }
 
+// colNames returns a slice of column names in a deterministic order
+func (c Columns) colNames() []string {
+	var xs []string
+	for _, t := range c.Cols {
+		xs = append(xs, t.Name)
+	}
+	sort.Strings(xs)
+	return xs
+}
+
 type quoter interface {
 	Quote(key string) string
 }
 
 // QuotedString gives the columns list quoted with the given quoter function.
 func (c Columns) QuotedString(quoter quoter) string {
-	var xs []string
-	for _, t := range c.Cols {
-		xs = append(xs, quoter.Quote(t.Name))
+	xs := c.colNames()
+	for i, n := range xs {
+		xs[i] = quoter.Quote(n)
 	}
-	sort.Strings(xs)
 	return strings.Join(xs, ", ")
 }
 
 func (c Columns) String() string {
-	var xs []string
-	for _, t := range c.Cols {
-		xs = append(xs, t.Name)
-	}
-	sort.Strings(xs)
+	xs := c.colNames()
 	return strings.Join(xs, ", ")
 }
 
 // SymbolizedString returns a list of tokens (:token) to bind
 // a value to an INSERT query.
 func (c Columns) SymbolizedString() string {
-	var xs []string
-	for _, t := range c.Cols {
-		xs = append(xs, ":"+t.Name)
+	xs := c.colNames()
+	for i, n := range xs {
+		xs[i] = ":" + n
 	}
-	sort.Strings(xs)
 	return strings.Join(xs, ", ")
 }
 
diff --git a/columns/columns_test.go b/columns/columns_test.go
index 71095b5f..c4194d2e 100644
--- a/columns/columns_test.go
+++ b/columns/columns_test.go
@@ -1,6 +1,7 @@
 package columns_test
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/gobuffalo/pop/columns"
@@ -60,3 +61,23 @@ func Test_Columns_Remove(t *testing.T) {
 		r.Equal(len(c.Cols), 3)
 	}
 }
+
+type fooWithSuffix struct {
+	Amount      float64 `db:"amount"`
+	AmountUnits string  `db:"amount_units"`
+}
+type fooQuoter struct{}
+
+func (fooQuoter) Quote(key string) string {
+	return fmt.Sprintf("`%v`", key)
+}
+
+func Test_Columns_Sorted(t *testing.T) {
+	r := require.New(t)
+
+	c := columns.ForStruct(fooWithSuffix{}, "fooWithSuffix")
+	r.Equal(len(c.Cols), 2)
+	r.Equal(c.SymbolizedString(), ":amount, :amount_units")
+	r.Equal(c.String(), "amount, amount_units")
+	r.Equal(c.QuotedString(fooQuoter{}), "`amount`, `amount_units`")
+}