Skip to content

Commit f1b7ac4

Browse files
committed
refs #64 add SQLServer flavor
1 parent 02da50f commit f1b7ac4

File tree

8 files changed

+427
-168
lines changed

8 files changed

+427
-168
lines changed

args.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,9 @@ func (args *Args) compileArg(buf *bytes.Buffer, flavor Flavor, values []interfac
236236
case MySQL, SQLite:
237237
buf.WriteRune('?')
238238
case PostgreSQL:
239-
fmt.Fprintf(buf, "$%v", len(values)+1)
239+
fmt.Fprintf(buf, "$%d", len(values)+1)
240+
case SQLServer:
241+
fmt.Fprintf(buf, "@p%d", len(values)+1)
240242
default:
241243
panic(fmt.Errorf("Args.CompileWithFlavor: invalid flavor %v (%v)", flavor, int(flavor)))
242244
}

args_test.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ func TestArgs(t *testing.T) {
4040
}
4141

4242
old := DefaultFlavor
43-
DefaultFlavor = PostgreSQL
4443
defer func() {
4544
DefaultFlavor = old
4645
}()
4746

47+
DefaultFlavor = PostgreSQL
48+
4849
// PostgreSQL flavor compiled sql.
4950
for expected, c := range cases {
5051
args := new(Args)
@@ -59,6 +60,23 @@ func TestArgs(t *testing.T) {
5960

6061
a.Equal(actual, expected)
6162
}
63+
64+
DefaultFlavor = SQLServer
65+
66+
// SQLServer flavor compiled sql.
67+
for expected, c := range cases {
68+
args := new(Args)
69+
70+
for i := 1; i < len(c); i++ {
71+
args.Add(c[i])
72+
}
73+
74+
sql, values := args.Compile(c[0].(string))
75+
actual := fmt.Sprintf("%v\n%v", sql, values)
76+
expected = toSQLServerSQL(expected)
77+
78+
a.Equal(actual, expected)
79+
}
6280
}
6381

