github.com/blong14/gache@v0.0.0-20240124023949-89416fd8bbfa/sql/sql.go (about)

     1  package sql
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"errors"
     7  	"io"
     8  	"strconv"
     9  	"strings"
    10  
    11  	gdb "github.com/blong14/gache/internal/db"
    12  )
    13  
    14  type parseContext struct {
    15  	scanner    *bufio.Scanner
    16  	query      *gdb.Query
    17  	tkn        string
    18  	evaluators map[string]func(s *bufio.Scanner, q *gdb.Query) error
    19  }
    20  
    21  func newParseContext(scanner *bufio.Scanner, query *gdb.Query) *parseContext {
    22  	return &parseContext{
    23  		scanner: scanner,
    24  		query:   query,
    25  		evaluators: map[string]func(s *bufio.Scanner, q *gdb.Query) error{
    26  			"copy": func(scanner *bufio.Scanner, query *gdb.Query) error {
    27  				query.Header.Inst = gdb.Load
    28  				for scanner.Scan() {
    29  					switch scanner.Text() {
    30  					case "from":
    31  						if scanner.Scan() {
    32  							file := strings.TrimSpace(scanner.Text())
    33  							if strings.HasSuffix(file, ";") {
    34  								query.Header.FileName = []byte(strings.TrimSuffix(file, ";"))
    35  								return nil
    36  							}
    37  							query.Header.FileName = []byte(file)
    38  						}
    39  						return nil
    40  					default:
    41  						table := strings.TrimSpace(scanner.Text())
    42  						query.Header.TableName = []byte(table)
    43  					}
    44  				}
    45  				return nil
    46  			},
    47  			"create": func(scanner *bufio.Scanner, query *gdb.Query) error {
    48  				query.Header.Inst = gdb.AddTable
    49  				return nil
    50  			},
    51  			"from": func(scanner *bufio.Scanner, query *gdb.Query) error {
    52  				if scanner.Scan() {
    53  					table := strings.TrimSpace(scanner.Text())
    54  					query.Header.TableName = []byte(strings.TrimSuffix(table, ";"))
    55  				}
    56  				return nil
    57  			},
    58  			"insert": func(scanner *bufio.Scanner, query *gdb.Query) error {
    59  				query.Header.Inst = gdb.SetValue
    60  				return nil
    61  			},
    62  			"key": func(scanner *bufio.Scanner, query *gdb.Query) error {
    63  				for scanner.Scan() {
    64  					switch scanner.Text() {
    65  					case "=":
    66  						if query.Header.Inst == gdb.GetRange {
    67  							query.Header.Inst = gdb.GetValue
    68  						}
    69  						continue
    70  					case "and":
    71  						if scanner.Scan() {
    72  							query.KeyRange.End = []byte(strings.TrimSuffix(scanner.Text(), ";"))
    73  						}
    74  					case "between":
    75  						if scanner.Scan() {
    76  							query.KeyRange.Start = []byte(scanner.Text())
    77  						}
    78  						continue
    79  					default:
    80  						key := strings.TrimSpace(scanner.Text())
    81  						if strings.HasSuffix(key, ";") {
    82  							query.Key = []byte(strings.TrimSuffix(key, ";"))
    83  							return nil
    84  						}
    85  						query.Key = []byte(strings.TrimSuffix(key, ","))
    86  					}
    87  					break
    88  				}
    89  				return nil
    90  			},
    91  			"select": func(scanner *bufio.Scanner, query *gdb.Query) error {
    92  				if scanner.Scan() {
    93  					query.Header.Inst = gdb.GetRange
    94  					switch scanner.Text() {
    95  					case "count":
    96  						query.Header.Inst = gdb.Count
    97  					default:
    98  					}
    99  					return nil
   100  				}
   101  				return errors.New("missing token")
   102  			},
   103  			"into": func(scanner *bufio.Scanner, query *gdb.Query) error {
   104  				if scanner.Scan() {
   105  					table := strings.TrimSpace(scanner.Text())
   106  					if strings.HasSuffix(table, ";") {
   107  						query.Header.TableName = []byte(strings.TrimSuffix(table, ";"))
   108  						return nil
   109  					}
   110  					query.Header.TableName = []byte(table)
   111  					return nil
   112  				}
   113  				return errors.New("missing table")
   114  			},
   115  			"table": func(scanner *bufio.Scanner, query *gdb.Query) error {
   116  				if scanner.Scan() {
   117  					table := strings.TrimSpace(scanner.Text())
   118  					if strings.HasSuffix(table, ";") {
   119  						query.Header.TableName = []byte(strings.TrimSuffix(table, ";"))
   120  						return nil
   121  					}
   122  				}
   123  				return errors.New("missing table")
   124  			},
   125  			"value": func(scanner *bufio.Scanner, query *gdb.Query) error {
   126  				for scanner.Scan() {
   127  					switch scanner.Text() {
   128  					case "=":
   129  						continue
   130  					default:
   131  						value := strings.TrimSpace(scanner.Text())
   132  						if strings.HasSuffix(value, ";") {
   133  							query.Value = []byte(strings.TrimSuffix(value, ";"))
   134  							return nil
   135  						}
   136  						query.Value = []byte(value)
   137  					}
   138  					break
   139  				}
   140  				return nil
   141  			},
   142  			"limit": func(scanner *bufio.Scanner, query *gdb.Query) error {
   143  				if scanner.Scan() {
   144  					limit := strings.TrimSpace(scanner.Text())
   145  					l, err := strconv.Atoi(strings.TrimSuffix(limit, ";"))
   146  					if err != nil {
   147  						l = 0
   148  					}
   149  					query.KeyRange.Limit = l
   150  				}
   151  				return nil
   152  			},
   153  		},
   154  	}
   155  }
   156  
   157  func (p *parseContext) SetToken(tkn string) {
   158  	p.tkn = tkn
   159  }
   160  
   161  func (p *parseContext) Evaluate() error {
   162  	if eval, ok := p.evaluators[p.tkn]; ok {
   163  		return eval(p.scanner, p.query)
   164  	}
   165  	return nil
   166  }
   167  
   168  func parse(src io.Reader) (*gdb.Query, error) {
   169  	scanner := bufio.NewScanner(src)
   170  	scanner.Split(bufio.ScanWords)
   171  	query := gdb.NewQuery(context.Background(), nil)
   172  	ctx := newParseContext(scanner, query)
   173  	for scanner.Scan() {
   174  		ctx.SetToken(scanner.Text())
   175  		if err := ctx.Evaluate(); err != nil {
   176  			return nil, err
   177  		}
   178  	}
   179  	return query, nil
   180  }