Skip to content

Commit 6933080

Browse files
committed
Rewrote casts as a root object collection
1 parent 643cb6c commit 6933080

59 files changed

Lines changed: 1979 additions & 1007 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/casts/collection.go

Lines changed: 643 additions & 0 deletions
Large diffs are not rendered by default.

core/casts/collection_funcs.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright 2026 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 casts
16+
17+
import (
18+
"context"
19+
20+
"github.com/cockroachdb/errors"
21+
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
22+
"github.com/dolthub/dolt/go/libraries/doltcore/merge"
23+
"github.com/dolthub/dolt/go/store/hash"
24+
"github.com/dolthub/dolt/go/store/prolly"
25+
"github.com/dolthub/dolt/go/store/prolly/tree"
26+
27+
"github.com/dolthub/doltgresql/core/id"
28+
"github.com/dolthub/doltgresql/core/rootobject/objinterface"
29+
"github.com/dolthub/doltgresql/flatbuffers/gen/serial"
30+
)
31+
32+
// storage is used to read from and write to the root.
33+
var storage = objinterface.RootObjectSerializer{
34+
Bytes: (*serial.RootValue).CastsBytes,
35+
RootValueAdd: serial.RootValueAddCasts,
36+
}
37+
38+
// HandleMerge implements the interface objinterface.Collection.
39+
func (*Collection) HandleMerge(ctx context.Context, mro merge.MergeRootObject) (doltdb.RootObject, *merge.MergeStats, error) {
40+
ourCast := mro.OurRootObj.(Cast)
41+
theirCast := mro.TheirRootObj.(Cast)
42+
// Ensure that they have the same identifier
43+
if ourCast.ID != theirCast.ID {
44+
return nil, nil, errors.Newf("attempted to merge different casts: `%s` and `%s`",
45+
ourCast.Name().String(), theirCast.Name().String())
46+
}
47+
ourHash, err := ourCast.HashOf(ctx)
48+
if err != nil {
49+
return nil, nil, err
50+
}
51+
theirHash, err := theirCast.HashOf(ctx)
52+
if err != nil {
53+
return nil, nil, err
54+
}
55+
if ourHash.Equal(theirHash) {
56+
return mro.OurRootObj, &merge.MergeStats{
57+
Operation: merge.TableUnmodified,
58+
Adds: 0,
59+
Deletes: 0,
60+
Modifications: 0,
61+
DataConflicts: 0,
62+
SchemaConflicts: 0,
63+
ConstraintViolations: 0,
64+
}, nil
65+
}
66+
// TODO: figure out a decent merge strategy
67+
return nil, nil, errors.Errorf("unable to merge `%s`", theirCast.Name().String())
68+
}
69+
70+
// LoadCollection implements the interface objinterface.Collection.
71+
func (*Collection) LoadCollection(ctx context.Context, root objinterface.RootValue) (objinterface.Collection, error) {
72+
return LoadCasts(ctx, root)
73+
}
74+
75+
// LoadCollectionHash implements the interface objinterface.Collection.
76+
func (*Collection) LoadCollectionHash(ctx context.Context, root objinterface.RootValue) (hash.Hash, error) {
77+
m, ok, err := storage.GetProllyMap(ctx, root)
78+
if err != nil || !ok {
79+
return hash.Hash{}, err
80+
}
81+
return m.HashOf(), nil
82+
}
83+
84+
// LoadCasts loads the casts collection from the given root.
85+
func LoadCasts(ctx context.Context, root objinterface.RootValue) (*Collection, error) {
86+
m, ok, err := storage.GetProllyMap(ctx, root)
87+
if err != nil {
88+
return nil, err
89+
}
90+
if !ok {
91+
m, err = prolly.NewEmptyAddressMap(root.NodeStore())
92+
if err != nil {
93+
return nil, err
94+
}
95+
}
96+
return NewCollection(ctx, m, root.NodeStore())
97+
}
98+
99+
// ResolveNameFromObjects implements the interface objinterface.Collection.
100+
func (*Collection) ResolveNameFromObjects(ctx context.Context, name doltdb.TableName, rootObjects []objinterface.RootObject) (doltdb.TableName, id.Id, error) {
101+
// There are root objects to search through, so we'll create a temporary store
102+
ns := tree.NewTestNodeStore()
103+
addressMap, err := prolly.NewEmptyAddressMap(ns)
104+
if err != nil {
105+
return doltdb.TableName{}, id.Null, err
106+
}
107+
tempCollection, err := NewCollection(ctx, addressMap, ns)
108+
if err != nil {
109+
return doltdb.TableName{}, id.Null, err
110+
}
111+
for _, rootObject := range rootObjects {
112+
if c, ok := rootObject.(Cast); ok {
113+
if err = tempCollection.AddCast(ctx, c); err != nil {
114+
return doltdb.TableName{}, id.Null, err
115+
}
116+
}
117+
}
118+
return tempCollection.ResolveName(ctx, name)
119+
}
120+
121+
// Serializer implements the interface objinterface.Collection.
122+
func (*Collection) Serializer() objinterface.RootObjectSerializer {
123+
return storage
124+
}
125+
126+
// UpdateRoot implements the interface objinterface.Collection.
127+
func (pgc *Collection) UpdateRoot(ctx context.Context, root objinterface.RootValue) (objinterface.RootValue, error) {
128+
m, err := pgc.Map(ctx)
129+
if err != nil {
130+
return nil, err
131+
}
132+
return storage.WriteProllyMap(ctx, root, m)
133+
}

