diff --git a/cmd/gitql/query.go b/cmd/gitql/query.go index 19914a1e2..85248bc1d 100644 --- a/cmd/gitql/query.go +++ b/cmd/gitql/query.go @@ -95,6 +95,7 @@ func (c *CmdQuery) printQuery(schema sql.Schema, iter sql.RowIter) { w := tablewriter.NewWriter(os.Stdout) headers := []string{} for _, f := range schema { + fmt.Printf("HEADER: %s\n", f.Name) headers = append(headers, f.Name) } w.SetHeader(headers) diff --git a/sql/analyzer/analyzer_test.go b/sql/analyzer/analyzer_test.go index 909bcc3c7..5950b9e9c 100644 --- a/sql/analyzer/analyzer_test.go +++ b/sql/analyzer/analyzer_test.go @@ -50,6 +50,28 @@ func TestAnalyzer_Analyze(t *testing.T) { assert.Nil(err) assert.Equal(expected, analyzed) + notAnalyzed = plan.NewProject( + []sql.Expression{ + expression.NewAlias( + expression.NewUnresolvedColumn("i"), + "foo", + ), + }, + plan.NewUnresolvedTable("mytable"), + ) + analyzed, err = a.Analyze(notAnalyzed) + expected = plan.NewProject( + []sql.Expression{ + expression.NewAlias( + expression.NewGetField(0, sql.Integer, "i"), + "foo", + ), + }, + table, + ) + assert.Nil(err) + assert.Equal(expected, analyzed) + notAnalyzed = plan.NewProject( []sql.Expression{expression.NewUnresolvedColumn("i")}, plan.NewFilter( diff --git a/sql/parse/parse.go b/sql/parse/parse.go index e2a9e88e5..be41e136c 100644 --- a/sql/parse/parse.go +++ b/sql/parse/parse.go @@ -175,6 +175,25 @@ func selectToProject(se sqlparser.SelectExprs, child sql.Node) (*plan.Project, e return plan.NewProject(exprs, child), nil } +func exprToExpression(e sqlparser.Expr) (sql.Expression, error) { + be, ok := e.(sqlparser.BoolExpr) + if ok { + return boolExprToExpression(be) + } + + c, ok := e.(*sqlparser.ComparisonExpr) + if ok { + return comparisonExprToExpression(c) + } + + v, ok := e.(sqlparser.ValExpr) + if ok { + return valExprToExpression(v) + } + + return nil, errUnsupported(e) +} + func boolExprToExpression(be sqlparser.BoolExpr) (sql.Expression, error) { switch b := be.(type) { default: @@ -239,6 +258,7 @@ func valExprToExpression(ve sqlparser.ValExpr) (sql.Expression, error) { //TODO return expression.NewLiteral(nil, sql.Null), nil case *sqlparser.ColName: + //TODO: add handling of case sensitiveness. return expression.NewUnresolvedColumn(v.Name.Lowered()), nil } } @@ -251,14 +271,16 @@ func selectExprToExpression(se sqlparser.SelectExpr) (sql.Expression, error) { //TODO: Add support for qualified start. return expression.NewStar(), nil case *sqlparser.NonStarExpr: - //TODO: Add support for aliases and functions. - cn, ok := e.Expr.(*sqlparser.ColName) - if !ok { - return nil, errUnsupportedFeature("column aliases or functions") + expr, err := exprToExpression(e.Expr) + if err != nil { + return nil, err + } + + if e.As.String() == "" { + return expr, nil } - //TODO: Add support for column qualifiers. //TODO: Handle case-sensitiveness when needed. - return expression.NewUnresolvedColumn(cn.Name.Lowered()), nil + return expression.NewAlias(expr, e.As.Lowered()), nil } } diff --git a/sql/parse/parse_test.go b/sql/parse/parse_test.go index e4dcdc182..f87edd86c 100644 --- a/sql/parse/parse_test.go +++ b/sql/parse/parse_test.go @@ -18,6 +18,15 @@ var fixtures = map[string]sql.Node{ }, plan.NewUnresolvedTable("foo"), ), + `SELECT foo AS bar FROM foo;`: plan.NewProject( + []sql.Expression{ + expression.NewAlias( + expression.NewUnresolvedColumn("foo"), + "bar", + ), + }, + plan.NewUnresolvedTable("foo"), + ), `SELECT foo, bar FROM foo WHERE foo = bar;`: plan.NewProject( []sql.Expression{ expression.NewUnresolvedColumn("foo"), diff --git a/sql/plan/project.go b/sql/plan/project.go index 5849194b7..0b3d99382 100644 --- a/sql/plan/project.go +++ b/sql/plan/project.go @@ -7,29 +7,25 @@ import ( type Project struct { UnaryNode expressions []sql.Expression - schema sql.Schema } func NewProject(expressions []sql.Expression, child sql.Node) *Project { - schema := sql.Schema{} - childSchema := child.Schema() - for _, expr := range expressions { - for _, field := range childSchema { - if expr.Name() == field.Name { - schema = append(schema, field) - break - } - } - } return &Project{ UnaryNode: UnaryNode{child}, expressions: expressions, - schema: schema, } } func (p *Project) Schema() sql.Schema { - return p.schema + var s sql.Schema + for _, e := range p.expressions { + f := sql.Field{ + Name: e.Name(), + Type: e.Type(), + } + s = append(s, f) + } + return s } func (p *Project) Resolved() bool { diff --git a/sql/plan/project_test.go b/sql/plan/project_test.go index 12bd41128..6b76d3fee 100644 --- a/sql/plan/project_test.go +++ b/sql/plan/project_test.go @@ -46,5 +46,16 @@ func TestProject(t *testing.T) { require.Nil(row) p = NewProject(nil, child) - require.Equal(0, len(p.schema)) + require.Equal(0, len(p.Schema())) + + p = NewProject([]sql.Expression{ + expression.NewAlias( + expression.NewGetField(1, sql.String, "col2"), + "foo", + ), + }, child) + schema = sql.Schema{ + sql.Field{"foo", sql.String}, + } + require.Equal(schema, p.Schema()) }