Add bindedSQL support #21
44
builder.go
44
builder.go
|
@ -4,10 +4,6 @@
|
|||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type optype byte
|
||||
|
||||
const (
|
||||
|
@ -228,40 +224,12 @@ func (b *Builder) ToSQL() (string, []interface{}, error) {
|
|||
return w.writer.String(), w.args, nil
|
||||
}
|
||||
|
||||
// ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix
|
||||
func ConvertPlaceholder(sql, prefix string) (string, error) {
|
||||
buf := StringBuilder{}
|
||||
var j, start = 0, 0
|
||||
for i := 0; i < len(sql); i++ {
|
||||
if sql[i] == '?' {
|
||||
_, err := buf.WriteString(sql[start:i])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
start = i + 1
|
||||
|
||||
_, err = buf.WriteString(prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
j = j + 1
|
||||
_, err = buf.WriteString(fmt.Sprintf("%d", j))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
// ToBindedSQL
|
||||
func (b *Builder) ToBindedSQL() (string, error) {
|
||||
w := NewWriter()
|
||||
if err := b.WriteTo(w); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// ToSQL convert a builder or condtions to SQL and args
|
||||
func ToSQL(cond interface{}) (string, []interface{}, error) {
|
||||
switch cond.(type) {
|
||||
case Cond:
|
||||
return condToSQL(cond.(Cond))
|
||||
case *Builder:
|
||||
return cond.(*Builder).ToSQL()
|
||||
}
|
||||
return "", nil, ErrNotSupportType
|
||||
return ConvertToBindedSQL(w.writer.String(), w.args)
|
||||
}
|
||||
|
|
|
@ -460,18 +460,3 @@ func TestExprCond(t *testing.T) {
|
|||
assert.EqualValues(t, "SELECT id FROM table1 WHERE (a=? OR b=?) AND (c=? OR d=?)", sql)
|
||||
assert.EqualValues(t, []interface{}{1, 2, 3, 4}, args)
|
||||
}
|
||||
|
||||
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=?"
|
||||
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"
|
||||
|
||||
func TestPlaceholderConverter(t *testing.T) {
|
||||
newSQL, err := ConvertPlaceholder(placeholderConverterSQL, "$")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, placeholderConvertedSQL, newSQL)
|
||||
}
|
||||
|
||||
func BenchmarkPlaceholderConverter(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ConvertPlaceholder(placeholderConverterSQL, "$")
|
||||
}
|
||||
}
|
||||
|
|
12
cond.go
12
cond.go
|
@ -72,15 +72,3 @@ func (condEmpty) Or(conds ...Cond) Cond {
|
|||
func (condEmpty) IsValid() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func condToSQL(cond Cond) (string, []interface{}, error) {
|
||||
if cond == nil || !cond.IsValid() {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
w := NewWriter()
|
||||
if err := cond.WriteTo(w); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return w.writer.String(), w.args, nil
|
||||
}
|
||||
|
|
4
error.go
4
error.go
|
@ -8,9 +8,11 @@ import "errors"
|
|||
|
||||
var (
|
||||
// ErrNotSupportType not supported SQL type error
|
||||
ErrNotSupportType = errors.New("not supported SQL type")
|
||||
ErrNotSupportType = errors.New("Not supported SQL type")
|
||||
// ErrNoNotInConditions no NOT IN params error
|
||||
ErrNoNotInConditions = errors.New("No NOT IN conditions")
|
||||
// ErrNoInConditions no IN params error
|
||||
ErrNoInConditions = errors.New("No IN conditions")
|
||||
// ErrNeedMoreArguments need more arguments
|
||||
ErrNeedMoreArguments = errors.New("Need more sql arguments")
|
||||
)
|
||||
|
|
147
sql.go
Normal file
147
sql.go
Normal file
|
@ -0,0 +1,147 @@
|
|||
// Copyright 2018 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func condToSQL(cond Cond) (string, []interface{}, error) {
|
||||
if cond == nil || !cond.IsValid() {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
w := NewWriter()
|
||||
if err := cond.WriteTo(w); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return w.writer.String(), w.args, nil
|
||||
}
|
||||
|
||||
func condToBindedSQL(cond Cond) (string, error) {
|
||||
if cond == nil || !cond.IsValid() {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
w := NewWriter()
|
||||
if err := cond.WriteTo(w); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ConvertToBindedSQL(w.writer.String(), w.args)
|
||||
}
|
||||
|
||||
// ToSQL convert a builder or condtions to SQL and args
|
||||
func ToSQL(cond interface{}) (string, []interface{}, error) {
|
||||
switch cond.(type) {
|
||||
case Cond:
|
||||
return condToSQL(cond.(Cond))
|
||||
case *Builder:
|
||||
return cond.(*Builder).ToSQL()
|
||||
}
|
||||
return "", nil, ErrNotSupportType
|
||||
}
|
||||
|
||||
// ToBindedSQL convert a builder or condtions to parameters binded SQL
|
||||
func ToBindedSQL(cond interface{}) (string, error) {
|
||||
switch cond.(type) {
|
||||
case Cond:
|
||||
return condToBindedSQL(cond.(Cond))
|
||||
case *Builder:
|
||||
return cond.(*Builder).ToBindedSQL()
|
||||
}
|
||||
return "", ErrNotSupportType
|
||||
}
|
||||
|
||||
func noSQLQuoteNeeded(a interface{}) bool {
|
||||
switch a.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
return true
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return true
|
||||
case float32, float64:
|
||||
return true
|
||||
case bool:
|
||||
return true
|
||||
case string:
|
||||
return false
|
||||
case time.Time, *time.Time:
|
||||
return false
|
||||
}
|
||||
|
||||
t := reflect.TypeOf(a)
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.String:
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ConvertToBindedSQL will convert SQL and args to a binded SQL
|
||||
func ConvertToBindedSQL(sql string, args []interface{}) (string, error) {
|
||||
buf := StringBuilder{}
|
||||
var j, start = 0, 0
|
||||
for i := 0; i < len(sql); i++ {
|
||||
if sql[i] == '?' {
|
||||
_, err := buf.WriteString(sql[start:i])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
start = i + 1
|
||||
|
||||
if len(args) == j {
|
||||
return "", ErrNeedMoreArguments
|
||||
}
|
||||
|
||||
if noSQLQuoteNeeded(args[j]) {
|
||||
_, err = fmt.Fprint(&buf, args[j])
|
||||
} else {
|
||||
_, err = fmt.Fprintf(&buf, "'%v'", args[j])
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix
|
||||
func ConvertPlaceholder(sql, prefix string) (string, error) {
|
||||
buf := StringBuilder{}
|
||||
var j, start = 0, 0
|
||||
for i := 0; i < len(sql); i++ {
|
||||
if sql[i] == '?' {
|
||||
_, err := buf.WriteString(sql[start:i])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
start = i + 1
|
||||
|
||||
_, err = buf.WriteString(prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
j = j + 1
|
||||
_, err = buf.WriteString(fmt.Sprintf("%d", j))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
57
sql_test.go
Normal file
57
sql_test.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2018 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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=?"
|
||||
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"
|
||||
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"
|
||||
|
||||
func TestPlaceholderConverter(t *testing.T) {
|
||||
newSQL, err := ConvertPlaceholder(placeholderConverterSQL, "$")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, placeholderConvertedSQL, newSQL)
|
||||
}
|
||||
|
||||
func BenchmarkPlaceholderConverter(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ConvertPlaceholder(placeholderConverterSQL, "$")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBindedSQLConverter(t *testing.T) {
|
||||
newSQL, err := ConvertToBindedSQL(placeholderConverterSQL, []interface{}{1, 2.1, "3", 4, "5", true})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, placeholderBindedSQL, newSQL)
|
||||
|
||||
newSQL, err = ConvertToBindedSQL(placeholderConverterSQL, []interface{}{1, 2.1, "3", 4, "5"})
|
||||
assert.Error(t, err)
|
||||
assert.EqualValues(t, ErrNeedMoreArguments, err)
|
||||
|
||||
newSQL, err = ToBindedSQL(1)
|
||||
assert.Error(t, err)
|
||||
assert.EqualValues(t, ErrNotSupportType, err)
|
||||
}
|
||||
|
||||
func TestSQL(t *testing.T) {
|
||||
newSQL, args, err := ToSQL(In("a", 1, 2))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "a IN (?,?)", newSQL)
|
||||
assert.EqualValues(t, []interface{}{1, 2}, args)
|
||||
|
||||
newSQL, args, err = ToSQL(Select("id").From("table").Where(In("a", 1, 2)))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT id FROM table WHERE a IN (?,?)", newSQL)
|
||||
assert.EqualValues(t, []interface{}{1, 2}, args)
|
||||
|
||||
newSQL, args, err = ToSQL(1)
|
||||
assert.Error(t, err)
|
||||
assert.EqualValues(t, ErrNotSupportType, err)
|
||||
}
|
Loading…
Reference in New Issue
Block a user