This repository has been archived by the owner on Jun 14, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathindex.go
166 lines (156 loc) · 6.11 KB
/
index.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
package tengo
import (
"errors"
"fmt"
"strings"
)
// Index represents a single index (primary key, unique secondary index, or non-
// unique secondard index) in a table.
type Index struct {
Name string `json:"name"`
Parts []IndexPart `json:"parts"`
PrimaryKey bool `json:"primaryKey,omitempty"`
Unique bool `json:"unique,omitempty"`
Invisible bool `json:"invisible,omitempty"` // MySQL 8+, also used for MariaDB 10.6's IGNORED indexes
Comment string `json:"comment,omitempty"`
Type string `json:"type"`
FullTextParser string `json:"parser,omitempty"`
}
// IndexPart represents an individual indexed column or expression. Each index
// has one or more IndexPart values.
type IndexPart struct {
ColumnName string `json:"columnName,omitempty"` // name of column, or empty if expression
Expression string `json:"expression,omitempty"` // expression value (MySQL 8+), or empty if column
PrefixLength uint16 `json:"prefixLength,omitempty"` // nonzero if only a prefix of column is indexed
Descending bool `json:"descending,omitempty"` // if true, collation is descending (MySQL 8+)
}
// Definition returns this index's definition clause, for use as part of a DDL
// statement.
func (idx *Index) Definition(flavor Flavor) string {
parts := make([]string, len(idx.Parts))
for n := range idx.Parts {
parts[n] = idx.Parts[n].Definition(flavor)
}
var typeAndName, comment, invis, parser string
if idx.PrimaryKey {
if !idx.Unique {
panic(errors.New("Index is primary key, but isn't marked as unique"))
}
typeAndName = "PRIMARY KEY"
} else if idx.Unique {
typeAndName = fmt.Sprintf("UNIQUE KEY %s", EscapeIdentifier(idx.Name))
} else if idx.Type != "BTREE" && idx.Type != "" {
typeAndName = fmt.Sprintf("%s KEY %s", idx.Type, EscapeIdentifier(idx.Name))
} else {
typeAndName = fmt.Sprintf("KEY %s", EscapeIdentifier(idx.Name))
}
if idx.Comment != "" {
comment = fmt.Sprintf(" COMMENT '%s'", EscapeValueForCreateTable(idx.Comment))
}
if idx.Invisible {
if flavor.Vendor == VendorMariaDB {
invis = " IGNORED"
} else {
invis = " /*!80000 INVISIBLE */"
}
}
if idx.Type == "FULLTEXT" && idx.FullTextParser != "" {
// Note the trailing space here is intentional -- it's always present in SHOW
// CREATE TABLE for this particular clause
parser = fmt.Sprintf(" /*!50100 WITH PARSER `%s` */ ", idx.FullTextParser)
}
return fmt.Sprintf("%s (%s)%s%s%s", typeAndName, strings.Join(parts, ","), comment, invis, parser)
}
// Equals returns true if two indexes are completely identical, false otherwise.
func (idx *Index) Equals(other *Index) bool {
if idx == nil || other == nil {
return idx == other // only equal if BOTH are nil
}
return idx.Invisible == other.Invisible && idx.EqualsIgnoringVisibility(other)
}
// EqualsIgnoringVisibility returns true if two indexes are identical, or only
// differ in visibility.
func (idx *Index) EqualsIgnoringVisibility(other *Index) bool {
if idx == nil || other == nil {
return idx == other // only equal if BOTH are nil
}
return idx.Name == other.Name && idx.Comment == other.Comment && idx.Equivalent(other)
}
// sameParts returns true if two Indexes' Parts slices are identical.
func (idx *Index) sameParts(other *Index) bool {
if len(idx.Parts) != len(other.Parts) {
return false
}
for n := range idx.Parts {
if idx.Parts[n] != other.Parts[n] {
return false
}
}
return true
}
// Equivalent returns true if two Indexes are functionally equivalent,
// regardless of whether or not they have the same names, comments, or
// visibility.
func (idx *Index) Equivalent(other *Index) bool {
if idx == nil || other == nil {
return idx == other // only equivalent if BOTH are nil
}
if idx.PrimaryKey != other.PrimaryKey || idx.Unique != other.Unique || idx.Type != other.Type || idx.FullTextParser != other.FullTextParser {
return false
}
return idx.sameParts(other)
}
// RedundantTo returns true if idx is equivalent to, or a strict subset of,
// other. Both idx and other should be indexes of the same table.
// A non-unique index is considered redundant to any other index having the
// same (or more) columns in the same order, unless its parts have a greater
// column prefix length. A unique index can only be redundant to the primary key
// or an exactly equivalent unique index; another unique index with more cols
// may coexist due to the desired constraint semantics. A primary key is never
// redundant to another index.
func (idx *Index) RedundantTo(other *Index) bool {
if idx == nil || other == nil {
return false
}
if idx.PrimaryKey || (idx.Unique && !other.Unique) || idx.Type != other.Type || idx.FullTextParser != other.FullTextParser {
return false
}
if !idx.Invisible && other.Invisible {
return false // a visible index is never redundant to an invisible one
}
if idx.Unique && other.Unique {
// Since unique indexes are also unique *constraints*, two unique indexes are
// non-redundant unless they have identical parts.
return idx.sameParts(other)
} else if idx.Type == "FULLTEXT" && len(idx.Parts) != len(other.Parts) {
return false // FT composite indexes don't behave like BTREE in terms of left-right prefixing
} else if len(idx.Parts) > len(other.Parts) {
return false // can't be redundant to an index with fewer cols
}
for n, part := range idx.Parts {
if part.ColumnName != other.Parts[n].ColumnName || part.Expression != other.Parts[n].Expression || part.Descending != other.Parts[n].Descending {
return false
}
partPrefix, otherPrefix := part.PrefixLength, other.Parts[n].PrefixLength
if otherPrefix > 0 && (partPrefix == 0 || partPrefix > otherPrefix) {
return false
}
}
return true
}
// Definition returns this index part's definition clause.
func (part *IndexPart) Definition(_ Flavor) string {
var base, prefix, collation string
if part.ColumnName != "" {
base = EscapeIdentifier(part.ColumnName)
} else {
base = fmt.Sprintf("(%s)", part.Expression)
}
if part.PrefixLength > 0 {
prefix = fmt.Sprintf("(%d)", part.PrefixLength)
}
if part.Descending {
collation = " DESC"
}
return fmt.Sprintf("%s%s%s", base, prefix, collation)
}