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  }