forked from dolthub/vitess
-
Notifications
You must be signed in to change notification settings - Fork 0
/
schema.go
205 lines (187 loc) · 5.42 KB
/
schema.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright 2014, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package planbuilder
import (
"encoding/json"
"fmt"
"io/ioutil"
"sort"
)
// Schema represents the denormalized version of SchemaFormal,
// used for building routing plans.
type Schema struct {
Tables map[string]*Table
}
// Table represnts a table in Schema.
type Table struct {
Name string
Keyspace *Keyspace
ColVindexes []*ColVindex
Ordered []*ColVindex
Owned []*ColVindex
}
// Keyspace contains the keyspcae info for each Table.
type Keyspace struct {
Name string
Sharded bool
}
// ColVindex contains the index info for each index of a table.
type ColVindex struct {
Col string
Type string
Name string
Owned bool
Vindex Vindex
}
// BuildSchema builds a Schema from a SchemaFormal.
func BuildSchema(source *SchemaFormal) (schema *Schema, err error) {
schema = &Schema{Tables: make(map[string]*Table)}
for ksname, ks := range source.Keyspaces {
keyspace := &Keyspace{
Name: ksname,
Sharded: ks.Sharded,
}
vindexes := make(map[string]Vindex)
for vname, vindexInfo := range ks.Vindexes {
vindex, err := CreateVindex(vindexInfo.Type, vindexInfo.Params)
if err != nil {
return nil, err
}
switch vindex.(type) {
case Unique:
case NonUnique:
default:
return nil, fmt.Errorf("vindex %s needs to be Unique or NonUnique", vname)
}
vindexes[vname] = vindex
}
for tname, cname := range ks.Tables {
if _, ok := schema.Tables[tname]; ok {
return nil, fmt.Errorf("table %s has multiple definitions", tname)
}
t := &Table{
Name: tname,
Keyspace: keyspace,
}
if !keyspace.Sharded {
schema.Tables[tname] = t
continue
}
class, ok := ks.Classes[cname]
if !ok {
return nil, fmt.Errorf("class %s not found for table %s", cname, tname)
}
for i, ind := range class.ColVindexes {
vindexInfo, ok := ks.Vindexes[ind.Name]
if !ok {
return nil, fmt.Errorf("vindex %s not found for class %s", ind.Name, cname)
}
columnVindex := &ColVindex{
Col: ind.Col,
Type: vindexInfo.Type,
Name: ind.Name,
Owned: vindexInfo.Owner == tname,
Vindex: vindexes[ind.Name],
}
if i == 0 {
// Perform Primary vindex check.
if _, ok := columnVindex.Vindex.(Unique); !ok {
return nil, fmt.Errorf("primary index %s is not Unique for class %s", ind.Name, cname)
}
if columnVindex.Owned {
if _, ok := columnVindex.Vindex.(Functional); !ok {
return nil, fmt.Errorf("primary owned index %s is not Functional for class %s", ind.Name, cname)
}
}
} else {
// Perform non-primary vindex check.
if columnVindex.Owned {
if _, ok := columnVindex.Vindex.(Lookup); !ok {
return nil, fmt.Errorf("non-primary owned index %s is not Lookup for class %s", ind.Name, cname)
}
}
}
t.ColVindexes = append(t.ColVindexes, columnVindex)
if columnVindex.Owned {
t.Owned = append(t.Owned, columnVindex)
}
}
t.Ordered = colVindexSorted(t.ColVindexes)
schema.Tables[tname] = t
}
}
return schema, nil
}
// FindTable returns a pointer to the Table if found.
// Otherwise, it returns a reason, which is equivalent to an error.
func (schema *Schema) FindTable(tablename string) (table *Table, reason string) {
if tablename == "" {
return nil, "complex table expression"
}
table = schema.Tables[tablename]
if table == nil {
return nil, fmt.Sprintf("table %s not found", tablename)
}
return table, ""
}
// ByCost provides the interface needed for ColVindexes to
// be sorted by cost order.
type ByCost []*ColVindex
func (bc ByCost) Len() int { return len(bc) }
func (bc ByCost) Swap(i, j int) { bc[i], bc[j] = bc[j], bc[i] }
func (bc ByCost) Less(i, j int) bool { return bc[i].Vindex.Cost() < bc[j].Vindex.Cost() }
func colVindexSorted(cvs []*ColVindex) (sorted []*ColVindex) {
for _, cv := range cvs {
sorted = append(sorted, cv)
}
sort.Sort(ByCost(sorted))
return sorted
}
// SchemaFormal is the formal representation of the schema
// as loaded from the source.
type SchemaFormal struct {
Keyspaces map[string]KeyspaceFormal
}
// KeyspaceFormal is the keyspace info for each keyspace
// as loaded from the source.
type KeyspaceFormal struct {
Sharded bool
Vindexes map[string]VindexFormal
Classes map[string]ClassFormal
Tables map[string]string
}
// VindexFormal is the info for each index as loaded from
// the source.
type VindexFormal struct {
Type string
Params map[string]interface{}
Owner string
}
// ClassFormal is the info for each table class as loaded from
// the source.
type ClassFormal struct {
ColVindexes []ColVindexFormal
}
// ColVindexFormal is the info for each indexed column
// of a table as loaded from the source.
type ColVindexFormal struct {
Col string
Name string
}
// LoadFile creates a new Schema from a JSON file.
func LoadFile(filename string) (schema *Schema, err error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("ReadFile failed: %v %v", filename, err)
}
return NewSchema(data)
}
// NewSchema creates a new Schema from a JSON byte array.
func NewSchema(data []byte) (schema *Schema, err error) {
var source SchemaFormal
if err := json.Unmarshal(data, &source); err != nil {
return nil, fmt.Errorf("Unmarshal failed: %v %s %v", source, data, err)
}
return BuildSchema(&source)
}