Skip to content

Commit 52e5593

Browse files
authored
sql/parse: add support for expression aliases. (src-d#61)
* Add alias parsing support. * Fix plan.Project so that it calculates schema properly when child schema differs in names.
1 parent e7e6183 commit 52e5593

File tree

5 files changed

+80
-20
lines changed

5 files changed

+80
-20
lines changed

sql/analyzer/analyzer_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,28 @@ func TestAnalyzer_Analyze(t *testing.T) {
5050
assert.Nil(err)
5151
assert.Equal(expected, analyzed)
5252

53+
notAnalyzed = plan.NewProject(
54+
[]sql.Expression{
55+
expression.NewAlias(
56+
expression.NewUnresolvedColumn("i"),
57+
"foo",
58+
),
59+
},
60+
plan.NewUnresolvedTable("mytable"),
61+
)
62+
analyzed, err = a.Analyze(notAnalyzed)
63+
expected = plan.NewProject(
64+
[]sql.Expression{
65+
expression.NewAlias(
66+
expression.NewGetField(0, sql.Integer, "i"),
67+
"foo",
68+
),
69+
},
70+
table,
71+
)
72+
assert.Nil(err)
73+
assert.Equal(expected, analyzed)
74+
5375
notAnalyzed = plan.NewProject(
5476
[]sql.Expression{expression.NewUnresolvedColumn("i")},
5577
plan.NewFilter(

sql/parse/parse.go

+28-6
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,25 @@ func selectToProject(se sqlparser.SelectExprs, child sql.Node) (*plan.Project, e
175175
return plan.NewProject(exprs, child), nil
176176
}
177177

178+
func exprToExpression(e sqlparser.Expr) (sql.Expression, error) {
179+
be, ok := e.(sqlparser.BoolExpr)
180+
if ok {
181+
return boolExprToExpression(be)
182+
}
183+
184+
c, ok := e.(*sqlparser.ComparisonExpr)
185+
if ok {
186+
return comparisonExprToExpression(c)
187+
}
188+
189+
v, ok := e.(sqlparser.ValExpr)
190+
if ok {
191+
return valExprToExpression(v)
192+
}
193+
194+
return nil, errUnsupported(e)
195+
}
196+
178197
func boolExprToExpression(be sqlparser.BoolExpr) (sql.Expression, error) {
179198
switch b := be.(type) {
180199
default:
@@ -239,6 +258,7 @@ func valExprToExpression(ve sqlparser.ValExpr) (sql.Expression, error) {
239258
//TODO
240259
return expression.NewLiteral(nil, sql.Null), nil
241260
case *sqlparser.ColName:
261+
//TODO: add handling of case sensitiveness.
242262
return expression.NewUnresolvedColumn(v.Name.Lowered()), nil
243263
}
244264
}
@@ -251,14 +271,16 @@ func selectExprToExpression(se sqlparser.SelectExpr) (sql.Expression, error) {
251271
//TODO: Add support for qualified start.
252272
return expression.NewStar(), nil
253273
case *sqlparser.NonStarExpr:
254-
//TODO: Add support for aliases and functions.
255-
cn, ok := e.Expr.(*sqlparser.ColName)
256-
if !ok {
257-
return nil, errUnsupportedFeature("column aliases or functions")
274+
expr, err := exprToExpression(e.Expr)
275+
if err != nil {
276+
return nil, err
277+
}
278+
279+
if e.As.String() == "" {
280+
return expr, nil
258281
}
259282

260-
//TODO: Add support for column qualifiers.
261283
//TODO: Handle case-sensitiveness when needed.
262-
return expression.NewUnresolvedColumn(cn.Name.Lowered()), nil
284+
return expression.NewAlias(expr, e.As.Lowered()), nil
263285
}
264286
}

sql/parse/parse_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ var fixtures = map[string]sql.Node{
1818
},
1919
plan.NewUnresolvedTable("foo"),
2020
),
21+
`SELECT foo AS bar FROM foo;`: plan.NewProject(
22+
[]sql.Expression{
23+
expression.NewAlias(
24+
expression.NewUnresolvedColumn("foo"),
25+
"bar",
26+
),
27+
},
28+
plan.NewUnresolvedTable("foo"),
29+
),
2130
`SELECT foo, bar FROM foo WHERE foo = bar;`: plan.NewProject(
2231
[]sql.Expression{
2332
expression.NewUnresolvedColumn("foo"),

sql/plan/project.go

+9-13
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,25 @@ import (
77
type Project struct {
88
UnaryNode
99
expressions []sql.Expression
10-
schema sql.Schema
1110
}
1211

1312
func NewProject(expressions []sql.Expression, child sql.Node) *Project {
14-
schema := sql.Schema{}
15-
childSchema := child.Schema()
16-
for _, expr := range expressions {
17-
for _, field := range childSchema {
18-
if expr.Name() == field.Name {
19-
schema = append(schema, field)
20-
break
21-
}
22-
}
23-
}
2413
return &Project{
2514
UnaryNode: UnaryNode{child},
2615
expressions: expressions,
27-
schema: schema,
2816
}
2917
}
3018

3119
func (p *Project) Schema() sql.Schema {
32-
return p.schema
20+
var s sql.Schema
21+
for _, e := range p.expressions {
22+
f := sql.Field{
23+
Name: e.Name(),
24+
Type: e.Type(),
25+
}
26+
s = append(s, f)
27+
}
28+
return s
3329
}
3430

3531
func (p *Project) Resolved() bool {

sql/plan/project_test.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,16 @@ func TestProject(t *testing.T) {
4646
require.Nil(row)
4747

4848
p = NewProject(nil, child)
49-
require.Equal(0, len(p.schema))
49+
require.Equal(0, len(p.Schema()))
50+
51+
p = NewProject([]sql.Expression{
52+
expression.NewAlias(
53+
expression.NewGetField(1, sql.String, "col2"),
54+
"foo",
55+
),
56+
}, child)
57+
schema = sql.Schema{
58+
sql.Field{"foo", sql.String},
59+
}
60+
require.Equal(schema, p.Schema())
5061
}

0 commit comments

Comments
 (0)