HTRACE-285. htraced tool: fix query parsing and add query_test (cmccabe)
diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go b/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go
index f3668ea..14b7f73 100644
--- a/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go
+++ b/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go
@@ -43,7 +43,7 @@
const EXIT_SUCCESS = 0
const EXIT_FAILURE = 1
-var verbose *bool
+var verbose bool
const USAGE = `The Apache HTrace command-line tool. This tool retrieves and modifies settings and
other data on a running htraced daemon.
@@ -68,7 +68,7 @@
app.Flag("Dmy.key", "Set configuration key 'my.key' to 'my.value'. Replace 'my.key' "+
"with any key you want to set.").Default("my.value").String()
addr := app.Flag("addr", "Server address.").String()
- verbose = app.Flag("verbose", "Verbose.").Default("false").Bool()
+ verbose = *app.Flag("verbose", "Verbose.").Default("false").Bool()
version := app.Command("version", "Print the version of this program.")
serverInfo := app.Command("serverInfo", "Print information retrieved from an htraced server.")
serverStats := app.Command("serverStats", "Print statistics retrieved from the htraced server.")
@@ -100,7 +100,7 @@
queryArg := query.Arg("query", "The query string to send. Query strings have the format "+
"[TYPE] [OPERATOR] [CONST], joined by AND statements.").Required().String()
rawQuery := app.Command("rawQuery", "Send a raw JSON query to htraced.")
- rawQueryArg := query.Arg("json", "The query JSON to send.").Required().String()
+ rawQueryArg := rawQuery.Arg("json", "The query JSON to send.").Required().String()
cmd := kingpin.MustParse(app.Parse(os.Args[1:]))
// Add the command-line settings into the configuration.
@@ -327,7 +327,7 @@
}
spans = append(spans, &span)
}
- if *verbose {
+ if verbose {
fmt.Printf("Writing ")
prefix := ""
for i := range spans {
@@ -390,7 +390,7 @@
if err == nil {
_, err = fmt.Fprintf(w, "%s\n", span.ToJson())
}
- if *verbose {
+ if verbose {
numSpans++
now := time.Now()
if !now.Before(nextLogTime) {
diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/queries.go b/htrace-htraced/go/src/org/apache/htrace/htrace/queries.go
index 4ff246c..442df4f 100644
--- a/htrace-htraced/go/src/org/apache/htrace/htrace/queries.go
+++ b/htrace-htraced/go/src/org/apache/htrace/htrace/queries.go
@@ -36,12 +36,12 @@
switch {
case c == prevQuote:
prevQuote = rune(0)
- return false
+ return true
case prevQuote != rune(0):
return false
case unicode.In(c, unicode.Quotation_Mark):
prevQuote = c
- return false
+ return true
default:
return unicode.IsSpace(c)
}
@@ -57,7 +57,7 @@
}
func (ps *predicateParser) Parse() (*common.Predicate, error) {
- if ps.curToken > len(ps.tokens) {
+ if ps.curToken >= len(ps.tokens) {
return nil, nil
}
if ps.curToken > 0 {
@@ -72,7 +72,7 @@
"token %d", ps.curToken))
}
}
- field := common.Field(ps.tokens[ps.curToken])
+ field := common.Field(strings.ToLower(ps.tokens[ps.curToken]))
if !field.IsValid() {
return nil, errors.New(fmt.Sprintf("Invalid field specifier at token %d. "+
"Can't understand %s. Valid field specifiers are %v", ps.curToken,
@@ -83,7 +83,7 @@
return nil, errors.New(fmt.Sprintf("Nothing found after field specifier "+
"at token %d", ps.curToken))
}
- op := common.Op(ps.tokens[ps.curToken])
+ op := common.Op(strings.ToLower(ps.tokens[ps.curToken]))
if !op.IsValid() {
return nil, errors.New(fmt.Sprintf("Invalid operation specifier at token %d. "+
"Can't understand %s. Valid operation specifiers are %v", ps.curToken,
@@ -95,20 +95,31 @@
"at token %d", ps.curToken))
}
val := ps.tokens[ps.curToken]
+ ps.curToken++
return &common.Predicate{Op: op, Field: field, Val: val}, nil
}
func parseQueryString(str string) ([]common.Predicate, error) {
ps := predicateParser{tokens: tokenize(str)}
+ if verbose {
+ fmt.Printf("Running query [ ")
+ prefix := ""
+ for tokenIdx := range(ps.tokens) {
+ fmt.Printf("%s'%s'", prefix, ps.tokens[tokenIdx])
+ prefix = ", "
+ }
+ fmt.Printf(" ]\n")
+ }
preds := make([]common.Predicate, 0)
for {
pred, err := ps.Parse()
- if pred == nil {
- break
- }
if err != nil {
return nil, err
}
+ if pred == nil {
+ break
+ }
+ preds = append(preds, *pred)
}
if len(preds) == 0 {
return nil, errors.New("Empty query string")
@@ -140,7 +151,7 @@
// Send a query.
func doQuery(hcl *htrace.Client, query *common.Query) error {
- if *verbose {
+ if verbose {
qbytes, err := json.Marshal(*query)
if err != nil {
qbytes = []byte("marshaling error: " + err.Error())
@@ -151,7 +162,7 @@
if err != nil {
return err
}
- if *verbose {
+ if verbose {
fmt.Printf("%d results...\n", len(spans))
}
for i := range spans {
diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/query_test.go b/htrace-htraced/go/src/org/apache/htrace/htrace/query_test.go
new file mode 100644
index 0000000..cab1e92
--- /dev/null
+++ b/htrace-htraced/go/src/org/apache/htrace/htrace/query_test.go
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+ "encoding/json"
+ "org/apache/htrace/common"
+ "reflect"
+ "testing"
+)
+
+func predsToStr(preds []common.Predicate) string {
+ b, err := json.MarshalIndent(preds, "", " ")
+ if err != nil {
+ return "JSON marshaling error: " + err.Error()
+ }
+ return string(b)
+}
+
+func checkParseQueryString(t *testing.T, str string, epreds []common.Predicate) {
+ preds, err := parseQueryString(str)
+ if err != nil {
+ t.Fatalf("got unexpected parseQueryString error: %s\n", err.Error())
+ }
+ if !reflect.DeepEqual(preds, epreds) {
+ t.Fatalf("Unexpected result from parseQueryString. " +
+ "Expected: %s, got: %s\n", predsToStr(epreds), predsToStr(preds))
+ }
+}
+
+func TestParseQueryString(t *testing.T) {
+ verbose = testing.Verbose()
+ checkParseQueryString(t, "description eq ls", []common.Predicate {
+ common.Predicate {
+ Op: common.EQUALS,
+ Field: common.DESCRIPTION,
+ Val: "ls",
+ },
+ })
+ checkParseQueryString(t, "begin gt 123 and end le 456", []common.Predicate {
+ common.Predicate {
+ Op: common.GREATER_THAN,
+ Field: common.BEGIN_TIME,
+ Val: "123",
+ },
+ common.Predicate {
+ Op: common.LESS_THAN_OR_EQUALS,
+ Field: common.END_TIME,
+ Val: "456",
+ },
+ })
+ checkParseQueryString(t, `DESCRIPTION cn "Foo Bar" and ` +
+ `BEGIN ge "999" and SPANID eq "4565d8abc4f70ac1216a3f1834c6860b"`,
+ []common.Predicate {
+ common.Predicate {
+ Op: common.CONTAINS,
+ Field: common.DESCRIPTION,
+ Val: "Foo Bar",
+ },
+ common.Predicate {
+ Op: common.GREATER_THAN_OR_EQUALS,
+ Field: common.BEGIN_TIME,
+ Val: "999",
+ },
+ common.Predicate {
+ Op: common.EQUALS,
+ Field: common.SPAN_ID,
+ Val: "4565d8abc4f70ac1216a3f1834c6860b",
+ },
+ })
+}