github.com/Ali-iotechsys/sqlboiler/v4@v4.0.0-20221208124957-6aec9a5f1f71/types/hstore.go (about)

     1  // Copyright (c) 2011-2013, 'pq' Contributors Portions Copyright (C) 2011 Blake Mizerany. MIT license.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining
     4  // a copy of this software and associated documentation files (the "Software"),
     5  // to deal in the Software without restriction, including without limitation the
     6  // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software
     8  // is furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included
    11  // in all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    14  // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    15  // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    16  // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    17  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    18  // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    19  
    20  package types
    21  
    22  import (
    23  	"database/sql"
    24  	"database/sql/driver"
    25  	"strings"
    26  
    27  	"github.com/volatiletech/null/v8"
    28  	"github.com/volatiletech/randomize"
    29  )
    30  
    31  // HStore is a wrapper for transferring HStore values back and forth easily.
    32  type HStore map[string]null.String
    33  
    34  // escapes and quotes hstore keys/values
    35  // s should be a sql.NullString or string
    36  func hQuote(s interface{}) string {
    37  	var str string
    38  	switch v := s.(type) {
    39  	case null.String:
    40  		if !v.Valid {
    41  			return "NULL"
    42  		}
    43  		str = v.String
    44  	case sql.NullString:
    45  		if !v.Valid {
    46  			return "NULL"
    47  		}
    48  		str = v.String
    49  	case string:
    50  		str = v
    51  	default:
    52  		panic("not a string or sql.NullString")
    53  	}
    54  
    55  	str = strings.ReplaceAll(str, "\\", "\\\\")
    56  	return `"` + strings.ReplaceAll(str, "\"", "\\\"") + `"`
    57  }
    58  
    59  // Scan implements the Scanner interface.
    60  //
    61  // Note h is reallocated before the scan to clear existing values. If the
    62  // hstore column's database value is NULL, then h is set to nil instead.
    63  func (h *HStore) Scan(value interface{}) error {
    64  	if value == nil {
    65  		h = nil
    66  		return nil
    67  	}
    68  	*h = make(map[string]null.String)
    69  	var b byte
    70  	pair := [][]byte{{}, {}}
    71  	pi := 0
    72  	inQuote := false
    73  	didQuote := false
    74  	sawSlash := false
    75  	bindex := 0
    76  	for bindex, b = range value.([]byte) {
    77  		if sawSlash {
    78  			pair[pi] = append(pair[pi], b)
    79  			sawSlash = false
    80  			continue
    81  		}
    82  
    83  		switch b {
    84  		case '\\':
    85  			sawSlash = true
    86  			continue
    87  		case '"':
    88  			inQuote = !inQuote
    89  			if !didQuote {
    90  				didQuote = true
    91  			}
    92  			continue
    93  		default:
    94  			if !inQuote {
    95  				switch b {
    96  				case ' ', '\t', '\n', '\r':
    97  					continue
    98  				case '=':
    99  					continue
   100  				case '>':
   101  					pi = 1
   102  					didQuote = false
   103  					continue
   104  				case ',':
   105  					s := string(pair[1])
   106  					if !didQuote && len(s) == 4 && strings.EqualFold(s, "null") {
   107  						(*h)[string(pair[0])] = null.String{String: "", Valid: false}
   108  					} else {
   109  						(*h)[string(pair[0])] = null.String{String: string(pair[1]), Valid: true}
   110  					}
   111  					pair[0] = []byte{}
   112  					pair[1] = []byte{}
   113  					pi = 0
   114  					continue
   115  				}
   116  			}
   117  		}
   118  		pair[pi] = append(pair[pi], b)
   119  	}
   120  	if bindex > 0 {
   121  		s := string(pair[1])
   122  		if !didQuote && len(s) == 4 && strings.EqualFold(s, "null") {
   123  			(*h)[string(pair[0])] = null.String{String: "", Valid: false}
   124  		} else {
   125  			(*h)[string(pair[0])] = null.String{String: string(pair[1]), Valid: true}
   126  		}
   127  	}
   128  	return nil
   129  }
   130  
   131  // Value implements the driver Valuer interface. Note if h is nil, the
   132  // database column value will be set to NULL.
   133  func (h HStore) Value() (driver.Value, error) {
   134  	if h == nil {
   135  		return nil, nil
   136  	}
   137  	parts := []string{}
   138  	for key, val := range h {
   139  		thispart := hQuote(key) + "=>" + hQuote(val)
   140  		parts = append(parts, thispart)
   141  	}
   142  	return []byte(strings.Join(parts, ",")), nil
   143  }
   144  
   145  // Randomize for sqlboiler
   146  func (h *HStore) Randomize(nextInt func() int64, fieldType string, shouldBeNull bool) {
   147  	if shouldBeNull {
   148  		*h = nil
   149  		return
   150  	}
   151  
   152  	*h = make(map[string]null.String)
   153  	(*h)[randomize.Str(nextInt, 3)] = null.String{String: randomize.Str(nextInt, 3), Valid: nextInt()%3 == 0}
   154  	(*h)[randomize.Str(nextInt, 3)] = null.String{String: randomize.Str(nextInt, 3), Valid: nextInt()%3 == 0}
   155  }