Skip to content

Commit 1d8c7ed

Browse files
authored
Add engine and integrate CLI (#31)
* Add Engine. * This is the main entry point. * integrate command * remove debug left overs
1 parent 2a519f0 commit 1d8c7ed

File tree

13 files changed

+173
-32
lines changed

13 files changed

+173
-32
lines changed

cmd/gitql/query.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import (
66
"os"
77
"path/filepath"
88

9-
gitql "github.com/mvader/gitql/git"
9+
gitqlgit "github.com/mvader/gitql/git"
1010
"github.com/mvader/gitql/sql"
1111

1212
"gopkg.in/src-d/go-git.v4"
13+
"github.com/mvader/gitql"
14+
"io"
15+
"github.com/olekukonko/tablewriter"
1316
)
1417

1518
type CmdQuery struct {
@@ -75,17 +78,51 @@ func (c *CmdQuery) buildDatabase() error {
7578
c.print("current HEAD %q\n", head.Hash())
7679

7780
name := filepath.Base(filepath.Join(c.Path, ".."))
78-
c.db = gitql.NewDatabase(name, c.r)
81+
c.db = gitqlgit.NewDatabase(name, c.r)
7982
return nil
8083
}
8184

8285
func (c *CmdQuery) executeQuery() error {
8386
c.print("executing %q at %q\n", c.Args.SQL, c.db.Name())
8487

8588
fmt.Println(c.Args.SQL)
89+
e := gitql.New()
90+
e.AddDatabase(c.db)
91+
schema, iter, err := e.Query(c.Args.SQL)
92+
if err != nil {
93+
return err
94+
}
95+
96+
c.printQuery(schema, iter)
97+
8698
return nil
8799
}
88100

101+
func (c *CmdQuery) printQuery(schema sql.Schema, iter sql.RowIter) {
102+
w := tablewriter.NewWriter(os.Stdout)
103+
headers := []string{}
104+
for _, f := range schema {
105+
headers = append(headers, f.Name)
106+
}
107+
w.SetHeader(headers)
108+
for {
109+
row, err := iter.Next()
110+
if err == io.EOF {
111+
break
112+
}
113+
if err != nil {
114+
fmt.Printf("Error: %v\n", err)
115+
return
116+
}
117+
rowStrings := []string{}
118+
for _, v := range row.Fields() {
119+
rowStrings = append(rowStrings, fmt.Sprintf("%v", v))
120+
}
121+
w.Append(rowStrings)
122+
}
123+
w.Render()
124+
}
125+
89126
func findDotGitFolder(path string) (string, error) {
90127
if path == "" {
91128
var err error

engine.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package gitql
2+
3+
import (
4+
"strings"
5+
6+
"github.com/mvader/gitql/sql/analyzer"
7+
"github.com/mvader/gitql/sql/parse"
8+
"github.com/mvader/gitql/sql"
9+
)
10+
11+
type Engine struct {
12+
Catalog *sql.Catalog
13+
Analyzer *analyzer.Analyzer
14+
}
15+
16+
func New() *Engine {
17+
c := &sql.Catalog{}
18+
a := analyzer.New(c)
19+
return &Engine{c, a}
20+
}
21+
22+
func (e *Engine) AddDatabase(db sql.Database) {
23+
e.Catalog.Databases = append(e.Catalog.Databases, db)
24+
e.Analyzer.CurrentDatabase = db.Name()
25+
}
26+
27+
func (e *Engine) Query(query string) (sql.Schema, sql.RowIter, error) {
28+
parsed, err := parse.Parse(strings.NewReader(query))
29+
if err != nil {
30+
return nil, nil, err
31+
}
32+
33+
analyzed, err := e.Analyzer.Analyze(parsed)
34+
if err != nil {
35+
return nil, nil, err
36+
}
37+
38+
iter, err := analyzed.RowIter()
39+
if err != nil {
40+
return nil, nil, err
41+
}
42+
43+
return analyzed.Schema(), iter, nil
44+
}

engine_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package gitql_test
2+
3+
import (
4+
"testing"
5+
"io"
6+
7+
"github.com/stretchr/testify/require"
8+
"github.com/mvader/gitql/sql"
9+
"github.com/mvader/gitql/mem"
10+
"github.com/mvader/gitql"
11+
)
12+
13+
func TestEngine_Query(t *testing.T) {
14+
assert := require.New(t)
15+
16+
table := mem.NewTable("mytable", sql.Schema{{"i", sql.Integer}})
17+
assert.Nil(table.Insert(int32(1)))
18+
assert.Nil(table.Insert(int32(2)))
19+
assert.Nil(table.Insert(int32(3)))
20+
21+
db := mem.NewDatabase("mydb")
22+
db.AddTable("mytable", table)
23+
24+
e := gitql.New()
25+
e.AddDatabase(db)
26+
27+
q := "SELECT i FROM mytable;"
28+
schema, iter, err := e.Query(q)
29+
assert.Nil(err)
30+
assert.NotNil(iter)
31+
assert.NotNil(schema)
32+
33+
results := []sql.Row{}
34+
for {
35+
el, err := iter.Next()
36+
if err == io.EOF {
37+
break
38+
}
39+
if err != nil {
40+
assert.Fail("returned err distinct of io.EOF: %q", err)
41+
}
42+
results = append(results, el)
43+
}
44+
assert.Len(results, 3)
45+
assert.Equal(sql.NewMemoryRow(int32(1)), results[0])
46+
assert.Equal(sql.NewMemoryRow(int32(2)), results[1])
47+
assert.Equal(sql.NewMemoryRow(int32(3)), results[2])
48+
}
49+

mem/database.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ type Database struct {
77
tables map[string]sql.PhysicalRelation
88
}
99

10-
func NewDatabase(name string) Database {
11-
return Database{
12-
name: name,
10+
func NewDatabase(name string) *Database {
11+
return &Database{
12+
name: name,
1313
tables: map[string]sql.PhysicalRelation{},
1414
}
1515
}

sql/analyzer/analyzer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const maxAnalysisIterations = 1000
1212

1313
type Analyzer struct {
1414
Rules []Rule
15-
Catalog sql.Catalog
15+
Catalog *sql.Catalog
1616
CurrentDatabase string
1717
}
1818

@@ -21,7 +21,7 @@ type Rule struct {
2121
Apply func(*Analyzer, sql.Node) sql.Node
2222
}
2323

24-
func New(catalog sql.Catalog) *Analyzer {
24+
func New(catalog *sql.Catalog) *Analyzer {
2525
return &Analyzer{
2626
Rules: DefaultRules,
2727
Catalog: catalog,

sql/analyzer/analyzer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
1919
db := mem.NewDatabase("mydb")
2020
db.AddTable("mytable", table)
2121

22-
catalog := sql.Catalog{db}
22+
catalog := &sql.Catalog{Databases:[]sql.Database{db}}
2323
a := analyzer.New(catalog)
2424
a.CurrentDatabase = "mydb"
2525

@@ -53,7 +53,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
5353
func TestAnalyzer_Analyze_MaxIterations(t *testing.T) {
5454
assert := require.New(t)
5555

56-
catalog := sql.Catalog{}
56+
catalog := &sql.Catalog{}
5757
a := analyzer.New(catalog)
5858
a.CurrentDatabase = "mydb"
5959

sql/analyzer/rules.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package analyzer
22

33
import (
4-
"fmt"
54
"github.com/mvader/gitql/sql"
65
"github.com/mvader/gitql/sql/expression"
76
"github.com/mvader/gitql/sql/plan"
@@ -20,6 +19,7 @@ func resolveTables(a *Analyzer, n sql.Node) sql.Node {
2019
}
2120

2221
rt, err := a.Catalog.Table(a.CurrentDatabase, t.Name)
22+
2323
if err != nil {
2424
return n
2525
}

sql/analyzer/rules_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func Test_resolveTables(t *testing.T) {
2020
db := mem.NewDatabase("mydb")
2121
db.AddTable("mytable", table)
2222

23-
catalog := sql.Catalog{db}
23+
catalog := &sql.Catalog{Databases:[]sql.Database{db}}
2424

2525
a := analyzer.New(catalog)
2626
a.Rules = []analyzer.Rule{f}
@@ -48,7 +48,7 @@ func Test_resolveTables_Nested(t *testing.T) {
4848
db := mem.NewDatabase("mydb")
4949
db.AddTable("mytable", table)
5050

51-
catalog := sql.Catalog{db}
51+
catalog := &sql.Catalog{Databases:[]sql.Database{db}}
5252

5353
a := analyzer.New(catalog)
5454
a.Rules = []analyzer.Rule{f}

sql/catalog.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"fmt"
55
)
66

7-
type Catalog []Database
7+
type Catalog struct {
8+
Databases []Database
9+
}
810

911
func (c Catalog) Database(name string) (Database, error) {
10-
for _, db := range []Database(c) {
12+
for _, db := range c.Databases {
1113
if db.Name() == name {
1214
return db, nil
1315
}

sql/parse/parse.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -232,18 +232,12 @@ func (p *parser) parse() error {
232232
return nil
233233
}
234234

235-
func (p *parser) buildPlan(relations []sql.PhysicalRelation) (sql.Node, error) {
236-
var node sql.Node
237-
for _, r := range relations {
238-
if r.Name() == p.relation {
239-
node = r
240-
break
241-
}
235+
func (p *parser) buildPlan() (sql.Node, error) {
236+
if p.relation == "" {
237+
return nil, errors.New("missing relation name")
242238
}
243239

244-
if node == nil {
245-
return nil, fmt.Errorf("unknown table name %q", p.relation)
246-
}
240+
var node sql.Node = plan.NewUnresolvedRelation(p.relation)
247241

248242
if len(p.filterClauses) > 0 {
249243
node = plan.NewFilter(p.filterClauses[0], node)
@@ -257,13 +251,13 @@ func (p *parser) buildPlan(relations []sql.PhysicalRelation) (sql.Node, error) {
257251
return node, nil
258252
}
259253

260-
func Parse(input io.Reader, relations []sql.PhysicalRelation) (sql.Node, error) {
254+
func Parse(input io.Reader) (sql.Node, error) {
261255
p := newParser(input)
262256
if err := p.parse(); err != nil {
263257
return nil, err
264258
}
265259

266-
return p.buildPlan(relations)
260+
return p.buildPlan()
267261
}
268262

269263
func LastStates(input io.Reader) (ParseState, ParseState, error) {

0 commit comments

Comments
 (0)