github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/merge/violations_unique_prolly.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 merge 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "strings" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/dolthub/go-mysql-server/sql/types" 25 26 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 27 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 28 "github.com/dolthub/dolt/go/store/pool" 29 "github.com/dolthub/dolt/go/store/prolly" 30 "github.com/dolthub/dolt/go/store/val" 31 ) 32 33 func makeUniqViolMeta(sch schema.Schema, idx schema.Index) (UniqCVMeta, error) { 34 schCols := sch.GetAllCols() 35 idxTags := idx.IndexedColumnTags() 36 colNames := make([]string, len(idxTags)) 37 for i, tag := range idxTags { 38 if col, ok := schCols.TagToCol[tag]; !ok { 39 return UniqCVMeta{}, fmt.Errorf("unique key '%s' references tag '%d' on table but it cannot be found", 40 idx.Name(), tag) 41 } else { 42 colNames[i] = col.Name 43 } 44 } 45 46 return UniqCVMeta{ 47 Columns: colNames, 48 Name: idx.Name(), 49 }, nil 50 } 51 52 type UniqCVMeta struct { 53 Columns []string `json:"Columns"` 54 Name string `json:"Name"` 55 } 56 57 func (m UniqCVMeta) ToInterface() (interface{}, error) { 58 return map[string]interface{}{ 59 "Columns": m.Columns, 60 "Name": m.Name, 61 }, nil 62 } 63 64 var _ sql.JSONWrapper = UniqCVMeta{} 65 66 func (m UniqCVMeta) Unmarshall(ctx *sql.Context) (val types.JSONDocument, err error) { 67 return types.JSONDocument{Val: m}, nil 68 } 69 70 func (m UniqCVMeta) PrettyPrint() string { 71 jsonStr := fmt.Sprintf(`{`+ 72 `"Name": "%s", `+ 73 `"Columns": ["%s"]}`, 74 m.Name, 75 strings.Join(m.Columns, `', '`)) 76 return jsonStr 77 } 78 79 func replaceUniqueKeyViolation(ctx context.Context, edt *prolly.ArtifactsEditor, m prolly.Map, k val.Tuple, kd val.TupleDesc, theirRootIsh doltdb.Rootish, vInfo []byte, tblName string) error { 80 var value val.Tuple 81 err := m.Get(ctx, k, func(_, v val.Tuple) error { 82 value = v 83 return nil 84 }) 85 if err != nil { 86 return err 87 } 88 89 meta := prolly.ConstraintViolationMeta{ 90 VInfo: vInfo, 91 Value: value, 92 } 93 94 theirsHash, err := theirRootIsh.HashOf() 95 if err != nil { 96 return err 97 } 98 99 err = edt.ReplaceConstraintViolation(ctx, k, theirsHash, prolly.ArtifactTypeUniqueKeyViol, meta) 100 if err != nil { 101 return err 102 } 103 104 return nil 105 } 106 107 func getPKFromSecondaryKey(pKB *val.TupleBuilder, pool pool.BuffPool, pkMapping val.OrdinalMapping, k val.Tuple) val.Tuple { 108 for to := range pkMapping { 109 from := pkMapping.MapOrdinal(to) 110 pKB.PutRaw(to, k.GetField(from)) 111 } 112 return pKB.Build(pool) 113 } 114 115 func ordinalMappingFromIndex(def schema.Index) (m val.OrdinalMapping) { 116 pks := def.PrimaryKeyTags() 117 if len(pks) == 0 { // keyless index 118 m = make(val.OrdinalMapping, 1) 119 m[0] = len(def.AllTags()) 120 return m 121 } 122 123 m = make(val.OrdinalMapping, len(pks)) 124 for i, pk := range pks { 125 for j, tag := range def.AllTags() { 126 if tag == pk { 127 m[i] = j 128 break 129 } 130 } 131 } 132 return 133 } 134 135 type NullViolationMeta struct { 136 Columns []string `json:"Columns"` 137 } 138 139 var _ sql.JSONWrapper = NullViolationMeta{} 140 141 func newNotNullViolationMeta(violations []string, value val.Tuple) (prolly.ConstraintViolationMeta, error) { 142 info, err := json.Marshal(NullViolationMeta{Columns: violations}) 143 if err != nil { 144 return prolly.ConstraintViolationMeta{}, err 145 } 146 return prolly.ConstraintViolationMeta{ 147 VInfo: info, 148 Value: value, 149 }, nil 150 } 151 152 func (m NullViolationMeta) ToInterface() (interface{}, error) { 153 return map[string]interface{}{ 154 "Columns": m.Columns, 155 }, nil 156 } 157 158 func (m NullViolationMeta) Unmarshall(ctx *sql.Context) (val types.JSONDocument, err error) { 159 return types.JSONDocument{Val: m}, nil 160 } 161 162 // CheckCVMeta holds metadata describing a check constraint violation. 163 type CheckCVMeta struct { 164 Name string `json:"Name"` 165 Expression string `json:"Expression"` 166 } 167 168 var _ sql.JSONWrapper = CheckCVMeta{} 169 170 // newCheckCVMeta creates a new CheckCVMeta from a schema |sch| and a check constraint name |checkName|. If the 171 // check constraint is not found in the specified schema, an error is returned. 172 func newCheckCVMeta(sch schema.Schema, checkName string) (CheckCVMeta, error) { 173 found := false 174 var check schema.Check 175 for _, check = range sch.Checks().AllChecks() { 176 if check.Name() == checkName { 177 found = true 178 break 179 } 180 } 181 if !found { 182 return CheckCVMeta{}, fmt.Errorf("check constraint '%s' not found in schema", checkName) 183 } 184 185 return CheckCVMeta{ 186 Name: check.Name(), 187 Expression: check.Expression(), 188 }, nil 189 } 190 191 // Unmarshall implements sql.JSONWrapper 192 func (m CheckCVMeta) Unmarshall(_ *sql.Context) (val types.JSONDocument, err error) { 193 return types.JSONDocument{Val: m}, nil 194 } 195 196 func (m CheckCVMeta) ToInterface() (interface{}, error) { 197 return map[string]interface{}{ 198 "Name": m.Name, 199 "Expression": m.Expression, 200 }, nil 201 }