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 }