github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/json/contains_testers.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  // This file contains (ha!) helpers to test @>.
    12  
    13  package json
    14  
    15  import "math/rand"
    16  
    17  type containsTester interface {
    18  	// slowContains is a slower but simpler implementation of contains to check
    19  	// against the substantially more complex actual implementation.
    20  	slowContains(other JSON) bool
    21  
    22  	// subdocument returns a JSON document which is contained by this one.
    23  	subdocument(isRoot bool, rng *rand.Rand) JSON
    24  }
    25  
    26  func slowContains(a, b JSON) bool {
    27  	// This is a unique case of contains (and is described as such in the
    28  	// Postgres docs) - an array contains a scalar which is an element of it.
    29  	// This contradicts the general rule of contains that the contained object
    30  	// must have the same "shape" as the containing object.
    31  	if a.Type() == ArrayJSONType {
    32  		ary := a.MaybeDecode().(jsonArray)
    33  		if b.isScalar() {
    34  			for _, j := range ary {
    35  				cmp, _ := j.Compare(b)
    36  				if cmp == 0 {
    37  					return true
    38  				}
    39  			}
    40  			return false
    41  		}
    42  	}
    43  
    44  	return a.(containsTester).slowContains(b)
    45  }
    46  
    47  func (j jsonNull) slowContains(other JSON) bool {
    48  	c, _ := j.Compare(other)
    49  	return c == 0
    50  }
    51  func (j jsonTrue) slowContains(other JSON) bool {
    52  	c, _ := j.Compare(other)
    53  	return c == 0
    54  }
    55  func (j jsonFalse) slowContains(other JSON) bool {
    56  	c, _ := j.Compare(other)
    57  	return c == 0
    58  }
    59  func (j jsonNumber) slowContains(other JSON) bool {
    60  	c, _ := j.Compare(other)
    61  	return c == 0
    62  }
    63  func (j jsonString) slowContains(other JSON) bool {
    64  	c, _ := j.Compare(other)
    65  	return c == 0
    66  }
    67  
    68  func (j jsonArray) slowContains(other JSON) bool {
    69  	other = other.MaybeDecode()
    70  	if ary, ok := other.(jsonArray); ok {
    71  		for i := 0; i < len(ary); i++ {
    72  			found := false
    73  			for k := 0; k < len(j); k++ {
    74  				if j[k].Type() == ary[i].Type() && j[k].(containsTester).slowContains(ary[i]) {
    75  					found = true
    76  					break
    77  				}
    78  			}
    79  			if !found {
    80  				return false
    81  			}
    82  		}
    83  		return true
    84  	}
    85  	return false
    86  }
    87  
    88  func (j jsonObject) slowContains(other JSON) bool {
    89  	other = other.MaybeDecode()
    90  	if obj, ok := other.(jsonObject); ok {
    91  		for i := 0; i < len(obj); i++ {
    92  			leftVal, _ := j.FetchValKey(string(obj[i].k))
    93  			if leftVal == nil || !leftVal.(containsTester).slowContains(obj[i].v) {
    94  				return false
    95  			}
    96  		}
    97  		return true
    98  	}
    99  	return false
   100  }
   101  
   102  func (j *jsonEncoded) slowContains(other JSON) bool {
   103  	return j.mustDecode().(containsTester).slowContains(other)
   104  }
   105  
   106  func (j jsonNull) subdocument(_ bool, _ *rand.Rand) JSON   { return j }
   107  func (j jsonTrue) subdocument(_ bool, _ *rand.Rand) JSON   { return j }
   108  func (j jsonFalse) subdocument(_ bool, _ *rand.Rand) JSON  { return j }
   109  func (j jsonNumber) subdocument(_ bool, _ *rand.Rand) JSON { return j }
   110  func (j jsonString) subdocument(_ bool, _ *rand.Rand) JSON { return j }
   111  
   112  func (j jsonArray) subdocument(isRoot bool, rng *rand.Rand) JSON {
   113  	// Root arrays contain their scalar elements.
   114  	if isRoot && rng.Intn(5) == 0 {
   115  		idx := rng.Intn(len(j))
   116  		if j[idx].isScalar() {
   117  			return j[idx]
   118  		}
   119  	}
   120  	result := make(jsonArray, 0)
   121  	i := 0
   122  	for i < len(j) {
   123  		if rng.Intn(2) == 0 {
   124  			result = append(result, j[i].(containsTester).subdocument(false /* isRoot */, rng))
   125  		}
   126  		if rng.Intn(2) == 0 {
   127  			i++
   128  		}
   129  	}
   130  	// Shuffle the slice.
   131  	for i := range result {
   132  		j := rng.Intn(i + 1)
   133  		result[i], result[j] = result[j], result[i]
   134  	}
   135  
   136  	return result
   137  }
   138  
   139  func (j jsonObject) subdocument(_ bool, rng *rand.Rand) JSON {
   140  	result := make(jsonObject, 0)
   141  	for _, e := range j {
   142  		if rng.Intn(2) == 0 {
   143  			result = append(result, jsonKeyValuePair{
   144  				k: e.k,
   145  				v: e.v.(containsTester).subdocument(false /* isRoot */, rng),
   146  			})
   147  		}
   148  	}
   149  	return result
   150  }
   151  
   152  func (j *jsonEncoded) subdocument(isRoot bool, rng *rand.Rand) JSON {
   153  	return j.mustDecode().(containsTester).subdocument(isRoot, rng)
   154  }