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  }