github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sqlbase/column_resolver.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package sqlbase 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 18 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 ) 22 23 // ProcessTargetColumns returns the column descriptors identified by the 24 // given name list. It also checks that a given column name is only 25 // listed once. If no column names are given (special case for INSERT) 26 // and ensureColumns is set, the descriptors for all visible columns 27 // are returned. If allowMutations is set, even columns undergoing 28 // mutations are added. 29 func ProcessTargetColumns( 30 tableDesc *ImmutableTableDescriptor, nameList tree.NameList, ensureColumns, allowMutations bool, 31 ) ([]ColumnDescriptor, error) { 32 if len(nameList) == 0 { 33 if ensureColumns { 34 // VisibleColumns is used here to prevent INSERT INTO <table> VALUES (...) 35 // (as opposed to INSERT INTO <table> (...) VALUES (...)) from writing 36 // hidden columns. At present, the only hidden column is the implicit rowid 37 // primary key column. 38 return tableDesc.VisibleColumns(), nil 39 } 40 return nil, nil 41 } 42 43 cols := make([]ColumnDescriptor, len(nameList)) 44 colIDSet := make(map[ColumnID]struct{}, len(nameList)) 45 for i, colName := range nameList { 46 var col *ColumnDescriptor 47 var err error 48 if allowMutations { 49 col, _, err = tableDesc.FindColumnByName(colName) 50 } else { 51 col, err = tableDesc.FindActiveColumnByName(string(colName)) 52 } 53 if err != nil { 54 return nil, err 55 } 56 57 if _, ok := colIDSet[col.ID]; ok { 58 return nil, pgerror.Newf(pgcode.Syntax, 59 "multiple assignments to the same column %q", &nameList[i]) 60 } 61 colIDSet[col.ID] = struct{}{} 62 cols[i] = *col 63 } 64 65 return cols, nil 66 } 67 68 // sourceNameMatches checks whether a request for table name toFind 69 // can be satisfied by the FROM source name srcName. 70 // 71 // For example: 72 // - a request for "kv" is matched by a source named "db1.public.kv" 73 // - a request for "public.kv" is not matched by a source named just "kv" 74 func sourceNameMatches(srcName *tree.TableName, toFind tree.TableName) bool { 75 if srcName.ObjectName != toFind.ObjectName { 76 return false 77 } 78 if toFind.ExplicitSchema { 79 if !srcName.ExplicitSchema || srcName.SchemaName != toFind.SchemaName { 80 return false 81 } 82 if toFind.ExplicitCatalog { 83 if !srcName.ExplicitCatalog || srcName.CatalogName != toFind.CatalogName { 84 return false 85 } 86 } 87 } 88 return true 89 } 90 91 // ColumnResolver is a utility struct to be used when resolving column 92 // names to point to one of the data sources and one of the column IDs 93 // in that data source. 94 type ColumnResolver struct { 95 Source *DataSourceInfo 96 97 // ResolverState is modified in-place by the implementation of the 98 // tree.ColumnItemResolver interface in resolver.go. 99 ResolverState struct { 100 ColIdx int 101 } 102 } 103 104 // FindSourceMatchingName is part of the tree.ColumnItemResolver interface. 105 func (r *ColumnResolver) FindSourceMatchingName( 106 ctx context.Context, tn tree.TableName, 107 ) ( 108 res tree.NumResolutionResults, 109 prefix *tree.TableName, 110 srcMeta tree.ColumnSourceMeta, 111 err error, 112 ) { 113 if !sourceNameMatches(&r.Source.SourceAlias, tn) { 114 return tree.NoResults, nil, nil, nil 115 } 116 prefix = &r.Source.SourceAlias 117 return tree.ExactlyOne, prefix, nil, nil 118 } 119 120 const invalidColIdx = -1 121 122 // FindSourceProvidingColumn is part of the tree.ColumnItemResolver interface. 123 func (r *ColumnResolver) FindSourceProvidingColumn( 124 ctx context.Context, col tree.Name, 125 ) (prefix *tree.TableName, srcMeta tree.ColumnSourceMeta, colHint int, err error) { 126 colIdx := invalidColIdx 127 colName := string(col) 128 129 for idx := range r.Source.SourceColumns { 130 colIdx, err = r.findColHelper(colName, colIdx, idx) 131 if err != nil { 132 return nil, nil, -1, err 133 } 134 if colIdx != invalidColIdx { 135 prefix = &r.Source.SourceAlias 136 break 137 } 138 } 139 if colIdx == invalidColIdx { 140 colAlloc := col 141 return nil, nil, -1, NewUndefinedColumnError(tree.ErrString(&colAlloc)) 142 } 143 r.ResolverState.ColIdx = colIdx 144 return prefix, nil, colIdx, nil 145 } 146 147 // Resolve is part of the tree.ColumnItemResolver interface. 148 func (r *ColumnResolver) Resolve( 149 ctx context.Context, 150 prefix *tree.TableName, 151 srcMeta tree.ColumnSourceMeta, 152 colHint int, 153 col tree.Name, 154 ) (tree.ColumnResolutionResult, error) { 155 if colHint != -1 { 156 // (*ColumnItem).Resolve() is telling us that we found the source 157 // via FindSourceProvidingColumn(). So we can count on 158 // r.ResolverState.ColIdx being set already. There's nothing remaining 159 // to do! 160 return nil, nil 161 } 162 163 // If we're here, we just know that some source alias was found that 164 // matches the column prefix, but we haven't found the column 165 // yet. Do this now. 166 // FindSourceMatchingName() was careful to set r.ResolverState.SrcIdx 167 // and r.ResolverState.ColSetIdx for us. 168 colIdx := invalidColIdx 169 colName := string(col) 170 for idx := range r.Source.SourceColumns { 171 var err error 172 colIdx, err = r.findColHelper(colName, colIdx, idx) 173 if err != nil { 174 return nil, err 175 } 176 } 177 178 if colIdx == invalidColIdx { 179 r.ResolverState.ColIdx = invalidColIdx 180 return nil, NewUndefinedColumnError( 181 tree.ErrString(tree.NewColumnItem(&r.Source.SourceAlias, tree.Name(colName)))) 182 } 183 r.ResolverState.ColIdx = colIdx 184 return nil, nil 185 } 186 187 // findColHelper is used by FindSourceProvidingColumn and Resolve above. 188 // It checks whether a column name is available in a given data source. 189 func (r *ColumnResolver) findColHelper(colName string, colIdx, idx int) (int, error) { 190 col := r.Source.SourceColumns[idx] 191 if col.Name == colName { 192 if colIdx != invalidColIdx { 193 colString := tree.ErrString(r.Source.NodeFormatter(idx)) 194 var msgBuf bytes.Buffer 195 name := tree.ErrString(&r.Source.SourceAlias) 196 if len(name) == 0 { 197 name = "<anonymous>" 198 } 199 fmt.Fprintf(&msgBuf, "%s.%s", name, colString) 200 return invalidColIdx, pgerror.Newf(pgcode.AmbiguousColumn, 201 "column reference %q is ambiguous (candidates: %s)", colString, msgBuf.String()) 202 } 203 colIdx = idx 204 } 205 return colIdx, nil 206 } 207 208 // NameResolutionResult implements the tree.NameResolutionResult interface. 209 func (*TableDescriptor) NameResolutionResult() {} 210 211 // SchemaMeta implements the tree.SchemaMeta interface. 212 func (*DatabaseDescriptor) SchemaMeta() {} 213 214 // SchemaMeta implements the tree.SchemaMeta interface. 215 func (Descriptor) SchemaMeta() {} 216 217 // NameResolutionResult implements the tree.NameResolutionResult interface. 218 func (Descriptor) NameResolutionResult() {}