Skip to content

Commit 575c790

Browse files
committed
Add commit_trees table
Signed-off-by: Javi Fontan <[email protected]>
1 parent 951c470 commit 575c790

File tree

4 files changed

+330
-0
lines changed

4 files changed

+330
-0
lines changed

commit_trees.go

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package gitbase
2+
3+
import (
4+
"io"
5+
6+
"gopkg.in/src-d/go-git.v4/plumbing"
7+
8+
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
9+
"gopkg.in/src-d/go-git.v4/plumbing/object"
10+
"gopkg.in/src-d/go-mysql-server.v0/sql"
11+
)
12+
13+
type commitTreesTable struct{}
14+
15+
// CommitTreesSchema is the schema for the commit trees table.
16+
var CommitTreesSchema = sql.Schema{
17+
{Name: "repository_id", Type: sql.Text, Source: CommitTreesTableName},
18+
{Name: "commit_hash", Type: sql.Text, Source: CommitTreesTableName},
19+
{Name: "tree_hash", Type: sql.Text, Source: CommitTreesTableName},
20+
}
21+
22+
var _ sql.PushdownProjectionAndFiltersTable = (*commitTreesTable)(nil)
23+
24+
func newCommitTreesTable() sql.Table {
25+
return new(commitTreesTable)
26+
}
27+
28+
func (commitTreesTable) isGitbaseTable() {}
29+
30+
func (commitTreesTable) String() string {
31+
return printTable(CommitTreesTableName, CommitTreesSchema)
32+
}
33+
34+
func (commitTreesTable) Resolved() bool { return true }
35+
36+
func (commitTreesTable) Name() string { return CommitTreesTableName }
37+
38+
func (commitTreesTable) Schema() sql.Schema { return CommitTreesSchema }
39+
40+
func (t *commitTreesTable) TransformUp(f sql.TransformNodeFunc) (sql.Node, error) {
41+
return f(t)
42+
}
43+
44+
func (t *commitTreesTable) TransformExpressionsUp(f sql.TransformExprFunc) (sql.Node, error) {
45+
return t, nil
46+
}
47+
48+
func (commitTreesTable) Children() []sql.Node { return nil }
49+
50+
func (commitTreesTable) RowIter(ctx *sql.Context) (sql.RowIter, error) {
51+
span, ctx := ctx.Span("gitbase.CommitTreesTable")
52+
iter, err := NewRowRepoIter(ctx, &commitTreesIter{ctx: ctx})
53+
if err != nil {
54+
span.Finish()
55+
return nil, err
56+
}
57+
58+
return sql.NewSpanIter(span, iter), nil
59+
}
60+
61+
func (commitTreesTable) HandledFilters(filters []sql.Expression) []sql.Expression {
62+
return handledFilters(CommitTreesTableName, CommitTreesSchema, filters)
63+
}
64+
65+
func (commitTreesTable) WithProjectAndFilters(
66+
ctx *sql.Context,
67+
_, filters []sql.Expression,
68+
) (sql.RowIter, error) {
69+
span, ctx := ctx.Span("gitbase.CommitTreesTable")
70+
iter, err := rowIterWithSelectors(
71+
ctx, CommitTreesSchema, CommitTreesTableName, filters,
72+
[]string{"commit_hash", "repository_id"},
73+
func(selectors selectors) (RowRepoIter, error) {
74+
repos, err := selectors.textValues("repository_id")
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
hashes, err := selectors.textValues("commit_hash")
80+
if err != nil {
81+
return nil, err
82+
}
83+
84+
return &commitTreesIter{
85+
ctx: ctx,
86+
commitHashes: hashes,
87+
repos: repos,
88+
}, nil
89+
},
90+
)
91+
92+
if err != nil {
93+
span.Finish()
94+
return nil, err
95+
}
96+
97+
return sql.NewSpanIter(span, iter), nil
98+
}
99+
100+
type commitTreesIter struct {
101+
ctx *sql.Context
102+
repo *Repository
103+
104+
commits object.CommitIter
105+
commit *object.Commit
106+
trees *object.TreeWalker
107+
seen map[plumbing.Hash]bool
108+
109+
// selectors for faster filtering
110+
repos []string
111+
commitHashes []string
112+
}
113+
114+
func (i *commitTreesIter) NewIterator(repo *Repository) (RowRepoIter, error) {
115+
var commits object.CommitIter
116+
if len(i.repos) == 0 || stringContains(i.repos, repo.ID) {
117+
var err error
118+
commits, err = repo.Repo.CommitObjects()
119+
if err != nil {
120+
return nil, err
121+
}
122+
}
123+
124+
return &commitTreesIter{
125+
ctx: i.ctx,
126+
repo: repo,
127+
commits: commits,
128+
repos: i.repos,
129+
commitHashes: i.commitHashes,
130+
}, nil
131+
}
132+
133+
func (i *commitTreesIter) shouldVisitCommit(commit *object.Commit) bool {
134+
if len(i.commitHashes) > 0 && !stringContains(i.commitHashes, commit.Hash.String()) {
135+
return false
136+
}
137+
138+
return true
139+
}
140+
141+
func (i *commitTreesIter) Next() (sql.Row, error) {
142+
s, ok := i.ctx.Session.(*Session)
143+
if !ok {
144+
return nil, ErrInvalidGitbaseSession.New(i.ctx.Session)
145+
}
146+
147+
for {
148+
if i.commits == nil {
149+
return nil, io.EOF
150+
}
151+
152+
if i.trees == nil {
153+
commit, err := i.commits.Next()
154+
if err != nil {
155+
if err == io.EOF {
156+
i.commits.Close()
157+
return nil, io.EOF
158+
}
159+
160+
if s.SkipGitErrors {
161+
continue
162+
}
163+
164+
return nil, err
165+
}
166+
167+
if !i.shouldVisitCommit(commit) {
168+
continue
169+
}
170+
171+
tree, err := commit.Tree()
172+
if err != nil {
173+
if s.SkipGitErrors {
174+
continue
175+
}
176+
177+
return nil, err
178+
}
179+
180+
i.seen = make(map[plumbing.Hash]bool)
181+
i.trees = object.NewTreeWalker(tree, true, i.seen)
182+
i.commit = commit
183+
}
184+
185+
_, entry, err := i.trees.Next()
186+
if err != nil {
187+
i.trees.Close()
188+
i.trees = nil
189+
190+
if err == io.EOF || s.SkipGitErrors {
191+
continue
192+
}
193+
194+
return nil, err
195+
}
196+
197+
if entry.Mode != filemode.Dir {
198+
continue
199+
}
200+
201+
return sql.NewRow(
202+
i.repo.ID,
203+
i.commit.Hash.String(),
204+
entry.Hash.String(),
205+
), nil
206+
}
207+
return nil, nil
208+
}
209+
210+
func (i *commitTreesIter) Close() error {
211+
if i.commits != nil {
212+
i.commits.Close()
213+
}
214+
215+
if i.trees != nil {
216+
i.trees.Close()
217+
}
218+
219+
return nil
220+
}

