Skip to content

Commit 4767869

Browse files
buddenfilhodanuvem
authored andcommitted
Add support to count commits (#80)
* count(*) works like '*' * count(*) kind of works, some tests are still missing * Added more tests * Fix typo * Don't apply limit when counting records (rembmer limit=0 is forbidden)
1 parent 0f264eb commit 4767869

16 files changed

Lines changed: 187 additions & 27 deletions

lexical/lexemes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ const L_ASC = "asc"
1313
const L_DESC = "desc"
1414
const L_LIKE = "like"
1515
const L_NOT = "not"
16+
const L_COUNT = "count"

lexical/lexical.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ func lexemeToToken(lexeme string) uint8 {
222222
return T_LIKE
223223
case L_NOT:
224224
return T_NOT
225+
case L_COUNT:
226+
return T_COUNT
225227
}
226228
return T_ID
227229
}

lexical/lexical_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func TestRecognizeTokensWithLexemesOfTwoChars(t *testing.T) {
8686

8787
func TestRecognizeTokensWithSourceManySpaced(t *testing.T) {
8888
setUp()
89-
source = "= < >= != cloudson"
89+
source = "= < >= != cloudson count"
9090
char = nextChar()
9191

9292
var token uint8
@@ -105,6 +105,9 @@ func TestRecognizeTokensWithSourceManySpaced(t *testing.T) {
105105

106106
token, _ = Token()
107107
assertToken(t, token, T_ID)
108+
109+
token, _ = Token()
110+
assertToken(t, token, T_COUNT)
108111
}
109112

110113
func TestErrorUnrecognizeChar(t *testing.T) {
@@ -127,7 +130,7 @@ func TestErrorUnrecognizeChar(t *testing.T) {
127130

128131
func TestReservedWords(t *testing.T) {
129132
setUp()
130-
source = "SELECT from WHEre in not"
133+
source = "SELECT from WHEre in not cOuNt"
131134
char = nextChar()
132135

133136
var token uint8
@@ -147,6 +150,9 @@ func TestReservedWords(t *testing.T) {
147150
token, _ = Token()
148151
assertToken(t, token, T_NOT)
149152

153+
token, _ = Token()
154+
assertToken(t, token, T_COUNT)
155+
150156
token, _ = Token()
151157
assertToken(t, token, T_EOF)
152158
}

lexical/tokens.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const T_IN = 24
2727
const T_ASC = 25
2828
const T_LIKE = 26
2929
const T_NOT = 27
30+
const T_COUNT = 28
3031
const T_EOF = 0
3132
const T_FUCK = 66
3233

@@ -59,7 +60,8 @@ func allocMapTokenNames() {
5960
T_IN: "T_IN",
6061
T_EOF: "T_EOF",
6162
T_ASC: "T_ASC",
62-
T_NOT: "T_NOT",
63+
T_NOT: "T_NOT",
64+
T_COUNT: "T_COUNT",
6365
}
6466
}
6567
}

parser/ast.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type NodeProgram struct {
2323

2424
type NodeSelect struct {
2525
WildCard bool
26+
Count bool
2627
Fields []string
2728
Tables []string
2829
Where NodeExpr

parser/parser.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ func gSelect() (*NodeSelect, error) {
7474
return nil, throwSyntaxError(lexical.T_SELECT, look_ahead)
7575
}
7676
token, tokenError := lexical.Token()
77+
7778
look_ahead = token
7879
if tokenError != nil {
7980
return nil, tokenError
@@ -86,8 +87,13 @@ func gSelect() (*NodeSelect, error) {
8687
return nil, err
8788
}
8889

89-
if len(fields) == 1 && fields[0] == "*" {
90-
s.WildCard = true
90+
if len(fields) == 1 {
91+
f0 := fields[0]
92+
if f0 == "*" {
93+
s.WildCard = true
94+
} else if f0 == "#" {
95+
s.Count = true
96+
}
9197
}
9298
s.Fields = fields
9399

@@ -159,6 +165,9 @@ func gTableParams() ([]string, error) {
159165
}
160166
look_ahead = token
161167
return []string{"*"}, nil
168+
} else if look_ahead == lexical.T_COUNT {
169+
result, err := gCount()
170+
return result, err
162171
}
163172
var fields = []string{}
164173
if look_ahead == lexical.T_ID {
@@ -173,7 +182,41 @@ func gTableParams() ([]string, error) {
173182
return fields, errorSyntax
174183
}
175184
return nil, throwSyntaxError(lexical.T_ID, look_ahead)
185+
}
186+
187+
// consume count(*)
188+
func gCount() ([]string, error) {
189+
// by construction, T_COUNT is consumed and stored
190+
// in the look_ahead
191+
err := gExactlyASpecificToken(lexical.T_COUNT)
192+
if err != nil {
193+
return nil, err
194+
}
195+
err = gExactlyASpecificToken(lexical.T_PARENTH_L)
196+
if err != nil {
197+
return nil, err
198+
}
199+
err = gExactlyASpecificToken(lexical.T_WILD_CARD)
200+
if err != nil {
201+
return nil, err
202+
}
203+
err = gExactlyASpecificToken(lexical.T_PARENTH_R)
204+
if err != nil {
205+
return nil, err
206+
}
207+
return []string{"#"}, nil
208+
}
176209

210+
func gExactlyASpecificToken(expected uint8) error {
211+
if look_ahead != expected {
212+
return throwSyntaxError(expected, look_ahead)
213+
}
214+
token, err := lexical.Token()
215+
if err != nil {
216+
return err
217+
}
218+
look_ahead = token
219+
return nil
177220
}
178221

179222
func gTableParamsRest(fields *[]string, count int) ([]string, error) {

parser/parser_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,25 @@ func TestUsingWildCard(t *testing.T) {
5555
}
5656
}
5757

58+
func TestUsingCount(t *testing.T) {
59+
New("select count(*) from users")
60+
ast, error := AST()
61+
62+
if error != nil {
63+
t.Errorf(error.Error())
64+
}
65+
66+
if ast.Child == nil {
67+
t.Errorf("Program is empty")
68+
}
69+
70+
selectNode := ast.Child.(*NodeSelect)
71+
if !selectNode.Count {
72+
t.Errorf("Expected count setted")
73+
}
74+
}
75+
76+
5877
func TestUsingOneFieldName(t *testing.T) {
5978
New("select name from files")
6079

@@ -119,6 +138,17 @@ func TestErrorWithUnexpectedComma(t *testing.T) {
119138
}
120139
}
121140

141+
func TestErrorWithMalformedCount(t *testing.T) {
142+
New("select count(*]")
143+
144+
_, error := AST()
145+
146+
if error == nil {
147+
t.Errorf("Expected error 'Expected )'")
148+
}
149+
}
150+
151+
122152
func TestErrorWithInvalidRootNode(t *testing.T) {
123153
New("name from files")
124154

runtime/commits.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package runtime
22

33
import (
44
"fmt"
5+
"strconv"
56
"log"
67
"strings"
78

@@ -26,7 +27,7 @@ func walkCommits(n *parser.NodeProgram, visitor *RuntimeVisitor) (*TableData, er
2627
resultFields := fields // These are the fields in output with wildcards expanded
2728
rows := make([]tableRow, s.Limit)
2829
usingOrder := false
29-
if s.Order != nil {
30+
if s.Order != nil && !s.Count {
3031
usingOrder = true
3132
// Check if the order by field is in the selected fields. If not, add them to selected fields list
3233
if !utilities.IsFieldPresentInArray(fields, s.Order.Field) {
@@ -38,15 +39,16 @@ func walkCommits(n *parser.NodeProgram, visitor *RuntimeVisitor) (*TableData, er
3839
boolRegister = true
3940
visitor.VisitExpr(where)
4041
if boolRegister {
41-
newRow := make(tableRow)
42-
for _, f := range fields {
43-
newRow[f] = metadataCommit(f, object)
42+
if !s.Count {
43+
newRow := make(tableRow)
44+
for _, f := range fields {
45+
newRow[f] = metadataCommit(f, object)
46+
}
47+
rows = append(rows, newRow)
4448
}
45-
rows = append(rows, newRow)
46-
4749
counter = counter + 1
4850
}
49-
if !usingOrder && counter > s.Limit {
51+
if !usingOrder && !s.Count && counter > s.Limit {
5052
return false
5153
}
5254
return true
@@ -56,13 +58,20 @@ func walkCommits(n *parser.NodeProgram, visitor *RuntimeVisitor) (*TableData, er
5658
if err != nil {
5759
fmt.Printf(err.Error())
5860
}
61+
if s.Count {
62+
newRow := make(tableRow)
63+
// counter was started from 1!
64+
newRow[COUNT_FIELD_NAME] = strconv.Itoa(counter-1)
65+
counter = 2
66+
rows = append(rows, newRow)
67+
}
5968
rowsSliced := rows[len(rows)-counter+1:]
6069
rowsSliced, err = orderTable(rowsSliced, s.Order)
6170
if err != nil {
6271
return nil, err
6372
}
6473

65-
if usingOrder && counter > s.Limit {
74+
if usingOrder && !s.Count && counter > s.Limit {
6675
counter = s.Limit
6776
rowsSliced = rowsSliced[0:counter]
6877
}

runtime/reference.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package runtime
22

33
import (
4+
"strconv"
45
"log"
56

67
"github.com/cloudson/git2go"
@@ -23,7 +24,7 @@ func walkReferences(n *parser.NodeProgram, visitor *RuntimeVisitor) (*TableData,
2324
}
2425
rows := make([]tableRow, s.Limit)
2526
usingOrder := false
26-
if s.Order != nil {
27+
if s.Order != nil && !s.Count {
2728
usingOrder = true
2829
}
2930
for object, inTheEnd := iterator.Next(); inTheEnd == nil; object, inTheEnd = iterator.Next() {
@@ -36,17 +37,26 @@ func walkReferences(n *parser.NodeProgram, visitor *RuntimeVisitor) (*TableData,
3637
if s.WildCard {
3738
fields = builder.possibleTables[s.Tables[0]]
3839
}
39-
newRow := make(tableRow)
40-
for _, f := range fields {
41-
newRow[f] = metadataReference(f, object)
40+
if !s.Count {
41+
newRow := make(tableRow)
42+
for _, f := range fields {
43+
newRow[f] = metadataReference(f, object)
44+
}
45+
rows = append(rows, newRow)
4246
}
43-
rows = append(rows, newRow)
4447
counter = counter + 1
4548
if !usingOrder && counter > s.Limit {
4649
break
4750
}
4851
}
4952
}
53+
if s.Count {
54+
newRow := make(tableRow)
55+
// counter was started from 1!
56+
newRow[COUNT_FIELD_NAME] = strconv.Itoa(counter-1)
57+
counter = 2
58+
rows = append(rows, newRow)
59+
}
5060
rowsSliced := rows[len(rows)-counter+1:]
5161
rowsSliced, err = orderTable(rowsSliced, s.Order)
5262
if err != nil {

runtime/remotes.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package runtime
22

33
import (
4+
"strconv"
45
"log"
56

67
"github.com/cloudson/git2go"
@@ -24,7 +25,7 @@ func walkRemotes(n *parser.NodeProgram, visitor *RuntimeVisitor) (*TableData, er
2425
}
2526
rows := make([]tableRow, s.Limit)
2627
usingOrder := false
27-
if s.Order != nil {
28+
if s.Order != nil && !s.Count {
2829
usingOrder = true
2930
}
3031
for _, remoteName := range remoteNames {
@@ -37,18 +38,27 @@ func walkRemotes(n *parser.NodeProgram, visitor *RuntimeVisitor) (*TableData, er
3738
boolRegister = true
3839
visitor.VisitExpr(where)
3940
if boolRegister {
40-
newRow := make(map[string]interface{})
41-
for _, f := range fields {
42-
newRow[f] = metadataRemote(f, object)
41+
if !s.Count {
42+
newRow := make(map[string]interface{})
43+
for _, f := range fields {
44+
newRow[f] = metadataRemote(f, object)
45+
}
46+
rows = append(rows, newRow)
4347
}
44-
rows = append(rows, newRow)
4548

4649
counter = counter + 1
4750
if !usingOrder && counter > s.Limit {
4851
break
4952
}
5053
}
5154
}
55+
if s.Count {
56+
newRow := make(tableRow)
57+
// counter was started from 1!
58+
newRow[COUNT_FIELD_NAME] = strconv.Itoa(counter-1)
59+
counter = 2
60+
rows = append(rows, newRow)
61+
}
5262
rowsSliced := rows[len(rows)-counter+1:]
5363
rowsSliced, err = orderTable(rowsSliced, s.Order)
5464
if err != nil {

0 commit comments

Comments
 (0)