/
index_def.go
231 lines (195 loc) · 7.32 KB
/
index_def.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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package sc
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"strconv"
"strings"
)
const (
DefaultStringComponentLen int64 = 64
MinStringComponentLen int64 = 16
MaxStringComponentLen int64 = 1024
)
type IdxSortOrder string
const (
IdxSortAsc IdxSortOrder = "asc"
IdxSortDesc IdxSortOrder = "desc"
IdxSortUnknown IdxSortOrder = "unknown"
)
type IdxCaseSensitivity string
const (
IdxCaseSensitive IdxCaseSensitivity = "case_sensitive"
IdxIgnoreCase IdxCaseSensitivity = "ignore_case"
IdxCaseSensitivityUnknown IdxCaseSensitivity = "case_sensitivity_unknown"
)
type IdxComponentDef struct {
FieldName string
CaseSensitivity IdxCaseSensitivity
SortOrder IdxSortOrder
StringLen int64 // For string fields only, default 64
FieldType TableFieldType // Populated from tgt_table def
}
type IdxUniqueness string
const (
IdxUnique IdxUniqueness = "unique"
IdxNonUnique IdxUniqueness = "non_unique"
IdxUniquenessUnknown IdxUniqueness = "unknown"
)
type IdxDef struct {
Uniqueness IdxUniqueness
Components []IdxComponentDef
}
type IdxDefMap map[string]*IdxDef
type IndexRef struct {
TableName string
IdxName string
}
func (idxDef *IdxDef) getComponentFieldRefs(tableName string) FieldRefs {
fieldRefs := make([]FieldRef, len(idxDef.Components))
for i := 0; i < len(idxDef.Components); i++ {
fieldRefs[i] = FieldRef{
TableName: tableName,
FieldName: idxDef.Components[i].FieldName,
FieldType: idxDef.Components[i].FieldType}
}
return fieldRefs
}
func (idxDef *IdxDef) parseComponentExpr(fldExp *ast.Expr, fieldRefs *FieldRefs) error {
// Initialize index component with defaults and append it to idx def
idxCompDef := IdxComponentDef{
FieldName: FieldNameUnknown,
CaseSensitivity: IdxCaseSensitivityUnknown,
SortOrder: IdxSortUnknown,
StringLen: DefaultStringComponentLen, // Users can override it, see below
FieldType: FieldTypeUnknown}
switch typedFldExp := (*fldExp).(type) {
case *ast.CallExpr:
identExp, ok := typedFldExp.Fun.(*ast.Ident)
if !ok {
return fmt.Errorf("cannot parse order component func expression, field %s is not an ident", identExp.Name)
}
fieldRef, ok := fieldRefs.FindByFieldName(identExp.Name)
if !ok {
return fmt.Errorf("cannot parse order component func expression, field %s unknown", identExp.Name)
}
// Defaults
idxCompDef.FieldType = (*fieldRef).FieldType
idxCompDef.FieldName = identExp.Name
// Parse args: asc/desc, case_sensitive/ignore_case, number-string length
for _, modifierExp := range typedFldExp.Args {
switch modifierExpType := modifierExp.(type) {
case *ast.Ident:
switch modifierExpType.Name {
case string(IdxCaseSensitive):
idxCompDef.CaseSensitivity = IdxCaseSensitive
case string(IdxIgnoreCase):
idxCompDef.CaseSensitivity = IdxIgnoreCase
case string(IdxSortAsc):
idxCompDef.SortOrder = IdxSortAsc
case string(IdxSortDesc):
idxCompDef.SortOrder = IdxSortDesc
default:
return fmt.Errorf(
"unknown modifier %s for field %s, expected %s,%s,%s,%s",
modifierExpType.Name, identExp.Name, IdxIgnoreCase, IdxCaseSensitive, IdxSortAsc, IdxSortDesc)
}
case *ast.BasicLit:
switch modifierExpType.Kind {
case token.INT:
if idxCompDef.FieldType != FieldTypeString {
return fmt.Errorf("invalid expression %v in %s, component length modifier is valid only for string fields, but %s has type %s",
modifierExpType, identExp.Name, idxCompDef.FieldName, idxCompDef.FieldType)
}
idxCompDef.StringLen, _ = strconv.ParseInt(modifierExpType.Value, 10, 64)
if idxCompDef.StringLen < MinStringComponentLen {
idxCompDef.StringLen = MinStringComponentLen
} else if idxCompDef.StringLen > MaxStringComponentLen {
return fmt.Errorf("invalid expression %v in %s, component length modifier for string fields cannot exceed %d",
modifierExpType, identExp.Name, MaxStringComponentLen)
}
default:
return fmt.Errorf("invalid expression %v in %s, expected an integer for string component length", modifierExpType, identExp.Name)
}
default:
return fmt.Errorf(
"invalid expression %v, expected a modifier for field %s: expected %s,%s,%s,%s or an integer",
modifierExpType, identExp.Name, IdxIgnoreCase, IdxCaseSensitive, IdxSortAsc, IdxSortDesc)
}
// Check some rules
if idxCompDef.FieldType != FieldTypeString && idxCompDef.CaseSensitivity != IdxCaseSensitivityUnknown {
return fmt.Errorf(
"index component for field %s of type %s cannot have case sensitivity modifier %s, remove it from index component definition",
identExp.Name, idxCompDef.FieldType, idxCompDef.CaseSensitivity)
}
}
case *ast.Ident:
// This is a component def without modifiers (not filed1(...), just field1), so just apply defaults
fieldRef, ok := fieldRefs.FindByFieldName(typedFldExp.Name)
if !ok {
return fmt.Errorf("cannot parse order component ident expression, field %s unknown", typedFldExp.Name)
}
// Defaults
idxCompDef.FieldType = (*fieldRef).FieldType
idxCompDef.FieldName = typedFldExp.Name
default:
return fmt.Errorf(
"invalid expression in index component definition, expected 'field([modifiers])' or 'field' where 'field' is one of the fields of the table created by this node")
}
// Apply defaults if no modifiers supplied: string -> case sensitive, ordered idx -> sort asc
if idxCompDef.FieldType == FieldTypeString && idxCompDef.CaseSensitivity == IdxCaseSensitivityUnknown {
idxCompDef.CaseSensitivity = IdxCaseSensitive
}
if idxCompDef.SortOrder == IdxSortUnknown {
idxCompDef.SortOrder = IdxSortAsc
}
// All good - add it to the list of idx components
idxDef.Components = append(idxDef.Components, idxCompDef)
return nil
}
func (idxDefMap *IdxDefMap) parseRawIndexDefMap(rawIdxDefMap map[string]string, fieldRefs *FieldRefs) error {
errors := make([]string, 0, 10)
for idxName, rawIdxDef := range rawIdxDefMap {
expIdxDef, err := parser.ParseExpr(rawIdxDef)
if err != nil {
return fmt.Errorf("cannot parse order def '%s': %v", rawIdxDef, err)
}
switch typedExp := expIdxDef.(type) {
case *ast.CallExpr:
identExp, ok := typedExp.Fun.(*ast.Ident)
if !ok {
return fmt.Errorf("cannot parse call exp %v, expected ident", typedExp.Fun)
}
// Init idx def, defaults here if needed
idxDef := IdxDef{Uniqueness: IdxUniquenessUnknown}
switch identExp.Name {
case string(IdxUnique):
idxDef.Uniqueness = IdxUnique
case string(IdxNonUnique):
idxDef.Uniqueness = IdxNonUnique
default:
return fmt.Errorf(
"cannot parse index def [%s]: expected top level unique()) or non_unique() definition, found %s",
rawIdxDef, identExp.Name)
}
// Walk through args - idx field components
for _, fldExp := range typedExp.Args {
err := idxDef.parseComponentExpr(&fldExp, fieldRefs)
if err != nil {
errors = append(errors, fmt.Sprintf("index %s: [%s]", rawIdxDef, err.Error()))
}
}
// All good - add it to the idx map of the table def
(*idxDefMap)[idxName] = &idxDef
default:
return fmt.Errorf(
"cannot parse index def [%s]: expected top level unique()) or non_unique() definition, found unknown expression",
rawIdxDef)
}
}
if len(errors) > 0 {
return fmt.Errorf("cannot parse order definitions: [%s]", strings.Join(errors, "; "))
}
return nil
}