commit_trees_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package gitbase
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
"gopkg.in/src-d/go-mysql-server.v0/sql"
8+
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
9+
)
10+
11+
func TestCommitTreesRowIter(t *testing.T) {
12+
require := require.New(t)
13+
ctx, _, cleanup := setup(t)
14+
defer cleanup()
15+
16+
iter, err := new(commitTreesTable).RowIter(ctx)
17+
require.NoError(err)
18+
19+
rows, err := sql.RowIterToRows(iter)
20+
require.NoError(err)
21+
22+
for i, row := range rows {
23+
// remove repository ids
24+
rows[i] = row[1:]
25+
}
26+
27+
expected := []sql.Row{
28+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "a39771a7651f97faf5c72e08224d857fc35133db"},
29+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
30+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
31+
32+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "a39771a7651f97faf5c72e08224d857fc35133db"},
33+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
34+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
35+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b"},
36+
37+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "a39771a7651f97faf5c72e08224d857fc35133db"},
38+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
39+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
40+
41+
{"af2d6a6954d532f8ffb47615169c8fdf9d383a1a", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
42+
}
43+
44+
require.Equal(expected, rows)
45+
}
46+
47+
func TestCommitTreesPushdown(t *testing.T) {
48+
ctx, _, cleanup := setup(t)
49+
defer cleanup()
50+
51+
table := new(commitTreesTable)
52+
testCases := []struct {
53+
name string
54+
filters []sql.Expression
55+
expected []sql.Row
56+
}{
57+
{
58+
"commit filter",
59+
[]sql.Expression{
60+
expression.NewEquals(
61+
expression.NewGetFieldWithTable(1, sql.Text, CommitTreesTableName, "commit_hash", false),
62+
expression.NewLiteral("918c48b83bd081e863dbe1b80f8998f058cd8294", sql.Text),
63+
),
64+
},
65+
[]sql.Row{
66+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "a39771a7651f97faf5c72e08224d857fc35133db"},
67+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
68+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
69+
},
70+
},
71+
{
72+
"tree filter",
73+
[]sql.Expression{
74+
expression.NewEquals(
75+
expression.NewGetFieldWithTable(2, sql.Text, CommitTreesTableName, "tree_hash", false),
76+
expression.NewLiteral("586af567d0bb5e771e49bdd9434f5e0fb76d25fa", sql.Text),
77+
),
78+
},
79+
[]sql.Row{
80+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
81+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
82+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
83+
},
84+
},
85+
}
86+
87+
for _, tt := range testCases {
88+
t.Run(tt.name, func(t *testing.T) {
89+
require := require.New(t)
90+
iter, err := table.WithProjectAndFilters(ctx, nil, tt.filters)
91+
require.NoError(err)
92+
93+
rows, err := sql.RowIterToRows(iter)
94+
require.NoError(err)
95+
96+
for i, row := range rows {
97+
// remove repository_ids
98+
rows[i] = row[1:]
99+
}
100+
101+
require.Equal(tt.expected, rows)
102+
})
103+
}
104+
}

