github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dtables/constraint_violations_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 dtables 16 17 import ( 18 "encoding/json" 19 20 "github.com/dolthub/go-mysql-server/sql" 21 22 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 23 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable" 24 "github.com/dolthub/dolt/go/libraries/doltcore/merge" 25 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 26 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" 27 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 28 "github.com/dolthub/dolt/go/store/hash" 29 "github.com/dolthub/dolt/go/store/pool" 30 "github.com/dolthub/dolt/go/store/prolly" 31 "github.com/dolthub/dolt/go/store/prolly/tree" 32 "github.com/dolthub/dolt/go/store/val" 33 ) 34 35 func newProllyCVTable(ctx *sql.Context, tblName string, root doltdb.RootValue, rs RootSetter) (sql.Table, error) { 36 tbl, tblName, ok, err := doltdb.GetTableInsensitive(ctx, root, tblName) 37 if err != nil { 38 return nil, err 39 } else if !ok { 40 return nil, sql.ErrTableNotFound.New(tblName) 41 } 42 cvSch, err := tbl.GetConstraintViolationsSchema(ctx) 43 if err != nil { 44 return nil, err 45 } 46 sqlSch, err := sqlutil.FromDoltSchema("", doltdb.DoltConstViolTablePrefix+tblName, cvSch) 47 if err != nil { 48 return nil, err 49 } 50 51 arts, err := tbl.GetArtifacts(ctx) 52 if err != nil { 53 return nil, err 54 } 55 m := durable.ProllyMapFromArtifactIndex(arts) 56 return &prollyConstraintViolationsTable{ 57 tblName: tblName, 58 root: root, 59 sqlSch: sqlSch, 60 tbl: tbl, 61 rs: rs, 62 artM: m, 63 }, nil 64 } 65 66 // prollyConstraintViolationsTable is a sql.Table implementation that provides access to the constraint violations that exist 67 // for a user table for the v1 format. 68 type prollyConstraintViolationsTable struct { 69 tblName string 70 root doltdb.RootValue 71 sqlSch sql.PrimaryKeySchema 72 tbl *doltdb.Table 73 rs RootSetter 74 artM prolly.ArtifactMap 75 } 76 77 var _ sql.Table = (*prollyConstraintViolationsTable)(nil) 78 var _ sql.DeletableTable = (*prollyConstraintViolationsTable)(nil) 79 80 // Name implements the interface sql.Table. 81 func (cvt *prollyConstraintViolationsTable) Name() string { 82 return doltdb.DoltConstViolTablePrefix + cvt.tblName 83 } 84 85 // String implements the interface sql.Table. 86 func (cvt *prollyConstraintViolationsTable) String() string { 87 return doltdb.DoltConstViolTablePrefix + cvt.tblName 88 } 89 90 // Schema implements the interface sql.Table. 91 func (cvt *prollyConstraintViolationsTable) Schema() sql.Schema { 92 return cvt.sqlSch.Schema 93 } 94 95 // Collation implements the interface sql.Table. 96 func (cvt *prollyConstraintViolationsTable) Collation() sql.CollationID { 97 return sql.Collation_Default 98 } 99 100 // Partitions implements the interface sql.Table. 101 func (cvt *prollyConstraintViolationsTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { 102 return index.SinglePartitionIterFromNomsMap(nil), nil 103 } 104 105 func (cvt *prollyConstraintViolationsTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) { 106 idx, err := cvt.tbl.GetArtifacts(ctx) 107 if err != nil { 108 return nil, err 109 } 110 m := durable.ProllyMapFromArtifactIndex(idx) 111 itr, err := m.IterAllCVs(ctx) 112 if err != nil { 113 return nil, err 114 } 115 sch, err := cvt.tbl.GetSchema(ctx) 116 if err != nil { 117 return nil, err 118 } 119 kd, vd := sch.GetMapDescriptors() 120 121 // value tuples encoded in ConstraintViolationMeta may 122 // violate the not null constraints assumed by fixed access 123 kd = kd.WithoutFixedAccess() 124 vd = vd.WithoutFixedAccess() 125 126 return prollyCVIter{ 127 itr: itr, 128 sch: sch, 129 kd: kd, 130 vd: vd, 131 ns: cvt.artM.NodeStore(), 132 }, nil 133 } 134 135 func (cvt *prollyConstraintViolationsTable) Deleter(context *sql.Context) sql.RowDeleter { 136 ed := cvt.artM.Editor() 137 p := cvt.artM.Pool() 138 kd, _ := cvt.artM.Descriptors() 139 kb := val.NewTupleBuilder(kd) 140 141 return &prollyCVDeleter{ 142 kd: kd, 143 kb: kb, 144 ed: ed, 145 pool: p, 146 cvt: cvt, 147 } 148 } 149 150 type prollyCVIter struct { 151 itr prolly.ArtifactIter 152 sch schema.Schema 153 kd, vd val.TupleDesc 154 ns tree.NodeStore 155 } 156 157 func (itr prollyCVIter) Next(ctx *sql.Context) (sql.Row, error) { 158 art, err := itr.itr.Next(ctx) 159 if err != nil { 160 return nil, err 161 } 162 163 // In addition to the table's columns, the constraint violations table adds 164 // three more columns: from_root_ish, violation_type, and violation_info 165 additionalColumns := 3 166 if schema.IsKeyless(itr.sch) { 167 // If this is for a keyless table, then there is no PK in the schema, so we 168 // add one additional column for the generated hash. This is necessary for 169 // being able to uniquely identify rows in the constraint violations table. 170 additionalColumns++ 171 } 172 173 r := make(sql.Row, itr.sch.GetAllCols().Size()+additionalColumns) 174 r[0] = art.SourceRootish.String() 175 r[1] = merge.MapCVType(art.ArtType) 176 177 var meta prolly.ConstraintViolationMeta 178 err = json.Unmarshal(art.Metadata, &meta) 179 if err != nil { 180 return nil, err 181 } 182 183 o := 2 184 if !schema.IsKeyless(itr.sch) { 185 for i := 0; i < itr.kd.Count(); i++ { 186 r[o+i], err = tree.GetField(ctx, itr.kd, i, art.SourceKey, itr.ns) 187 if err != nil { 188 return nil, err 189 } 190 } 191 o += itr.kd.Count() 192 193 for i := 0; i < itr.vd.Count(); i++ { 194 r[o+i], err = tree.GetField(ctx, itr.vd, i, meta.Value, itr.ns) 195 if err != nil { 196 return nil, err 197 } 198 } 199 o += itr.vd.Count() 200 } else { 201 // For a keyless table, we still need a key to uniquely identify the row in the constraint 202 // violation table, so we add in the unique hash for the row. 203 r[o], err = tree.GetField(ctx, itr.kd, 0, art.SourceKey, itr.ns) 204 if err != nil { 205 return nil, err 206 } 207 o += 1 208 209 for i := 0; i < itr.vd.Count()-1; i++ { 210 r[o+i], err = tree.GetField(ctx, itr.vd, i+1, meta.Value, itr.ns) 211 if err != nil { 212 return nil, err 213 } 214 } 215 o += itr.vd.Count() - 1 216 } 217 218 switch art.ArtType { 219 case prolly.ArtifactTypeForeignKeyViol: 220 var m merge.FkCVMeta 221 err = json.Unmarshal(meta.VInfo, &m) 222 if err != nil { 223 return nil, err 224 } 225 r[o] = m 226 case prolly.ArtifactTypeUniqueKeyViol: 227 var m merge.UniqCVMeta 228 err = json.Unmarshal(meta.VInfo, &m) 229 if err != nil { 230 return nil, err 231 } 232 r[o] = m 233 case prolly.ArtifactTypeNullViol: 234 var m merge.NullViolationMeta 235 err = json.Unmarshal(meta.VInfo, &m) 236 if err != nil { 237 return nil, err 238 } 239 r[o] = m 240 case prolly.ArtifactTypeChkConsViol: 241 var m merge.CheckCVMeta 242 err = json.Unmarshal(meta.VInfo, &m) 243 if err != nil { 244 return nil, err 245 } 246 r[o] = m 247 default: 248 panic("json not implemented for artifact type") 249 } 250 251 return r, nil 252 } 253 254 type prollyCVDeleter struct { 255 kd val.TupleDesc 256 kb *val.TupleBuilder 257 pool pool.BuffPool 258 ed *prolly.ArtifactsEditor 259 cvt *prollyConstraintViolationsTable 260 } 261 262 var _ sql.RowDeleter = (*prollyCVDeleter)(nil) 263 264 // Delete implements the interface sql.RowDeleter. 265 func (d *prollyCVDeleter) Delete(ctx *sql.Context, r sql.Row) error { 266 // When we delete a row, we need to build the primary key from the row data. 267 // The PK has 3+ fields: from_root_ish, violation_type, plus all PK fields from the source table. 268 // If the source table is keyless and has no PK, then we use the unique row hash provided by keyless tables. 269 for i := 0; i < d.kd.Count()-2; i++ { 270 err := tree.PutField(ctx, d.cvt.artM.NodeStore(), d.kb, i, r[i+2]) 271 if err != nil { 272 return err 273 } 274 } 275 276 // then the hash 277 h := hash.Parse(r[0].(string)) 278 d.kb.PutCommitAddr(d.kd.Count()-2, h) 279 280 // Finally the artifact type 281 artType := merge.UnmapCVType(merge.CvType(r[1].(uint64))) 282 d.kb.PutUint8(d.kd.Count()-1, uint8(artType)) 283 284 key := d.kb.Build(d.pool) 285 err := d.ed.Delete(ctx, key) 286 if err != nil { 287 return err 288 } 289 290 return nil 291 } 292 293 // StatementBegin implements the interface sql.TableEditor. Currently a no-op. 294 func (d *prollyCVDeleter) StatementBegin(ctx *sql.Context) {} 295 296 // DiscardChanges implements the interface sql.TableEditor. Currently a no-op. 297 func (d *prollyCVDeleter) DiscardChanges(ctx *sql.Context, errorEncountered error) error { 298 return nil 299 } 300 301 // StatementComplete implements the interface sql.TableEditor. Currently a no-op. 302 func (d *prollyCVDeleter) StatementComplete(ctx *sql.Context) error { 303 return nil 304 } 305 306 // Close implements the interface sql.RowDeleter. 307 func (d *prollyCVDeleter) Close(ctx *sql.Context) error { 308 arts, err := d.ed.Flush(ctx) 309 if err != nil { 310 return err 311 } 312 313 // TODO: We can delete from more than one table in a single statement. Root 314 // updates should be restricted to write session and not individual table 315 // editors. 316 317 updatedTbl, err := d.cvt.tbl.SetArtifacts(ctx, durable.ArtifactIndexFromProllyMap(arts)) 318 if err != nil { 319 return err 320 } 321 322 updatedRoot, err := d.cvt.root.PutTable(ctx, doltdb.TableName{Name: d.cvt.tblName}, updatedTbl) 323 if err != nil { 324 return err 325 } 326 327 return d.cvt.rs.SetRoot(ctx, updatedRoot) 328 } 329 330 func (itr prollyCVIter) Close(ctx *sql.Context) error { 331 return nil 332 }