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  }