github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/quote.go (about)

     1  package sqlite3
     2  
     3  import (
     4  	"bytes"
     5  	"math"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  	"unsafe"
    10  
    11  	"github.com/ncruces/go-sqlite3/internal/util"
    12  )
    13  
    14  // Quote escapes and quotes a value
    15  // making it safe to embed in SQL text.
    16  func Quote(value any) string {
    17  	switch v := value.(type) {
    18  	case nil:
    19  		return "NULL"
    20  	case bool:
    21  		if v {
    22  			return "1"
    23  		} else {
    24  			return "0"
    25  		}
    26  
    27  	case int:
    28  		return strconv.Itoa(v)
    29  	case int64:
    30  		return strconv.FormatInt(v, 10)
    31  	case float64:
    32  		switch {
    33  		case math.IsNaN(v):
    34  			return "NULL"
    35  		case math.IsInf(v, 1):
    36  			return "9.0e999"
    37  		case math.IsInf(v, -1):
    38  			return "-9.0e999"
    39  		}
    40  		return strconv.FormatFloat(v, 'g', -1, 64)
    41  	case time.Time:
    42  		return "'" + v.Format(time.RFC3339Nano) + "'"
    43  
    44  	case string:
    45  		if strings.IndexByte(v, 0) >= 0 {
    46  			break
    47  		}
    48  
    49  		buf := make([]byte, 2+len(v)+strings.Count(v, "'"))
    50  		buf[0] = '\''
    51  		i := 1
    52  		for _, b := range []byte(v) {
    53  			if b == '\'' {
    54  				buf[i] = b
    55  				i += 1
    56  			}
    57  			buf[i] = b
    58  			i += 1
    59  		}
    60  		buf[i] = '\''
    61  		return unsafe.String(&buf[0], len(buf))
    62  
    63  	case []byte:
    64  		buf := make([]byte, 3+2*len(v))
    65  		buf[0] = 'x'
    66  		buf[1] = '\''
    67  		i := 2
    68  		for _, b := range v {
    69  			const hex = "0123456789ABCDEF"
    70  			buf[i+0] = hex[b/16]
    71  			buf[i+1] = hex[b%16]
    72  			i += 2
    73  		}
    74  		buf[i] = '\''
    75  		return unsafe.String(&buf[0], len(buf))
    76  
    77  	case ZeroBlob:
    78  		if v > ZeroBlob(1e9-3)/2 {
    79  			break
    80  		}
    81  
    82  		buf := bytes.Repeat([]byte("0"), int(3+2*int64(v)))
    83  		buf[0] = 'x'
    84  		buf[1] = '\''
    85  		buf[len(buf)-1] = '\''
    86  		return unsafe.String(&buf[0], len(buf))
    87  	}
    88  
    89  	panic(util.ValueErr)
    90  }
    91  
    92  // QuoteIdentifier escapes and quotes an identifier
    93  // making it safe to embed in SQL text.
    94  func QuoteIdentifier(id string) string {
    95  	if strings.IndexByte(id, 0) >= 0 {
    96  		panic(util.ValueErr)
    97  	}
    98  
    99  	buf := make([]byte, 2+len(id)+strings.Count(id, `"`))
   100  	buf[0] = '"'
   101  	i := 1
   102  	for _, b := range []byte(id) {
   103  		if b == '"' {
   104  			buf[i] = b
   105  			i += 1
   106  		}
   107  		buf[i] = b
   108  		i += 1
   109  	}
   110  	buf[i] = '"'
   111  	return unsafe.String(&buf[0], len(buf))
   112  }