forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 0
/
table_name.go
280 lines (239 loc) · 8.12 KB
/
table_name.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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package parser
import (
"bytes"
"fmt"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
)
// Table names are used in statements like CREATE TABLE,
// INSERT INTO, etc.
// General syntax:
// [ <database-name> '.' ] <table-name>
//
// The other syntax nodes hold a mutable NormalizableTableName
// attribute. This is populated during parsing with an
// UnresolvedName, and gets assigned an actual TableName upon the first
// call to its Normalize() method.
// NormalizableTableName implements an editable table name.
type NormalizableTableName struct {
TableNameReference
}
// Format implements the NodeFormatter interface.
func (nt *NormalizableTableName) Format(buf *bytes.Buffer, f FmtFlags) {
if f.tableNameFormatter != nil {
f.tableNameFormatter(nt, buf, f)
} else {
FormatNode(buf, f, nt.TableNameReference)
}
}
func (nt *NormalizableTableName) String() string { return AsString(nt) }
// Normalize checks if the table name is already normalized and
// normalizes it as necessary.
func (nt *NormalizableTableName) Normalize() (*TableName, error) {
switch t := nt.TableNameReference.(type) {
case *TableName:
return t, nil
case UnresolvedName:
tn, err := t.NormalizeTableName()
if err != nil {
return nil, err
}
nt.TableNameReference = tn
return tn, nil
default:
panic(fmt.Sprintf("unsupported table name reference: %+v (%T)",
nt.TableNameReference, nt.TableNameReference))
}
}
// NormalizeWithDatabaseName combines Normalize and QualifyWithDatabase.
func (nt *NormalizableTableName) NormalizeWithDatabaseName(database string) (*TableName, error) {
tn, err := nt.Normalize()
if err != nil {
return nil, err
}
if err := tn.QualifyWithDatabase(database); err != nil {
return nil, err
}
return tn, nil
}
// TableName asserts that the table name has been previously normalized.
func (nt *NormalizableTableName) TableName() *TableName {
return nt.TableNameReference.(*TableName)
}
// tableExpr implements the TableExpr interface.
func (*NormalizableTableName) tableExpr() {}
// TableNameReference implements the editable cell of a TableExpr that
// refers to a single table.
type TableNameReference interface {
fmt.Stringer
NodeFormatter
NormalizeTableName() (*TableName, error)
}
// TableName corresponds to the name of a table in a FROM clause,
// INSERT or UPDATE statement (and possibly other places).
type TableName struct {
PrefixName Name
DatabaseName Name
TableName Name
// DBNameOriginallyOmitted, when set to true, causes the
// String()/Format() methods to omit the database name even if one
// is set. This is used to ensure that pretty-printing
// a TableName normalized from a parser input yields back
// the original syntax.
DBNameOriginallyOmitted bool
// PrefixOriginallySpecified indicates whether a prefix was
// explicitly indicated in the input syntax.
PrefixOriginallySpecified bool
}
// Format implements the NodeFormatter interface.
func (t *TableName) Format(buf *bytes.Buffer, f FmtFlags) {
if !t.DBNameOriginallyOmitted || f.alwaysQualify || f.tableNameFormatter != nil {
if t.PrefixOriginallySpecified {
FormatNode(buf, f, t.PrefixName)
buf.WriteByte('.')
}
FormatNode(buf, f, t.DatabaseName)
buf.WriteByte('.')
}
FormatNode(buf, f, t.TableName)
}
func (t *TableName) String() string { return AsString(t) }
// NormalizeTableName implements the TableNameReference interface.
func (t *TableName) NormalizeTableName() (*TableName, error) { return t, nil }
// Table retrieves the unqualified table name.
func (t *TableName) Table() string {
return string(t.TableName)
}
// Database retrieves the unqualified database name.
func (t *TableName) Database() string {
return string(t.DatabaseName)
}
// NewInvalidNameErrorf initializes an error carrying the pg code CodeInvalidNameError.
func NewInvalidNameErrorf(fmt string, args ...interface{}) error {
return pgerror.NewErrorf(pgerror.CodeInvalidNameError, fmt, args...)
}
// normalizeTableNameAsValue transforms an UnresolvedName to a TableName.
// The resulting TableName may lack a db qualification. This is
// valid if e.g. the name refers to a in-query table alias
// (AS) or is qualified later using the QualifyWithDatabase method.
func (n UnresolvedName) normalizeTableNameAsValue() (res TableName, err error) {
if len(n) == 0 || len(n) > 3 {
return res, NewInvalidNameErrorf("invalid table name: %q", ErrString(n))
}
name, ok := n[len(n)-1].(Name)
if !ok {
return res, NewInvalidNameErrorf("invalid table name: %q", ErrString(n))
}
if len(name) == 0 {
return res, NewInvalidNameErrorf("empty table name: %q", ErrString(n))
}
res = TableName{TableName: name, DBNameOriginallyOmitted: true}
if len(n) > 1 {
res.DatabaseName, ok = n[len(n)-2].(Name)
if !ok {
return res, NewInvalidNameErrorf("invalid database name: %q", ErrString(n[len(n)-2]))
}
if len(res.DatabaseName) == 0 {
return res, NewInvalidNameErrorf("empty database name: %q", ErrString(n))
}
res.DBNameOriginallyOmitted = false
if len(n) > 2 {
res.PrefixName, ok = n[len(n)-3].(Name)
if !ok {
return res, NewInvalidNameErrorf("invalid prefix: %q", ErrString(n[len(n)-3]))
}
res.PrefixOriginallySpecified = true
}
}
return res, nil
}
// NormalizeTableName implements the TableNameReference interface.
func (n UnresolvedName) NormalizeTableName() (*TableName, error) {
tn, err := n.normalizeTableNameAsValue()
if err != nil {
return nil, err
}
return &tn, nil
}
// QualifyWithDatabase adds an indirection for the database, if it's missing.
// It transforms:
// table -> database.table
// table@index -> database.table@index
func (t *TableName) QualifyWithDatabase(database string) error {
if !t.DBNameOriginallyOmitted {
return nil
}
if database == "" {
return pgerror.NewErrorf(pgerror.CodeUndefinedTableError, "no database specified: %q", t)
}
t.DatabaseName = Name(database)
return nil
}
// TableNames represents a comma separated list (see the Format method)
// of table names.
type TableNames []TableName
// Format implements the NodeFormatter interface.
func (ts TableNames) Format(buf *bytes.Buffer, f FmtFlags) {
for i := range ts {
if i > 0 {
buf.WriteString(", ")
}
FormatNode(buf, f, &ts[i])
}
}
func (ts TableNames) String() string { return AsString(ts) }
// TableNameReferences corresponds to a comma-delimited
// list of table name references.
type TableNameReferences []TableNameReference
// Format implements the NodeFormatter interface.
func (t TableNameReferences) Format(buf *bytes.Buffer, f FmtFlags) {
for i, t := range t {
if i > 0 {
buf.WriteString(", ")
}
FormatNode(buf, f, t)
}
}
// TableNameWithIndex represents a "table@index", used in statements that
// specifically refer to an index.
type TableNameWithIndex struct {
Table NormalizableTableName
Index Name
// SearchTable indicates that we have just an index (no table name); we will
// need to search for a table that has an index with the given name.
//
// To allow schema-qualified index names in this case, the index is actually
// specified in Table as the table name, and Index is empty.
SearchTable bool
}
// Format implements the NodeFormatter interface.
func (n *TableNameWithIndex) Format(buf *bytes.Buffer, f FmtFlags) {
FormatNode(buf, f, &n.Table)
if n.Index != "" {
buf.WriteByte('@')
FormatNode(buf, f, n.Index)
}
}
// TableNameWithIndexList is a list of indexes.
type TableNameWithIndexList []*TableNameWithIndex
// Format implements the NodeFormatter interface.
func (n TableNameWithIndexList) Format(buf *bytes.Buffer, f FmtFlags) {
for i, e := range n {
if i > 0 {
buf.WriteString(", ")
}
FormatNode(buf, f, e)
}
}