github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/types/conversion.go (about) 1 /* 2 * Copyright 2016-2018 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package types 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "encoding/json" 23 "math" 24 "strconv" 25 "time" 26 27 "github.com/pkg/errors" 28 geom "github.com/twpayne/go-geom" 29 "github.com/twpayne/go-geom/encoding/geojson" 30 "github.com/twpayne/go-geom/encoding/wkb" 31 32 "github.com/dgraph-io/dgo/protos/api" 33 ) 34 35 // Convert converts the value to given scalar type. 36 func Convert(from Val, toID TypeID) (Val, error) { 37 var to Val 38 39 // sanity: we expect a value 40 data, ok := from.Value.([]byte) 41 if !ok { 42 return to, errors.Errorf("Invalid data to convert to %s", toID.Name()) 43 } 44 to = ValueForType(toID) 45 fromID := from.Tid 46 res := &to.Value 47 48 // Convert from-type to to-type and store in the result interface. 49 switch fromID { 50 case BinaryID: 51 { 52 // Unmarshal from Binary to type interfaces. 53 switch toID { 54 case BinaryID: 55 *res = data 56 case StringID, DefaultID: 57 *res = string(data) 58 case IntID: 59 if len(data) < 8 { 60 return to, errors.Errorf("Invalid data for int64 %v", data) 61 } 62 *res = int64(binary.LittleEndian.Uint64(data)) 63 case FloatID: 64 if len(data) < 8 { 65 return to, errors.Errorf("Invalid data for float %v", data) 66 } 67 i := binary.LittleEndian.Uint64(data) 68 *res = math.Float64frombits(i) 69 case BoolID: 70 if len(data) == 0 || data[0] == 0 { 71 *res = false 72 return to, nil 73 } else if data[0] == 1 { 74 *res = true 75 return to, nil 76 } 77 return to, errors.Errorf("Invalid value for bool %v", data[0]) 78 case DateTimeID: 79 var t time.Time 80 if err := t.UnmarshalBinary(data); err != nil { 81 return to, err 82 } 83 *res = t 84 case GeoID: 85 w, err := wkb.Unmarshal(data) 86 if err != nil { 87 return to, err 88 } 89 *res = w 90 case PasswordID: 91 *res = string(data) 92 default: 93 return to, cantConvert(fromID, toID) 94 } 95 } 96 case StringID, DefaultID: 97 { 98 vc := string(data) 99 switch toID { 100 case BinaryID: 101 *res = []byte(vc) 102 case IntID: 103 val, err := strconv.ParseInt(vc, 10, 64) 104 if err != nil { 105 return to, err 106 } 107 *res = val 108 case FloatID: 109 val, err := strconv.ParseFloat(vc, 64) 110 if err != nil { 111 return to, err 112 } 113 if math.IsNaN(val) { 114 return to, errors.Errorf("Got invalid value: NaN") 115 } 116 *res = val 117 case StringID, DefaultID: 118 *res = vc 119 case BoolID: 120 val, err := strconv.ParseBool(vc) 121 if err != nil { 122 return to, err 123 } 124 *res = val 125 case DateTimeID: 126 t, err := ParseTime(vc) 127 if err != nil { 128 return to, err 129 } 130 *res = t 131 case GeoID: 132 var g geom.T 133 text := bytes.Replace([]byte(vc), []byte("'"), []byte("\""), -1) 134 if err := geojson.Unmarshal(text, &g); err != nil { 135 return to, 136 errors.Wrapf(err, "Error while unmarshalling: [%s] as geojson", vc) 137 } 138 *res = g 139 case PasswordID: 140 p, err := Encrypt(vc) 141 if err != nil { 142 return to, err 143 } 144 *res = p 145 default: 146 return to, cantConvert(fromID, toID) 147 } 148 } 149 case IntID: 150 { 151 if len(data) < 8 { 152 return to, errors.Errorf("Invalid data for int64 %v", data) 153 } 154 vc := int64(binary.LittleEndian.Uint64(data)) 155 switch toID { 156 case IntID: 157 *res = vc 158 case BinaryID: 159 var bs [8]byte 160 binary.LittleEndian.PutUint64(bs[:], uint64(vc)) 161 *res = bs[:] 162 case FloatID: 163 *res = float64(vc) 164 case BoolID: 165 *res = vc != 0 166 case StringID, DefaultID: 167 *res = strconv.FormatInt(vc, 10) 168 case DateTimeID: 169 *res = time.Unix(vc, 0).UTC() 170 default: 171 return to, cantConvert(fromID, toID) 172 } 173 } 174 case FloatID: 175 { 176 if len(data) < 8 { 177 return to, errors.Errorf("Invalid data for float %v", data) 178 } 179 i := binary.LittleEndian.Uint64(data) 180 vc := math.Float64frombits(i) 181 switch toID { 182 case FloatID: 183 *res = vc 184 case BinaryID: 185 var bs [8]byte 186 u := math.Float64bits(vc) 187 binary.LittleEndian.PutUint64(bs[:], u) 188 *res = bs[:] 189 case IntID: 190 if vc > math.MaxInt64 || vc < math.MinInt64 || math.IsNaN(vc) { 191 return to, errors.Errorf("Float out of int64 range") 192 } 193 *res = int64(vc) 194 case BoolID: 195 *res = vc != 0 196 case StringID, DefaultID: 197 *res = strconv.FormatFloat(vc, 'G', -1, 64) 198 case DateTimeID: 199 secs := int64(vc) 200 fracSecs := vc - float64(secs) 201 nsecs := int64(fracSecs * nanoSecondsInSec) 202 *res = time.Unix(secs, nsecs).UTC() 203 default: 204 return to, cantConvert(fromID, toID) 205 } 206 } 207 case BoolID: 208 { 209 var vc bool 210 if len(data) == 0 || data[0] > 1 { 211 return to, errors.Errorf("Invalid value for bool %v", data) 212 } 213 vc = data[0] == 1 214 215 switch toID { 216 case BoolID: 217 *res = vc 218 case BinaryID: 219 *res = []byte{0} 220 if vc { 221 *res = []byte{1} 222 } 223 case IntID: 224 *res = int64(0) 225 if vc { 226 *res = int64(1) 227 } 228 case FloatID: 229 *res = float64(0) 230 if vc { 231 *res = float64(1) 232 } 233 case StringID, DefaultID: 234 *res = strconv.FormatBool(vc) 235 default: 236 return to, cantConvert(fromID, toID) 237 } 238 } 239 case DateTimeID: 240 { 241 var t time.Time 242 if err := t.UnmarshalBinary(data); err != nil { 243 return to, err 244 } 245 switch toID { 246 case DateTimeID: 247 *res = t 248 case BinaryID: 249 r, err := t.MarshalBinary() 250 if err != nil { 251 return to, err 252 } 253 *res = r 254 case StringID, DefaultID: 255 val, err := t.MarshalText() 256 if err != nil { 257 return to, err 258 } 259 *res = string(val) 260 case IntID: 261 *res = t.Unix() 262 case FloatID: 263 *res = float64(t.UnixNano()) / float64(nanoSecondsInSec) 264 default: 265 return to, cantConvert(fromID, toID) 266 } 267 } 268 case GeoID: 269 { 270 vc, err := wkb.Unmarshal(data) 271 if err != nil { 272 return to, err 273 } 274 switch toID { 275 case GeoID: 276 *res = vc 277 case BinaryID: 278 r, err := wkb.Marshal(vc, binary.LittleEndian) 279 if err != nil { 280 return to, err 281 } 282 *res = r 283 case StringID, DefaultID: 284 val, err := geojson.Marshal(vc) 285 if err != nil { 286 return to, nil 287 } 288 *res = string(bytes.Replace(val, []byte("\""), []byte("'"), -1)) 289 default: 290 return to, cantConvert(fromID, toID) 291 } 292 } 293 case PasswordID: 294 { 295 vc := string(data) 296 switch toID { 297 case BinaryID: 298 *res = []byte(vc) 299 case StringID, PasswordID: 300 *res = vc 301 default: 302 return to, cantConvert(fromID, toID) 303 } 304 } 305 default: 306 return to, cantConvert(fromID, toID) 307 } 308 return to, nil 309 } 310 311 func Marshal(from Val, to *Val) error { 312 if to == nil { 313 return errors.Errorf("Invalid conversion %s to nil", from.Tid.Name()) 314 } 315 316 fromID := from.Tid 317 toID := to.Tid 318 val := from.Value 319 res := &to.Value 320 321 // This is a default value from sg.fillVars, don't convert it's empty. 322 // Fixes issue #2980. 323 if val == nil { 324 *to = ValueForType(toID) 325 return nil 326 } 327 328 switch fromID { 329 case BinaryID: 330 vc := val.([]byte) 331 switch toID { 332 case StringID, DefaultID: 333 *res = string(vc) 334 case BinaryID: 335 *res = vc 336 default: 337 return cantConvert(fromID, toID) 338 } 339 case StringID, DefaultID: 340 vc := val.(string) 341 switch toID { 342 case StringID, DefaultID: 343 *res = vc 344 case BinaryID: 345 *res = []byte(vc) 346 default: 347 return cantConvert(fromID, toID) 348 } 349 case IntID: 350 vc := val.(int64) 351 switch toID { 352 case StringID, DefaultID: 353 *res = strconv.FormatInt(vc, 10) 354 case BinaryID: 355 var bs [8]byte 356 binary.LittleEndian.PutUint64(bs[:], uint64(vc)) 357 *res = bs[:] 358 default: 359 return cantConvert(fromID, toID) 360 } 361 case FloatID: 362 vc := val.(float64) 363 switch toID { 364 case StringID, DefaultID: 365 *res = strconv.FormatFloat(vc, 'G', -1, 64) 366 case BinaryID: 367 var bs [8]byte 368 u := math.Float64bits(vc) 369 binary.LittleEndian.PutUint64(bs[:], u) 370 *res = bs[:] 371 default: 372 return cantConvert(fromID, toID) 373 } 374 case BoolID: 375 vc := val.(bool) 376 switch toID { 377 case StringID, DefaultID: 378 *res = strconv.FormatBool(vc) 379 case BinaryID: 380 *res = []byte{0} 381 if vc { 382 *res = []byte{1} 383 } 384 default: 385 return cantConvert(fromID, toID) 386 } 387 case DateTimeID: 388 vc := val.(time.Time) 389 switch toID { 390 case StringID, DefaultID: 391 val, err := vc.MarshalText() 392 if err != nil { 393 return err 394 } 395 *res = string(val) 396 case BinaryID: 397 r, err := vc.MarshalBinary() 398 if err != nil { 399 return err 400 } 401 *res = r 402 default: 403 return cantConvert(fromID, toID) 404 } 405 case GeoID: 406 vc, ok := val.(geom.T) 407 if !ok { 408 return errors.Errorf("Expected a Geo type") 409 } 410 switch toID { 411 case BinaryID: 412 r, err := wkb.Marshal(vc, binary.LittleEndian) 413 if err != nil { 414 return err 415 } 416 *res = r 417 case StringID, DefaultID: 418 val, err := geojson.Marshal(vc) 419 if err != nil { 420 return nil 421 } 422 *res = string(bytes.Replace(val, []byte("\""), []byte("'"), -1)) 423 default: 424 return cantConvert(fromID, toID) 425 } 426 case PasswordID: 427 vc := val.(string) 428 switch toID { 429 case StringID: 430 *res = vc 431 case BinaryID: 432 *res = []byte(vc) 433 default: 434 return cantConvert(fromID, toID) 435 } 436 default: 437 return cantConvert(fromID, toID) 438 } 439 return nil 440 } 441 442 // ObjectValue converts into api.Value. 443 func ObjectValue(id TypeID, value interface{}) (*api.Value, error) { 444 def := &api.Value{Val: &api.Value_StrVal{StrVal: ""}} 445 var ok bool 446 // Lets set the object value according to the storage type. 447 switch id { 448 case StringID: 449 var v string 450 if v, ok = value.(string); !ok { 451 return def, errors.Errorf("Expected value of type string. Got : %v", value) 452 } 453 return &api.Value{Val: &api.Value_StrVal{StrVal: v}}, nil 454 case DefaultID: 455 var v string 456 if v, ok = value.(string); !ok { 457 return def, errors.Errorf("Expected value of type string. Got : %v", value) 458 } 459 return &api.Value{Val: &api.Value_DefaultVal{DefaultVal: v}}, nil 460 case IntID: 461 var v int64 462 if v, ok = value.(int64); !ok { 463 return def, errors.Errorf("Expected value of type int64. Got : %v", value) 464 } 465 return &api.Value{Val: &api.Value_IntVal{IntVal: v}}, nil 466 case FloatID: 467 var v float64 468 if v, ok = value.(float64); !ok { 469 return def, errors.Errorf("Expected value of type float64. Got : %v", value) 470 } 471 return &api.Value{Val: &api.Value_DoubleVal{DoubleVal: v}}, nil 472 case BoolID: 473 var v bool 474 if v, ok = value.(bool); !ok { 475 return def, errors.Errorf("Expected value of type bool. Got : %v", value) 476 } 477 return &api.Value{Val: &api.Value_BoolVal{BoolVal: v}}, nil 478 case BinaryID: 479 var v []byte 480 if v, ok = value.([]byte); !ok { 481 return def, errors.Errorf("Expected value of type []byte. Got : %v", value) 482 } 483 return &api.Value{Val: &api.Value_BytesVal{BytesVal: v}}, nil 484 // Geo and datetime are stored in binary format in the N-Quad, so lets 485 // convert them here. 486 case GeoID: 487 b, err := toBinary(id, value) 488 if err != nil { 489 return def, err 490 } 491 return &api.Value{Val: &api.Value_GeoVal{GeoVal: b}}, nil 492 case DateTimeID: 493 b, err := toBinary(id, value) 494 if err != nil { 495 return def, err 496 } 497 return &api.Value{Val: &api.Value_DatetimeVal{DatetimeVal: b}}, nil 498 case PasswordID: 499 var v string 500 if v, ok = value.(string); !ok { 501 return def, errors.Errorf("Expected value of type password. Got : %v", value) 502 } 503 return &api.Value{Val: &api.Value_PasswordVal{PasswordVal: v}}, nil 504 default: 505 return def, errors.Errorf("ObjectValue not available for: %v", id) 506 } 507 } 508 509 func toBinary(id TypeID, b interface{}) ([]byte, error) { 510 p1 := ValueForType(BinaryID) 511 if err := Marshal(Val{id, b}, &p1); err != nil { 512 return nil, err 513 } 514 return p1.Value.([]byte), nil 515 } 516 517 func cantConvert(from TypeID, to TypeID) error { 518 return errors.Errorf("Cannot convert %s to type %s", from.Name(), to.Name()) 519 } 520 521 // MarshalJSON makes Val satisfy the json.Marshaler interface. 522 func (v Val) MarshalJSON() ([]byte, error) { 523 switch v.Tid { 524 case IntID: 525 return json.Marshal(v.Value.(int64)) 526 case BoolID: 527 return json.Marshal(v.Value.(bool)) 528 case FloatID: 529 return json.Marshal(v.Value.(float64)) 530 case DateTimeID: 531 return json.Marshal(v.Value.(time.Time)) 532 case GeoID: 533 return geojson.Marshal(v.Value.(geom.T)) 534 case StringID, DefaultID: 535 return json.Marshal(v.Safe().(string)) 536 case PasswordID: 537 return json.Marshal(v.Value.(string)) 538 } 539 return nil, errors.Errorf("Invalid type for MarshalJSON: %v", v.Tid) 540 }