6482
func toPostgreSQL(sql string) string {
@@ -74,6 +92,19 @@ func toPostgreSQL(sql string) string {
7492
return buf.String()
7593
}
7694

95+
func toSQLServerSQL(sql string) string {
96+
parts := strings.Split(sql, "?")
97+
buf := &bytes.Buffer{}
98+
buf.WriteString(parts[0])
99+
100+
for i, p := range parts[1:] {
101+
fmt.Fprintf(buf, "@p%v", i+1)
102+
buf.WriteString(p)
103+
}
104+
105+
return buf.String()
106+
}
107+
77108
func TestArgsAdd(t *testing.T) {
78109
a := assert.New(t)
79110
args := &Args{}

flavor.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const (
1515
MySQL
1616
PostgreSQL
1717
SQLite
18+
SQLServer
1819
)
1920

2021
var (
@@ -46,6 +47,8 @@ func (f Flavor) String() string {
4647
return "PostgreSQL"
4748
case SQLite:
4849
return "SQLite"
50+
case SQLServer:
51+
return "SQLServer"
4952
}
5053

5154
return "<invalid>"
@@ -64,6 +67,8 @@ func (f Flavor) Interpolate(sql string, args []interface{}) (string, error) {
6467
return postgresqlInterpolate(sql, args...)
6568
case SQLite:
6669
return sqliteInterpolate(sql, args...)
70+
case SQLServer:
71+
return sqlserverInterpolate(sql, args...)
6772
}
6873

6974
return "", ErrInterpolateNotImplemented
@@ -114,13 +119,13 @@ func (f Flavor) NewUnionBuilder() *UnionBuilder {
114119
// Quote adds quote for name to make sure the name can be used safely
115120
// as table name or field name.
116121
//
117-
// * For MySQL, use back quote (`) to quote name;
118-
// * For PostgreSQL and SQLite, use double quote (") to quote name.
122+
// * For MySQL, use back quote (`) to quote name;
123+
// * For PostgreSQL, SQL Server and SQLite, use double quote (") to quote name.
119124
func (f Flavor) Quote(name string) string {
120125
switch f {
121126
case MySQL:
122127
return fmt.Sprintf("`%s`", name)
123-
case PostgreSQL, SQLite:
128+
case PostgreSQL, SQLServer, SQLite:
124129
return fmt.Sprintf(`"%s"`, name)
125130
}
126131

flavor_test.go

Lines changed: 19 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ package sqlbuilder
55

66
import (
77
"fmt"
8-
"strconv"
98
"testing"
10-
"time"
119

1210
"github.com/huandu/go-assert"
1311
)
@@ -19,6 +17,7 @@ func TestFlavor(t *testing.T) {
1917
MySQL: "MySQL",
2018
PostgreSQL: "PostgreSQL",
2119
SQLite: "SQLite",
20+
SQLServer: "SQLServer",
2221
}
2322

2423
for f, expected := range cases {
@@ -27,120 +26,6 @@ func TestFlavor(t *testing.T) {
2726
}
2827
}
2928

30-
func TestFlavorInterpolate(t *testing.T) {
31-
a := assert.New(t)
32-
dt := time.Date(2019, 4, 24, 12, 23, 34, 123456789, time.FixedZone("CST", 8*60*60)) // 2019-04-24 12:23:34.987654321 CST
33-
_, errOutOfRange := strconv.ParseInt("12345678901234567890", 10, 32)
34-
cases := []struct {
35-
flavor Flavor
36-
sql string
37-
args []interface{}
38-
query string
39-
err error
40-
}{
41-
{
42-
MySQL,
43-
"SELECT * FROM a WHERE name = ? AND state IN (?, ?, ?, ?, ?)", []interface{}{"I'm fine", 42, int8(8), int16(-16), int32(32), int64(64)},
44-
"SELECT * FROM a WHERE name = 'I\\'m fine' AND state IN (42, 8, -16, 32, 64)", nil,
45-
},
46-
{
47-
MySQL,
48-
"SELECT * FROM `a?` WHERE name = \"?\" AND state IN (?, '?', ?, ?, ?, ?, ?)", []interface{}{"\r\n\b\t\x1a\x00\\\"'", uint(42), uint8(8), uint16(16), uint32(32), uint64(64), "useless"},
49-
"SELECT * FROM `a?` WHERE name = \"?\" AND state IN ('\\r\\n\\b\\t\\Z\\0\\\\\\\"\\'', '?', 42, 8, 16, 32, 64)", nil,
50-
},
51-
{
52-
MySQL,
53-
"SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?", []interface{}{true, false, float32(1.234567), float64(9.87654321), []byte(nil), []byte("I'm bytes"), dt, time.Time{}, nil},
54-
"SELECT TRUE, FALSE, 1.234567, 9.87654321, NULL, _binary'I\\'m bytes', '2019-04-24 12:23:34.123457', '0000-00-00', NULL", nil,
55-
},
56-
{
57-
MySQL,
58-
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\?", []interface{}{MySQL},
59-
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\'MySQL'", nil,
60-
},
61-
{
62-
MySQL,
63-
"SELECT ?", nil,
64-
"", ErrInterpolateMissingArgs,
65-
},
66-
{
67-
MySQL,
68-
"SELECT ?", []interface{}{complex(1, 2)},
69-
"", ErrInterpolateUnsupportedArgs,
70-
},
71-
72-
{
73-
PostgreSQL,
74-
"SELECT * FROM a WHERE name = $3 AND state IN ($2, $4, $1, $6, $5)", []interface{}{"I'm fine", 42, int8(8), int16(-16), int32(32), int64(64)},
75-
"SELECT * FROM a WHERE name = 8 AND state IN (42, -16, E'I\\'m fine', 64, 32)", nil,
76-
},
77-
{
78-
PostgreSQL,
79-
"SELECT * FROM $abc$$1$abc$1$1 WHERE name = \"$1\" AND state IN ($2, '$1', $3, $6, $5, $4, $2) $3", []interface{}{"\r\n\b\t\x1a\x00\\\"'", uint(42), uint8(8), uint16(16), uint32(32), uint64(64), "useless"},
80-
"SELECT * FROM $abc$$1$abc$1E'\\r\\n\\b\\t\\Z\\0\\\\\\\"\\'' WHERE name = \"$1\" AND state IN (42, '$1', 8, 64, 32, 16, 42) 8", nil,
81-
},
82-
{
83-
PostgreSQL,
84-
"SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $11, $a", []interface{}{true, false, float32(1.234567), float64(9.87654321), []byte(nil), []byte("I'm bytes"), dt, time.Time{}, nil, 10, 11, 12},
85-
"SELECT TRUE, FALSE, 1.234567, 9.87654321, NULL, E'\\\\x49276D206279746573'::bytea, '2019-04-24 12:23:34.123457 CST', '0000-00-00', NULL, 11, $a", nil,
86-
},
87-
{
88-
PostgreSQL,
89-
"SELECT '\\'$1', \"\\\"$1\", `$1`, \\$1a, $$1$$, $a $b$ $a $ $1$b$1$1 $a$ $", []interface{}{MySQL},
90-
"SELECT '\\'$1', \"\\\"$1\", `E'MySQL'`, \\E'MySQL'a, $$1$$, $a $b$ $a $ $1$b$1E'MySQL' $a$ $", nil,
91-
},
92-
{
93-
PostgreSQL,
94-
"SELECT * FROM a WHERE name = 'Huan''Du''$1' AND desc = $1", []interface{}{"c'mon"},
95-
"SELECT * FROM a WHERE name = 'Huan''Du''$1' AND desc = E'c\\'mon'", nil,
96-
},
97-
{
98-
PostgreSQL,
99-
"SELECT $1", nil,
100-
"", ErrInterpolateMissingArgs,
101-
},
102-
{
103-
PostgreSQL,
104-
"SELECT $1", []interface{}{complex(1, 2)},
105-
"", ErrInterpolateUnsupportedArgs,
106-
},
107-
{
108-
PostgreSQL,
109-
"SELECT $12345678901234567890", nil,
110-
"", errOutOfRange,
111-
},
112-
113-
{
114-
SQLite,
115-
"SELECT * FROM a WHERE name = ? AND state IN (?, ?, ?, ?, ?)", []interface{}{"I'm fine", 42, int8(8), int16(-16), int32(32), int64(64)},
116-
"SELECT * FROM a WHERE name = 'I\\'m fine' AND state IN (42, 8, -16, 32, 64)", nil,
117-
},
118-
{
119-
SQLite,
120-
"SELECT * FROM `a?` WHERE name = \"?\" AND state IN (?, '?', ?, ?, ?, ?, ?)", []interface{}{"\r\n\b\t\x1a\x00\\\"'", uint(42), uint8(8), uint16(16), uint32(32), uint64(64), "useless"},
121-
"SELECT * FROM `a?` WHERE name = \"?\" AND state IN ('\\r\\n\\b\\t\\Z\\0\\\\\\\"\\'', '?', 42, 8, 16, 32, 64)", nil,
122-
},
123-
{
124-
SQLite,
125-
"SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?", []interface{}{true, false, float32(1.234567), float64(9.87654321), []byte(nil), []byte("I'm bytes"), dt, time.Time{}, nil},
126-
"SELECT TRUE, FALSE, 1.234567, 9.87654321, NULL, X'49276D206279746573', '2019-04-24 12:23:34.123', '0000-00-00', NULL", nil,
127-
},
128-
{
129-
SQLite,
130-
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\?", []interface{}{SQLite},
131-
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\'SQLite'", nil,
132-
},
133-
}
134-
135-
for idx, c := range cases {
136-
a.Use(&idx, &c)
137-
query, err := c.flavor.Interpolate(c.sql, c.args)
138-
139-
a.Equal(query, c.query)
140-
a.Assert(err == c.err || err.Error() == c.err.Error())
141-
}
142-
}
143-
14429
func ExampleFlavor() {
14530
// Create a flavored builder.
14631
sb := PostgreSQL.NewSelectBuilder()
@@ -218,3 +103,21 @@ func ExampleFlavor_Interpolate_sqlite() {
218103
// SELECT name FROM user WHERE id <> 1234 AND name = 'Charmy Liu' AND desc LIKE '%mother\'s day%'
219104
// <nil>
220105
}
106+
107+
func ExampleFlavor_Interpolate_sqlServer() {
108+
sb := SQLServer.NewSelectBuilder()
109+
sb.Select("name").From("user").Where(
110+
sb.NE("id", 1234),
111+
sb.E("name", "Charmy Liu"),
112+
sb.Like("desc", "%mother's day%"),
113+
)
114+
sql, args := sb.Build()
115+
query, err := SQLServer.Interpolate(sql, args)
116+
117+
fmt.Println(query)
118+
fmt.Println(err)
119+
120+
// Output:
121+
// SELECT name FROM user WHERE id <> 1234 AND name = N'Charmy Liu' AND desc LIKE N'%mother\'s day%'
122+
// <nil>
123+
}

0 commit comments

Comments
 (0)