core/casts/init.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2024 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 casts
16+
17+
import "github.com/dolthub/doltgresql/core/id"
18+
19+
// Init initializes this package.
20+
func Init() map[id.Cast]Cast {
21+
return builtInCasts
22+
}

core/casts/root_object.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright 2026 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 casts
16+
17+
import (
18+
"context"
19+
"io"
20+
21+
"github.com/cockroachdb/errors"
22+
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
23+
"github.com/dolthub/dolt/go/store/hash"
24+
25+
"github.com/dolthub/doltgresql/core/id"
26+
"github.com/dolthub/doltgresql/core/rootobject/objinterface"
27+
pgtypes "github.com/dolthub/doltgresql/server/types"
28+
)
29+
30+
// DeserializeRootObject implements the interface objinterface.Collection.
31+
func (pgc *Collection) DeserializeRootObject(ctx context.Context, data []byte) (objinterface.RootObject, error) {
32+
return DeserializeCast(ctx, data)
33+
}
34+
35+
// DiffRootObjects implements the interface objinterface.Collection.
36+
func (pgc *Collection) DiffRootObjects(ctx context.Context, fromHash string, ours objinterface.RootObject, theirs objinterface.RootObject, ancestor objinterface.RootObject) ([]objinterface.RootObjectDiff, objinterface.RootObject, error) {
37+
return nil, nil, errors.New("cast conflict detection has not yet been implemented")
38+
}
39+
40+
// DropRootObject implements the interface objinterface.Collection.
41+
func (pgc *Collection) DropRootObject(ctx context.Context, identifier id.Id) error {
42+
if identifier.Section() != id.Section_Cast {
43+
return errors.Errorf(`cast %s does not exist`, identifier.String())
44+
}
45+
return pgc.DropCast(ctx, id.Cast(identifier))
46+
}
47+
48+
// GetFieldType implements the interface objinterface.Collection.
49+
func (pgc *Collection) GetFieldType(ctx context.Context, fieldName string) *pgtypes.DoltgresType {
50+
return nil
51+
}
52+
53+
// GetID implements the interface objinterface.Collection.
54+
func (pgc *Collection) GetID() objinterface.RootObjectID {
55+
return objinterface.RootObjectID_Casts
56+
}
57+
58+
// GetRootObject implements the interface objinterface.Collection.
59+
func (pgc *Collection) GetRootObject(ctx context.Context, identifier id.Id) (objinterface.RootObject, bool, error) {
60+
if identifier.Section() != id.Section_Cast {
61+
return nil, false, nil
62+
}
63+
c, err := pgc.getCast(ctx, id.Cast(identifier), nil, nil, CastType_Explicit)
64+
return c, err == nil && c.ID.IsValid(), err
65+
}
66+
67+
// HasRootObject implements the interface objinterface.Collection.
68+
func (pgc *Collection) HasRootObject(ctx context.Context, identifier id.Id) (bool, error) {
69+
if identifier.Section() != id.Section_Cast {
70+
return false, nil
71+
}
72+
return pgc.HasCast(ctx, id.Cast(identifier)), nil
73+
}
74+
75+
// IDToTableName implements the interface objinterface.Collection.
76+
func (pgc *Collection) IDToTableName(identifier id.Id) doltdb.TableName {
77+
if identifier.Section() != id.Section_Cast {
78+
return doltdb.TableName{}
79+
}
80+
return CastIDToTableName(id.Cast(identifier))
81+
}
82+
83+
// IterAll implements the interface objinterface.Collection.
84+
func (pgc *Collection) IterAll(ctx context.Context, callback func(rootObj objinterface.RootObject) (stop bool, err error)) error {
85+
return pgc.IterateCasts(ctx, func(c Cast) (stop bool, err error) {
86+
return callback(c)
87+
})
88+
}
89+
90+
// IterIDs implements the interface objinterface.Collection.
91+
func (pgc *Collection) IterIDs(ctx context.Context, callback func(identifier id.Id) (stop bool, err error)) error {
92+
err := pgc.underlyingMap.IterAll(ctx, func(k string, _ hash.Hash) error {
93+
stop, err := callback(id.Id(k))
94+
if err != nil {
95+
return err
96+
} else if stop {
97+
return io.EOF
98+
} else {
99+
return nil
100+
}
101+
})
102+
return err
103+
}
104+
105+
// PutRootObject implements the interface objinterface.Collection.
106+
func (pgc *Collection) PutRootObject(ctx context.Context, rootObj objinterface.RootObject) error {
107+
c, ok := rootObj.(Cast)
108+
if !ok {
109+
return errors.Newf("invalid cast root object: %T", rootObj)
110+
}
111+
return pgc.AddCast(ctx, c)
112+
}
113+
114+
// RenameRootObject implements the interface objinterface.Collection.
115+
func (pgc *Collection) RenameRootObject(ctx context.Context, oldName id.Id, newName id.Id) error {
116+
if !oldName.IsValid() || !newName.IsValid() || oldName.Section() != newName.Section() || oldName.Section() != id.Section_Cast {
117+
return errors.New("cannot rename cast due to invalid id")
118+
}
119+
oldCastName := id.Cast(oldName)
120+
newCastName := id.Cast(newName)
121+
c, err := pgc.getCast(ctx, oldCastName, nil, nil, CastType_Explicit)
122+
if err != nil {
123+
return err
124+
}
125+
if err = pgc.DropCast(ctx, newCastName); err != nil {
126+
return err
127+
}
128+
c.ID = newCastName
129+
return pgc.AddCast(ctx, c)
130+
}
131+
132+
// ResolveName implements the interface objinterface.Collection.
133+
func (pgc *Collection) ResolveName(ctx context.Context, name doltdb.TableName) (doltdb.TableName, id.Id, error) {
134+
rawID, err := pgc.resolveName(ctx, name.Schema, name.Name)
135+
if err != nil || !rawID.IsValid() {
136+
return doltdb.TableName{}, id.Null, err
137+
}
138+
return CastIDToTableName(rawID), rawID.AsId(), nil
139+
}
140+
141+
// TableNameToID implements the interface objinterface.Collection.
142+
func (pgc *Collection) TableNameToID(name doltdb.TableName) id.Id {
143+
return pgc.tableNameToID(name.Schema, name.Name).AsId()
144+
}
145+
146+
// UpdateField implements the interface objinterface.Collection.
147+
func (pgc *Collection) UpdateField(ctx context.Context, rootObject objinterface.RootObject, fieldName string, newValue any) (objinterface.RootObject, error) {
148+
return nil, errors.New("updating through the conflicts table for this object type is not yet supported")
149+
}