database.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const (
1919
RemotesTableName = "remotes"
2020
// RefCommitsTableName is the name of the ref commits table.
2121
RefCommitsTableName = "ref_commits"
22+
// CommitTreesTableName is the name of the commit trees table.
23+
CommitTreesTableName = "commit_trees"
2224
)
2325

2426
// Database holds all git repository tables
@@ -31,6 +33,7 @@ type Database struct {
3133
repositories sql.Table
3234
remotes sql.Table
3335
refCommits sql.Table
36+
commitTrees sql.Table
3437
}
3538

3639
// NewDatabase creates a new Database structure and initializes its
@@ -45,6 +48,7 @@ func NewDatabase(name string) sql.Database {
4548
repositories: newRepositoriesTable(),
4649
remotes: newRemotesTable(),
4750
refCommits: newRefCommitsTable(),
51+
commitTrees: newCommitTreesTable(),
4852
}
4953
}
5054

@@ -63,5 +67,6 @@ func (d *Database) Tables() map[string]sql.Table {
6367
RepositoriesTableName: d.repositories,
6468
RemotesTableName: d.remotes,
6569
RefCommitsTableName: d.refCommits,
70+
CommitTreesTableName: d.commitTrees,
6671
}
6772
}

database_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func TestDatabase_Tables(t *testing.T) {
3232
sort.Strings(tableNames)
3333
expected := []string{
3434
CommitsTableName,
35+
CommitTreesTableName,
3536
RefCommitsTableName,
3637
ReferencesTableName,
3738
TreeEntriesTableName,

0 commit comments

Comments
 (0)