github.com/jgbaldwinbrown/perf@v0.1.1/storage/db/query.go (about)

     1  // Copyright 2017 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package db
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"strings"
    11  	"unicode"
    12  )
    13  
    14  // operation is the enum for possible query operations.
    15  type operation rune
    16  
    17  // Query operations.
    18  // Only equals, lt, and gt can be specified in a query.
    19  // The order of these operations is used by merge.
    20  const (
    21  	equals operation = iota
    22  	ltgt
    23  	lt
    24  	gt
    25  )
    26  
    27  // A part is a single query part with a key, operator, and value.
    28  type part struct {
    29  	key      string
    30  	operator operation
    31  	// value and value2 hold the values to compare against.
    32  	value, value2 string
    33  }
    34  
    35  // sepToOperation maps runes to operation values.
    36  var sepToOperation = map[byte]operation{
    37  	':': equals,
    38  	'<': lt,
    39  	'>': gt,
    40  }
    41  
    42  // parseWord parse a single query part (as returned by SplitWords with quoting and escaping already removed) into a part struct.
    43  func parseWord(word string) (part, error) {
    44  	sepIndex := strings.IndexFunc(word, func(r rune) bool {
    45  		return r == ':' || r == '>' || r == '<' || unicode.IsSpace(r) || unicode.IsUpper(r)
    46  	})
    47  	if sepIndex < 0 {
    48  		return part{}, fmt.Errorf("query part %q is missing operator", word)
    49  	}
    50  	key, sep, value := word[:sepIndex], word[sepIndex], word[sepIndex+1:]
    51  	if oper, ok := sepToOperation[sep]; ok {
    52  		return part{key, oper, value, ""}, nil
    53  	}
    54  	return part{}, fmt.Errorf("query part %q has invalid key", word)
    55  }
    56  
    57  // merge merges two query parts together into a single query part.
    58  // The keys of the two parts must be equal.
    59  // If the result is a query part that can never match, io.EOF is returned as the error.
    60  func (p part) merge(p2 part) (part, error) {
    61  	if p2.operator < p.operator {
    62  		// Sort the parts so we only need half the table below.
    63  		p, p2 = p2, p
    64  	}
    65  	switch p.operator {
    66  	case equals:
    67  		switch p2.operator {
    68  		case equals:
    69  			if p.value == p2.value {
    70  				return p, nil
    71  			}
    72  			return part{}, io.EOF
    73  		case lt:
    74  			if p.value < p2.value {
    75  				return p, nil
    76  			}
    77  			return part{}, io.EOF
    78  		case gt:
    79  			if p.value > p2.value {
    80  				return p, nil
    81  			}
    82  			return part{}, io.EOF
    83  		case ltgt:
    84  			if p.value < p2.value && p.value > p2.value2 {
    85  				return p, nil
    86  			}
    87  			return part{}, io.EOF
    88  		}
    89  	case ltgt:
    90  		switch p2.operator {
    91  		case ltgt:
    92  			if p2.value < p.value {
    93  				p.value = p2.value
    94  			}
    95  			if p2.value2 > p.value2 {
    96  				p.value2 = p2.value2
    97  			}
    98  		case lt:
    99  			if p2.value < p.value {
   100  				p.value = p2.value
   101  			}
   102  		case gt:
   103  			if p2.value > p.value2 {
   104  				p.value2 = p2.value
   105  			}
   106  		}
   107  	case lt:
   108  		switch p2.operator {
   109  		case lt:
   110  			if p2.value < p.value {
   111  				return p2, nil
   112  			}
   113  			return p, nil
   114  		case gt:
   115  			p = part{p.key, ltgt, p.value, p2.value}
   116  		}
   117  	case gt:
   118  		// p2.operator == gt
   119  		if p2.value > p.value {
   120  			return p2, nil
   121  		}
   122  		return p, nil
   123  	}
   124  	// p.operator == ltgt
   125  	if p.value <= p.value2 || p.value == "" {
   126  		return part{}, io.EOF
   127  	}
   128  	if p.value2 == "" {
   129  		return part{p.key, lt, p.value, ""}, nil
   130  	}
   131  	return p, nil
   132  }
   133  
   134  // sql returns a SQL expression and a list of arguments for finding records matching p.
   135  func (p part) sql() (sql string, args []interface{}, err error) {
   136  	if p.key == "upload" {
   137  		switch p.operator {
   138  		case equals:
   139  			return "SELECT UploadID, RecordID FROM Records WHERE UploadID = ?", []interface{}{p.value}, nil
   140  		case lt:
   141  			return "SELECT UploadID, RecordID FROM Records WHERE UploadID < ?", []interface{}{p.value}, nil
   142  		case gt:
   143  			return "SELECT UploadID, RecordID FROM Records WHERE UploadID > ?", []interface{}{p.value}, nil
   144  		case ltgt:
   145  			return "SELECT UploadID, RecordID FROM Records WHERE UploadID < ? AND UploadID > ?", []interface{}{p.value, p.value2}, nil
   146  		}
   147  	}
   148  	switch p.operator {
   149  	case equals:
   150  		if p.value == "" {
   151  			// TODO(quentin): Implement support for searching for missing labels.
   152  			return "", nil, fmt.Errorf("missing value for key %q", p.key)
   153  		}
   154  		return "SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ? AND Value = ?", []interface{}{p.key, p.value}, nil
   155  	case lt:
   156  		return "SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ? AND Value < ?", []interface{}{p.key, p.value}, nil
   157  	case gt:
   158  		if p.value == "" {
   159  			// Simplify queries for any value.
   160  			return "SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ?", []interface{}{p.key}, nil
   161  		}
   162  		return "SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ? AND Value > ?", []interface{}{p.key, p.value}, nil
   163  	case ltgt:
   164  		return "SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ? AND Value < ? AND Value > ?", []interface{}{p.key, p.value, p.value2}, nil
   165  	default:
   166  		panic("unknown operator " + string(p.operator))
   167  	}
   168  }