Merge pull request #376 from influxdb/list-series-376
"list series" should allow filtering using a regular expressionpull/820/head
commit
23b7ae02ce
|
@ -170,12 +170,23 @@ func (self *CoordinatorImpl) runQuery(querySpec *parser.QuerySpec, seriesWriter
|
|||
}
|
||||
|
||||
func (self *CoordinatorImpl) runListSeriesQuery(querySpec *parser.QuerySpec, seriesWriter SeriesWriter) error {
|
||||
series := self.clusterConfiguration.MetaStore.GetSeriesForDatabase(querySpec.Database())
|
||||
allSeries := self.clusterConfiguration.MetaStore.GetSeriesForDatabase(querySpec.Database())
|
||||
matchingSeries := allSeries
|
||||
if q := querySpec.Query().GetListSeriesQuery(); q.HasRegex() {
|
||||
matchingSeries = nil
|
||||
regex := q.GetRegex()
|
||||
for _, s := range allSeries {
|
||||
if !regex.MatchString(s) {
|
||||
continue
|
||||
}
|
||||
matchingSeries = append(matchingSeries, s)
|
||||
}
|
||||
}
|
||||
name := "list_series_result"
|
||||
fields := []string{"name"}
|
||||
points := make([]*protocol.Point, len(series), len(series))
|
||||
points := make([]*protocol.Point, len(matchingSeries), len(matchingSeries))
|
||||
|
||||
for i, s := range series {
|
||||
for i, s := range matchingSeries {
|
||||
fieldValues := []*protocol.FieldValue{{StringValue: proto.String(s)}}
|
||||
points[i] = &protocol.Point{Values: fieldValues}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,42 @@ func (self *SingleServerSuite) TestDroppingSeries(c *C) {
|
|||
c.Assert(maps[0]["column1"], Equals, 1.0)
|
||||
}
|
||||
|
||||
// issue #376
|
||||
func (self *SingleServerSuite) TestListSeriesRegex(c *C) {
|
||||
client := self.server.GetClient("", c)
|
||||
c.Assert(client.CreateDatabase("test_list_series_regex"), IsNil)
|
||||
c.Assert(client.CreateDatabaseUser("test_list_series_regex", "user", "pass"), IsNil)
|
||||
user := self.server.GetClientWithUser("test_list_series_regex", "user", "pass", c)
|
||||
for _, n := range []string{"foo1", "foo2", "foobar"} {
|
||||
err := user.WriteSeries([]*influxdb.Series{{
|
||||
Name: n,
|
||||
Columns: []string{"column1"},
|
||||
Points: [][]interface{}{{1}},
|
||||
}})
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
for _, r := range []string{"/Foo\\d+/i", "/foo\\d+/"} {
|
||||
s, err := user.Query(fmt.Sprintf("list series %s", r))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(s, HasLen, 1)
|
||||
maps := ToMap(s[0])
|
||||
c.Assert(maps, HasLen, 2)
|
||||
names := map[string]bool{}
|
||||
for _, p := range maps {
|
||||
names[p["name"].(string)] = true
|
||||
}
|
||||
c.Assert(names["foo1"], Equals, true)
|
||||
c.Assert(names["foo2"], Equals, true)
|
||||
}
|
||||
|
||||
s, err := user.Query("list series")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(s, HasLen, 1)
|
||||
maps := ToMap(s[0])
|
||||
c.Assert(maps, HasLen, 3)
|
||||
}
|
||||
|
||||
// pr #483
|
||||
func (self *SingleServerSuite) TestConflictStatusCode(c *C) {
|
||||
client := self.server.GetClient("", c)
|
||||
|
|
|
@ -145,6 +145,12 @@ close_query (query *q)
|
|||
free(q->select_query);
|
||||
}
|
||||
|
||||
if (q->list_series_query) {
|
||||
if (q->list_series_query->has_regex)
|
||||
free_value(q->list_series_query->regex);
|
||||
free(q->list_series_query);
|
||||
}
|
||||
|
||||
if (q->drop_series_query) {
|
||||
free_drop_series_query(q->drop_series_query);
|
||||
free(q->drop_series_query);
|
||||
|
|
|
@ -55,10 +55,12 @@ type ListType int
|
|||
const (
|
||||
Series ListType = iota
|
||||
ContinuousQueries
|
||||
SeriesWithRegex
|
||||
)
|
||||
|
||||
type ListQuery struct {
|
||||
Type ListType
|
||||
value *Value
|
||||
}
|
||||
|
||||
type DropQuery struct {
|
||||
|
@ -126,14 +128,31 @@ func (self *Query) IsExplainQuery() bool {
|
|||
return self.SelectQuery != nil && self.SelectQuery.Explain
|
||||
}
|
||||
|
||||
func (self *Query) GetListSeriesQuery() *ListQuery {
|
||||
return self.ListQuery
|
||||
}
|
||||
|
||||
func (self *Query) IsListSeriesQuery() bool {
|
||||
return self.ListQuery != nil && self.ListQuery.Type == Series
|
||||
return self.ListQuery != nil && (self.ListQuery.Type == Series || self.ListQuery.Type == SeriesWithRegex)
|
||||
}
|
||||
|
||||
func (self *Query) IsListContinuousQueriesQuery() bool {
|
||||
return self.ListQuery != nil && self.ListQuery.Type == ContinuousQueries
|
||||
}
|
||||
|
||||
func (self *ListQuery) HasRegex() bool {
|
||||
return self.Type == SeriesWithRegex
|
||||
}
|
||||
|
||||
func (self *ListQuery) IsCaseSensitive() bool {
|
||||
return self.value.IsInsensitive
|
||||
}
|
||||
|
||||
func (self *ListQuery) GetRegex() *regexp.Regexp {
|
||||
regex, _ := self.value.GetCompiledRegex()
|
||||
return regex
|
||||
}
|
||||
|
||||
func (self *DeleteQuery) GetQueryString(withTime bool) string {
|
||||
buffer := bytes.NewBufferString("delete ")
|
||||
fmt.Fprintf(buffer, "from %s", self.FromClause.GetString())
|
||||
|
@ -596,8 +615,18 @@ func ParseQuery(query string) ([]*Query, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if q.list_series_query != 0 {
|
||||
return []*Query{{QueryString: query, ListQuery: &ListQuery{Type: Series}}}, nil
|
||||
if q.list_series_query != nil {
|
||||
var value *Value
|
||||
var err error
|
||||
t := Series
|
||||
if q.list_series_query.has_regex != 0 {
|
||||
t = SeriesWithRegex
|
||||
value, err = GetValue(q.list_series_query.regex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return []*Query{{QueryString: query, ListQuery: &ListQuery{Type: t, value: value}}}, nil
|
||||
}
|
||||
|
||||
if q.list_continuous_queries_query != 0 {
|
||||
|
|
|
@ -2,6 +2,7 @@ package parser
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -269,11 +270,41 @@ func (self *QueryParserSuite) TestParseFromWithJoinedTable(c *C) {
|
|||
c.Assert(fromClause.Names[1].Name.Name, Equals, "user.signups")
|
||||
}
|
||||
|
||||
func (self *QueryParserSuite) TestIncompleteRegex(c *C) {
|
||||
_, err := ParseQuery("list series /")
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (self *QueryParserSuite) TestParseListSeries(c *C) {
|
||||
queries, err := ParseQuery("list series")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(queries, HasLen, 1)
|
||||
c.Assert(queries[0].IsListQuery(), Equals, true)
|
||||
listSeriesQuery := queries[0].GetListSeriesQuery()
|
||||
c.Assert(listSeriesQuery, NotNil)
|
||||
c.Assert(listSeriesQuery.HasRegex(), Equals, false)
|
||||
|
||||
// test the case sensitive and case insensitive list series
|
||||
for i := 0; i < 2; i++ {
|
||||
query := "list series /^foo.*/"
|
||||
if i == 1 {
|
||||
query += "i"
|
||||
}
|
||||
queries, err = ParseQuery(query)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(queries, HasLen, 1)
|
||||
c.Assert(queries[0].IsListSeriesQuery(), Equals, true)
|
||||
listSeriesQuery = queries[0].GetListSeriesQuery()
|
||||
c.Assert(listSeriesQuery, NotNil)
|
||||
c.Assert(listSeriesQuery.HasRegex(), Equals, true)
|
||||
var regularExpression *regexp.Regexp
|
||||
if i == 1 {
|
||||
regularExpression, _ = regexp.Compile("(?i)^foo.*")
|
||||
} else {
|
||||
regularExpression, _ = regexp.Compile("^foo.*")
|
||||
}
|
||||
c.Assert(listSeriesQuery.GetRegex(), DeepEquals, regularExpression)
|
||||
}
|
||||
}
|
||||
|
||||
// issue #267
|
||||
|
|
|
@ -21,6 +21,7 @@ static int yycolumn = 1;
|
|||
%option bison-locations
|
||||
%option noyywrap
|
||||
%s FROM_CLAUSE REGEX_CONDITION
|
||||
%x LIST_SERIES
|
||||
%x IN_REGEX
|
||||
%x IN_TABLE_NAME
|
||||
%x IN_SIMPLE_NAME
|
||||
|
@ -30,17 +31,22 @@ static int yycolumn = 1;
|
|||
, { return *yytext; }
|
||||
"merge" { return MERGE; }
|
||||
"list" { return LIST; }
|
||||
"series" { return SERIES; }
|
||||
"series" { BEGIN(LIST_SERIES); return SERIES; }
|
||||
"continuous query" { return CONTINUOUS_QUERY; }
|
||||
"continuous queries" { return CONTINUOUS_QUERIES; }
|
||||
"inner" { return INNER; }
|
||||
"join" { return JOIN; }
|
||||
"from" { BEGIN(FROM_CLAUSE); return FROM; }
|
||||
<FROM_CLAUSE,REGEX_CONDITION>\/ { BEGIN(IN_REGEX); yylval->string=calloc(1, sizeof(char)); }
|
||||
<LIST_SERIES,FROM_CLAUSE,REGEX_CONDITION>\/ { BEGIN(IN_REGEX); yylval->string=calloc(1, sizeof(char)); }
|
||||
<IN_REGEX>\\\/ {
|
||||
yylval->string = realloc(yylval->string, strlen(yylval->string) + 2);
|
||||
strcat(yylval->string, "/");
|
||||
}
|
||||
<IN_REGEX><<EOF>> {
|
||||
free(yylval->string);
|
||||
BEGIN(INITIAL);
|
||||
return UNKNOWN;
|
||||
}
|
||||
<IN_REGEX>\\ {
|
||||
yylval->string = realloc(yylval->string, strlen(yylval->string) + 2);
|
||||
strcat(yylval->string, "\\");
|
||||
|
|
|
@ -75,7 +75,7 @@ value *create_expression_value(char *operator, size_t size, ...) {
|
|||
%lex-param {void *scanner}
|
||||
|
||||
// define types of tokens (terminals)
|
||||
%token SELECT DELETE FROM WHERE EQUAL GROUP BY LIMIT ORDER ASC DESC MERGE INNER JOIN AS LIST SERIES INTO CONTINUOUS_QUERIES CONTINUOUS_QUERY DROP DROP_SERIES EXPLAIN
|
||||
%token SELECT DELETE FROM WHERE EQUAL GROUP BY LIMIT ORDER ASC DESC MERGE INNER JOIN AS LIST SERIES INTO CONTINUOUS_QUERIES CONTINUOUS_QUERY DROP DROP_SERIES EXPLAIN UNKNOWN
|
||||
%token <string> STRING_VALUE INT_VALUE FLOAT_VALUE BOOLEAN_VALUE TABLE_NAME SIMPLE_NAME INTO_NAME REGEX_OP
|
||||
%token <string> NEGATION_REGEX_OP REGEX_STRING INSENSITIVE_REGEX_STRING DURATION
|
||||
|
||||
|
@ -165,7 +165,15 @@ QUERY:
|
|||
LIST SERIES
|
||||
{
|
||||
$$ = calloc(1, sizeof(query));
|
||||
$$->list_series_query = TRUE;
|
||||
$$->list_series_query = calloc(1, sizeof(list_series_query));
|
||||
}
|
||||
|
|
||||
LIST SERIES REGEX_VALUE
|
||||
{
|
||||
$$ = calloc(1, sizeof(query));
|
||||
$$->list_series_query = calloc(1, sizeof(list_series_query));
|
||||
$$->list_series_query->has_regex = TRUE;
|
||||
$$->list_series_query->regex = $3;
|
||||
}
|
||||
|
|
||||
DROP_SERIES_QUERY
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#define FALSE 0
|
||||
#define TRUE !FALSE
|
||||
|
||||
// #define DEBUG
|
||||
/* #define DEBUG */
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
|
@ -95,6 +95,11 @@ typedef struct {
|
|||
char explain;
|
||||
} select_query;
|
||||
|
||||
typedef struct {
|
||||
char has_regex;
|
||||
value *regex;
|
||||
} list_series_query;
|
||||
|
||||
typedef struct {
|
||||
from_clause *from_clause;
|
||||
condition *where_condition;
|
||||
|
@ -114,7 +119,7 @@ typedef struct {
|
|||
delete_query *delete_query;
|
||||
drop_series_query *drop_series_query;
|
||||
drop_query *drop_query;
|
||||
char list_series_query;
|
||||
list_series_query *list_series_query;
|
||||
char list_continuous_queries_query;
|
||||
error *error;
|
||||
} query;
|
||||
|
|
|
@ -14,6 +14,14 @@ int main(int argc, char **argv) {
|
|||
q = parse_query("select * from foo where time < -1s");
|
||||
close_query(&q);
|
||||
|
||||
// test partial regex
|
||||
q = parse_query("list series /");
|
||||
close_query(&q);
|
||||
|
||||
// test freeing list series query
|
||||
q = parse_query("list series /foo/ bar");
|
||||
close_query(&q);
|
||||
|
||||
// test freeing on error
|
||||
q = parse_query("select count(*) from users.events group_by user_email,time(1h) where time >> now()-1d;");
|
||||
close_query(&q);
|
||||
|
|
Loading…
Reference in New Issue