diff --git a/go/libraries/doltcore/doltdb/foreign_key_coll.go b/go/libraries/doltcore/doltdb/foreign_key_coll.go index 99cf7468446..f542131c9cc 100644 --- a/go/libraries/doltcore/doltdb/foreign_key_coll.go +++ b/go/libraries/doltcore/doltdb/foreign_key_coll.go @@ -22,6 +22,7 @@ import ( "fmt" "sort" "strings" + "sync" "github.com/dolthub/dolt/go/libraries/doltcore/schema" "github.com/dolthub/dolt/go/libraries/utils/set" @@ -179,46 +180,47 @@ func (fk ForeignKey) DeepEquals(other ForeignKey) bool { fk.ReferencedTableIndex == other.ReferencedTableIndex } +var fkBufPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, 1024) + }, +} + // HashOf returns the Noms hash of a ForeignKey. func (fk ForeignKey) HashOf() (hash.Hash, error) { - var fields []interface{} - fields = append(fields, fk.Name, fk.TableName, fk.TableIndex) - for _, t := range fk.TableColumns { - fields = append(fields, t) + bb := fkBufPool.Get().([]byte) + defer func() { + bb = bb[:0] + fkBufPool.Put(bb) + }() + + var err error + bb = append(bb, fk.Name...) + bb = append(bb, fk.TableName.String()...) + bb = append(bb, fk.TableIndex...) + for _, col := range fk.TableColumns { + bb, err = binary.Append(bb, binary.LittleEndian, col) + if err != nil { + return hash.Hash{}, err + } } - fields = append(fields, fk.ReferencedTableName, fk.ReferencedTableIndex) - for _, t := range fk.ReferencedTableColumns { - fields = append(fields, t) + bb = append(bb, fk.ReferencedTableName.String()...) + bb = append(bb, fk.ReferencedTableIndex...) + for _, col := range fk.ReferencedTableColumns { + bb, err = binary.Append(bb, binary.LittleEndian, col) + if err != nil { + return hash.Hash{}, err + } } - fields = append(fields, []byte{byte(fk.OnUpdate), byte(fk.OnDelete)}) + bb = append(bb, byte(fk.OnUpdate)) + bb = append(bb, byte(fk.OnDelete)) for _, col := range fk.UnresolvedFKDetails.TableColumns { - fields = append(fields, col) + bb = append(bb, col...) } for _, col := range fk.UnresolvedFKDetails.ReferencedTableColumns { - fields = append(fields, col) + bb = append(bb, col...) } - - var bb bytes.Buffer - for _, field := range fields { - var err error - switch t := field.(type) { - case string: - _, err = bb.Write([]byte(t)) - case []byte: - _, err = bb.Write(t) - case uint64: - err = binary.Write(&bb, binary.LittleEndian, t) - case TableName: - _, err = bb.Write([]byte(t.String())) - default: - return hash.Hash{}, fmt.Errorf("unsupported type %T", t) - } - if err != nil { - return hash.Hash{}, err - } - } - - return hash.Of(bb.Bytes()), nil + return hash.Of(bb), nil } // CombinedHash returns a combined hash value for all foreign keys in the slice provided. diff --git a/go/libraries/doltcore/doltdb/foreign_key_coll_test.go b/go/libraries/doltcore/doltdb/foreign_key_coll_test.go new file mode 100644 index 00000000000..f54ad024b0e --- /dev/null +++ b/go/libraries/doltcore/doltdb/foreign_key_coll_test.go @@ -0,0 +1,44 @@ +// Copyright 2026 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package doltdb + +import "testing" + +func BenchmarkForeignKeyHashOf(b *testing.B) { + fk := ForeignKey{ + Name: "name", + TableName: TableName{ + Name: "tbl_name", + Schema: "tbl_schema", + }, + TableIndex: "tbl_index", + TableColumns: []uint64{1, 2, 3}, + ReferencedTableName: TableName{ + Name: "reftbl_name", + Schema: "reftbl_schema", + }, + ReferencedTableIndex: "reftbl_index", + ReferencedTableColumns: []uint64{1, 2, 3}, + OnUpdate: ForeignKeyReferentialAction_Cascade, + OnDelete: ForeignKeyReferentialAction_Cascade, + UnresolvedFKDetails: UnresolvedFKDetails{ + TableColumns: []string{"asdf"}, + ReferencedTableColumns: []string{"asdf"}, + }, + } + for i := 0; i < b.N; i++ { + _, _ = fk.HashOf() + } +}