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  }