Skip to content
This repository was archived by the owner on Aug 2, 2021. It is now read-only.

Commit 89f5fb7

Browse files
janoszelig
authored andcommitted
shed: allow to change the index name for easier migrations (#1912)
1 parent ab8e73d commit 89f5fb7

File tree

2 files changed

+154
-9
lines changed

2 files changed

+154
-9
lines changed

shed/schema.go

+34-5
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,20 @@ type fieldSpec struct {
4646
Type string `json:"type"`
4747
}
4848

49-
// indxSpec holds information about a particular index.
49+
// indexSpec holds information about a particular index.
5050
// It does not contain index type, as indexes do not have type.
5151
type indexSpec struct {
5252
Name string `json:"name"`
5353
}
5454

5555
// schemaFieldKey retrieves the complete LevelDB key for
56-
// a particular field form the schema definition.
56+
// a particular field from the schema definition.
5757
func (db *DB) schemaFieldKey(name, fieldType string) (key []byte, err error) {
5858
if name == "" {
59-
return nil, errors.New("field name can not be blank")
59+
return nil, errors.New("field name cannot be blank")
6060
}
6161
if fieldType == "" {
62-
return nil, errors.New("field type can not be blank")
62+
return nil, errors.New("field type cannot be blank")
6363
}
6464
s, err := db.getSchema()
6565
if err != nil {
@@ -86,11 +86,40 @@ func (db *DB) schemaFieldKey(name, fieldType string) (key []byte, err error) {
8686
return append([]byte{keyPrefixFields}, []byte(name)...), nil
8787
}
8888

89+
// RenameIndex changes the schema so that an existing index name is changed
90+
// while preserving its data by keeping the same internal key prefix.
91+
// Renaming indexes is useful when encoding functions can be backward compatible
92+
// to avoid data migrations.
93+
func (db *DB) RenameIndex(name, newName string) (renamed bool, err error) {
94+
if name == "" {
95+
return false, errors.New("index name cannot be blank")
96+
}
97+
if newName == "" {
98+
return false, errors.New("new index name cannot be blank")
99+
}
100+
if newName == name {
101+
return false, nil
102+
}
103+
s, err := db.getSchema()
104+
if err != nil {
105+
return false, err
106+
}
107+
for i, f := range s.Indexes {
108+
if f.Name == name {
109+
s.Indexes[i] = indexSpec{
110+
Name: newName,
111+
}
112+
return true, db.putSchema(s)
113+
}
114+
}
115+
return false, nil
116+
}
117+
89118
// schemaIndexID retrieves the complete LevelDB prefix for
90119
// a particular index.
91120
func (db *DB) schemaIndexPrefix(name string) (id byte, err error) {
92121
if name == "" {
93-
return 0, errors.New("index name can not be blank")
122+
return 0, errors.New("index name cannot be blank")
94123
}
95124
s, err := db.getSchema()
96125
if err != nil {

shed/schema_test.go

+120-4
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@ func TestDB_schemaFieldKey(t *testing.T) {
2929
t.Run("empty name or type", func(t *testing.T) {
3030
_, err := db.schemaFieldKey("", "")
3131
if err == nil {
32-
t.Errorf("error not returned, but expected")
32+
t.Error("error not returned, but expected")
3333
}
3434
_, err = db.schemaFieldKey("", "type")
3535
if err == nil {
36-
t.Errorf("error not returned, but expected")
36+
t.Error("error not returned, but expected")
3737
}
3838

3939
_, err = db.schemaFieldKey("test", "")
4040
if err == nil {
41-
t.Errorf("error not returned, but expected")
41+
t.Error("error not returned, but expected")
4242
}
4343
})
4444

@@ -82,7 +82,7 @@ func TestDB_schemaFieldKey(t *testing.T) {
8282

8383
_, err = db.schemaFieldKey("the-field", "another-type")
8484
if err == nil {
85-
t.Errorf("error not returned, but expected")
85+
t.Error("error not returned, but expected")
8686
}
8787
})
8888
}
@@ -124,3 +124,119 @@ func TestDB_schemaIndexPrefix(t *testing.T) {
124124
}
125125
})
126126
}
127+
128+
// TestDB_RenameIndex checks if index name is correctly changed.
129+
func TestDB_RenameIndex(t *testing.T) {
130+
131+
t.Run("empty names", func(t *testing.T) {
132+
db, cleanupFunc := newTestDB(t)
133+
defer cleanupFunc()
134+
135+
// empty names
136+
renamed, err := db.RenameIndex("", "")
137+
if err == nil {
138+
t.Error("error not returned, but expected")
139+
}
140+
if renamed {
141+
t.Fatal("index should not be renamed")
142+
}
143+
144+
// empty index name
145+
renamed, err = db.RenameIndex("", "new")
146+
if err == nil {
147+
t.Error("error not returned, but expected")
148+
}
149+
if renamed {
150+
t.Fatal("index should not be renamed")
151+
}
152+
153+
// empty new index name
154+
renamed, err = db.RenameIndex("current", "")
155+
if err == nil {
156+
t.Error("error not returned, but expected")
157+
}
158+
if renamed {
159+
t.Fatal("index should not be renamed")
160+
}
161+
})
162+
163+
t.Run("same names", func(t *testing.T) {
164+
db, cleanupFunc := newTestDB(t)
165+
defer cleanupFunc()
166+
167+
renamed, err := db.RenameIndex("index1", "index1")
168+
if err != nil {
169+
t.Error(err)
170+
}
171+
if renamed {
172+
t.Fatal("index should not be renamed")
173+
}
174+
})
175+
176+
t.Run("unknown name", func(t *testing.T) {
177+
db, cleanupFunc := newTestDB(t)
178+
defer cleanupFunc()
179+
180+
renamed, err := db.RenameIndex("index1", "index1new")
181+
if err != nil {
182+
t.Error(err)
183+
}
184+
if renamed {
185+
t.Fatal("index should not be renamed")
186+
}
187+
})
188+
189+
t.Run("valid names", func(t *testing.T) {
190+
db, cleanupFunc := newTestDB(t)
191+
defer cleanupFunc()
192+
193+
// initial indexes
194+
key1, err := db.schemaIndexPrefix("index1")
195+
if err != nil {
196+
t.Fatal(err)
197+
}
198+
key2, err := db.schemaIndexPrefix("index2")
199+
if err != nil {
200+
t.Fatal(err)
201+
}
202+
203+
// name the first one
204+
renamed, err := db.RenameIndex("index1", "index1new")
205+
if err != nil {
206+
t.Fatal(err)
207+
}
208+
if !renamed {
209+
t.Fatal("index not renamed")
210+
}
211+
212+
// validate that the index key stays the same
213+
key1same, err := db.schemaIndexPrefix("index1new")
214+
if err != nil {
215+
t.Fatal(err)
216+
}
217+
if key1 != key1same {
218+
t.Fatal("indexes renamed, but keys are not the same")
219+
}
220+
221+
// validate that the independent index is not changed
222+
key2same, err := db.schemaIndexPrefix("index2")
223+
if err != nil {
224+
t.Fatal(err)
225+
}
226+
if key2 != key2same {
227+
t.Fatal("independent index key has changed")
228+
}
229+
230+
// validate that it is safe to create a new index with previous name
231+
key1renew, err := db.schemaIndexPrefix("index1")
232+
if err != nil {
233+
t.Fatal(err)
234+
}
235+
if key1 == key1renew {
236+
t.Fatal("renewed index and the original one have the same key")
237+
}
238+
if key2 == key1renew {
239+
t.Fatal("renewed index and the independent one have the same key")
240+
}
241+
})
242+
}

0 commit comments

Comments
 (0)