core/casts/serialization.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2026 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 casts
16+
17+
import (
18+
"context"
19+
20+
"github.com/cockroachdb/errors"
21+
22+
"github.com/dolthub/doltgresql/core/id"
23+
"github.com/dolthub/doltgresql/utils"
24+
)
25+
26+
// Serialize returns the Cast as a byte slice. If the Cast is invalid, then this returns a nil slice.
27+
func (cast Cast) Serialize(ctx context.Context) ([]byte, error) {
28+
if !cast.ID.IsValid() {
29+
return nil, nil
30+
}
31+
32+
// Initialize the writer and version
33+
writer := utils.NewWriter(256)
34+
writer.VariableUint(0) // Version
35+
// Write the cast data
36+
writer.Id(cast.ID.AsId())
37+
writer.Uint8(uint8(cast.CastType))
38+
writer.Id(cast.Function.AsId())
39+
writer.Bool(cast.UseInOut)
40+
// Returns the data
41+
return writer.Data(), nil
42+
}
43+
44+
// DeserializeCast returns the Cast that was serialized in the byte slice. Returns an empty Cast (invalid ID) if data is
45+
// nil or empty.
46+
func DeserializeCast(ctx context.Context, data []byte) (Cast, error) {
47+
if len(data) == 0 {
48+
return Cast{}, nil
49+
}
50+
reader := utils.NewReader(data)
51+
version := reader.VariableUint()
52+
if version != 0 {
53+
return Cast{}, errors.Errorf("version %d of casts is not supported, please upgrade the server", version)
54+
}
55+
56+
// Read from the reader
57+
t := Cast{}
58+
t.ID = id.Cast(reader.Id())
59+
t.CastType = CastType(reader.Uint8())
60+
t.Function = id.Function(reader.Id())
61+
t.UseInOut = reader.Bool()
62+
if !reader.IsEmpty() {
63+
return Cast{}, errors.Errorf("extra data found while deserializing a cast")
64+
}
65+
// Return the deserialized object
66+
return t, nil
67+
}

0 commit comments

Comments
 (0)