...

Source file src/github.com/lib/pq/hstore/hstore.go

Documentation: github.com/lib/pq/hstore

     1  package hstore
     2  
     3  import (
     4  	"database/sql"
     5  	"database/sql/driver"
     6  	"strings"
     7  )
     8  
     9  // Hstore is a wrapper for transferring Hstore values back and forth easily.
    10  type Hstore struct {
    11  	Map map[string]sql.NullString
    12  }
    13  
    14  // escapes and quotes hstore keys/values
    15  // s should be a sql.NullString or string
    16  func hQuote(s interface{}) string {
    17  	var str string
    18  	switch v := s.(type) {
    19  	case sql.NullString:
    20  		if !v.Valid {
    21  			return "NULL"
    22  		}
    23  		str = v.String
    24  	case string:
    25  		str = v
    26  	default:
    27  		panic("not a string or sql.NullString")
    28  	}
    29  
    30  	str = strings.Replace(str, "\\", "\\\\", -1)
    31  	return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"`
    32  }
    33  
    34  // Scan implements the Scanner interface.
    35  //
    36  // Note h.Map is reallocated before the scan to clear existing values. If the
    37  // hstore column's database value is NULL, then h.Map is set to nil instead.
    38  func (h *Hstore) Scan(value interface{}) error {
    39  	if value == nil {
    40  		h.Map = nil
    41  		return nil
    42  	}
    43  	h.Map = make(map[string]sql.NullString)
    44  	var b byte
    45  	pair := [][]byte{{}, {}}
    46  	pi := 0
    47  	inQuote := false
    48  	didQuote := false
    49  	sawSlash := false
    50  	bindex := 0
    51  	for bindex, b = range value.([]byte) {
    52  		if sawSlash {
    53  			pair[pi] = append(pair[pi], b)
    54  			sawSlash = false
    55  			continue
    56  		}
    57  
    58  		switch b {
    59  		case '\\':
    60  			sawSlash = true
    61  			continue
    62  		case '"':
    63  			inQuote = !inQuote
    64  			if !didQuote {
    65  				didQuote = true
    66  			}
    67  			continue
    68  		default:
    69  			if !inQuote {
    70  				switch b {
    71  				case ' ', '\t', '\n', '\r':
    72  					continue
    73  				case '=':
    74  					continue
    75  				case '>':
    76  					pi = 1
    77  					didQuote = false
    78  					continue
    79  				case ',':
    80  					s := string(pair[1])
    81  					if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" {
    82  						h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false}
    83  					} else {
    84  						h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true}
    85  					}
    86  					pair[0] = []byte{}
    87  					pair[1] = []byte{}
    88  					pi = 0
    89  					continue
    90  				}
    91  			}
    92  		}
    93  		pair[pi] = append(pair[pi], b)
    94  	}
    95  	if bindex > 0 {
    96  		s := string(pair[1])
    97  		if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" {
    98  			h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false}
    99  		} else {
   100  			h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true}
   101  		}
   102  	}
   103  	return nil
   104  }
   105  
   106  // Value implements the driver Valuer interface. Note if h.Map is nil, the
   107  // database column value will be set to NULL.
   108  func (h Hstore) Value() (driver.Value, error) {
   109  	if h.Map == nil {
   110  		return nil, nil
   111  	}
   112  	parts := []string{}
   113  	for key, val := range h.Map {
   114  		thispart := hQuote(key) + "=>" + hQuote(val)
   115  		parts = append(parts, thispart)
   116  	}
   117  	return []byte(strings.Join(parts, ",")), nil
   118  }
   119  

View as plain text