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 }