github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/colexec/constraint_util.go (about)

     1  // Copyright 2022 Matrix Origin
     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 colexec
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/catalog"
    22  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    23  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    24  	"github.com/matrixorigin/matrixone/pkg/container/nulls"
    25  	"github.com/matrixorigin/matrixone/pkg/container/types"
    26  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    28  	"github.com/matrixorigin/matrixone/pkg/sql/util"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/engine"
    30  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    31  )
    32  
    33  type tableInfo struct {
    34  	hasAutoCol      bool
    35  	pkPos           int
    36  	updateNameToPos map[string]int
    37  	compositePkey   string
    38  	clusterBy       string
    39  	attrs           []string
    40  	idxList         []int32
    41  }
    42  
    43  func FilterAndDelByRowId(proc *process.Process, bat *batch.Batch, idxList []int32, rels []engine.Relation) (uint64, error) {
    44  	var affectedRows uint64
    45  	for i, idx := range idxList {
    46  		delBatch := filterRowIdForDel(proc, bat, int(idx))
    47  		affectedRows = affectedRows + uint64(delBatch.Length())
    48  		if delBatch.Length() > 0 {
    49  			err := rels[i].Delete(proc.Ctx, delBatch, catalog.Row_ID)
    50  			if err != nil {
    51  				delBatch.Clean(proc.Mp())
    52  				return 0, err
    53  			}
    54  		}
    55  		delBatch.Clean(proc.Mp())
    56  	}
    57  	return affectedRows, nil
    58  }
    59  
    60  func FilterAndUpdateByRowId(
    61  	eg engine.Engine,
    62  	proc *process.Process,
    63  	bat *batch.Batch,
    64  	idxList [][]int32,
    65  	rels []engine.Relation,
    66  	ref []*plan.ObjectRef,
    67  	tableDefs []*plan.TableDef,
    68  	updateCols []map[string]int32,
    69  	parentIdxs []map[string]int32,
    70  	uniqueRels [][]engine.Relation,
    71  ) (uint64, error) {
    72  	var affectedRows uint64
    73  	var delBatch *batch.Batch
    74  	var updateBatch *batch.Batch
    75  	var err error
    76  	defer func() {
    77  		if delBatch != nil {
    78  			delBatch.Clean(proc.Mp())
    79  		}
    80  		if updateBatch != nil {
    81  			updateBatch.Clean(proc.Mp())
    82  		}
    83  	}()
    84  
    85  	for i, setIdxList := range idxList {
    86  		// get attrs, hasAutoCol
    87  		tableDef := tableDefs[i]
    88  		updateCol := updateCols[i]
    89  		uniqueRel := uniqueRels[i]
    90  		var parentIdx map[string]int32 // nil means don't need check parent constraint
    91  		if len(parentIdxs) > 0 {
    92  			parentIdx = parentIdxs[i]
    93  		}
    94  		info := getInfoForInsertAndUpdate(tableDef, updateCol)
    95  
    96  		delBatch, updateBatch, err = filterRowIdForUpdate(proc, bat, setIdxList, info.attrs, parentIdx)
    97  		if err != nil {
    98  			return 0, err
    99  		}
   100  		if delBatch == nil && updateBatch == nil {
   101  			continue
   102  		}
   103  		affectedRows = affectedRows + uint64(delBatch.Length())
   104  		if delBatch.Length() > 0 {
   105  			// delete old rows
   106  			err = rels[i].Delete(proc.Ctx, delBatch, catalog.Row_ID)
   107  			if err != nil {
   108  				return 0, err
   109  			}
   110  
   111  			// fill auto incr column
   112  			if info.hasAutoCol {
   113  				if err = UpdateInsertBatch(eg, proc.Ctx, proc, tableDef.Cols, updateBatch, uint64(ref[i].Obj), ref[i].SchemaName, tableDef.Name); err != nil {
   114  					return 0, err
   115  				}
   116  			}
   117  
   118  			// check new rows not null
   119  			err := batchDataNotNullCheck(updateBatch, tableDef, proc.Ctx)
   120  			if err != nil {
   121  				return 0, err
   122  			}
   123  
   124  			//  append hidden columns
   125  			if info.compositePkey != "" {
   126  				util.FillCompositeClusterByBatch(updateBatch, info.compositePkey, proc)
   127  			}
   128  			if info.clusterBy != "" && util.JudgeIsCompositeClusterByColumn(info.clusterBy) {
   129  				util.FillCompositeClusterByBatch(updateBatch, info.clusterBy, proc)
   130  			}
   131  
   132  			// write unique key table
   133  			writeUniqueTable(nil, eg, proc, updateBatch, tableDef, ref[i].SchemaName, info.updateNameToPos, info.pkPos, uniqueRel)
   134  
   135  			// write origin table
   136  			err = rels[i].Write(proc.Ctx, updateBatch)
   137  			if err != nil {
   138  				return 0, err
   139  			}
   140  		}
   141  	}
   142  	return affectedRows, nil
   143  }
   144  
   145  func writeUniqueTable(s3Container *WriteS3Container, eg engine.Engine, proc *process.Process, updateBatch *batch.Batch,
   146  	tableDef *plan.TableDef, dbName string, updateNameToPos map[string]int, pkPos int, rels []engine.Relation) error {
   147  	var ukBatch *batch.Batch
   148  
   149  	defer func() {
   150  		if ukBatch != nil {
   151  			ukBatch.Clean(proc.Mp())
   152  		}
   153  	}()
   154  
   155  	uIdx := 0
   156  	if tableDef.Indexes != nil {
   157  		for _, indexdef := range tableDef.Indexes {
   158  			if indexdef.Unique {
   159  				partsLength := len(indexdef.Parts)
   160  				uniqueColumnPos := make([]int, partsLength)
   161  				for p, column := range indexdef.Parts {
   162  					uniqueColumnPos[p] = updateNameToPos[column]
   163  				}
   164  
   165  				colCount := len(uniqueColumnPos)
   166  				if pkPos == -1 {
   167  					//have no pk
   168  					ukBatch = batch.New(true, []string{catalog.IndexTableIndexColName})
   169  				} else {
   170  					ukBatch = batch.New(true, []string{catalog.IndexTableIndexColName, catalog.IndexTablePrimaryColName})
   171  				}
   172  
   173  				var vec *vector.Vector
   174  				var bitMap *nulls.Nulls
   175  				if colCount == 1 {
   176  					idx := uniqueColumnPos[0]
   177  					vec, bitMap = util.CompactSingleIndexCol(updateBatch.Vecs[idx], proc)
   178  				} else {
   179  					vs := make([]*vector.Vector, colCount)
   180  					for vIdx, pIdx := range uniqueColumnPos {
   181  						vs[vIdx] = updateBatch.Vecs[pIdx]
   182  					}
   183  					vec, bitMap = util.SerialWithCompacted(vs, proc)
   184  				}
   185  				ukBatch.SetVector(0, vec)
   186  				ukBatch.SetZs(vec.Length(), proc.Mp())
   187  
   188  				if pkPos != -1 {
   189  					// have pk, append pk vector
   190  					vec = util.CompactPrimaryCol(updateBatch.Vecs[pkPos], bitMap, proc)
   191  					ukBatch.SetVector(1, vec)
   192  				}
   193  
   194  				// db, err := eg.Database(proc.Ctx, dbName, proc.TxnOperator)
   195  				// if err != nil {
   196  				// 	return err
   197  				// }
   198  				// rel, err := db.Relation(proc.Ctx, tblName)
   199  				// if err != nil {
   200  				// 	return err
   201  				// }
   202  
   203  				if s3Container == nil {
   204  					rel := rels[uIdx]
   205  					err := rel.Write(proc.Ctx, ukBatch)
   206  					if err != nil {
   207  						return err
   208  					}
   209  					uIdx++
   210  				} else {
   211  					uIdx++
   212  					s3Container.WriteS3Batch(ukBatch, proc, uIdx)
   213  				}
   214  			} else {
   215  				continue
   216  			}
   217  		}
   218  	}
   219  	return nil
   220  }
   221  
   222  func filterRowIdForDel(proc *process.Process, bat *batch.Batch, idx int) *batch.Batch {
   223  	retVec := vector.New(types.T_Rowid.ToType())
   224  	rowIdMap := make(map[types.Rowid]struct{})
   225  	for i, r := range vector.MustTCols[types.Rowid](bat.Vecs[idx]) {
   226  		if !bat.Vecs[idx].Nsp.Contains(uint64(i)) {
   227  			rowIdMap[r] = struct{}{}
   228  		}
   229  	}
   230  	rowIdList := make([]types.Rowid, len(rowIdMap))
   231  	i := 0
   232  	for rowId := range rowIdMap {
   233  		rowIdList[i] = rowId
   234  		i++
   235  	}
   236  	vector.AppendFixed(retVec, rowIdList, proc.Mp())
   237  	retBatch := batch.New(true, []string{catalog.Row_ID})
   238  	retBatch.SetZs(retVec.Length(), proc.Mp())
   239  	retBatch.SetVector(0, retVec)
   240  	return retBatch
   241  }
   242  
   243  func filterRowIdForUpdate(proc *process.Process, bat *batch.Batch, idxList []int32, attrs []string, parentIdx map[string]int32) (*batch.Batch, *batch.Batch, error) {
   244  	rowIdMap := make(map[types.Rowid]struct{})
   245  	var rowSkip []bool
   246  	foundRowId := false
   247  	for i, idx := range idxList {
   248  		if bat.Vecs[idx].Typ.Oid == types.T_Rowid {
   249  			for j, r := range vector.MustTCols[types.Rowid](bat.Vecs[idx]) {
   250  				if _, exist := rowIdMap[r]; exist {
   251  					rowSkip = append(rowSkip, true)
   252  				} else if bat.Vecs[idx].Nsp.Contains(uint64(j)) {
   253  					rowSkip = append(rowSkip, true)
   254  				} else {
   255  					rowIdMap[r] = struct{}{}
   256  					rowSkip = append(rowSkip, false)
   257  				}
   258  			}
   259  			foundRowId = true
   260  			idxList = append(idxList[:i], idxList[i+1:]...)
   261  			break
   262  		}
   263  	}
   264  	if !foundRowId {
   265  		return nil, nil, moerr.NewInternalError(proc.Ctx, "need rowid vector for update")
   266  	}
   267  	batLen := len(rowIdMap)
   268  	if batLen == 0 {
   269  		return nil, nil, nil
   270  	}
   271  
   272  	// get delete batch
   273  	delVec := vector.New(types.T_Rowid.ToType())
   274  	rowIdList := make([]types.Rowid, len(rowIdMap))
   275  	i := 0
   276  	for rowId := range rowIdMap {
   277  		rowIdList[i] = rowId
   278  		i++
   279  	}
   280  	mp := proc.Mp()
   281  	vector.AppendFixed(delVec, rowIdList, mp)
   282  	delBatch := batch.New(true, []string{catalog.Row_ID})
   283  	delBatch.SetVector(0, delVec)
   284  	delBatch.SetZs(batLen, mp)
   285  
   286  	// get update batch
   287  	updateBatch, err := GetUpdateBatch(proc, bat, idxList, batLen, attrs, rowSkip, parentIdx)
   288  	if err != nil {
   289  		delBatch.Clean(proc.Mp())
   290  		return nil, nil, err
   291  	}
   292  
   293  	return delBatch, updateBatch, nil
   294  }
   295  
   296  func GetUpdateBatch(proc *process.Process, bat *batch.Batch, idxList []int32, batLen int, attrs []string, rowSkip []bool, parentIdx map[string]int32) (*batch.Batch, error) {
   297  	updateBatch := batch.New(true, attrs)
   298  	var toVec *vector.Vector
   299  	var err error
   300  
   301  	for i, idx := range idxList {
   302  		fromVec := bat.Vecs[idx]
   303  		colName := attrs[i]
   304  
   305  		// if update values is not null, but parent is null, throw error
   306  		if parentIdx != nil {
   307  			if pIdx, exists := parentIdx[colName]; exists {
   308  				parentVec := bat.Vecs[pIdx]
   309  				if fromVec.IsConst() {
   310  					if !fromVec.IsScalarNull() {
   311  						if rowSkip == nil {
   312  							for j := 0; j < batLen; j++ {
   313  								if parentVec.Nsp.Contains(uint64(j)) {
   314  									return nil, moerr.NewInternalError(proc.Ctx, "Cannot add or update a child row: a foreign key constraint fails")
   315  								}
   316  							}
   317  						} else {
   318  							for j := 0; j < batLen; j++ {
   319  								if !rowSkip[j] && parentVec.Nsp.Contains(uint64(j)) {
   320  									return nil, moerr.NewInternalError(proc.Ctx, "Cannot add or update a child row: a foreign key constraint fails")
   321  								}
   322  							}
   323  						}
   324  					}
   325  				} else {
   326  					if rowSkip == nil {
   327  						for j := 0; j < fromVec.Length(); j++ {
   328  							if !fromVec.Nsp.Contains(uint64(j)) && parentVec.Nsp.Contains(uint64(j)) {
   329  								return nil, moerr.NewInternalError(proc.Ctx, "Cannot add or update a child row: a foreign key constraint fails")
   330  							}
   331  						}
   332  					} else {
   333  						for j := 0; j < fromVec.Length(); j++ {
   334  							if !rowSkip[j] && !fromVec.Nsp.Contains(uint64(j)) && parentVec.Nsp.Contains(uint64(j)) {
   335  								return nil, moerr.NewInternalError(proc.Ctx, "Cannot add or update a child row: a foreign key constraint fails")
   336  							}
   337  						}
   338  					}
   339  				}
   340  			}
   341  		}
   342  
   343  		if fromVec.IsConst() {
   344  			toVec = vector.New(bat.Vecs[idx].Typ)
   345  			if fromVec.IsScalarNull() {
   346  				defVal := vector.GetInitConstVal(bat.Vecs[idx].Typ)
   347  				for j := 0; j < batLen; j++ {
   348  					err := toVec.Append(defVal, true, proc.Mp())
   349  					if err != nil {
   350  						updateBatch.Clean(proc.Mp())
   351  						return nil, err
   352  					}
   353  				}
   354  			} else {
   355  				err = vector.CopyConst(toVec, fromVec, batLen, proc.Mp())
   356  				if err != nil {
   357  					updateBatch.Clean(proc.Mp())
   358  					return nil, err
   359  				}
   360  			}
   361  		} else {
   362  			toVec = vector.New(bat.Vecs[idx].Typ)
   363  			if rowSkip == nil {
   364  				for j := 0; j < fromVec.Length(); j++ {
   365  					vector.UnionOne(toVec, fromVec, int64(j), proc.Mp())
   366  				}
   367  			} else {
   368  				for j := 0; j < fromVec.Length(); j++ {
   369  					if !rowSkip[j] {
   370  						vector.UnionOne(toVec, fromVec, int64(j), proc.Mp())
   371  					}
   372  				}
   373  			}
   374  		}
   375  		updateBatch.SetVector(int32(i), toVec)
   376  	}
   377  	updateBatch.SetZs(batLen, proc.Mp())
   378  	return updateBatch, nil
   379  }
   380  
   381  func getInfoForInsertAndUpdate(tableDef *plan.TableDef, updateCol map[string]int32) *tableInfo {
   382  	info := &tableInfo{
   383  		hasAutoCol:      false,
   384  		pkPos:           -1,
   385  		updateNameToPos: make(map[string]int),
   386  		compositePkey:   "",
   387  		clusterBy:       "",
   388  		attrs:           make([]string, 0, len(tableDef.Cols)),
   389  		idxList:         make([]int32, 0, len(tableDef.Cols)),
   390  	}
   391  	if tableDef.CompositePkey != nil {
   392  		info.compositePkey = tableDef.CompositePkey.Name
   393  	}
   394  	if tableDef.ClusterBy != nil {
   395  		info.clusterBy = tableDef.ClusterBy.Name
   396  	}
   397  	pos := 0
   398  	for j, col := range tableDef.Cols {
   399  		if col.Typ.AutoIncr {
   400  			if updateCol == nil { // update statement
   401  				info.hasAutoCol = true
   402  			} else if _, ok := updateCol[col.Name]; ok { // insert statement
   403  				info.hasAutoCol = true
   404  			}
   405  		}
   406  		if info.compositePkey == "" && col.Name != catalog.Row_ID && col.Primary {
   407  			info.pkPos = j
   408  		}
   409  		if col.Name != catalog.Row_ID {
   410  			info.attrs = append(info.attrs, col.Name)
   411  			info.idxList = append(info.idxList, int32(pos))
   412  			info.updateNameToPos[col.Name] = pos
   413  			pos++
   414  		}
   415  	}
   416  	if info.compositePkey != "" {
   417  		info.pkPos = pos
   418  	}
   419  
   420  	return info
   421  }
   422  
   423  func InsertBatch(
   424  	container *WriteS3Container,
   425  	eg engine.Engine,
   426  	proc *process.Process,
   427  	bat *batch.Batch,
   428  	rel engine.Relation,
   429  	ref *plan.ObjectRef,
   430  	tableDef *plan.TableDef,
   431  	parentIdx map[string]int32,
   432  	uniqueRel []engine.Relation) (uint64, error) {
   433  	var insertBatch *batch.Batch
   434  	var err error
   435  	affectedRows := bat.Vecs[0].Length()
   436  	defer func() {
   437  		if insertBatch != nil {
   438  			insertBatch.Clean(proc.Mp())
   439  		}
   440  	}()
   441  
   442  	info := getInfoForInsertAndUpdate(tableDef, nil)
   443  
   444  	//get insert batch
   445  	insertBatch, err = GetUpdateBatch(proc, bat, info.idxList, bat.Length(), info.attrs, nil, parentIdx)
   446  	if err != nil {
   447  		return 0, err
   448  	}
   449  
   450  	// fill auto incr column
   451  	if info.hasAutoCol {
   452  		if err = UpdateInsertBatch(eg, proc.Ctx, proc, tableDef.Cols, insertBatch, uint64(ref.Obj), ref.SchemaName, tableDef.Name); err != nil {
   453  			return 0, err
   454  		}
   455  	}
   456  
   457  	// check new rows not null
   458  	err = batchDataNotNullCheck(insertBatch, tableDef, proc.Ctx)
   459  	if err != nil {
   460  		return 0, err
   461  	}
   462  
   463  	// append hidden columns
   464  	if info.compositePkey != "" {
   465  		util.FillCompositeClusterByBatch(insertBatch, info.compositePkey, proc)
   466  	}
   467  	if info.clusterBy != "" && util.JudgeIsCompositeClusterByColumn(info.clusterBy) {
   468  		util.FillCompositeClusterByBatch(insertBatch, info.clusterBy, proc)
   469  	}
   470  
   471  	if container != nil {
   472  		// write to s3
   473  		err = container.WriteS3Batch(insertBatch, proc, 0)
   474  		if err != nil {
   475  			return 0, err
   476  		}
   477  
   478  		err = writeUniqueTable(container, eg, proc, insertBatch, tableDef, ref.SchemaName, info.updateNameToPos, info.pkPos, uniqueRel)
   479  		if err != nil {
   480  			return 0, err
   481  		}
   482  
   483  	} else {
   484  		// write unique key table
   485  		err = writeUniqueTable(nil, eg, proc, insertBatch, tableDef, ref.SchemaName, info.updateNameToPos, info.pkPos, uniqueRel)
   486  		if err != nil {
   487  			return 0, err
   488  		}
   489  
   490  		// write origin table
   491  		err = rel.Write(proc.Ctx, insertBatch)
   492  	}
   493  
   494  	if err != nil {
   495  		return 0, err
   496  	}
   497  
   498  	return uint64(affectedRows), nil
   499  }
   500  
   501  func batchDataNotNullCheck(tmpBat *batch.Batch, tableDef *plan.TableDef, ctx context.Context) error {
   502  	compNameMap := make(map[string]struct{})
   503  	if tableDef.CompositePkey != nil {
   504  		names := util.SplitCompositePrimaryKeyColumnName(tableDef.CompositePkey.Name)
   505  		for _, name := range names {
   506  			compNameMap[name] = struct{}{}
   507  		}
   508  	}
   509  
   510  	for j := range tmpBat.Vecs {
   511  		nsp := tmpBat.Vecs[j].Nsp
   512  		if tableDef.Cols[j].Default != nil && !tableDef.Cols[j].Default.NullAbility {
   513  			if nulls.Any(nsp) {
   514  				return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", tmpBat.Attrs[j]))
   515  			}
   516  		}
   517  		if _, ok := compNameMap[tmpBat.Attrs[j]]; ok {
   518  			if nulls.Any(nsp) {
   519  				return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", tmpBat.Attrs[j]))
   520  			}
   521  		}
   522  	}
   523  	return nil
   524  }