Skip to content

Commit 690d0d1

Browse files
authored
Merge pull request #284 from jfontan/feature/commit_trees
Add commit_trees table
2 parents 951c470 + 8bd1d81 commit 690d0d1

File tree

6 files changed

+421
-37
lines changed

6 files changed

+421
-37
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ gitbase exposes the following tables:
140140
| refs | repository_id, ref_name, commit_hash |
141141
| tree_entries | repository_id, tree_hash, blob_hash, tree_entry_mode, tree_entry_name |
142142
| references | repository_id, ref_name, commit_hash |
143+
| commit_trees | repository_id, commit_hash, tree_hash |
143144

144145
## Functions
145146

commit_trees.go

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
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+
108+
// selectors for faster filtering
109+
repos []string
110+
commitHashes []string
111+
}
112+
113+
func (i *commitTreesIter) NewIterator(repo *Repository) (RowRepoIter, error) {
114+
var commits object.CommitIter
115+
if len(i.repos) == 0 || stringContains(i.repos, repo.ID) {
116+
var err error
117+
commits, err = NewCommitsByHashIter(repo, i.commitHashes)
118+
if err != nil {
119+
return nil, err
120+
}
121+
}
122+
123+
return &commitTreesIter{
124+
ctx: i.ctx,
125+
repo: repo,
126+
commits: commits,
127+
repos: i.repos,
128+
commitHashes: i.commitHashes,
129+
}, nil
130+
}
131+
132+
func (i *commitTreesIter) Next() (sql.Row, error) {
133+
s, ok := i.ctx.Session.(*Session)
134+
if !ok {
135+
return nil, ErrInvalidGitbaseSession.New(i.ctx.Session)
136+
}
137+
138+
for {
139+
if i.commits == nil {
140+
return nil, io.EOF
141+
}
142+
143+
var tree *object.Tree
144+
if i.trees == nil {
145+
commit, err := i.commits.Next()
146+
if err != nil {
147+
if err == io.EOF {
148+
i.commits.Close()
149+
return nil, io.EOF
150+
}
151+
152+
if s.SkipGitErrors {
153+
continue
154+
}
155+
156+
return nil, err
157+
}
158+
159+
tree, err = commit.Tree()
160+
if err != nil {
161+
if s.SkipGitErrors {
162+
continue
163+
}
164+
165+
return nil, err
166+
}
167+
168+
i.trees = object.NewTreeWalker(tree, true, make(map[plumbing.Hash]bool))
169+
i.commit = commit
170+
}
171+
172+
if tree != nil {
173+
return sql.NewRow(
174+
i.repo.ID,
175+
i.commit.Hash.String(),
176+
tree.Hash.String(),
177+
), nil
178+
}
179+
180+
_, entry, err := i.trees.Next()
181+
if err != nil {
182+
i.trees.Close()
183+
i.trees = nil
184+
185+
if err == io.EOF || s.SkipGitErrors {
186+
continue
187+
}
188+
189+
return nil, err
190+
}
191+
192+
if entry.Mode != filemode.Dir {
193+
continue
194+
}
195+
196+
return sql.NewRow(
197+
i.repo.ID,
198+
i.commit.Hash.String(),
199+
entry.Hash.String(),
200+
), nil
201+
}
202+
}
203+
204+
func (i *commitTreesIter) Close() error {
205+
if i.commits != nil {
206+
i.commits.Close()
207+
}
208+
209+
if i.trees != nil {
210+
i.trees.Close()
211+
}
212+
213+
return nil
214+
}

commit_trees_test.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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", "dbd3641b371024f44d0e469a9c8f5457b0660de1"},
29+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "a39771a7651f97faf5c72e08224d857fc35133db"},
30+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
31+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
32+
33+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "a8d315b2b1c615d43042c3a62402b8a54288cf5c"},
34+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "a39771a7651f97faf5c72e08224d857fc35133db"},
35+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
36+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
37+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b"},
38+
39+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "fb72698cab7617ac416264415f13224dfd7a165e"},
40+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "a39771a7651f97faf5c72e08224d857fc35133db"},
41+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
42+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
43+
44+
{"af2d6a6954d532f8ffb47615169c8fdf9d383a1a", "4d081c50e250fa32ea8b1313cf8bb7c2ad7627fd"},
45+
{"af2d6a6954d532f8ffb47615169c8fdf9d383a1a", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
46+
47+
{"1669dce138d9b841a518c64b10914d88f5e488ea", "eba74343e2f15d62adedfd8c883ee0262b5c8021"},
48+
{"a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69", "c2d30fa8ef288618f65f6eed6e168e0d514886f4"},
49+
{"b8e471f58bcbca63b07bda20e428190409c2db47", "c2d30fa8ef288618f65f6eed6e168e0d514886f4"},
50+
{"35e85108805c84807bc66a02d91535e1e24b38b9", "8dcef98b1d52143e1e2dbc458ffe38f925786bf2"},
51+
{"b029517f6300c2da0f4b651b8642506cd6aaf45d", "aa9b383c260e1d05fbbf6b30a02914555e20c725"},
52+
}
53+
54+
require.Equal(expected, rows)
55+
}
56+
57+
func TestCommitTreesPushdown(t *testing.T) {
58+
ctx, _, cleanup := setup(t)
59+
defer cleanup()
60+
61+
table := new(commitTreesTable)
62+
testCases := []struct {
63+
name string
64+
filters []sql.Expression
65+
expected []sql.Row
66+
}{
67+
{
68+
"commit filter",
69+
[]sql.Expression{
70+
expression.NewEquals(
71+
expression.NewGetFieldWithTable(1, sql.Text, CommitTreesTableName, "commit_hash", false),
72+
expression.NewLiteral("918c48b83bd081e863dbe1b80f8998f058cd8294", sql.Text),
73+
),
74+
},
75+
[]sql.Row{
76+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "fb72698cab7617ac416264415f13224dfd7a165e"},
77+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "a39771a7651f97faf5c72e08224d857fc35133db"},
78+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
79+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
80+
},
81+
},
82+
{
83+
"tree filter",
84+
[]sql.Expression{
85+
expression.NewEquals(
86+
expression.NewGetFieldWithTable(2, sql.Text, CommitTreesTableName, "tree_hash", false),
87+
expression.NewLiteral("586af567d0bb5e771e49bdd9434f5e0fb76d25fa", sql.Text),
88+
),
89+
},
90+
[]sql.Row{
91+
{"e8d3ffab552895c19b9fcf7aa264d277cde33881", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
92+
{"6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
93+
{"918c48b83bd081e863dbe1b80f8998f058cd8294", "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
94+
},
95+
},
96+
}
97+
98+
for _, tt := range testCases {
99+
t.Run(tt.name, func(t *testing.T) {
100+
require := require.New(t)
101+
iter, err := table.WithProjectAndFilters(ctx, nil, tt.filters)
102+
require.NoError(err)
103+
104+
rows, err := sql.RowIterToRows(iter)
105+
require.NoError(err)
106+
107+
for i, row := range rows {
108+
// remove repository_ids
109+
rows[i] = row[1:]
110+
}
111+
112+
require.Equal(tt.expected, rows)
113+
})
114+
}
115+
}

0 commit comments

Comments
 (0)