diff --git a/CHANGELOG.md b/CHANGELOG.md index 227e557ff2e..0a1ce3bc43d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - [#2100](https://github.com/influxdb/influxdb/pull/2100): Synchronize access to shard index. - [#2131](https://github.com/influxdb/influxdb/pull/2131): Optimize marshalTags(). - [#2130](https://github.com/influxdb/influxdb/pull/2130): Make fewer calls to marshalTags(). +- [#2105](https://github.com/influxdb/influxdb/pull/2105): Support != for tag values. Fix issue #2097, thanks to @smonkewitz for bug report. +- [#2105](https://github.com/influxdb/influxdb/pull/2105): Support !~ tags values. ## v0.9.0-rc17 [2015-03-29] diff --git a/cmd/influxd/server_integration_test.go b/cmd/influxd/server_integration_test.go index cf8703fd254..efebc3ff1d4 100644 --- a/cmd/influxd/server_integration_test.go +++ b/cmd/influxd/server_integration_test.go @@ -592,16 +592,73 @@ func runTestsData(t *testing.T, testName string, nodes Cluster, database, retent { reset: true, name: "WHERE tags SELECT single field (EQ tag value1)", - write: `{"database" : "%DB%", "retentionPolicy" : "%RP%", "points": [{"name": "cpu", "timestamp": "2015-02-28T01:03:36.703820946Z", "tags": {"host": "server01"}, "fields": {"value": 100}}, - {"name": "cpu", "timestamp": "2010-02-28T01:03:37.703820946Z", "tags": {"host": "server02"}, "fields": {"value": 200}}]}`, + write: `{"database" : "%DB%", "retentionPolicy" : "%RP%", "points": [{"name": "cpu", "timestamp": "2015-02-28T01:03:36.703820946Z", "tags": {"host": "server01", "region": "us-west"}, "fields": {"value": 100}}, + {"name": "cpu", "timestamp": "2010-02-28T01:03:37.703820946Z", "tags": {"host": "server02"}, "fields": {"value": 200}}, + {"name": "cpu", "timestamp": "2012-02-28T01:03:38.703820946Z", "tags": {"host": "server03"}, "fields": {"value": 300}}]}`, query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host = 'server01'`, expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2015-02-28T01:03:36.703820946Z",100]]}]}]}`, }, + { + name: "WHERE tags SELECT single field (2 EQ tags)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host = 'server01' AND region = 'us-west'`, + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2015-02-28T01:03:36.703820946Z",100]]}]}]}`, + }, + { + name: "WHERE tags SELECT single field (1 EQ and 1 NEQ tag)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host = 'server01' AND region != 'us-west'`, + expected: `{"results":[{}]}`, + }, { name: "WHERE tags SELECT single field (EQ tag value2)", query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host = 'server02'`, expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2010-02-28T01:03:37.703820946Z",200]]}]}]}`, }, + { + name: "WHERE tags SELECT single field (NEQ tag value1)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host != 'server01'`, + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2010-02-28T01:03:37.703820946Z",200],["2012-02-28T01:03:38.703820946Z",300]]}]}]}`, + }, + { + name: "WHERE tags SELECT single field (NEQ tag value1 AND NEQ tag value2)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host != 'server01' AND host != 'server02'`, + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2012-02-28T01:03:38.703820946Z",300]]}]}]}`, + }, + { + name: "WHERE tags SELECT single field (NEQ tag value1 OR NEQ tag value2)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host != 'server01' OR host != 'server02'`, // Yes, this is always true, but that's the point. + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2010-02-28T01:03:37.703820946Z",200],["2012-02-28T01:03:38.703820946Z",300],["2015-02-28T01:03:36.703820946Z",100]]}]}]}`, + }, + { + name: "WHERE tags SELECT single field (NEQ tag value1 AND NEQ tag value2 AND NEQ tag value3)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host != 'server01' AND host != 'server02' AND host != 'server03'`, + expected: `{"results":[{}]}`, + }, + { + reset: true, + name: "WHERE tags SELECT single field (NEQ tag value1, point without any tags)", + write: `{"database" : "%DB%", "retentionPolicy" : "%RP%", "points": [{"name": "cpu", "timestamp": "2015-02-28T01:03:36.703820946Z", "tags": {"host": "server01"}, "fields": {"value": 100}}, + {"name": "cpu", "timestamp": "2012-02-28T01:03:38.703820946Z", "fields": {"value": 200}}]}`, + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host != 'server01'`, + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2012-02-28T01:03:38.703820946Z",200]]}]}]}`, + }, + { + reset: true, + name: "WHERE tags SELECT single field (regex tag no match)", + write: `{"database" : "%DB%", "retentionPolicy" : "%RP%", "points": [{"name": "cpu", "timestamp": "2015-02-28T01:03:36.703820946Z", "tags": {"host": "server01"}, "fields": {"value": 100}}, + {"name": "cpu", "timestamp": "2012-02-28T01:03:38.703820946Z", "fields": {"value": 200}}]}`, + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host !~ /server01/`, + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2012-02-28T01:03:38.703820946Z",200]]}]}]}`, + }, + { + name: "WHERE tags SELECT single field (regex tag match)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host =~ /server01/`, + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2015-02-28T01:03:36.703820946Z",100]]}]}]}`, + }, + { + name: "WHERE tags SELECT single field (regex tag match)", + query: `SELECT value FROM "%DB%"."%RP%".cpu WHERE host !~ /server[23]/`, + expected: `{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["2012-02-28T01:03:38.703820946Z",200],["2015-02-28T01:03:36.703820946Z",100]]}]}]}`, + }, // WHERE fields queries { diff --git a/database.go b/database.go index ca087a66393..cbdd1d319bf 100644 --- a/database.go +++ b/database.go @@ -376,19 +376,36 @@ func (m *Measurement) idsForExpr(n *influxql.BinaryExpr) (seriesIDs, bool, influ return nil, true, nil } - // if we're looking for series with a specific tag value + // if we're looking for series with specific tag values if str, ok := value.(*influxql.StringLiteral); ok { - return tagVals[str.Val], true, nil + var ids seriesIDs + + if n.Op == influxql.EQ { + // return series that have a tag of specific value. + ids = tagVals[str.Val] + } else if n.Op == influxql.NEQ { + ids = m.seriesIDs.reject(tagVals[str.Val]) + } + return ids, true, nil } // if we're looking for series with tag values that match a regex if re, ok := value.(*influxql.RegexLiteral); ok { var ids seriesIDs + + // The operation is a NEQREGEX, code must start by assuming all match, even + // series without any tags. + if n.Op == influxql.NEQREGEX { + ids = m.seriesIDs + } + for k := range tagVals { match := re.Val.MatchString(k) if (match && n.Op == influxql.EQREGEX) || (!match && n.Op == influxql.NEQREGEX) { ids = ids.union(tagVals[k]) + } else if match && n.Op == influxql.NEQREGEX { + ids = ids.reject(tagVals[k]) } } return ids, true, nil