Skip to content

Commit 1dc0af2

Browse files
authored
add bindedSQL support (go-xorm#21)
1 parent b6b65af commit 1dc0af2

File tree

6 files changed

+213
-66
lines changed

6 files changed

+213
-66
lines changed

builder.go

+6-38
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44

55
package builder
66

7-
import (
8-
"fmt"
9-
)
10-
117
type optype byte
128

139
const (
@@ -228,40 +224,12 @@ func (b *Builder) ToSQL() (string, []interface{}, error) {
228224
return w.writer.String(), w.args, nil
229225
}
230226

231-
// ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix
232-
func ConvertPlaceholder(sql, prefix string) (string, error) {
233-
buf := StringBuilder{}
234-
var j, start = 0, 0
235-
for i := 0; i < len(sql); i++ {
236-
if sql[i] == '?' {
237-
_, err := buf.WriteString(sql[start:i])
238-
if err != nil {
239-
return "", err
240-
}
241-
start = i + 1
242-
243-
_, err = buf.WriteString(prefix)
244-
if err != nil {
245-
return "", err
246-
}
247-
248-
j = j + 1
249-
_, err = buf.WriteString(fmt.Sprintf("%d", j))
250-
if err != nil {
251-
return "", err
252-
}
253-
}
227+
// ToBindedSQL
228+
func (b *Builder) ToBindedSQL() (string, error) {
229+
w := NewWriter()
230+
if err := b.WriteTo(w); err != nil {
231+
return "", err
254232
}
255-
return buf.String(), nil
256-
}
257233

258-
// ToSQL convert a builder or condtions to SQL and args
259-
func ToSQL(cond interface{}) (string, []interface{}, error) {
260-
switch cond.(type) {
261-
case Cond:
262-
return condToSQL(cond.(Cond))
263-
case *Builder:
264-
return cond.(*Builder).ToSQL()
265-
}
266-
return "", nil, ErrNotSupportType
234+
return ConvertToBindedSQL(w.writer.String(), w.args)
267235
}

builder_test.go

-15
Original file line numberDiff line numberDiff line change
@@ -460,18 +460,3 @@ func TestExprCond(t *testing.T) {
460460
assert.EqualValues(t, "SELECT id FROM table1 WHERE (a=? OR b=?) AND (c=? OR d=?)", sql)
461461
assert.EqualValues(t, []interface{}{1, 2, 3, 4}, args)
462462
}
463-
464-
const placeholderConverterSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=?) AND id=? AND c=? AND d=? AND e=? AND f=?"
465-
const placeholderConvertedSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=$1) AND id=$2 AND c=$3 AND d=$4 AND e=$5 AND f=$6"
466-
467-
func TestPlaceholderConverter(t *testing.T) {
468-
newSQL, err := ConvertPlaceholder(placeholderConverterSQL, "$")
469-
assert.NoError(t, err)
470-
assert.EqualValues(t, placeholderConvertedSQL, newSQL)
471-
}
472-
473-
func BenchmarkPlaceholderConverter(b *testing.B) {
474-
for i := 0; i < b.N; i++ {
475-
ConvertPlaceholder(placeholderConverterSQL, "$")
476-
}
477-
}

cond.go

-12
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,3 @@ func (condEmpty) Or(conds ...Cond) Cond {
7272
func (condEmpty) IsValid() bool {
7373
return false
7474
}
75-
76-
func condToSQL(cond Cond) (string, []interface{}, error) {
77-
if cond == nil || !cond.IsValid() {
78-
return "", nil, nil
79-
}
80-
81-
w := NewWriter()
82-
if err := cond.WriteTo(w); err != nil {
83-
return "", nil, err
84-
}
85-
return w.writer.String(), w.args, nil
86-
}

error.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import "errors"
88

99
var (
1010
// ErrNotSupportType not supported SQL type error
11-
ErrNotSupportType = errors.New("not supported SQL type")
11+
ErrNotSupportType = errors.New("Not supported SQL type")
1212
// ErrNoNotInConditions no NOT IN params error
1313
ErrNoNotInConditions = errors.New("No NOT IN conditions")
1414
// ErrNoInConditions no IN params error
1515
ErrNoInConditions = errors.New("No IN conditions")
16+
// ErrNeedMoreArguments need more arguments
17+
ErrNeedMoreArguments = errors.New("Need more sql arguments")
1618
)

sql.go

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright 2018 The Xorm Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package builder
6+
7+
import (
8+
"fmt"
9+
"reflect"
10+
"time"
11+
)
12+
13+
func condToSQL(cond Cond) (string, []interface{}, error) {
14+
if cond == nil || !cond.IsValid() {
15+
return "", nil, nil
16+
}
17+
18+
w := NewWriter()
19+
if err := cond.WriteTo(w); err != nil {
20+
return "", nil, err
21+
}
22+
return w.writer.String(), w.args, nil
23+
}
24+
25+
func condToBindedSQL(cond Cond) (string, error) {
26+
if cond == nil || !cond.IsValid() {
27+
return "", nil
28+
}
29+
30+
w := NewWriter()
31+
if err := cond.WriteTo(w); err != nil {
32+
return "", err
33+
}
34+
return ConvertToBindedSQL(w.writer.String(), w.args)
35+
}
36+
37+
// ToSQL convert a builder or condtions to SQL and args
38+
func ToSQL(cond interface{}) (string, []interface{}, error) {
39+
switch cond.(type) {
40+
case Cond:
41+
return condToSQL(cond.(Cond))
42+
case *Builder:
43+
return cond.(*Builder).ToSQL()
44+
}
45+
return "", nil, ErrNotSupportType
46+
}
47+
48+
// ToBindedSQL convert a builder or condtions to parameters binded SQL
49+
func ToBindedSQL(cond interface{}) (string, error) {
50+
switch cond.(type) {
51+
case Cond:
52+
return condToBindedSQL(cond.(Cond))
53+
case *Builder:
54+
return cond.(*Builder).ToBindedSQL()
55+
}
56+
return "", ErrNotSupportType
57+
}
58+
59+
func noSQLQuoteNeeded(a interface{}) bool {
60+
switch a.(type) {
61+
case int, int8, int16, int32, int64:
62+
return true
63+
case uint, uint8, uint16, uint32, uint64:
64+
return true
65+
case float32, float64:
66+
return true
67+
case bool:
68+
return true
69+
case string:
70+
return false
71+
case time.Time, *time.Time:
72+
return false
73+
}
74+
75+
t := reflect.TypeOf(a)
76+
switch t.Kind() {
77+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
78+
return true
79+
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
80+
return true
81+
case reflect.Float32, reflect.Float64:
82+
return true
83+
case reflect.Bool:
84+
return true
85+
case reflect.String:
86+
return false
87+
}
88+
89+
return false
90+
}
91+
92+
// ConvertToBindedSQL will convert SQL and args to a binded SQL
93+
func ConvertToBindedSQL(sql string, args []interface{}) (string, error) {
94+
buf := StringBuilder{}
95+
var j, start = 0, 0
96+
for i := 0; i < len(sql); i++ {
97+
if sql[i] == '?' {
98+
_, err := buf.WriteString(sql[start:i])
99+
if err != nil {
100+
return "", err
101+
}
102+
start = i + 1
103+
104+
if len(args) == j {
105+
return "", ErrNeedMoreArguments
106+
}
107+
108+
if noSQLQuoteNeeded(args[j]) {
109+
_, err = fmt.Fprint(&buf, args[j])
110+
} else {
111+
_, err = fmt.Fprintf(&buf, "'%v'", args[j])
112+
}
113+
if err != nil {
114+
return "", err
115+
}
116+
j = j + 1
117+
}
118+
}
119+
return buf.String(), nil
120+
}
121+
122+
// ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix
123+
func ConvertPlaceholder(sql, prefix string) (string, error) {
124+
buf := StringBuilder{}
125+
var j, start = 0, 0
126+
for i := 0; i < len(sql); i++ {
127+
if sql[i] == '?' {
128+
_, err := buf.WriteString(sql[start:i])
129+
if err != nil {
130+
return "", err
131+
}
132+
start = i + 1
133+
134+
_, err = buf.WriteString(prefix)
135+
if err != nil {
136+
return "", err
137+
}
138+
139+
j = j + 1
140+
_, err = buf.WriteString(fmt.Sprintf("%d", j))
141+
if err != nil {
142+
return "", err
143+
}
144+
}
145+
}
146+
return buf.String(), nil
147+
}

sql_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2018 The Xorm Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package builder
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
const placeholderConverterSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=?) AND id=? AND c=? AND d=? AND e=? AND f=?"
14+
const placeholderConvertedSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=$1) AND id=$2 AND c=$3 AND d=$4 AND e=$5 AND f=$6"
15+
const placeholderBindedSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=1) AND id=2.1 AND c='3' AND d=4 AND e='5' AND f=true"
16+
17+
func TestPlaceholderConverter(t *testing.T) {
18+
newSQL, err := ConvertPlaceholder(placeholderConverterSQL, "$")
19+
assert.NoError(t, err)
20+
assert.EqualValues(t, placeholderConvertedSQL, newSQL)
21+
}
22+
23+
func BenchmarkPlaceholderConverter(b *testing.B) {
24+
for i := 0; i < b.N; i++ {
25+
ConvertPlaceholder(placeholderConverterSQL, "$")
26+
}
27+
}
28+
29+
func TestBindedSQLConverter(t *testing.T) {
30+
newSQL, err := ConvertToBindedSQL(placeholderConverterSQL, []interface{}{1, 2.1, "3", 4, "5", true})
31+
assert.NoError(t, err)
32+
assert.EqualValues(t, placeholderBindedSQL, newSQL)
33+
34+
newSQL, err = ConvertToBindedSQL(placeholderConverterSQL, []interface{}{1, 2.1, "3", 4, "5"})
35+
assert.Error(t, err)
36+
assert.EqualValues(t, ErrNeedMoreArguments, err)
37+
38+
newSQL, err = ToBindedSQL(1)
39+
assert.Error(t, err)
40+
assert.EqualValues(t, ErrNotSupportType, err)
41+
}
42+
43+
func TestSQL(t *testing.T) {
44+
newSQL, args, err := ToSQL(In("a", 1, 2))
45+
assert.NoError(t, err)
46+
assert.EqualValues(t, "a IN (?,?)", newSQL)
47+
assert.EqualValues(t, []interface{}{1, 2}, args)
48+
49+
newSQL, args, err = ToSQL(Select("id").From("table").Where(In("a", 1, 2)))
50+
assert.NoError(t, err)
51+
assert.EqualValues(t, "SELECT id FROM table WHERE a IN (?,?)", newSQL)
52+
assert.EqualValues(t, []interface{}{1, 2}, args)
53+
54+
newSQL, args, err = ToSQL(1)
55+
assert.Error(t, err)
56+
assert.EqualValues(t, ErrNotSupportType, err)
57+
}

0 commit comments

Comments
 (0)