github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/doltdb/foreign_key_serialization.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 doltdb
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	fb "github.com/dolthub/flatbuffers/v23/go"
    22  
    23  	"github.com/dolthub/dolt/go/gen/fb/serial"
    24  	"github.com/dolthub/dolt/go/store/datas"
    25  	"github.com/dolthub/dolt/go/store/marshal"
    26  	"github.com/dolthub/dolt/go/store/types"
    27  )
    28  
    29  const (
    30  	fkBuilderSize = 2048
    31  )
    32  
    33  func DeserializeForeignKeys(ctx context.Context, nbf *types.NomsBinFormat, fks types.Value) (*ForeignKeyCollection, error) {
    34  	if nbf.UsesFlatbuffers() {
    35  		return deserializeFlatbufferForeignKeys(fks.(types.SerialMessage))
    36  	} else {
    37  		return deserializeNomsForeignKeys(ctx, fks.(types.Map))
    38  	}
    39  }
    40  
    41  func SerializeForeignKeys(ctx context.Context, vrw types.ValueReadWriter, fkc *ForeignKeyCollection) (types.Value, error) {
    42  	if vrw.Format().UsesFlatbuffers() {
    43  		return serializeFlatbufferForeignKeys(fkc), nil
    44  	} else {
    45  		return serializeNomsForeignKeys(ctx, vrw, fkc)
    46  	}
    47  }
    48  
    49  // deserializeNomsForeignKeys returns a new ForeignKeyCollection using the provided map returned previously by GetMap.
    50  func deserializeNomsForeignKeys(ctx context.Context, fkMap types.Map) (*ForeignKeyCollection, error) {
    51  	fkc := &ForeignKeyCollection{
    52  		foreignKeys: make(map[string]ForeignKey),
    53  	}
    54  	err := fkMap.IterAll(ctx, func(key, value types.Value) error {
    55  		foreignKey := &ForeignKey{}
    56  		err := marshal.Unmarshal(ctx, fkMap.Format(), value, foreignKey)
    57  		if err != nil {
    58  			return err
    59  		}
    60  		fkc.foreignKeys[string(key.(types.String))] = *foreignKey
    61  		return nil
    62  	})
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	return fkc, nil
    67  }
    68  
    69  // serializeNomsForeignKeys serializes a ForeignKeyCollection as a types.Map.
    70  func serializeNomsForeignKeys(ctx context.Context, vrw types.ValueReadWriter, fkc *ForeignKeyCollection) (types.Map, error) {
    71  	fkMap, err := types.NewMap(ctx, vrw)
    72  	if err != nil {
    73  		return types.EmptyMap, err
    74  	}
    75  	fkMapEditor := fkMap.Edit()
    76  	for hashOf, foreignKey := range fkc.foreignKeys {
    77  		val, err := marshal.Marshal(ctx, vrw, foreignKey)
    78  		if err != nil {
    79  			return types.EmptyMap, err
    80  		}
    81  		fkMapEditor.Set(types.String(hashOf), val)
    82  	}
    83  	return fkMapEditor.Map(ctx)
    84  }
    85  
    86  // deserializeFlatbufferForeignKeys returns a new ForeignKeyCollection using the provided map returned previously by GetMap.
    87  func deserializeFlatbufferForeignKeys(msg types.SerialMessage) (*ForeignKeyCollection, error) {
    88  	if serial.GetFileID(msg) != serial.ForeignKeyCollectionFileID {
    89  		return nil, fmt.Errorf("expect Serial Message with ForeignKeyCollectionFileID")
    90  	}
    91  
    92  	var c serial.ForeignKeyCollection
    93  	err := serial.InitForeignKeyCollectionRoot(&c, msg, serial.MessagePrefixSz)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	collection := &ForeignKeyCollection{
    98  		foreignKeys: make(map[string]ForeignKey, c.ForeignKeysLength()),
    99  	}
   100  
   101  	var fk serial.ForeignKey
   102  	for i := 0; i < c.ForeignKeysLength(); i++ {
   103  		_, err = c.TryForeignKeys(&fk, i)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  
   108  		childCols := make([]uint64, fk.ChildTableColumnsLength())
   109  		for j := range childCols {
   110  			childCols[j] = fk.ChildTableColumns(j)
   111  		}
   112  		parentCols := make([]uint64, fk.ParentTableColumnsLength())
   113  		for j := range parentCols {
   114  			parentCols[j] = fk.ParentTableColumns(j)
   115  		}
   116  
   117  		var childUnresolved []string
   118  		cn := fk.UnresolvedChildColumnsLength()
   119  		if cn > 0 {
   120  			childUnresolved = make([]string, cn)
   121  			for j := range childUnresolved {
   122  				childUnresolved[j] = string(fk.UnresolvedChildColumns(j))
   123  			}
   124  		}
   125  		var parentUnresolved []string
   126  		pn := fk.UnresolvedParentColumnsLength()
   127  		if pn > 0 {
   128  			parentUnresolved = make([]string, pn)
   129  			for j := range parentUnresolved {
   130  				parentUnresolved[j] = string(fk.UnresolvedParentColumns(j))
   131  			}
   132  		}
   133  
   134  		err := collection.AddKeys(ForeignKey{
   135  			Name:                   string(fk.Name()),
   136  			TableName:              string(fk.ChildTableName()),
   137  			TableIndex:             string(fk.ChildTableIndex()),
   138  			TableColumns:           childCols,
   139  			ReferencedTableName:    string(fk.ParentTableName()),
   140  			ReferencedTableIndex:   string(fk.ParentTableIndex()),
   141  			ReferencedTableColumns: parentCols,
   142  			OnUpdate:               ForeignKeyReferentialAction(fk.OnUpdate()),
   143  			OnDelete:               ForeignKeyReferentialAction(fk.OnDelete()),
   144  			UnresolvedFKDetails: UnresolvedFKDetails{
   145  				TableColumns:           childUnresolved,
   146  				ReferencedTableColumns: parentUnresolved,
   147  			},
   148  		})
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  	}
   153  	return collection, nil
   154  }
   155  
   156  // serializeFlatbufferForeignKeys serializes a ForeignKeyCollection as a types.Map.
   157  func serializeFlatbufferForeignKeys(fkc *ForeignKeyCollection) types.SerialMessage {
   158  	foreignKeys := fkc.AllKeys()
   159  	offsets := make([]fb.UOffsetT, len(foreignKeys))
   160  	b := fb.NewBuilder(fkBuilderSize)
   161  
   162  	for i := len(foreignKeys) - 1; i >= 0; i-- {
   163  		var (
   164  			foreignKeyName   fb.UOffsetT
   165  			childTable       fb.UOffsetT
   166  			childIndex       fb.UOffsetT
   167  			childCols        fb.UOffsetT
   168  			parentTable      fb.UOffsetT
   169  			parentIndex      fb.UOffsetT
   170  			parentCols       fb.UOffsetT
   171  			unresolvedChild  fb.UOffsetT
   172  			unresolvedParent fb.UOffsetT
   173  		)
   174  
   175  		fk := foreignKeys[i]
   176  		if fk.UnresolvedFKDetails.ReferencedTableColumns != nil {
   177  			unresolvedParent = datas.SerializeStringVector(b, fk.UnresolvedFKDetails.ReferencedTableColumns)
   178  		}
   179  		if fk.UnresolvedFKDetails.TableColumns != nil {
   180  			unresolvedChild = datas.SerializeStringVector(b, fk.UnresolvedFKDetails.TableColumns)
   181  		}
   182  		parentCols = serializeUint64Vector(b, fk.ReferencedTableColumns)
   183  		childCols = serializeUint64Vector(b, fk.TableColumns)
   184  		parentTable = b.CreateString(fk.ReferencedTableName)
   185  		parentIndex = b.CreateString(fk.ReferencedTableIndex)
   186  		childTable = b.CreateString(fk.TableName)
   187  		childIndex = b.CreateString(fk.TableIndex)
   188  		foreignKeyName = b.CreateString(fk.Name)
   189  
   190  		serial.ForeignKeyStart(b)
   191  		serial.ForeignKeyAddName(b, foreignKeyName)
   192  		serial.ForeignKeyAddChildTableName(b, childTable)
   193  		serial.ForeignKeyAddChildTableIndex(b, childIndex)
   194  		serial.ForeignKeyAddChildTableColumns(b, childCols)
   195  		serial.ForeignKeyAddParentTableName(b, parentTable)
   196  		serial.ForeignKeyAddParentTableIndex(b, parentIndex)
   197  		serial.ForeignKeyAddParentTableColumns(b, parentCols)
   198  		serial.ForeignKeyAddUnresolvedChildColumns(b, unresolvedChild)
   199  		serial.ForeignKeyAddUnresolvedParentColumns(b, unresolvedParent)
   200  		serial.ForeignKeyAddOnUpdate(b, serial.ForeignKeyReferentialAction(fk.OnUpdate))
   201  		serial.ForeignKeyAddOnDelete(b, serial.ForeignKeyReferentialAction(fk.OnDelete))
   202  		offsets[i] = serial.ForeignKeyEnd(b)
   203  	}
   204  
   205  	serial.ForeignKeyCollectionStartForeignKeysVector(b, len(offsets))
   206  	for i := len(offsets) - 1; i >= 0; i-- {
   207  		b.PrependUOffsetT(offsets[i])
   208  	}
   209  	vec := b.EndVector(len(offsets))
   210  
   211  	serial.ForeignKeyCollectionStart(b)
   212  	serial.ForeignKeyCollectionAddForeignKeys(b, vec)
   213  	o := serial.ForeignKeyCollectionEnd(b)
   214  	return []byte(serial.FinishMessage(b, o, []byte(serial.ForeignKeyCollectionFileID)))
   215  }
   216  
   217  func serializeUint64Vector(b *fb.Builder, u []uint64) fb.UOffsetT {
   218  	b.StartVector(8, len(u), 8)
   219  	for j := len(u) - 1; j >= 0; j-- {
   220  		b.PrependUint64(u[j])
   221  	}
   222  	return b.EndVector(len(u))
   223  }
   224  
   225  func EmptyForeignKeyCollection(msg types.SerialMessage) (bool, error) {
   226  	if serial.GetFileID(msg) != serial.ForeignKeyCollectionFileID {
   227  		return false, nil
   228  	}
   229  	var c serial.ForeignKeyCollection
   230  	err := serial.InitForeignKeyCollectionRoot(&c, msg, serial.MessagePrefixSz)
   231  	if err != nil {
   232  		return false, err
   233  	}
   234  	return c.ForeignKeysLength() == 0, nil
   235  }