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  }