github.com/mithrandie/csvq@v1.18.1/lib/query/header.go (about) 1 package query 2 3 import ( 4 "errors" 5 "strconv" 6 "strings" 7 8 "github.com/mithrandie/csvq/lib/parser" 9 ) 10 11 const InternalIdColumn = "@__internal_id" 12 13 type HeaderField struct { 14 View string 15 Identifier string 16 Column string 17 Aliases []string 18 Number int 19 IsFromTable bool 20 IsJoinColumn bool 21 IsGroupKey bool 22 } 23 24 var errFieldAmbiguous = errors.New("field ambiguous") 25 var errFieldNotExist = errors.New("field not exists") 26 27 type Header []HeaderField 28 29 func NewHeaderWithId(view string, words []string) Header { 30 h := make([]HeaderField, len(words)+1) 31 32 h[0].View = view 33 h[0].Column = InternalIdColumn 34 35 for i := 1; i <= len(words); i++ { 36 h[i].View = view 37 h[i].Column = words[i-1] 38 h[i].Number = i 39 h[i].IsFromTable = true 40 } 41 42 return h 43 } 44 45 func NewHeader(view string, words []string) Header { 46 h := make([]HeaderField, len(words)) 47 48 for i, v := range words { 49 h[i].View = view 50 h[i].Column = v 51 h[i].Number = i + 1 52 h[i].IsFromTable = true 53 } 54 55 return h 56 } 57 58 func NewHeaderWithAutofill(view string, words []string) Header { 59 for i, v := range words { 60 if v == "" { 61 words[i] = "__@" + strconv.Itoa(i+1) + "__" 62 } 63 } 64 return NewHeader(view, words) 65 } 66 67 func NewEmptyHeader(len int) Header { 68 return make([]HeaderField, len, len+2) 69 } 70 71 func AddHeaderField(h Header, identifier string, column string, alias string) (header Header, index int) { 72 hfield := HeaderField{ 73 Identifier: identifier, 74 Column: column, 75 } 76 if 0 < len(alias) && !strings.EqualFold(column, alias) { 77 hfield.Aliases = append(hfield.Aliases, alias) 78 } 79 80 header = append(h, hfield) 81 index = header.Len() - 1 82 return 83 } 84 85 func (h Header) Len() int { 86 return len(h) 87 } 88 89 func (h Header) TableColumns() []parser.QueryExpression { 90 columns := make([]parser.QueryExpression, 0, h.Len()) 91 for _, f := range h { 92 if !f.IsFromTable { 93 continue 94 } 95 96 fieldRef := parser.FieldReference{ 97 Column: parser.Identifier{Literal: f.Column}, 98 } 99 if 0 < len(f.View) { 100 fieldRef.View = parser.Identifier{Literal: f.View} 101 } 102 103 columns = append(columns, fieldRef) 104 } 105 return columns 106 } 107 108 func (h Header) TableColumnNames() []string { 109 names := make([]string, 0, h.Len()) 110 for _, f := range h { 111 if !f.IsFromTable { 112 continue 113 } 114 names = append(names, f.Column) 115 } 116 return names 117 } 118 119 func (h Header) ContainsObject(obj parser.QueryExpression) (int, bool) { 120 switch obj.(type) { 121 case parser.FieldReference, parser.ColumnNumber: 122 if n, err := h.SearchIndex(obj); err == nil { 123 return n, true 124 } else { 125 return -1, false 126 } 127 } 128 129 column := FormatFieldIdentifier(obj) 130 131 idx := -1 132 for i, f := range h { 133 if f.IsFromTable || len(f.Identifier) < 1 { 134 continue 135 } 136 137 if !strings.EqualFold(f.Identifier, column) { 138 continue 139 } 140 141 idx = i 142 break 143 } 144 145 if idx < 0 { 146 return -1, false 147 } 148 return idx, true 149 } 150 151 func (h Header) SearchIndex(fieldRef parser.QueryExpression) (int, error) { 152 if number, ok := fieldRef.(parser.ColumnNumber); ok { 153 return h.FieldNumberIndex(number) 154 } 155 return h.FieldIndex(fieldRef.(parser.FieldReference)) 156 } 157 158 func (h Header) FieldNumberIndex(number parser.ColumnNumber) (int, error) { 159 view := number.View.Literal 160 idx := int(number.Number.Raw()) 161 162 if idx < 1 { 163 return -1, errFieldNotExist 164 } 165 166 for i, f := range h { 167 if strings.EqualFold(f.View, view) && f.Number == idx { 168 return i, nil 169 } 170 } 171 return -1, errFieldNotExist 172 } 173 174 func (h Header) FieldIndex(fieldRef parser.FieldReference) (int, error) { 175 var view string 176 if 0 < len(fieldRef.View.Literal) { 177 view = fieldRef.View.Literal 178 } 179 180 col, ok := fieldRef.Column.(parser.Identifier) 181 if !ok { 182 return -1, errFieldAmbiguous 183 } 184 column := strings.TrimSpace(col.Literal) 185 186 idx := -1 187 188 for i := range h { 189 hcol := strings.TrimSpace(h[i].Column) 190 if 0 < len(view) { 191 if !strings.EqualFold(h[i].View, view) || !strings.EqualFold(hcol, column) { 192 continue 193 } 194 } else { 195 isEqual := strings.EqualFold(hcol, column) 196 if isEqual && h[i].IsJoinColumn { 197 idx = i 198 break 199 } 200 201 if !isEqual && !InStrSliceWithCaseInsensitive(column, h[i].Aliases) { 202 continue 203 } 204 } 205 206 if -1 < idx { 207 return -1, errFieldAmbiguous 208 } 209 idx = i 210 } 211 212 if idx < 0 { 213 return -1, errFieldNotExist 214 } 215 216 return idx, nil 217 } 218 219 func (h Header) ContainsInternalId(viewName string) (int, error) { 220 fieldRef := parser.FieldReference{ 221 View: parser.Identifier{Literal: viewName}, 222 Column: parser.Identifier{Literal: InternalIdColumn}, 223 } 224 return h.SearchIndex(fieldRef) 225 } 226 227 func (h Header) Update(reference string, fields []parser.QueryExpression) error { 228 if fields != nil && 0 < len(fields) { 229 if len(fields) != h.Len() { 230 return NewFieldLengthNotMatchError(fields[0]) 231 } 232 233 names := make(map[string]bool, len(fields)) 234 for i := range fields { 235 lit := strings.ToUpper(fields[i].(parser.Identifier).Literal) 236 if _, ok := names[lit]; ok { 237 return NewDuplicateFieldNameError(fields[i].(parser.Identifier)) 238 } 239 names[lit] = true 240 } 241 } 242 243 for i := range h { 244 h[i].View = reference 245 if fields != nil && 0 < len(fields) { 246 h[i].Column = fields[i].(parser.Identifier).Literal 247 } 248 h[i].Aliases = nil 249 } 250 return nil 251 } 252 253 func (h Header) Merge(h2 Header) Header { 254 header := make(Header, len(h)+len(h2)) 255 leftLen := len(h) 256 for i := range h { 257 header[i] = h[i] 258 } 259 for i := range h2 { 260 header[i+leftLen] = h2[i] 261 } 262 return header 263 } 264 265 func (h Header) Copy() Header { 266 header := make(Header, h.Len()) 267 for i := range h { 268 header[i] = h[i] 269 } 270 return header 271 }