github.com/dolthub/go-mysql-server@v0.18.0/sql/types/json_value.go (about) 1 // Copyright 2022 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 types 16 17 import ( 18 "database/sql/driver" 19 "fmt" 20 "io" 21 "regexp" 22 "sort" 23 "strconv" 24 "strings" 25 26 "github.com/dolthub/jsonpath" 27 "github.com/shopspring/decimal" 28 "golang.org/x/exp/maps" 29 30 "github.com/dolthub/go-mysql-server/sql" 31 ) 32 33 type JSONStringer interface { 34 JSONString() (string, error) 35 } 36 37 type JsonObject = map[string]interface{} 38 type JsonArray = []interface{} 39 40 type MutableJSON interface { 41 // Insert Adds the value at the given path, only if it is not present. Updated value returned, and bool indicating if 42 // a change was made. 43 Insert(path string, val sql.JSONWrapper) (MutableJSON, bool, error) 44 // Remove the value at the given path. Updated value returned, and bool indicating if a change was made. 45 Remove(path string) (MutableJSON, bool, error) 46 // Set the value at the given path. Updated value returned, and bool indicating if a change was made. 47 Set(path string, val sql.JSONWrapper) (MutableJSON, bool, error) 48 // Replace the value at the given path with the new value. If the path does not exist, no modification is made. 49 Replace(path string, val sql.JSONWrapper) (MutableJSON, bool, error) 50 // ArrayInsert inserts into the array object referenced by the given path. If the path does not exist, no modification is made. 51 ArrayInsert(path string, val sql.JSONWrapper) (MutableJSON, bool, error) 52 // ArrayAppend appends to an array object referenced by the given path. If the path does not exist, no modification is made, 53 // or if the path exists and is not an array, the element will be converted into an array and the element will be 54 // appended to it. 55 ArrayAppend(path string, val sql.JSONWrapper) (MutableJSON, bool, error) 56 } 57 58 type JSONDocument struct { 59 Val interface{} 60 } 61 62 var _ sql.JSONWrapper = JSONDocument{} 63 64 func (doc JSONDocument) ToInterface() interface{} { 65 return doc.Val 66 } 67 68 func (doc JSONDocument) Compare(other sql.JSONWrapper) (int, error) { 69 return CompareJSON(doc.Val, other.ToInterface()) 70 } 71 72 func (doc JSONDocument) JSONString() (string, error) { 73 return marshalToMySqlString(doc.Val) 74 } 75 76 // JSONDocument implements the fmt.Stringer interface. 77 func (doc JSONDocument) String() string { 78 result, err := doc.JSONString() 79 if err != nil { 80 return fmt.Sprintf("(Error marshalling JSON: %s, %s)", doc.Val, err.Error()) 81 } 82 return result 83 } 84 85 var _ sql.JSONWrapper = JSONDocument{} 86 var _ MutableJSON = JSONDocument{} 87 88 // Contains returns nil in case of a nil value for either the doc.Val or candidate. Otherwise 89 // it returns a bool 90 func (doc JSONDocument) Contains(candidate sql.JSONWrapper) (val interface{}, err error) { 91 return ContainsJSON(doc.Val, candidate.ToInterface()) 92 } 93 94 func (doc JSONDocument) Extract(path string) (sql.JSONWrapper, error) { 95 return LookupJSONValue(doc, path) 96 } 97 98 func LookupJSONValue(j sql.JSONWrapper, path string) (sql.JSONWrapper, error) { 99 if path == "$" { 100 // Special case the identity operation to handle a nil value for doc.Val 101 return j, nil 102 } 103 104 c, err := jsonpath.Compile(path) 105 if err != nil { 106 // Until we throw out jsonpath, let's at least make this error better. 107 if err.Error() == "should start with '$'" { 108 err = fmt.Errorf("Invalid JSON path expression. Path must start with '$', but received: '%s'", path) 109 } 110 return nil, err 111 } 112 113 // Lookup(obj) throws an error if obj is nil. We want lookups on a json null 114 // to always result in sql NULL, except in the case of the identity lookup 115 // $. 116 r := j.ToInterface() 117 if r == nil { 118 return nil, nil 119 } 120 121 val, err := c.Lookup(r) 122 if err != nil { 123 if strings.Contains(err.Error(), "key error") { 124 // A missing key results in a SQL null 125 return nil, nil 126 } 127 return nil, err 128 } 129 130 return JSONDocument{Val: val}, nil 131 } 132 133 var _ driver.Valuer = JSONDocument{} 134 135 // Value implements driver.Valuer for interoperability with other go libraries 136 func (doc JSONDocument) Value() (driver.Value, error) { 137 if doc.Val == nil { 138 return nil, nil 139 } 140 141 mysqlString, err := marshalToMySqlString(doc.Val) 142 if err != nil { 143 return nil, fmt.Errorf("failed to marshal document: %w", err) 144 } 145 146 return mysqlString, nil 147 } 148 149 func ConcatenateJSONValues(ctx *sql.Context, vals ...sql.JSONWrapper) (sql.JSONWrapper, error) { 150 arr := make(JsonArray, len(vals)) 151 for i, v := range vals { 152 arr[i] = v.ToInterface() 153 } 154 return JSONDocument{Val: arr}, nil 155 } 156 157 func ContainsJSON(a, b interface{}) (interface{}, error) { 158 if a == nil || b == nil { 159 return nil, nil 160 } 161 162 switch a := a.(type) { 163 case JsonArray: 164 return containsJSONArray(a, b) 165 case JsonObject: 166 return containsJSONObject(a, b) 167 case bool: 168 return containsJSONBool(a, b) 169 case string: 170 return containsJSONString(a, b) 171 case float64: 172 return containsJSONNumber(a, b) 173 default: 174 return false, sql.ErrInvalidType.New(a) 175 } 176 } 177 178 func containsJSONBool(a bool, b interface{}) (bool, error) { 179 switch b := b.(type) { 180 case bool: 181 return a == b, nil 182 default: 183 return false, nil 184 } 185 } 186 187 // containsJSONArray returns true if b is contained in the JSON array a. From the official 188 // MySQL docs: "A candidate array is contained in a target array if and only if every 189 // element in the candidate is contained in *some* element of the target. A candidate 190 // non-array is contained in a target array if and only if the candidate is contained 191 // in some element of the target." 192 // 193 // Examples: 194 // 195 // select json_contains('[1, [1, 2, 3], 10]', '[1, 10]'); => true 196 // select json_contains('[1, [1, 2, 3, 10]]', '[1, 10]'); => true 197 // select json_contains('[1, [1, 2, 3], [10]]', '[1, [10]]'); => true 198 func containsJSONArray(a JsonArray, b interface{}) (bool, error) { 199 if _, ok := b.(JsonArray); ok { 200 for _, bb := range b.(JsonArray) { 201 contains, err := containsJSONArray(a, bb) 202 if err != nil { 203 return false, err 204 } 205 if contains == false { 206 return false, nil 207 } 208 } 209 return true, nil 210 } else { 211 // A candidate non-array is contained in a target array if and only if the candidate is contained in some element of the target. 212 for _, aa := range a { 213 contains, err := ContainsJSON(aa, b) 214 if err != nil { 215 return false, err 216 } 217 if contains == true { 218 return true, nil 219 } 220 } 221 } 222 223 return false, nil 224 } 225 226 // containsJSONObject returns true if b is contained in the JSON object a. From the 227 // official MySQL docs: "A candidate object is contained in a target object if and only 228 // if for each key in the candidate there is a key with the same name in the target and 229 // the value associated with the candidate key is contained in the value associated with 230 // the target key." 231 // 232 // Examples: 233 // 234 // select json_contains('{"b": {"a": [1, 2, 3]}}', '{"a": [1]}'); => false 235 // select json_contains('{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}', '{"a": [1]}'); => true 236 // select json_contains('{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}', '{"a": []}'); => true 237 // select json_contains('{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}', '{"a": {}}'); => false 238 // select json_contains('{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}', '{"a": [2, 4]}'); => true 239 // select json_contains('{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}', '[2]'); => false 240 // select json_contains('{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}', '2'); => false 241 func containsJSONObject(a JsonObject, b interface{}) (bool, error) { 242 _, isMap := b.(JsonObject) 243 if !isMap { 244 // If b is a scalar or an array, json_contains always returns false when 245 // testing containment in a JSON object 246 return false, nil 247 } 248 249 for key, bvalue := range b.(JsonObject) { 250 avalue, ok := a[key] 251 if !ok { 252 return false, nil 253 } 254 255 contains, err := ContainsJSON(avalue, bvalue) 256 if err != nil { 257 return false, err 258 } 259 if contains == false { 260 return false, nil 261 } 262 } 263 return true, nil 264 } 265 266 func containsJSONString(a string, b interface{}) (bool, error) { 267 switch b := b.(type) { 268 case string: 269 return a == b, nil 270 default: 271 return false, nil 272 } 273 } 274 275 func containsJSONNumber(a float64, b interface{}) (bool, error) { 276 switch b := b.(type) { 277 case float64: 278 return a == b, nil 279 default: 280 return false, nil 281 } 282 } 283 284 // CompareJSON compares two JSON values. It returns 0 if the values are equal, -1 if a < b, and 1 if a > b. 285 // JSON values can be compared using the =, <, <=, >, >=, <>, !=, and <=> operators. BETWEEN IN() GREATEST() LEAST() are 286 // not yet supported with JSON values. 287 // 288 // For comparison of JSON and non-JSON values, the non-JSON value is first converted to JSON (see JsonType.Convert()). 289 // Comparison of JSON values takes place at two levels. The first level of comparison is based on the JSON types of the 290 // compared values. If the types differ, the comparison result is determined solely by which type has higher precedence. 291 // If the two values have the same JSON type, a second level of comparison occurs using type-specific rules. The 292 // following list shows the precedences of JSON types, from highest precedence to the lowest. (The type names are those 293 // returned by the JSON_TYPE() function.) Types shown together on a line have the same precedence. Any value having a 294 // JSON type listed earlier in the list compares greater than any value having a JSON type listed later in the list. 295 // 296 // BLOB, BIT, OPAQUE, DATETIME, TIME, DATE, BOOLEAN, ARRAY, OBJECT, STRING, INTEGER, DOUBLE, NULL 297 // TODO(andy): implement BLOB BIT OPAQUE DATETIME TIME DATE 298 // current precedence: BOOLEAN, ARRAY, OBJECT, STRING, DOUBLE, NULL 299 // 300 // For JSON values of the same precedence, the comparison rules are type specific: 301 // 302 // - ARRAY 303 // Two JSON arrays are equal if they have the same length and values in corresponding positions in the arrays are 304 // equal. If the arrays are not equal, their order is determined by the elements in the first position where there 305 // is a difference. The array with the smaller value in that position is ordered first. If all values of the 306 // shorter array are equal to the corresponding values in the longer array, the shorter array is ordered first. 307 // e.g. [] < ["a"] < ["ab"] < ["ab", "cd", "ef"] < ["ab", "ef"] 308 // 309 // - BOOLEAN 310 // The JSON false literal is less than the JSON true literal. 311 // 312 // - OBJECT 313 // Two JSON objects are equal if they have the same set of keys, and each key has the same value in both objects. 314 // The order of two objects that are not equal is unspecified but deterministic. 315 // e.g. {"a": 1, "b": 2} = {"b": 2, "a": 1} 316 // 317 // - STRING 318 // Strings are ordered lexically on the first N bytes of the utf8mb4 representation of the two strings being 319 // compared, where N is the length of the shorter string. If the first N bytes of the two strings are identical, 320 // the shorter string is considered smaller than the longer string. 321 // e.g. "a" < "ab" < "b" < "bc" 322 // This ordering is equivalent to the ordering of SQL strings with collation utf8mb4_bin. Because utf8mb4_bin is a 323 // binary collation, comparison of JSON values is case-sensitive: 324 // e.g. "A" < "a" 325 // 326 // - DOUBLE 327 // JSON values can contain exact-value numbers and approximate-value numbers. For a general discussion of these 328 // types of numbers, see Section 9.1.2, “Numeric Literals”. The rules for comparing native MySQL numeric types are 329 // discussed in Section 12.3, “Type Conversion in Expression Evaluation”, but the rules for comparing numbers 330 // within JSON values differ somewhat: 331 // 332 // - In a comparison between two columns that use the native MySQL INT and DOUBLE numeric types, respectively, 333 // it is known that all comparisons involve an integer and a double, so the integer is converted to double for 334 // all rows. That is, exact-value numbers are converted to approximate-value numbers. 335 // 336 // - On the other hand, if the query compares two JSON columns containing numbers, it cannot be known in advance 337 // whether numbers are integer or double. To provide the most consistent behavior across all rows, MySQL 338 // converts approximate-value numbers to exact-value numbers. The resulting ordering is consistent and does 339 // not lose precision for the exact-value numbers. 340 // e.g. 9223372036854775805 < 9223372036854775806 < 9223372036854775807 < 9.223372036854776e18 341 // = 9223372036854776000 < 9223372036854776001 342 // 343 // - NULL 344 // For comparison of any JSON value to SQL NULL, the result is UNKNOWN. 345 // 346 // TODO(andy): BLOB, BIT, OPAQUE, DATETIME, TIME, DATE, INTEGER 347 // 348 // https://dev.mysql.com/doc/refman/8.0/en/json.html#json-comparison 349 func CompareJSON(a, b interface{}) (int, error) { 350 if hasNulls, res := CompareNulls(b, a); hasNulls { 351 return res, nil 352 } 353 354 switch a := a.(type) { 355 case bool: 356 return compareJSONBool(a, b) 357 case JsonArray: 358 return compareJSONArray(a, b) 359 case JsonObject: 360 return compareJSONObject(a, b) 361 case string: 362 return compareJSONString(a, b) 363 case int: 364 return compareJSONNumber(float64(a), b) 365 case uint8: 366 return compareJSONNumber(float64(a), b) 367 case uint16: 368 return compareJSONNumber(float64(a), b) 369 case uint32: 370 return compareJSONNumber(float64(a), b) 371 case uint64: 372 return compareJSONNumber(float64(a), b) 373 case int8: 374 return compareJSONNumber(float64(a), b) 375 case int16: 376 return compareJSONNumber(float64(a), b) 377 case int32: 378 return compareJSONNumber(float64(a), b) 379 case int64: 380 return compareJSONNumber(float64(a), b) 381 case float32: 382 return compareJSONNumber(float64(a), b) 383 case float64: 384 return compareJSONNumber(a, b) 385 case decimal.Decimal: 386 af, _ := a.Float64() 387 return compareJSONNumber(af, b) 388 case sql.JSONWrapper: 389 if jw, ok := b.(sql.JSONWrapper); ok { 390 b = jw.ToInterface() 391 } 392 return CompareJSON(a.ToInterface(), b) 393 default: 394 return 0, sql.ErrInvalidType.New(a) 395 } 396 } 397 398 func compareJSONBool(a bool, b interface{}) (int, error) { 399 switch b := b.(type) { 400 case bool: 401 // The JSON false literal is less than the JSON true literal. 402 if a == b { 403 return 0, nil 404 } 405 if a { 406 // a > b 407 return 1, nil 408 } else { 409 // a < b 410 return -1, nil 411 } 412 413 default: 414 // a is higher precedence 415 return 1, nil 416 } 417 } 418 419 func compareJSONArray(a JsonArray, b interface{}) (int, error) { 420 switch b := b.(type) { 421 case bool: 422 // a is lower precedence 423 return -1, nil 424 425 case JsonArray: 426 // Two JSON arrays are equal if they have the same length and values in corresponding positions in the arrays 427 // are equal. If the arrays are not equal, their order is determined by the elements in the first position 428 // where there is a difference. The array with the smaller value in that position is ordered first. 429 for i, aa := range a { 430 // If all values of the shorter array are equal to the corresponding values in the longer array, 431 // the shorter array is ordered first (is less). 432 if i >= len(b) { 433 return 1, nil 434 } 435 436 cmp, err := CompareJSON(aa, b[i]) 437 if err != nil { 438 return 0, err 439 } 440 if cmp != 0 { 441 return cmp, nil 442 } 443 } 444 if len(a) < len(b) { 445 return -1, nil 446 } else { 447 return 0, nil 448 } 449 450 default: 451 // a is higher precedence 452 return 1, nil 453 } 454 } 455 456 func compareJSONObject(a JsonObject, b interface{}) (int, error) { 457 switch b := b.(type) { 458 case 459 bool, 460 JsonArray: 461 // a is lower precedence 462 return -1, nil 463 464 case JsonObject: 465 // Two JSON objects are equal if they have the same set of keys, and each key has the same value in both 466 // objects. The order of two objects that are not equal is unspecified but deterministic. 467 inter := jsonObjectKeyIntersection(a, b) 468 for _, key := range inter { 469 cmp, err := CompareJSON(a[key], b[key]) 470 if err != nil { 471 return 0, err 472 } 473 if cmp != 0 { 474 return cmp, nil 475 } 476 } 477 if len(a) == len(b) && len(a) == len(inter) { 478 return 0, nil 479 } 480 return jsonObjectDeterministicOrder(a, b, inter) 481 482 default: 483 // a is higher precedence 484 return 1, nil 485 } 486 } 487 488 func compareJSONString(a string, b interface{}) (int, error) { 489 switch b := b.(type) { 490 case 491 bool, 492 JsonArray, 493 JsonObject: 494 // a is lower precedence 495 return -1, nil 496 497 case string: 498 return strings.Compare(a, b), nil 499 500 default: 501 // a is higher precedence 502 return 1, nil 503 } 504 } 505 506 func compareJSONNumber(a float64, b interface{}) (int, error) { 507 switch b := b.(type) { 508 case 509 bool, 510 JsonArray, 511 JsonObject, 512 string: 513 // a is lower precedence 514 return -1, nil 515 case int: 516 return compareJSONNumber(a, float64(b)) 517 case uint8: 518 return compareJSONNumber(a, float64(b)) 519 case uint16: 520 return compareJSONNumber(a, float64(b)) 521 case uint32: 522 return compareJSONNumber(a, float64(b)) 523 case uint64: 524 return compareJSONNumber(a, float64(b)) 525 case int8: 526 return compareJSONNumber(a, float64(b)) 527 case int16: 528 return compareJSONNumber(a, float64(b)) 529 case int32: 530 return compareJSONNumber(a, float64(b)) 531 case int64: 532 return compareJSONNumber(a, float64(b)) 533 case float32: 534 return compareJSONNumber(a, float64(b)) 535 case float64: 536 if a > b { 537 return 1, nil 538 } 539 if a < b { 540 return -1, nil 541 } 542 return 0, nil 543 case decimal.Decimal: 544 bf, _ := b.Float64() 545 return compareJSONNumber(a, bf) 546 default: 547 // a is higher precedence 548 return 1, nil 549 } 550 } 551 552 func jsonObjectKeyIntersection(a, b JsonObject) (ks []string) { 553 for key := range a { 554 if _, ok := b[key]; ok { 555 ks = append(ks, key) 556 } 557 } 558 sort.Strings(ks) 559 return 560 } 561 562 func jsonObjectDeterministicOrder(a, b JsonObject, inter []string) (int, error) { 563 if len(a) > len(b) { 564 return 1, nil 565 } 566 if len(a) < len(b) { 567 return -1, nil 568 } 569 570 // if equal length, compare least non-intersection key 571 iset := make(map[string]bool) 572 for _, key := range inter { 573 iset[key] = true 574 } 575 576 var aa string 577 for key := range a { 578 if _, ok := iset[key]; !ok { 579 if key < aa || aa == "" { 580 aa = key 581 } 582 } 583 } 584 585 var bb string 586 for key := range b { 587 if _, ok := iset[key]; !ok { 588 if key < bb || bb == "" { 589 bb = key 590 } 591 } 592 } 593 594 return strings.Compare(aa, bb), nil 595 } 596 597 func (doc JSONDocument) Insert(path string, val sql.JSONWrapper) (MutableJSON, bool, error) { 598 path = strings.TrimSpace(path) 599 return doc.unwrapAndExecute(path, val, INSERT) 600 } 601 602 func (doc JSONDocument) Remove(path string) (MutableJSON, bool, error) { 603 path = strings.TrimSpace(path) 604 if path == "$" { 605 return nil, false, fmt.Errorf("The path expression '$' is not allowed in this context.") 606 } 607 608 return doc.unwrapAndExecute(path, nil, REMOVE) 609 } 610 611 func (doc JSONDocument) Set(path string, val sql.JSONWrapper) (MutableJSON, bool, error) { 612 path = strings.TrimSpace(path) 613 return doc.unwrapAndExecute(path, val, SET) 614 } 615 616 func (doc JSONDocument) Replace(path string, val sql.JSONWrapper) (MutableJSON, bool, error) { 617 path = strings.TrimSpace(path) 618 return doc.unwrapAndExecute(path, val, REPLACE) 619 } 620 621 func (doc JSONDocument) ArrayAppend(path string, val sql.JSONWrapper) (MutableJSON, bool, error) { 622 path = strings.TrimSpace(path) 623 return doc.unwrapAndExecute(path, val, ARRAY_APPEND) 624 } 625 626 func (doc JSONDocument) ArrayInsert(path string, val sql.JSONWrapper) (MutableJSON, bool, error) { 627 path = strings.TrimSpace(path) 628 629 if path == "$" { 630 // json_array_insert is the only function that produces an error for the '$' path no matter what the value is. 631 return nil, false, fmt.Errorf("Path expression is not a path to a cell in an array: $") 632 } 633 634 return doc.unwrapAndExecute(path, val, ARRAY_INSERT) 635 } 636 637 const ( 638 SET = iota 639 INSERT 640 REPLACE 641 REMOVE 642 ARRAY_APPEND 643 ARRAY_INSERT 644 ) 645 646 // unwrapAndExecute unwraps the JSONDocument and executes the given path on the unwrapped value. The path string passed 647 // in at this point should be unmodified. 648 func (doc JSONDocument) unwrapAndExecute(path string, val sql.JSONWrapper, mode int) (MutableJSON, bool, error) { 649 if path == "" { 650 return nil, false, fmt.Errorf("Invalid JSON path expression. Empty path") 651 } 652 653 var err error 654 var unmarshalled interface{} 655 if val != nil { 656 unmarshalled = val.ToInterface() 657 } else if mode != REMOVE { 658 return nil, false, fmt.Errorf("Invariant violation. value may not be nil") 659 } 660 661 if path[0] != '$' { 662 return nil, false, fmt.Errorf("Invalid JSON path expression. Path must start with '$'") 663 } 664 665 path = path[1:] 666 // Cursor is used to track how many characters have been parsed in the path. It is used to enable better error messages, 667 // and is passed as a pointer because some function parse a variable number of characters. 668 cursor := 1 669 670 resultRaw, changed, parseErr := walkPathAndUpdate(path, doc.Val, unmarshalled, mode, &cursor) 671 if parseErr != nil { 672 err = fmt.Errorf("%s at character %d of $%s", parseErr.msg, parseErr.character, path) 673 return nil, false, err 674 } 675 return JSONDocument{Val: resultRaw}, changed, nil 676 } 677 678 // parseErr is used to track errors that occur during parsing of the path, specifically to track the index of the character 679 // where we believe there is a problem. 680 type parseErr struct { 681 msg string 682 character int 683 } 684 685 // walkPathAndUpdate walks the path and updates the document. 686 // JSONPath Spec (as documented) https://dev.mysql.com/doc/refman/8.0/en/json.html#json-path-syntax 687 // 688 // This function recursively consumes the path until it reaches the end, at which point it applies the mutation operation. 689 // 690 // Currently, our implementation focuses specifically on the mutation operations, so '*','**', and range index paths are 691 // not supported. 692 func walkPathAndUpdate(path string, doc interface{}, val interface{}, mode int, cursor *int) (interface{}, bool, *parseErr) { 693 if path == "" { 694 // End of Path is kind of a special snowflake for each type and mode. 695 switch mode { 696 case SET, REPLACE: 697 return val, true, nil 698 case INSERT: 699 return doc, false, nil 700 case ARRAY_APPEND: 701 if arr, ok := doc.(JsonArray); ok { 702 doc = append(arr, val) 703 return doc, true, nil 704 } else { 705 // Otherwise, turn it into an array and append to it, and append to it. 706 doc = JsonArray{doc, val} 707 return doc, true, nil 708 } 709 case ARRAY_INSERT, REMOVE: 710 // Some mutations should never reach the end of the path. 711 return nil, false, &parseErr{msg: "Runtime error when processing json path", character: *cursor} 712 default: 713 return nil, false, &parseErr{msg: "Invalid JSON path expression. End of path reached", character: *cursor} 714 } 715 } 716 717 if path[0] == '.' { 718 path = path[1:] 719 *cursor = *cursor + 1 720 strMap, ok := doc.(JsonObject) 721 if !ok { 722 // json_array_insert is the only function that produces an error when the path is to an object which 723 // lookup fails in this way. All other functions return the document unchanged. Go figure. 724 if mode == ARRAY_INSERT { 725 return nil, false, &parseErr{msg: "A path expression is not a path to a cell in an array", character: *cursor} 726 } 727 // not a map, can't do anything. NoOp 728 return doc, false, nil 729 } 730 return updateObject(path, strMap, val, mode, cursor) 731 } else if path[0] == '[' { 732 *cursor = *cursor + 1 733 right := strings.Index(path, "]") 734 if right == -1 { 735 return nil, false, &parseErr{msg: "Invalid JSON path expression. Missing ']'", character: *cursor} 736 } 737 738 remaining := path[right+1:] 739 indexString := path[1:right] 740 741 if arr, ok := doc.(JsonArray); ok { 742 return updateArray(indexString, remaining, arr, val, mode, cursor) 743 } else { 744 return updateObjectTreatAsArray(indexString, doc, val, mode, cursor) 745 } 746 } else { 747 return nil, false, &parseErr{msg: "Invalid JSON path expression. Expected '.' or '['", character: *cursor} 748 } 749 } 750 751 // updateObject Take a JsonObject and update the value at the given path. If we are not at the end of the path, 752 // the object is looked up and the walkPathAndUpdate function is called recursively. 753 func updateObject(path string, doc JsonObject, val interface{}, mode int, cursor *int) (interface{}, bool, *parseErr) { 754 name, remainingPath, err := parseNameAfterDot(path, cursor) 755 if err != nil { 756 return nil, false, err 757 } 758 759 if remainingPath == "" { 760 if mode == ARRAY_APPEND { 761 newDoc, ok := doc[name] 762 if !ok { 763 // end of the path with a nil value - no-op 764 return doc, false, nil 765 } 766 newObj, changed, err := walkPathAndUpdate(remainingPath, newDoc, val, mode, cursor) 767 if err != nil { 768 return nil, false, err 769 } 770 if changed { 771 doc[name] = newObj 772 } 773 return doc, changed, nil 774 } 775 776 // Found an item, and it must be an array in one case only. 777 if mode == ARRAY_INSERT { 778 return nil, false, &parseErr{msg: "A path expression is not a path to a cell in an array", character: *cursor} 779 } 780 781 // does the name exist in the map? 782 updated := false 783 _, destructive := doc[name] 784 if mode == SET || 785 (!destructive && mode == INSERT) || 786 (destructive && mode == REPLACE) { 787 doc[name] = val 788 updated = true 789 } else if destructive && mode == REMOVE { 790 delete(doc, name) 791 updated = true 792 } 793 return doc, updated, nil 794 } else { 795 // go deeper. 796 newObj, changed, err := walkPathAndUpdate(remainingPath, doc[name], val, mode, cursor) 797 if err != nil { 798 return nil, false, err 799 } 800 if changed { 801 doc[name] = newObj 802 return doc, true, nil 803 } 804 return doc, false, nil 805 } 806 } 807 808 // compiled regex used to parse the name of a field after a '.' in a JSON path. 809 var regex = regexp.MustCompile(`^(\w+)(.*)$`) 810 811 // findNextUnescapedOccurrence finds the first unescaped occurrence of the provided byte in the string. 812 // This can be used to find an ASCII codepoint without any risk of false positives. This is because strings 813 // are UTF-8, and bytes in the ASCII range (<128) cannot appear as part of a multi-byte codepoint. 814 func findNextUnescapedOccurrence(path string, target byte) int { 815 index := 0 816 for { 817 if index >= len(path) { 818 return -1 819 } 820 if path[index] == '\\' { 821 index++ 822 } else if path[index] == target { 823 break 824 } 825 index++ 826 } 827 return index 828 } 829 830 // parseNameAfterDot parses the json path immediately after a '.'. It returns the name of the field and the remaining path, 831 // and modifies the cursor to point to the end of the parsed path. 832 func parseNameAfterDot(path string, cursor *int) (name string, remainingPath string, err *parseErr) { 833 if path == "" { 834 return "", "", &parseErr{msg: "Invalid JSON path expression. Expected field name after '.'", character: *cursor} 835 } 836 837 if path[0] == '"' { 838 right := findNextUnescapedOccurrence(path[1:], '"') 839 if right < 0 { 840 return "", "", &parseErr{msg: "Invalid JSON path expression. '\"' expected", character: *cursor} 841 } 842 name = path[1 : right+1] 843 // if the name in the path contains escaped double quotes, unescape them. 844 name = strings.Replace(name, `\"`, `"`, -1) 845 remainingPath = path[right+2:] 846 *cursor = *cursor + right + 2 847 } else { 848 matches := regex.FindStringSubmatch(path) 849 if len(matches) != 3 { 850 return "", "", &parseErr{msg: "Invalid JSON path expression. Expected field name after '.'", character: *cursor} 851 } 852 name = matches[1] 853 remainingPath = matches[2] 854 *cursor = *cursor + len(name) 855 } 856 857 return 858 } 859 860 // updateArray will update an array element appropriately when the path element is an array. This includes parsing 861 // the special indexes. If there are more elements in the path after this element look up, the update will be performed 862 // by the walkPathAndUpdate function. 863 func updateArray(indexString string, remaining string, arr JsonArray, val interface{}, mode int, cursor *int) (interface{}, bool, *parseErr) { 864 index, err := parseIndex(indexString, len(arr)-1, cursor) 865 if err != nil { 866 return nil, false, err 867 } 868 869 // All operations, except for SET, ignore the underflow case. 870 if index.underflow && (mode != SET) { 871 return arr, false, nil 872 } 873 874 if len(arr) > index.index && !index.overflow { 875 // index exists in the array. 876 if remaining == "" && mode != ARRAY_APPEND { 877 updated := false 878 if mode == SET || mode == REPLACE { 879 arr[index.index] = val 880 updated = true 881 } else if mode == REMOVE { 882 arr = append(arr[:index.index], arr[index.index+1:]...) 883 updated = true 884 } else if mode == ARRAY_INSERT { 885 newArr := make(JsonArray, len(arr)+1) 886 copy(newArr, arr[:index.index]) 887 newArr[index.index] = val 888 copy(newArr[index.index+1:], arr[index.index:]) 889 arr = newArr 890 updated = true 891 } 892 return arr, updated, nil 893 } else { 894 newVal, changed, err := walkPathAndUpdate(remaining, arr[index.index], val, mode, cursor) 895 if err != nil { 896 return nil, false, err 897 } 898 if changed { 899 arr[index.index] = newVal 900 } 901 return arr, changed, nil 902 } 903 } else { 904 if mode == SET || mode == INSERT || mode == ARRAY_INSERT { 905 newArr := append(arr, val) 906 return newArr, true, nil 907 } 908 return arr, false, nil 909 } 910 } 911 912 // updateObjectTreatAsArray handles the case where the user is treating an object or scalar as an array. The behavior in MySQL here 913 // is a little nutty, but we try to match it as closely as possible. In particular, each mode has a different behavior, 914 // and the behavior defies logic. This is mimicking MySQL because it's not dangerous, and there may be some crazy 915 // use case which expects this behavior. 916 func updateObjectTreatAsArray(indexString string, doc interface{}, val interface{}, mode int, cursor *int) (interface{}, bool, *parseErr) { 917 parsedIndex, err := parseIndex(indexString, 0, cursor) 918 if err != nil { 919 return nil, false, err 920 } 921 922 if parsedIndex.underflow { 923 if mode == SET || mode == INSERT { 924 // SET and INSERT convert {}, to [val, {}] 925 var newArr = make(JsonArray, 0, 2) 926 newArr = append(newArr, val) 927 newArr = append(newArr, doc) 928 return newArr, true, nil 929 } 930 } else if parsedIndex.overflow { 931 if mode == SET || mode == INSERT { 932 // SET and INSERT convert {}, to [{}, val] 933 var newArr = make(JsonArray, 0, 2) 934 newArr = append(newArr, doc) 935 newArr = append(newArr, val) 936 return newArr, true, nil 937 } 938 } else if mode == SET || mode == REPLACE { 939 return val, true, nil 940 } else if mode == ARRAY_APPEND { 941 // ARRAY APPEND converts {}, to [{}, val] - Does nothing in the over/underflow cases. 942 var newArr = make(JsonArray, 0, 2) 943 newArr = append(newArr, doc) 944 newArr = append(newArr, val) 945 return newArr, true, nil 946 } 947 return doc, false, nil 948 } 949 950 // parseIndexResult is the result of parsing an index by the parseIndex function. 951 type parseIndexResult struct { 952 underflow bool // true if the index was under 0 - will only happen with last-1000, for example. 953 overflow bool // true if the index was greater than the length of the array. 954 index int // the index to use. Will be 0 if underflow is true, or the length of the array if overflow is true. 955 } 956 957 // parseIndex parses an array index string. These are of the form: 958 // 1. standard integer 959 // 2. "last" 960 // 3. "last-NUMBER" - to get the second to last element in an array. 961 // 4. "M to N", "last-4 to N", "M to last-4", "last-4 to last-2" (Currently we don't support this) 962 // 963 // White space is ignored completely. 964 // 965 // The lastIndex sets index of the last element. -1 for an empty array. 966 func parseIndex(indexStr string, lastIndex int, cursor *int) (*parseIndexResult, *parseErr) { 967 // trim whitespace off the ends 968 indexStr = strings.TrimSpace(indexStr) 969 970 if indexStr == "last" { 971 if lastIndex < 0 { 972 lastIndex = 0 // This happens for an empty array 973 } 974 return &parseIndexResult{index: lastIndex}, nil 975 } else { 976 // Attempt to split the string on "-". "last-2" gets the second to last element in an array. 977 parts := strings.Split(indexStr, "-") 978 if len(parts) == 2 { 979 part1, part2 := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) 980 if part1 == "last" { 981 lastMinus, err := strconv.Atoi(part2) 982 if err != nil || lastMinus < 0 { 983 *cursor = *cursor + 4 // len("last") 984 return nil, &parseErr{msg: "Invalid JSON path expression. Expected a positive integer after 'last-'", character: *cursor} 985 } 986 987 underFlow := false 988 reducedIdx := lastIndex - lastMinus 989 if reducedIdx < 0 { 990 reducedIdx = 0 991 underFlow = true 992 } 993 return &parseIndexResult{index: reducedIdx, underflow: underFlow}, nil 994 } else { 995 return nil, &parseErr{msg: "Invalid JSON path expression. Expected 'last-N'", character: *cursor} 996 } 997 } 998 } 999 1000 val, err := strconv.Atoi(indexStr) 1001 if err != nil { 1002 msg := fmt.Sprintf("Invalid JSON path expression. Unable to convert %s to an int", indexStr) 1003 return nil, &parseErr{msg: msg, character: *cursor} 1004 } 1005 1006 overflow := false 1007 if val > lastIndex { 1008 val = lastIndex 1009 overflow = true 1010 } 1011 1012 return &parseIndexResult{index: val, overflow: overflow}, nil 1013 } 1014 1015 type JSONIter struct { 1016 doc *JsonObject 1017 keys []string 1018 idx int 1019 } 1020 1021 func NewJSONIter(json JsonObject) JSONIter { 1022 json = maps.Clone(json) 1023 keys := maps.Keys(json) 1024 sort.Slice(keys, func(i, j int) bool { 1025 return keys[i] < keys[j] 1026 }) 1027 return JSONIter{ 1028 doc: &json, 1029 keys: keys, 1030 idx: 0, 1031 } 1032 } 1033 1034 func (iter *JSONIter) Next() (key string, value interface{}, err error) { 1035 if iter.idx >= len(iter.keys) { 1036 return "", nil, io.EOF 1037 } 1038 key = iter.keys[iter.idx] 1039 iter.idx++ 1040 value = (*iter.doc)[key] 1041 return key, value, nil 1042 } 1043 1044 func (iter *JSONIter) HasNext() bool { 1045 return iter.idx < len(iter.keys) 1046 }