github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/json/noms_json_value.go (about) 1 // Copyright 2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package json 16 17 import ( 18 "context" 19 "errors" 20 "strings" 21 22 "github.com/dolthub/go-mysql-server/sql" 23 24 "github.com/dolthub/dolt/go/store/types" 25 ) 26 27 var ErrUnexpectedJSONTypeIn = errors.New("unexpected type during JSON marshalling") 28 var ErrUnexpectedJSONTypeOut = errors.New("unexpected type during JSON unmarshalling") 29 30 const ( 31 JSONNull = "null" 32 ) 33 34 // NomsJSON is a type alias for types.JSON. The alias allows MySQL-specific 35 // logic to be kept separate from the storage-layer code in pkg types. 36 type NomsJSON types.JSON 37 38 var _ sql.JSONValue = NomsJSON{} 39 40 // NomsJSONFromJSONValue converts a sql.JSONValue to a NomsJSON value. 41 func NomsJSONFromJSONValue(ctx context.Context, vrw types.ValueReadWriter, val sql.JSONValue) (NomsJSON, error) { 42 if noms, ok := val.(NomsJSON); ok { 43 return noms, nil 44 } 45 46 sqlDoc, err := val.Unmarshall(sql.NewContext(ctx)) 47 if err != nil { 48 return NomsJSON{}, err 49 } 50 51 v, err := marshalJSON(ctx, vrw, sqlDoc.Val) 52 if err != nil { 53 return NomsJSON{}, err 54 } 55 56 doc, err := types.NewJSONDoc(vrw.Format(), vrw, v) 57 if err != nil { 58 return NomsJSON{}, err 59 } 60 61 return NomsJSON(doc), nil 62 } 63 64 func marshalJSON(ctx context.Context, vrw types.ValueReadWriter, val interface{}) (types.Value, error) { 65 if val == nil { 66 return types.NullValue, nil 67 } 68 69 switch val := val.(type) { 70 case []interface{}: 71 return marshalJSONArray(ctx, vrw, val) 72 case map[string]interface{}: 73 return marshalJSONObject(ctx, vrw, val) 74 case bool: 75 return types.Bool(val), nil 76 case string: 77 return types.String(val), nil 78 case float64: 79 return types.Float(val), nil 80 81 // TODO(andy): unclear how to handle these 82 case float32: 83 return types.Float(val), nil 84 case int: 85 return types.Float(val), nil 86 case int8: 87 return types.Float(val), nil 88 case int16: 89 return types.Float(val), nil 90 case int32: 91 return types.Float(val), nil 92 case int64: 93 return types.Float(val), nil 94 case uint: 95 return types.Float(val), nil 96 case uint8: 97 return types.Float(val), nil 98 case uint16: 99 return types.Float(val), nil 100 case uint32: 101 return types.Float(val), nil 102 case uint64: 103 return types.Float(val), nil 104 default: 105 return nil, ErrUnexpectedJSONTypeIn 106 } 107 } 108 109 func marshalJSONArray(ctx context.Context, vrw types.ValueReadWriter, arr []interface{}) (types.Value, error) { 110 var err error 111 vals := make([]types.Value, len(arr)) 112 for i, elem := range arr { 113 vals[i], err = marshalJSON(ctx, vrw, elem) 114 if err != nil { 115 return nil, err 116 } 117 } 118 return types.NewList(ctx, vrw, vals...) 119 } 120 121 func marshalJSONObject(ctx context.Context, vrw types.ValueReadWriter, obj map[string]interface{}) (types.Value, error) { 122 var err error 123 i := 0 124 vals := make([]types.Value, len(obj)*2) 125 for k, v := range obj { 126 vals[i] = types.String(k) 127 vals[i+1], err = marshalJSON(ctx, vrw, v) 128 if err != nil { 129 return nil, err 130 } 131 i += 2 132 } 133 return types.NewMap(ctx, vrw, vals...) 134 } 135 136 // Unmarshall implements the sql.JSONValue interface. 137 func (v NomsJSON) Unmarshall(ctx *sql.Context) (doc sql.JSONDocument, err error) { 138 nomsVal, err := types.JSON(v).Inner() 139 if err != nil { 140 return sql.JSONDocument{}, err 141 } 142 143 val, err := unmarshalJSON(ctx, nomsVal) 144 if err != nil { 145 return sql.JSONDocument{}, err 146 } 147 148 return sql.JSONDocument{Val: val}, nil 149 } 150 151 func unmarshalJSON(ctx context.Context, val types.Value) (interface{}, error) { 152 switch val := val.(type) { 153 case types.Null: 154 return nil, nil 155 case types.Bool: 156 return bool(val), nil 157 case types.String: 158 return string(val), nil 159 case types.Float: 160 return float64(val), nil 161 case types.List: 162 return unmarshalJSONArray(ctx, val) 163 case types.Map: 164 return unmarshalJSONObject(ctx, val) 165 default: 166 return nil, ErrUnexpectedJSONTypeIn 167 } 168 } 169 170 func unmarshalJSONArray(ctx context.Context, l types.List) (arr []interface{}, err error) { 171 arr = make([]interface{}, l.Len()) 172 err = l.Iter(ctx, func(v types.Value, index uint64) (stop bool, err error) { 173 arr[index], err = unmarshalJSON(ctx, v) 174 return 175 }) 176 return 177 } 178 179 func unmarshalJSONObject(ctx context.Context, m types.Map) (obj map[string]interface{}, err error) { 180 obj = make(map[string]interface{}, m.Len()) 181 err = m.Iter(ctx, func(key, value types.Value) (stop bool, err error) { 182 ks, ok := key.(types.String) 183 if !ok { 184 return false, ErrUnexpectedJSONTypeOut 185 } 186 187 obj[string(ks)], err = unmarshalJSON(ctx, value) 188 return 189 }) 190 return 191 } 192 193 // Compare implements the sql.JSONValue interface. 194 func (v NomsJSON) Compare(ctx *sql.Context, other sql.JSONValue) (cmp int, err error) { 195 noms, ok := other.(NomsJSON) 196 if !ok { 197 doc, err := v.Unmarshall(ctx) 198 if err != nil { 199 return 0, err 200 } 201 return doc.Compare(ctx, other) 202 } 203 204 return types.JSON(v).Compare(types.JSON(noms)) 205 } 206 207 // ToString implements the sql.JSONValue interface. 208 func (v NomsJSON) ToString(ctx *sql.Context) (string, error) { 209 jd, err := types.JSON(v).Inner() 210 if err != nil { 211 return "", err 212 } 213 214 sb := &strings.Builder{} 215 if err = marshalToString(ctx, sb, jd); err != nil { 216 return "", err 217 } 218 219 return sb.String(), nil 220 } 221 222 func marshalToString(ctx context.Context, sb *strings.Builder, val types.Value) (err error) { 223 switch val := val.(type) { 224 case types.Null: 225 sb.WriteString(JSONNull) 226 227 case types.Bool: 228 sb.WriteString(val.HumanReadableString()) 229 230 case types.String: 231 sb.WriteString(val.HumanReadableString()) 232 233 case types.Float: 234 sb.WriteString(val.HumanReadableString()) 235 236 case types.List: 237 sb.WriteRune('[') 238 seenOne := false 239 err = val.Iter(ctx, func(v types.Value, _ uint64) (stop bool, err error) { 240 if seenOne { 241 sb.WriteString(", ") 242 } 243 seenOne = true 244 err = marshalToString(ctx, sb, v) 245 return 246 }) 247 if err != nil { 248 return err 249 } 250 sb.WriteRune(']') 251 252 case types.Map: 253 sb.WriteRune('{') 254 seenOne := false 255 err = val.Iter(ctx, func(k, v types.Value) (stop bool, err error) { 256 if seenOne { 257 sb.WriteString(", ") 258 } 259 seenOne = true 260 261 sb.WriteString(k.HumanReadableString()) 262 sb.WriteString(": ") 263 264 err = marshalToString(ctx, sb, v) 265 return 266 }) 267 if err != nil { 268 return err 269 } 270 sb.WriteRune('}') 271 272 default: 273 err = ErrUnexpectedJSONTypeOut 274 } 275 return 276 }