github.com/yongjacky/phoenix-go-orm-builder@v0.3.5/sql.go (about) 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 sql2 "database/sql" 9 "fmt" 10 "reflect" 11 "strings" 12 "time" 13 ) 14 15 func condToSQL(cond Cond) (string, []interface{}, error) { 16 if cond == nil || !cond.IsValid() { 17 return "", nil, nil 18 } 19 20 w := NewWriter() 21 if err := cond.WriteTo(w); err != nil { 22 return "", nil, err 23 } 24 return w.String(), w.args, nil 25 } 26 27 func condToBoundSQL(cond Cond) (string, error) { 28 if cond == nil || !cond.IsValid() { 29 return "", nil 30 } 31 32 w := NewWriter() 33 if err := cond.WriteTo(w); err != nil { 34 return "", err 35 } 36 return ConvertToBoundSQL(w.String(), w.args) 37 } 38 39 // ToSQL convert a builder or conditions to SQL and args 40 func ToSQL(cond interface{}) (string, []interface{}, error) { 41 switch cond.(type) { 42 case Cond: 43 return condToSQL(cond.(Cond)) 44 case *Builder: 45 return cond.(*Builder).ToSQL() 46 } 47 return "", nil, ErrNotSupportType 48 } 49 50 // ToBoundSQL convert a builder or conditions to parameters bound SQL 51 func ToBoundSQL(cond interface{}) (string, error) { 52 switch cond.(type) { 53 case Cond: 54 return condToBoundSQL(cond.(Cond)) 55 case *Builder: 56 return cond.(*Builder).ToBoundSQL() 57 } 58 return "", ErrNotSupportType 59 } 60 61 func noSQLQuoteNeeded(a interface{}) bool { 62 switch a.(type) { 63 case int, int8, int16, int32, int64: 64 return true 65 case uint, uint8, uint16, uint32, uint64: 66 return true 67 case float32, float64: 68 return true 69 case bool: 70 return true 71 case string: 72 return false 73 case time.Time, *time.Time: 74 return false 75 } 76 77 t := reflect.TypeOf(a) 78 79 switch t.Kind() { 80 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 81 return true 82 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 83 return true 84 case reflect.Float32, reflect.Float64: 85 return true 86 case reflect.Bool: 87 return true 88 case reflect.String: 89 return false 90 } 91 92 return false 93 } 94 95 // ConvertToBoundSQL will convert SQL and args to a bound SQL 96 func ConvertToBoundSQL(sql string, args []interface{}) (string, error) { 97 buf := strings.Builder{} 98 var i, j, start int 99 for ; i < len(sql); i++ { 100 if sql[i] == '?' { 101 _, err := buf.WriteString(sql[start:i]) 102 if err != nil { 103 return "", err 104 } 105 start = i + 1 106 107 if len(args) == j { 108 return "", ErrNeedMoreArguments 109 } 110 111 arg := args[j] 112 if namedArg, ok := arg.(sql2.NamedArg); ok { 113 arg = namedArg.Value 114 } 115 116 if noSQLQuoteNeeded(arg) { 117 _, err = fmt.Fprint(&buf, arg) 118 } else { 119 // replace ' -> '' (standard replacement) to avoid critical SQL injection, 120 // NOTICE: may allow some injection like % (or _) in LIKE query 121 _, err = fmt.Fprintf(&buf, "'%v'", strings.Replace(fmt.Sprintf("%v", arg), "'", 122 "''", -1)) 123 } 124 if err != nil { 125 return "", err 126 } 127 j = j + 1 128 } 129 } 130 _, err := buf.WriteString(sql[start:]) 131 if err != nil { 132 return "", err 133 } 134 return buf.String(), nil 135 } 136 137 // ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix 138 func ConvertPlaceholder(sql, prefix string) (string, error) { 139 buf := strings.Builder{} 140 var i, j, start int 141 for ; i < len(sql); i++ { 142 if sql[i] == '?' { 143 if _, err := buf.WriteString(sql[start:i]); err != nil { 144 return "", err 145 } 146 147 start = i + 1 148 j = j + 1 149 150 if _, err := buf.WriteString(fmt.Sprintf("%v%d", prefix, j)); err != nil { 151 return "", err 152 } 153 } 154 } 155 156 if _, err := buf.WriteString(sql[start:]); err != nil { 157 return "", err 158 } 159 160 return buf.String(), nil 161 }