Skip to content

Commit b8f36a5

Browse files
committed
Add name alias support for select statement and order by can use name alias field
1 parent 9361ada commit b8f36a5

File tree

7 files changed

+101
-35
lines changed

7 files changed

+101
-35
lines changed

kvcmds/cmd_query.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (c QueryCmd) Handler() func(ctx context.Context) {
5959
return err
6060
}
6161
ret := [][]string{
62-
plan.FieldNames(),
62+
plan.FieldNameList(),
6363
}
6464
for {
6565
cols, err := plan.Next()

query/ast.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,12 @@ type Expression interface {
136136
}
137137

138138
type SelectStmt struct {
139-
AllFields bool
140-
Fields []Expression
141-
Where *WhereStmt
142-
Order *OrderStmt
143-
Limit *LimitStmt
139+
AllFields bool
140+
FieldNames []string
141+
Fields []Expression
142+
Where *WhereStmt
143+
Order *OrderStmt
144+
Limit *LimitStmt
144145
}
145146

146147
type WhereStmt struct {

query/lexer.go

+21
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const (
2828
DESC TokenType = 17
2929
TRUE TokenType = 18
3030
FALSE TokenType = 19
31+
AS TokenType = 20
3132
)
3233

3334
var (
@@ -147,6 +148,23 @@ func (l *Lexer) Split() []*Token {
147148
} else {
148149
curr += string(char)
149150
}
151+
case '`':
152+
if !strStart {
153+
strStart = true
154+
strStartChar = char
155+
tokStartPos = i
156+
} else if strStartChar == char {
157+
strStart = false
158+
token := &Token{
159+
Tp: NAME,
160+
Data: curr,
161+
Pos: tokStartPos,
162+
}
163+
ret = append(ret, token)
164+
curr = ""
165+
} else {
166+
curr += string(char)
167+
}
150168
case '~', '^', '=', '!', '*', '+', '-', '/', '>', '<':
151169
if strStart {
152170
curr += string(char)
@@ -342,6 +360,9 @@ func buildToken(curr string, pos int) *Token {
342360
case "false":
343361
token.Tp = FALSE
344362
return token
363+
case "as":
364+
token.Tp = AS
365+
return token
345366
default:
346367
if isNumber(curr) {
347368
token.Tp = NUMBER

query/optimizer.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ func (o *Optimizer) BuildPlan(t Txn) (*ProjectionPlan, error) {
4242
return nil, err
4343
}
4444
return &ProjectionPlan{
45-
Txn: t,
46-
ChildPlan: fp,
47-
AllFields: o.stmt.AllFields,
48-
Fields: o.stmt.Fields,
45+
Txn: t,
46+
ChildPlan: fp,
47+
AllFields: o.stmt.AllFields,
48+
FieldNames: o.stmt.FieldNames,
49+
Fields: o.stmt.Fields,
4950
}, nil
5051
}
5152

@@ -64,10 +65,11 @@ func (o *Optimizer) BuildPlan(t Txn) (*ProjectionPlan, error) {
6465
}
6566

6667
return &ProjectionPlan{
67-
Txn: t,
68-
ChildPlan: fp,
69-
AllFields: o.stmt.AllFields,
70-
Fields: o.stmt.Fields,
68+
Txn: t,
69+
ChildPlan: fp,
70+
AllFields: o.stmt.AllFields,
71+
FieldNames: o.stmt.FieldNames,
72+
Fields: o.stmt.Fields,
7173
}, nil
7274
}
7375

query/parser.go

+54-10
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import (
66
)
77

88
var (
9-
ErrSyntaxStartWhere = errors.New("Syntax Error: not start with `where`")
10-
ErrSyntaxEmptyFields = errors.New("Syntax Error: empty select fields")
11-
ErrSyntaxInvalidFields = errors.New("Syntax Error: invalid fields")
9+
ErrSyntaxStartWhere = errors.New("Syntax Error: not start with `where`")
10+
ErrSyntaxEmptyFields = errors.New("Syntax Error: empty select fields")
11+
ErrSyntaxInvalidFields = errors.New("Syntax Error: invalid fields")
12+
ErrSyntaxInvalidFieldName = errors.New("Syntax Error: invalid field name")
1213
)
1314

1415
const MaxNestLevel = 1e5
@@ -266,9 +267,10 @@ func (p *Parser) parseOperand() (Expression, error) {
266267

267268
func (p *Parser) parseSelect() (*SelectStmt, error) {
268269
var (
269-
fields = []Expression{}
270-
allFields = false
271-
err error
270+
fields = []Expression{}
271+
fieldNames = []string{}
272+
allFields = false
273+
err error
272274
)
273275
err = p.expect(&Token{Tp: SELECT, Data: "select"})
274276
if err != nil {
@@ -291,7 +293,17 @@ func (p *Parser) parseSelect() (*SelectStmt, error) {
291293
if err != nil {
292294
return nil, err
293295
}
296+
fieldName := field.String()
297+
if p.tok != nil && p.tok.Tp == AS {
298+
p.next()
299+
if p.tok.Tp != NAME {
300+
return nil, ErrSyntaxInvalidFieldName
301+
}
302+
fieldName = p.tok.Data
303+
}
304+
294305
fields = append(fields, field)
306+
fieldNames = append(fieldNames, fieldName)
295307
if p.tok != nil && p.tok.Tp == WHERE {
296308
break
297309
}
@@ -307,8 +319,9 @@ func (p *Parser) parseSelect() (*SelectStmt, error) {
307319
}
308320

309321
return &SelectStmt{
310-
Fields: fields,
311-
AllFields: allFields,
322+
Fields: fields,
323+
FieldNames: fieldNames,
324+
AllFields: allFields,
312325
}, nil
313326
}
314327

@@ -353,7 +366,7 @@ func (p *Parser) parseLimit() (*LimitStmt, error) {
353366
return ret, nil
354367
}
355368

356-
func (p *Parser) parseOrderBy() (*OrderStmt, error) {
369+
func (p *Parser) parseOrderBy(selStmt *SelectStmt) (*OrderStmt, error) {
357370
var (
358371
err error
359372
shouldBreak bool = false
@@ -377,6 +390,37 @@ func (p *Parser) parseOrderBy() (*OrderStmt, error) {
377390
switch field.ReturnType() {
378391
case TSTR, TNUMBER, TBOOL:
379392
break
393+
case TIDENT:
394+
// Check can convert to name expression
395+
nexpr, ok := field.(*NameExpr)
396+
if !ok {
397+
return nil, errors.New("Syntax error: invalid field name")
398+
}
399+
// Check has select statement
400+
if selStmt == nil {
401+
return nil, fmt.Errorf("Syntax error: unknown field %s in select statement", nexpr.Data)
402+
}
403+
fieldName := nexpr.Data
404+
foundIdx := -1
405+
// Found field in select statement
406+
for i, fname := range selStmt.FieldNames {
407+
if fname == fieldName {
408+
foundIdx = i
409+
break
410+
}
411+
}
412+
// Not found return error
413+
if foundIdx < 0 {
414+
return nil, fmt.Errorf("Syntax error: unknown field %s in select statement", nexpr.Data)
415+
}
416+
// Found then check return type is valid
417+
field = selStmt.Fields[foundIdx]
418+
switch field.ReturnType() {
419+
case TSTR, TNUMBER, TBOOL:
420+
break
421+
default:
422+
return nil, errors.New("Syntax error: order by field is wrong type.")
423+
}
380424
default:
381425
return nil, errors.New("Syntax error: order by field is wrong type.")
382426
}
@@ -453,7 +497,7 @@ func (p *Parser) Parse() (*SelectStmt, error) {
453497
if orderStmt != nil {
454498
return nil, errors.New("Syntax error duplicate order by expression")
455499
}
456-
orderStmt, err = p.parseOrderBy()
500+
orderStmt, err = p.parseOrderBy(selectStmt)
457501
if err != nil {
458502
return nil, err
459503
}

query/plan.go

+7-10
Original file line numberDiff line numberDiff line change
@@ -276,21 +276,18 @@ func (p *LimitPlan) Explain() []string {
276276
}
277277

278278
type ProjectionPlan struct {
279-
Txn Txn
280-
ChildPlan Plan
281-
AllFields bool
282-
Fields []Expression
279+
Txn Txn
280+
ChildPlan Plan
281+
AllFields bool
282+
FieldNames []string
283+
Fields []Expression
283284
}
284285

285-
func (p *ProjectionPlan) FieldNames() []string {
286+
func (p *ProjectionPlan) FieldNameList() []string {
286287
if p.AllFields {
287288
return []string{"Key", "Value"}
288289
}
289-
ret := []string{}
290-
for _, field := range p.Fields {
291-
ret = append(ret, field.String())
292-
}
293-
return ret
290+
return p.FieldNames
294291
}
295292

296293
func (p *ProjectionPlan) Next() ([]Column, error) {

utils/utils.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ func PrintTable(data [][]string) {
3333

3434
func PrintTableNoWrap(data [][]string) {
3535
table := tablewriter.NewWriter(os.Stdout)
36-
table.SetHeader(data[0])
3736
table.SetAutoWrapText(false)
37+
table.SetAutoFormatHeaders(false)
38+
table.SetHeader(data[0])
3839
table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
3940
table.SetCenterSeparator("|")
4041
table.AppendBulk(data[1:])

0 commit comments

Comments
 (0)