diff --git a/.gitignore b/.gitignore
index 86fa71248..c924add8b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,4 @@ default.etcd/
*.test
vendor
.protoc-gen-gofast
-
-/datadog/.DS_Store
+.DS_Store
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
diff --git a/Makefile b/Makefile
index 78c3e71f6..d5f1022d6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,7 @@
-.PHONY: glide vendor-update docker pilosa crossbuild install generate
+.PHONY: glide vendor-update docker pilosa crossbuild install generate statik
GLIDE := $(shell command -v glide 2>/dev/null)
+STATIK := $(shell command -v statik 2>/dev/null)
PROTOC := $(shell command -v protoc 2>/dev/null)
VERSION := $(shell git describe --tags 2> /dev/null || echo unknown)
IDENTIFIER := $(VERSION)-$(GOOS)-$(GOARCH)
@@ -41,14 +42,24 @@ install: vendor
.protoc-gen-gofast: vendor
ifndef PROTOC
- $(error "protoc is not available please install protoc from https://github.com/google/protobuf/releases")
+ $(error "protoc is not available. please install protoc from https://github.com/google/protobuf/releases")
endif
go build -o .protoc-gen-gofast ./vendor/github.com/gogo/protobuf/protoc-gen-gofast
cp ./.protoc-gen-gofast $(GOPATH)/bin/protoc-gen-gofast
-generate: .protoc-gen-gofast
+generate-protoc: .protoc-gen-gofast
go generate github.com/pilosa/pilosa/internal
+generate-statik: statik
+ go generate github.com/pilosa/pilosa
+
+generate: generate-protoc generate-statik
+
+statik:
+ifndef STATIK
+ go get github.com/rakyll/statik
+endif
+
docker:
docker build -t "pilosa:$(VERSION)" \
--build-arg ldflags="-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME)" .
diff --git a/README.md b/README.md
index acab512eb..377ea62aa 100644
--- a/README.md
+++ b/README.md
@@ -1,326 +1,64 @@
-# pilosa
-
-Pilosa is a bitmap index.
+
+
+
[](https://travis-ci.com/pilosa/pilosa)
+## An open source, distributed bitmap index.
+- [Docs](#docs)
+- [Getting Started](#getting-started)
+- [Data Model](#data-model)
+- [Query Language](#query-language)
+- [Client Libraries](#client-drivers)
+- [Get Support](#get-support)
+- [Contributing](#contributing)
+
+
+## Docs
+
+See our [Documentation](https://www.pilosa.com/docs/) for information about installing and working with Pilosa.
+
+
## Getting Started
-Pilosa requires Go 1.7 or greater.
+1. [Install Pilosa](https://www.pilosa.com/docs/installation/).
-You can download the source by running `go get`:
+2. [Start Pilosa](https://www.pilosa.com/docs/getting-started/#starting-pilosa) with the default configuration:
-```sh
-$ go get github.com/pilosa/pilosa
-```
+ ```shell
+ pilosa server
+ ```
+
+ and verify that it's running:
+
+ ```shell
+ curl localhost:10101/nodes
+ ```
-Now you can install the `pilosa` binary:
+3. Follow along with the [Sample Project](https://www.pilosa.com/docs/getting-started/#sample-project) to get a better understanding of Pilosa's capabilities.
-```sh
-$ go install github.com/pilosa/pilosa/cmd/...
-```
-Now run a single pilosa node with the default configuration:
+## Data Model
-```sh
-pilosa server
-```
+Check out how the Pilosa [Data Model](https://www.pilosa.com/docs/data-model/) works.
-## Configuration
-Running just `pilosa` will show a list of available subcommands. `pilosa help
-` will show usage information and the available flags for the command.
+## Query Language
-Any flag can be specified at the command line, in an environment variables,
-and/or in a toml config file. The environment variable for any flag is that
-flag, upper cased, prefixed with `PILOSA_`, and with any dashes converted to
-underscores. For example, if you would specify `--cluster.poll-interval=30s` at
-the command line, you would set `PILOSA_CLUSTER.POLL_INTERVAL=30s` in the
-environment. For the configuration file, a dot in a flag denotes nesting with in
-the config file. See the example config file below for examples of this.
+You can interact with Pilosa directly in the console using the [Pilosa Query Language](https://www.pilosa.com/docs/query-language/) (PQL).
-You can specify a configuration by setting the `--config` flag when running
-`pilosa`.
+## Client Libraries
-```sh
-pilosa server --config custom-config-file.cfg
-```
+There are supported libraries for the following languages:
+- [Go](https://www.pilosa.com/docs/client-libraries/#go)
+- [Java](https://www.pilosa.com/docs/client-libraries/#java)
+- [Python](https://www.pilosa.com/docs/client-libraries/#python)
-The config file uses the [TOML](https://github.com/toml-lang/toml) configuration file format,
-and should look like:
+## Get Support
-```
-data-dir = "/tmp/pil0"
-bind = "127.0.0.1:10101"
+There are [several channels](https://www.pilosa.com/community/#support) availble for you to reach out to us for support.
-[cluster]
- poll-interval = "2m0s"
- replicas = 2
- hosts = [
- "127.0.0.1:10101",
- "127.0.0.1:10102",
- ]
+## Contributing
-[anti-entropy]
- interval = "10m0s"
-
-[profile]
- cpu = "/home/mycpuprofile"
- cpu-time = "30s"
-```
-
-You can generate a template config file with default values with:
-
-```sh
-pilosa config
-```
-
-The first two configuration options will be unique to each node in the cluster:
-
-`data-dir`: directory in which data is stored to disk
-
-`bind`: IP and port that pilosa will listen on
-
-The remaining configuration options should be the same on every node in the cluster.
-
-`[cluster] replicas`: the number of replicas within the cluster
-
-`[cluster] hosts`: specifies each node within the cluster
-
-`[cluster] poll-interval`: TODO
-
-`[anti-entropy] interval`: TODO
-
-There are also some profiling options for debugging and performance tuning - these don't need to be the same across the cluster and are mostly useful for doing Pilosa development.
-
-`[profile] cpu`: Path at which to store cpu profiling data which will be taken when pilosa starts.
-
-`[profile] cpu-time`: Amount of time for which to collect cpu profiling data at startup.
-
-## Docker
-
-You can create a Pilosa container using `make docker` or equivalently:
-```
-docker build -t pilosa:latest .
-```
-
-You can run a temporary container using:
-```
-docker run -it --rm --name pilosa -p 10101:10101 pilosa:latest
-```
-
-When you click `Ctrl+C` to stop the container, the container and the data in the container will be erased. You can leave out `--rm` flag to keep the data in the container. See [Docker documentation](https://docs.docker.com) for other options.
-
-## Usage
-
-You can interact with Pilosa via HTTP requests to the host:port on which you have Pilosa running.
-The following examples illustrate how to do this using `curl` with a Pilosa cluster running on
-127.0.0.1 port 10101.
-
-Return the version of Pilosa:
-```sh
-$ curl "http://127.0.0.1:10101/version"
-```
-
-Return a list of all indexes and frames in the index:
-```sh
-$ curl "http://127.0.0.1:10101/schema"
-```
-
-### Index and Frame Schema
-
-Before running a query, the corresponding index and frame must be created. Note that index and frame names can contain only lower case letters, numbers, dash (`-`), underscore (`_`) and dot (`.`).
-
-You can create the index `sample-idx` using:
-
-```sh
-$ curl -XPOST "http://127.0.0.1:10101/index" \
- -d '{"index": "sample-idx"}'
-```
-
-Optionally, you can specify the column label on index creation:
-
-```sh
-$ curl -XPOST "http://127.0.0.1:10101/index" \
- -d '{"index": "sample-idx", "options": {"columnLabel": "user"}}'
-```
-
-The frame `collaboration` may be created using the following call:
-
-```sh
-$ curl -XPOST "http://127.0.0.1:10101/frame" \
- -d '{"index": "sample-idx", "frame": "collaboration"}'
-```
-
-It is possible to specify the frame row label on frame creation:
-
-```sh
-$ curl -XPOST "http://127.0.0.1:10101/frame" \
- -d '{"index": "sample-idx", "frame": "collaboration", "options": {"rowLabel": "project"}}'
-```
-
-### Queries
-
-Queries to Pilosa require sending a POST request where the query itself is sent as POST data.
-You specify the index on which to perform the query with a URL argument `index=index-name`.
-
-In this section, we assume both the index `sample-idx` with column label `user` and the frame `collaboration` with row label `project` was created.
-
-A query sent to index `sample-idx` will have the following format:
-
-```sh
-$ curl -X POST "http://127.0.0.1:10101/query?index=sample-idx" -d 'Query()'
-```
-
-The `Query()` object referenced above should be made up of one or more of the query types listed below.
-So for example, a SetBit() query would look like this:
-```sh
-$ curl -X POST "http://127.0.0.1:10101/query?index=sample-idx" -d 'SetBit(project=10, frame="collaboration", user=1)'
-```
-
-Query results have the format `{"results":[]}`, where `results` is a list of results for each `Query()`. This
-means that you can provide multiple `Query()` objects with each HTTP request and `results` will contain
-the results of all of the queries.
-
-```sh
-$ curl -X POST "http://127.0.0.1:10101/query?index=sample-idx" -d 'Query() Query() Query()'
-```
-
----
-#### SetBit()
-```
-SetBit(project=10, frame="collaboration", user=1)
-```
-A return value of `{"results":[true]}` indicates that the bit was toggled from 0 to 1.
-A return value of `{"results":[false]}` indicates that the bit was already set to 1 and therefore nothing changed.
-
-SetBit accepts an optional `timestamp` field:
-```
-SetBit(project=10, frame="collaboration", user=2, timestamp="2016-12-11T10:09:07")
-```
-
----
-#### ClearBit()
-```
-ClearBit(project=10, frame="collaboration", user=1)
-```
-A return value of `{"results":[true]}` indicates that the bit was toggled from 1 to 0.
-A return value of `{"results":[false]}` indicates that the bit was already set to 0 and therefore nothing changed.
-
----
-#### SetRowAttrs()
-```
-SetRowAttrs(project=10, frame="collaboration", stars=123, url="http://projects.pilosa.com/10", active=true)
-```
-Returns `{"results":[null]}`
-
----
-#### SetColumnAttrs()
----
-```
-SetColumnAttrs(user=10, friends=123, username="mrpi", active=true)
-```
-
-Returns `{"results":[null]}`
-
----
-#### Bitmap()
-```
-Bitmap(project=10, frame="collaboration")
-```
-Returns `{"results":[{"attrs":{"stars":123, "url":"http://projects.pilosa.com/10", "active":true},"bits":[1,2]}]}` where `attrs` are the
-attributes set using `SetRowAttrs()` and `bits` are the bits set using `SetBit()`.
-
-In order to return column attributes attached to the columns of a bitmap, add `&columnAttrs=true` to the query string. Sample response:
-```
-{"results":[{"attrs":{},"bits":[10]}],"columnAttrs":[{"user":10,"attrs":{"friends":123, "username":"mrpi", "active":true}}]}
-```
-
----
-#### Union()
-```
-Union(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration")))
-```
-Returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[1,2]}]}`.
-Note that a `Union()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`.
-
----
-#### Intersect()
-```
-Intersect(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration")))
-```
-Returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[1]}]}`.
-Note that an `Intersect()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`.
-
----
-#### Difference()
-```
-Difference(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration")))
-```
-`Difference()` represents all of the bits that are set in the first `Bitmap()` but are not set in the second `Bitmap()`. It returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[2]}]}`.
-Note that a `Difference()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`.
-
----
-#### Count()
-```
-Count(Bitmap(project=10, frame="collaboration"))
-```
-Returns the count of the number of bits set in `Bitmap()`: `{"results":[28]}`
-
----
-#### Range()
-```
-Range(project=10, frame="collaboration", start="1970-01-01T00:00", end="2000-01-02T03:04")
-```
-
----
-#### TopN()
-```
-TopN(frame="geo")
-```
-Returns all Bitmaps in the cache from frame `geo` sorted by the count of bits.
-
-```
-TopN(frame="geo", n=20)
-```
-Returns the top 20 Bitmaps from frame `geo`.
-
-```
-TopN(Bitmap(project=10, frame="collaboration"), frame="geo", n=20)
-```
-Returns the top 20 Bitmaps from `geo` sorted by the count of bits in the intersection with `Bitmap(project=10)`.
-
-```
-TopN(Bitmap(project=10, frame="collaboration"), frame="geo", n=20, field="category", [81,82])
-```
-Returns the top 20 Bitmaps from `geo`in attribute `category` with values `81 or
-82` sorted by the count of bits in the intersection with `Bitmap(project=10)`.
-
-## Development
-
-### Updating dependencies
-
-To update dependencies, you'll need to install [Glide][].
-
-Then add the new dependencies in your project:
-
-```sh
-$ glide get github.com/foo/bar
-```
-
-### Protobuf
-
-If you update protobuf (pilosa/internal/internal.proto), then you need to run `go generate`
-```sh
-$ go generate
-```
-
-### Version
-
-In order to set the version number, compile Pilosa with the following argument:
-```sh
-$ go install --ldflags="-X main.Version=1.0.0"
-```
-
-[Glide]: http://glide.sh/
+Pilosa is an open source project. Please see our [Contributing Guide](https://www.pilosa.com/docs/contributing/) for information about how to get involved.
diff --git a/attr.go b/attr.go
index 3859a1dc7..2e45aadf8 100644
--- a/attr.go
+++ b/attr.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
diff --git a/attr_test.go b/attr_test.go
index cfc31d54b..dda280214 100644
--- a/attr_test.go
+++ b/attr_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/bitmap.go b/bitmap.go
index ba1a6af93..ecdb4c076 100644
--- a/bitmap.go
+++ b/bitmap.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
// #cgo CFLAGS:-mpopcnt
diff --git a/broadcast.go b/broadcast.go
index 9e04d04f6..95a6d58f5 100644
--- a/broadcast.go
+++ b/broadcast.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -17,23 +31,27 @@ type NodeSet interface {
Open() error
}
-// StaticNodeSet represents a basic NodeSet for testing
+// StaticNodeSet represents a basic NodeSet for testing.
type StaticNodeSet struct {
nodes []*Node
}
+// NewStaticNodeSet creates a statically defined NodeSet.
func NewStaticNodeSet() *StaticNodeSet {
return &StaticNodeSet{}
}
+// Nodes implements the NodeSet interface and returns a list of nodes in the cluster.
func (s *StaticNodeSet) Nodes() []*Node {
return s.nodes
}
+// Open implements the NodeSet interface to start network activity, but for a static NodeSet it does nothing.
func (s *StaticNodeSet) Open() error {
return nil
}
+// Join sets the NodeSet nodes to the slice of Nodes passed in.
func (s *StaticNodeSet) Join(nodes []*Node) error {
s.nodes = nodes
return nil
@@ -49,9 +67,9 @@ func init() {
NopBroadcaster = &nopBroadcaster{}
}
+// NopBroadcaster represents a Broadcaster that doesn't do anything.
var NopBroadcaster Broadcaster
-// nopBroadcaster represents a Broadcaster that doesn't do anything.
type nopBroadcaster struct{}
// SendSync A no-op implemenetation of Broadcaster SendSync method.
@@ -85,8 +103,10 @@ type nopBroadcastReceiver struct{}
func (n *nopBroadcastReceiver) Start(b BroadcastHandler) error { return nil }
+// NopBroadcastReceiver is a no-op implementation of the BroadcastReceiver.
var NopBroadcastReceiver = &nopBroadcastReceiver{}
+// Broadcast message types.
const (
MessageTypeCreateSlice = 1
MessageTypeCreateIndex = 2
@@ -95,6 +115,7 @@ const (
MessageTypeDeleteFrame = 5
)
+// MarshalMessage encodes the protobuf message into a byte slice.
func MarshalMessage(m proto.Message) ([]byte, error) {
var typ uint8
switch obj := m.(type) {
@@ -118,6 +139,7 @@ func MarshalMessage(m proto.Message) ([]byte, error) {
return append([]byte{typ}, buf...), nil
}
+// UnmarshalMessage decodes the byte slice into a protobuf message.
func UnmarshalMessage(buf []byte) (proto.Message, error) {
typ, buf := buf[0], buf[1:]
diff --git a/broadcast_test.go b/broadcast_test.go
index de38239d4..8f0245c88 100644
--- a/broadcast_test.go
+++ b/broadcast_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/cache.go b/cache.go
index 5dc4d3188..e0c8047d8 100644
--- a/cache.go
+++ b/cache.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -58,6 +72,7 @@ func NewLRUCache(maxEntries uint32) *LRUCache {
return c
}
+// BulkAdd adds a count to the cache unsorted. You should Invalidate after completion.
func (c *LRUCache) BulkAdd(id, n uint64) {
c.Add(id, n)
}
@@ -211,6 +226,7 @@ func (c *RankCache) Invalidate() {
c.invalidate()
}
+// Recalculate rebuilds the cache.
func (c *RankCache) Recalculate() {
c.mu.Lock()
defer c.mu.Unlock()
@@ -328,19 +344,23 @@ type PairHeap struct {
Pairs
}
+// Less implemets the Sort interface.
+// reports whether the element with index i should sort before the element with index j.
func (p PairHeap) Less(i, j int) bool { return p.Pairs[i].Count < p.Pairs[j].Count }
-func (h *Pairs) Push(x interface{}) {
+// Push appends the element onto the Pair slice.
+func (p *Pairs) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
- *h = append(*h, x.(Pair))
+ *p = append(*p, x.(Pair))
}
-func (h *Pairs) Pop() interface{} {
- old := *h
+// Pop removes the minimum element from the Pair slice.
+func (p *Pairs) Pop() interface{} {
+ old := *p
n := len(old)
x := old[n-1]
- *h = old[0 : n-1]
+ *p = old[0 : n-1]
return x
}
diff --git a/client.go b/client.go
index 7606369ae..96e725876 100644
--- a/client.go
+++ b/client.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -315,6 +329,7 @@ func (c *Client) Import(ctx context.Context, index, frame string, slice uint64,
return nil
}
+// MarshalImportPayload marshalls the import parameters into a protobuf byte slice.
func MarshalImportPayload(index, frame string, slice uint64, bits []Bit) ([]byte, error) {
// Separate row and column IDs to reduce allocations.
rowIDs := Bits(bits).RowIDs()
@@ -982,36 +997,36 @@ func (p Bits) Less(i, j int) bool {
}
// RowIDs returns a slice of all the row IDs.
-func (a Bits) RowIDs() []uint64 {
- other := make([]uint64, len(a))
- for i := range a {
- other[i] = a[i].RowID
+func (p Bits) RowIDs() []uint64 {
+ other := make([]uint64, len(p))
+ for i := range p {
+ other[i] = p[i].RowID
}
return other
}
// ColumnIDs returns a slice of all the column IDs.
-func (a Bits) ColumnIDs() []uint64 {
- other := make([]uint64, len(a))
- for i := range a {
- other[i] = a[i].ColumnID
+func (p Bits) ColumnIDs() []uint64 {
+ other := make([]uint64, len(p))
+ for i := range p {
+ other[i] = p[i].ColumnID
}
return other
}
// Timestamps returns a slice of all the timestamps.
-func (a Bits) Timestamps() []int64 {
- other := make([]int64, len(a))
- for i := range a {
- other[i] = a[i].Timestamp
+func (p Bits) Timestamps() []int64 {
+ other := make([]int64, len(p))
+ for i := range p {
+ other[i] = p[i].Timestamp
}
return other
}
// GroupBySlice returns a map of bits by slice.
-func (a Bits) GroupBySlice() map[uint64][]Bit {
+func (p Bits) GroupBySlice() map[uint64][]Bit {
m := make(map[uint64][]Bit)
- for _, bit := range a {
+ for _, bit := range p {
slice := bit.ColumnID / SliceWidth
m[slice] = append(m[slice], bit)
}
diff --git a/client_test.go b/client_test.go
index 70676e399..a89a41cce 100644
--- a/client_test.go
+++ b/client_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/cluster.go b/cluster.go
index 4c6dc2b82..0ab8aea06 100644
--- a/cluster.go
+++ b/cluster.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -13,8 +27,10 @@ const (
// DefaultReplicaN is the default number of replicas per partition.
DefaultReplicaN = 1
+)
- // NodeState represents node state returned in /status endpoint for a node in the cluster.
+// NodeState represents node state returned in /status endpoint for a node in the cluster.
+const (
NodeStateUp = "UP"
NodeStateDown = "DOWN"
)
@@ -125,7 +141,7 @@ func NewCluster() *Cluster {
}
}
-// NodeSetHosts returns the list of host strings for NodeSet members
+// NodeSetHosts returns the list of host strings for NodeSet members.
func (c *Cluster) NodeSetHosts() []string {
if c.NodeSet == nil {
return []string{}
@@ -152,7 +168,7 @@ func (c *Cluster) NodeStates() map[string]string {
return h
}
-// State returns the internal ClusterState representation.
+// Status returns the internal ClusterStatus representation.
func (c *Cluster) Status() *internal.ClusterStatus {
return &internal.ClusterStatus{
Nodes: encodeClusterStatus(c.Nodes),
@@ -229,11 +245,10 @@ func (c *Cluster) OwnsSlices(index string, maxSlice uint64, host string) []uint6
for i := uint64(0); i <= maxSlice; i++ {
p := c.Partition(index, i)
// Determine primary owner node.
- node_index := c.Hasher.Hash(uint64(p), len(c.Nodes))
- if c.Nodes[node_index].Host == host {
+ nodeIndex := c.Hasher.Hash(uint64(p), len(c.Nodes))
+ if c.Nodes[nodeIndex].Host == host {
slices = append(slices, i)
}
-
}
return slices
}
diff --git a/cluster_test.go b/cluster_test.go
index 4c96cd5d9..3caea3fe2 100644
--- a/cluster_test.go
+++ b/cluster_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
@@ -118,6 +132,16 @@ func TestCluster_NodeStates(t *testing.T) {
}
}
+// Ensure OwnsSlices can find the actual slice list for node and index
+func TestCluster_OwnsSlices(t *testing.T) {
+ c := NewCluster(5)
+ slices := c.OwnsSlices("test", 10, "host2")
+
+ if !reflect.DeepEqual(slices, []uint64{1, 3, 10}) {
+ t.Fatalf("unexpected slices for node's index: %v", slices)
+ }
+}
+
// NewCluster returns a cluster with n nodes and uses a mod-based hasher.
func NewCluster(n int) *pilosa.Cluster {
c := pilosa.NewCluster()
diff --git a/cmd.go b/cmd.go
index 7ff0a877a..d97f7150b 100644
--- a/cmd.go
+++ b/cmd.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import "io"
diff --git a/cmd/backup.go b/cmd/backup.go
index d25b968f4..548781ca6 100644
--- a/cmd/backup.go
+++ b/cmd/backup.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/backup_test.go b/cmd/backup_test.go
index 4e8f8fe43..58d9bf21c 100644
--- a/cmd/backup_test.go
+++ b/cmd/backup_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/bench.go b/cmd/bench.go
index 2dd0d86fc..0ea32d22a 100644
--- a/cmd/bench.go
+++ b/cmd/bench.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/bench_test.go b/cmd/bench_test.go
index db47829ea..4b94d9392 100644
--- a/cmd/bench_test.go
+++ b/cmd/bench_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/check.go b/cmd/check.go
index 914d499fd..56ae13265 100644
--- a/cmd/check.go
+++ b/cmd/check.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/check_test.go b/cmd/check_test.go
index c65e38aa4..a50b48330 100644
--- a/cmd/check_test.go
+++ b/cmd/check_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/config.go b/cmd/config.go
index 8191d5972..589367712 100644
--- a/cmd/config.go
+++ b/cmd/config.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/doc.go b/cmd/doc.go
index f1e0f23e6..17a54043a 100644
--- a/cmd/doc.go
+++ b/cmd/doc.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd contains all the pilosa subcommand definitions (1 per file).
diff --git a/cmd/export.go b/cmd/export.go
index 186759909..310e3f05c 100644
--- a/cmd/export.go
+++ b/cmd/export.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/export_test.go b/cmd/export_test.go
index 45ddc58c6..2b30116c6 100644
--- a/cmd/export_test.go
+++ b/cmd/export_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/import.go b/cmd/import.go
index 43b26e7f8..6fd182f17 100644
--- a/cmd/import.go
+++ b/cmd/import.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/import_test.go b/cmd/import_test.go
index 0251d1894..8117d46fa 100644
--- a/cmd/import_test.go
+++ b/cmd/import_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/inspect.go b/cmd/inspect.go
index b3d19d013..e8526a414 100644
--- a/cmd/inspect.go
+++ b/cmd/inspect.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/inspect_test.go b/cmd/inspect_test.go
index 619f6fbe9..4c2b64d76 100644
--- a/cmd/inspect_test.go
+++ b/cmd/inspect_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/pilosa/main.go b/cmd/pilosa/main.go
index 1947288c7..648b41866 100644
--- a/cmd/pilosa/main.go
+++ b/cmd/pilosa/main.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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.
+
/*
This is the entrypoint for the Pilosa binary.
*/
diff --git a/cmd/restore.go b/cmd/restore.go
index 38a1a3145..80f0bdd74 100644
--- a/cmd/restore.go
+++ b/cmd/restore.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/restore_test.go b/cmd/restore_test.go
index 6f8b5c15b..cdad8ca92 100644
--- a/cmd/restore_test.go
+++ b/cmd/restore_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/root.go b/cmd/root.go
index 7cee0e77f..d50dd05b4 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
@@ -30,7 +44,7 @@ func NewRootCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command {
This binary contains Pilosa itself, as well as common
tools for administering pilosa, importing/exporting data,
backing up, and more. Complete documentation is available
-at http://pilosa.com/docs
+at https://www.pilosa.com/docs/
Version: ` + Version + `
Build Time: ` + BuildTime + "\n",
diff --git a/cmd/root_test.go b/cmd/root_test.go
index d59deb2e6..16bc82a8a 100644
--- a/cmd/root_test.go
+++ b/cmd/root_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/server.go b/cmd/server.go
index f18ee4e69..833e567bb 100644
--- a/cmd/server.go
+++ b/cmd/server.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/server_test.go b/cmd/server_test.go
index edd61c601..c2c50f996 100644
--- a/cmd/server_test.go
+++ b/cmd/server_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/cmd/sort.go b/cmd/sort.go
index 668206070..e74f6cc76 100644
--- a/cmd/sort.go
+++ b/cmd/sort.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd
import (
diff --git a/cmd/sort_test.go b/cmd/sort_test.go
index dbc5e8972..f95332886 100644
--- a/cmd/sort_test.go
+++ b/cmd/sort_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 cmd_test
import (
diff --git a/config.go b/config.go
index 646c77773..ebaf47e33 100644
--- a/config.go
+++ b/config.go
@@ -1,12 +1,32 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import "time"
const (
- // DefaultHost is the default hostname and port to use.
- DefaultHost = "localhost"
- DefaultPort = "10101"
- DefaultClusterType = "static"
+ // DefaultHost is the default hostname to use.
+ DefaultHost = "localhost"
+
+ // DefaultPort is the default port use with the hostname.
+ DefaultPort = "10101"
+
+ // DefaultClusterType sets the node intercommunication method.
+ DefaultClusterType = "static"
+
+ // DefaultInternalPort the port the nodes intercommunicate on.
DefaultInternalPort = "14000"
DefaultMetrics = "nop"
)
@@ -75,6 +95,7 @@ func (d *Duration) UnmarshalText(text []byte) error {
return nil
}
+// MarshalText writes duration value in text format.
func (d Duration) MarshalText() (text []byte, err error) {
return []byte(d.String()), nil
}
diff --git a/ctl/backup.go b/ctl/backup.go
index 369455e7c..7b8465637 100644
--- a/ctl/backup.go
+++ b/ctl/backup.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/bench.go b/ctl/bench.go
index b63d9165f..9e7b5d386 100644
--- a/ctl/bench.go
+++ b/ctl/bench.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/check.go b/ctl/check.go
index 40be60499..34b7033fc 100644
--- a/ctl/check.go
+++ b/ctl/check.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/config.go b/ctl/config.go
index 886240b7b..6de94bf8c 100644
--- a/ctl/config.go
+++ b/ctl/config.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/doc.go b/ctl/doc.go
index 1cc3981c2..65623d1f7 100644
--- a/ctl/doc.go
+++ b/ctl/doc.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl contains all pilosa subcommands other than 'server'. These are
// generally administration, testing, and debugging tools.
package ctl
diff --git a/ctl/export.go b/ctl/export.go
index 6bbf1ac0e..8f67b7896 100644
--- a/ctl/export.go
+++ b/ctl/export.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/import.go b/ctl/import.go
index cefe2e45b..55337d980 100644
--- a/ctl/import.go
+++ b/ctl/import.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/inspect.go b/ctl/inspect.go
index fa99ba464..f5825c35a 100644
--- a/ctl/inspect.go
+++ b/ctl/inspect.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/restore.go b/ctl/restore.go
index ecfcf5f5a..676e81331 100644
--- a/ctl/restore.go
+++ b/ctl/restore.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/ctl/sort.go b/ctl/sort.go
index 5590d2538..2d28e02c4 100644
--- a/ctl/sort.go
+++ b/ctl/sort.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 ctl
import (
diff --git a/datadog/datadog.go b/datadog/datadog.go
index d4cf1df65..5f260106f 100644
--- a/datadog/datadog.go
+++ b/datadog/datadog.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 datadog
import (
diff --git a/datadog/datadog_test.go b/datadog/datadog_test.go
index b5a8bf5ee..5de60165e 100644
--- a/datadog/datadog_test.go
+++ b/datadog/datadog_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 datadog_test
import (
diff --git a/doc.go b/doc.go
index e2265bef6..9afa1a532 100644
--- a/doc.go
+++ b/doc.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa implements the core of the Pilosa distributed bitmap index. It
diff --git a/executor.go b/executor.go
index e828100b8..a2c5ab8f1 100644
--- a/executor.go
+++ b/executor.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -56,17 +70,41 @@ func (e *Executor) Execute(ctx context.Context, index string, q *pql.Query, slic
opt = &ExecOptions{}
}
+ // Don't bother calculating slices for query types that don't require it.
+ needsSlices := needsSlices(q.Calls)
+
+ // MaxSlice can differ between inverse and standard views, so we need
+ // to send queries to different slices based on orientation.
+ var inverseSlices []uint64
+ rowLabel := DefaultRowLabel
+ columnLabel := DefaultColumnLabel
+
// If slices aren't specified, then include all of them.
if len(slices) == 0 {
- if needsSlices(q.Calls) {
+ // Determine slices and inverseSlices for use in e.executeCall().
+ if needsSlices {
// Round up the number of slices.
maxSlice := e.Holder.Index(index).MaxSlice()
+ maxInverseSlice := e.Holder.Index(index).MaxInverseSlice()
// Generate a slices of all slices.
slices = make([]uint64, maxSlice+1)
for i := range slices {
slices[i] = uint64(i)
}
+
+ // Generate a slices of all inverse slices.
+ inverseSlices = make([]uint64, maxInverseSlice+1)
+ for i := range inverseSlices {
+ inverseSlices[i] = uint64(i)
+ }
+
+ // Fetch column label from index.
+ idx := e.Holder.Index(index)
+ if idx == nil {
+ return nil, ErrIndexNotFound
+ }
+ columnLabel = idx.ColumnLabel()
}
}
@@ -78,6 +116,25 @@ func (e *Executor) Execute(ctx context.Context, index string, q *pql.Query, slic
// Execute each call serially.
results := make([]interface{}, 0, len(q.Calls))
for _, call := range q.Calls {
+
+ if call.SupportsInverse() && needsSlices {
+ // Fetch frame & row label based on argument.
+ frame, _ := call.Args["frame"].(string)
+ if frame == "" {
+ frame = DefaultFrame
+ }
+ f := e.Holder.Frame(index, frame)
+ if f == nil {
+ return nil, ErrFrameNotFound
+ }
+ rowLabel = f.RowLabel()
+
+ // If this call is to an inverse frame send to a different list of slices.
+ if call.IsInverse(rowLabel, columnLabel) {
+ slices = inverseSlices
+ }
+ }
+
v, err := e.executeCall(ctx, index, call, slices, opt)
if err != nil {
return nil, err
@@ -762,7 +819,7 @@ func (e *Executor) executeSetRowAttrs(ctx context.Context, index string, c *pql.
if err != nil {
return fmt.Errorf("reading SetRowAttrs() row: %v", err)
} else if !ok {
- return fmt.Errorf("SetRowAttrs() row field '%v' required.", rowLabel)
+ return fmt.Errorf("SetRowAttrs() row field '%v' required", rowLabel)
}
// Copy args and remove reserved fields.
@@ -822,7 +879,7 @@ func (e *Executor) executeBulkSetRowAttrs(ctx context.Context, index string, cal
if err != nil {
return nil, fmt.Errorf("reading SetRowAttrs() row: %v", rowLabel)
} else if !ok {
- return nil, fmt.Errorf("SetRowAttrs row field '%v' required.", rowLabel)
+ return nil, fmt.Errorf("SetRowAttrs row field '%v' required", rowLabel)
}
// Copy args and remove reserved fields.
diff --git a/executor_test.go b/executor_test.go
index 8fed950b4..084511fb0 100644
--- a/executor_test.go
+++ b/executor_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
@@ -27,9 +41,9 @@ func TestExecutor_Execute_Bitmap(t *testing.T) {
// Set bits.
if _, err := e.Execute(context.Background(), "i", MustParse(``+
- fmt.Sprintf("SetBit(frame=f, id=%d, columnID=%d)\n", 10, 3)+
- fmt.Sprintf("SetBit(frame=f, id=%d, columnID=%d)\n", 10, SliceWidth+1)+
- fmt.Sprintf("SetBit(frame=f, id=%d, columnID=%d)\n", 20, SliceWidth+1),
+ fmt.Sprintf("SetBit(frame=f, rowID=%d, columnID=%d)\n", 10, 3)+
+ fmt.Sprintf("SetBit(frame=f, rowID=%d, columnID=%d)\n", 10, SliceWidth+1)+
+ fmt.Sprintf("SetBit(frame=f, rowID=%d, columnID=%d)\n", 20, SliceWidth+1),
), nil, nil); err != nil {
t.Fatal(err)
}
@@ -37,7 +51,7 @@ func TestExecutor_Execute_Bitmap(t *testing.T) {
t.Fatal(err)
}
- if res, err := e.Execute(context.Background(), "i", MustParse(`Bitmap(id=10, frame=f)`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Bitmap(rowID=10, frame=f)`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{3, SliceWidth + 1}) {
t.Fatalf("unexpected bits: %+v", bits)
@@ -58,9 +72,9 @@ func TestExecutor_Execute_Bitmap(t *testing.T) {
// Set bits.
if _, err := e.Execute(context.Background(), "i", MustParse(``+
- fmt.Sprintf("SetBit(frame=f, id=%d, columnID=%d)\n", 10, 3)+
- fmt.Sprintf("SetBit(frame=f, id=%d, columnID=%d)\n", 10, SliceWidth+1)+
- fmt.Sprintf("SetBit(frame=f, id=%d, columnID=%d)\n", 20, SliceWidth+1),
+ fmt.Sprintf("SetBit(frame=f, rowID=%d, columnID=%d)\n", 10, 3)+
+ fmt.Sprintf("SetBit(frame=f, rowID=%d, columnID=%d)\n", 10, SliceWidth+1)+
+ fmt.Sprintf("SetBit(frame=f, rowID=%d, columnID=%d)\n", 20, SliceWidth+1),
), nil, nil); err != nil {
t.Fatal(err)
}
@@ -89,7 +103,7 @@ func TestExecutor_Execute_Difference(t *testing.T) {
hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 0).MustSetBits(11, 4)
e := NewExecutor(hldr.Holder, NewCluster(1))
- if res, err := e.Execute(context.Background(), "i", MustParse(`Difference(Bitmap(id=10), Bitmap(id=11))`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Difference(Bitmap(rowID=10), Bitmap(rowID=11))`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{1, 3}) {
t.Fatalf("unexpected bits: %+v", bits)
@@ -121,7 +135,7 @@ func TestExecutor_Execute_Intersect(t *testing.T) {
hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 1).MustSetBits(11, SliceWidth+2)
e := NewExecutor(hldr.Holder, NewCluster(1))
- if res, err := e.Execute(context.Background(), "i", MustParse(`Intersect(Bitmap(id=10), Bitmap(id=11))`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Intersect(Bitmap(rowID=10), Bitmap(rowID=11))`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{1, SliceWidth + 2}) {
t.Fatalf("unexpected bits: %+v", bits)
@@ -151,7 +165,7 @@ func TestExecutor_Execute_Union(t *testing.T) {
hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 1).MustSetBits(11, SliceWidth+2)
e := NewExecutor(hldr.Holder, NewCluster(1))
- if res, err := e.Execute(context.Background(), "i", MustParse(`Union(Bitmap(id=10), Bitmap(id=11))`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Union(Bitmap(rowID=10), Bitmap(rowID=11))`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{0, 2, SliceWidth + 1, SliceWidth + 2}) {
t.Fatalf("unexpected bits: %+v", bits)
@@ -181,7 +195,7 @@ func TestExecutor_Execute_Count(t *testing.T) {
hldr.MustCreateFragmentIfNotExists("i", "f", pilosa.ViewStandard, 1).MustSetBits(10, SliceWidth+2)
e := NewExecutor(hldr.Holder, NewCluster(1))
- if res, err := e.Execute(context.Background(), "i", MustParse(`Count(Bitmap(id=10, frame=f))`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Count(Bitmap(rowID=10, frame=f))`), nil, nil); err != nil {
t.Fatal(err)
} else if res[0] != uint64(3) {
t.Fatalf("unexpected n: %d", res[0])
@@ -199,7 +213,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
t.Fatalf("unexpected bitmap count: %d", n)
}
- if res, err := e.Execute(context.Background(), "i", MustParse(`SetBit(id=11, frame=f, columnID=1)`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`SetBit(rowID=11, frame=f, columnID=1)`), nil, nil); err != nil {
t.Fatal(err)
} else {
if !res[0].(bool) {
@@ -210,7 +224,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
if n := f.Row(11).Count(); n != 1 {
t.Fatalf("unexpected bitmap count: %d", n)
}
- if res, err := e.Execute(context.Background(), "i", MustParse(`SetBit(id=11, frame=f, columnID=1)`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`SetBit(rowID=11, frame=f, columnID=1)`), nil, nil); err != nil {
t.Fatal(err)
} else {
if res[0].(bool) {
@@ -235,16 +249,16 @@ func TestExecutor_Execute_SetRowAttrs(t *testing.T) {
// Set two fields on f/10.
// Also set fields on other bitmaps and frames to test isolation.
e := NewExecutor(hldr.Holder, NewCluster(1))
- if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(id=10, frame=f, foo="bar")`), nil, nil); err != nil {
+ if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(rowID=10, frame=f, foo="bar")`), nil, nil); err != nil {
t.Fatal(err)
}
- if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(id=200, frame=f, YYY=1)`), nil, nil); err != nil {
+ if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(rowID=200, frame=f, YYY=1)`), nil, nil); err != nil {
t.Fatal(err)
}
- if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(id=10, frame=xxx, YYY=1)`), nil, nil); err != nil {
+ if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(rowID=10, frame=xxx, YYY=1)`), nil, nil); err != nil {
t.Fatal(err)
}
- if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(id=10, frame=f, baz=123, bat=true)`), nil, nil); err != nil {
+ if _, err := e.Execute(context.Background(), "i", MustParse(`SetRowAttrs(rowID=10, frame=f, baz=123, bat=true)`), nil, nil); err != nil {
t.Fatal(err)
}
@@ -362,7 +376,7 @@ func TestExecutor_Execute_TopN_Src(t *testing.T) {
// Execute query.
e := NewExecutor(hldr.Holder, NewCluster(1))
- if result, err := e.Execute(context.Background(), "i", MustParse(`TopN(Bitmap(id=100, frame=other), frame=f, n=3)`), nil, nil); err != nil {
+ if result, err := e.Execute(context.Background(), "i", MustParse(`TopN(Bitmap(rowID=100, frame=other), frame=f, n=3)`), nil, nil); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result, []interface{}{[]pilosa.Pair{
{ID: 20, Count: 3},
@@ -409,7 +423,7 @@ func TestExecutor_Execute_TopN_Attr_Src(t *testing.T) {
t.Fatal(err)
}
e := NewExecutor(hldr.Holder, NewCluster(1))
- if result, err := e.Execute(context.Background(), "i", MustParse(`TopN(Bitmap(id=10,frame=f),frame="f", n=1, field="category", filters=[123])`), nil, nil); err != nil {
+ if result, err := e.Execute(context.Background(), "i", MustParse(`TopN(Bitmap(rowID=10,frame=f),frame="f", n=1, field="category", filters=[123])`), nil, nil); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result, []interface{}{[]pilosa.Pair{
{ID: 10, Count: 1},
@@ -448,7 +462,7 @@ func TestExecutor_Execute_Range(t *testing.T) {
f.MustSetBit(pilosa.ViewStandard, 10, 2, MustParseTimePtr("2001-01-01 00:00")) // different row
e := NewExecutor(hldr.Holder, NewCluster(1))
- if res, err := e.Execute(context.Background(), "i", MustParse(`Range(id=1, frame=f, start="1999-12-31T00:00", end="2002-01-01T03:00")`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Range(rowID=1, frame=f, start="1999-12-31T00:00", end="2002-01-01T03:00")`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{2, 3, 4, 5, 6, 7}) {
t.Fatalf("unexpected bits: %+v", bits)
@@ -468,7 +482,7 @@ func TestExecutor_Execute_Remote_Bitmap(t *testing.T) {
s.Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, slices []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
if index != "i" {
t.Fatalf("unexpected index: %s", index)
- } else if query.String() != `Bitmap(frame="f", id=10)` {
+ } else if query.String() != `Bitmap(frame="f", rowID=10)` {
t.Fatalf("unexpected query: %s", query.String())
} else if !reflect.DeepEqual(slices, []uint64{1}) {
t.Fatalf("unexpected slices: %+v", slices)
@@ -490,7 +504,7 @@ func TestExecutor_Execute_Remote_Bitmap(t *testing.T) {
hldr.MustCreateFragmentIfNotExists("i", "f", pilosa.ViewStandard, 1).MustSetBits(10, (1*SliceWidth)+1)
e := NewExecutor(hldr.Holder, c)
- if res, err := e.Execute(context.Background(), "i", MustParse(`Bitmap(id=10, frame=f)`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Bitmap(rowID=10, frame=f)`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{1, 2, 2*SliceWidth + 4}) {
t.Fatalf("unexpected bits: %+v", bits)
@@ -518,7 +532,7 @@ func TestExecutor_Execute_Remote_Count(t *testing.T) {
hldr.MustCreateFragmentIfNotExists("i", "f", pilosa.ViewStandard, 2).MustSetBits(10, (2*SliceWidth)+2)
e := NewExecutor(hldr.Holder, c)
- if res, err := e.Execute(context.Background(), "i", MustParse(`Count(Bitmap(id=10, frame=f))`), nil, nil); err != nil {
+ if res, err := e.Execute(context.Background(), "i", MustParse(`Count(Bitmap(rowID=10, frame=f))`), nil, nil); err != nil {
t.Fatal(err)
} else if res[0] != uint64(12) {
t.Fatalf("unexpected n: %d", res[0])
@@ -540,7 +554,7 @@ func TestExecutor_Execute_Remote_SetBit(t *testing.T) {
s.Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, slices []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
if index != `i` {
t.Fatalf("unexpected index: %s", index)
- } else if query.String() != `SetBit(columnID=2, frame="f", id=10)` {
+ } else if query.String() != `SetBit(columnID=2, frame="f", rowID=10)` {
t.Fatalf("unexpected query: %s", query.String())
}
remoteCalled = true
@@ -557,7 +571,7 @@ func TestExecutor_Execute_Remote_SetBit(t *testing.T) {
}
e := NewExecutor(hldr.Holder, c)
- if _, err := e.Execute(context.Background(), "i", MustParse(`SetBit(id=10, frame=f, columnID=2)`), nil, nil); err != nil {
+ if _, err := e.Execute(context.Background(), "i", MustParse(`SetBit(rowID=10, frame=f, columnID=2)`), nil, nil); err != nil {
t.Fatal(err)
}
@@ -585,7 +599,7 @@ func TestExecutor_Execute_Remote_SetBit_With_Timestamp(t *testing.T) {
s.Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, slices []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
if index != `i` {
t.Fatalf("unexpected index: %s", index)
- } else if query.String() != `SetBit(columnID=2, frame="f", id=10, timestamp="2016-12-11T10:09")` {
+ } else if query.String() != `SetBit(columnID=2, frame="f", rowID=10, timestamp="2016-12-11T10:09")` {
t.Fatalf("unexpected query: %s", query.String())
}
remoteCalled = true
@@ -604,7 +618,7 @@ func TestExecutor_Execute_Remote_SetBit_With_Timestamp(t *testing.T) {
}
e := NewExecutor(hldr.Holder, c)
- if _, err := e.Execute(context.Background(), "i", MustParse(`SetBit(id=10, frame=f, columnID=2, timestamp="2016-12-11T10:09")`), nil, nil); err != nil {
+ if _, err := e.Execute(context.Background(), "i", MustParse(`SetBit(rowID=10, frame=f, columnID=2, timestamp="2016-12-11T10:09")`), nil, nil); err != nil {
t.Fatal(err)
}
diff --git a/fragment.go b/fragment.go
index 092f8bd8d..1d8df780f 100644
--- a/fragment.go
+++ b/fragment.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -1447,10 +1461,10 @@ func (s *FragmentSyncer) syncBlock(id int) error {
// Only sync the standard block.
for j := 0; j < len(set.ColumnIDs); j++ {
- fmt.Fprintf(&buf, "SetBit(frame=%q, id=%d, columnID=%d)\n", f.Frame(), set.RowIDs[j], (f.Slice()*SliceWidth)+set.ColumnIDs[j])
+ fmt.Fprintf(&buf, "SetBit(frame=%q, rowID=%d, columnID=%d)\n", f.Frame(), set.RowIDs[j], (f.Slice()*SliceWidth)+set.ColumnIDs[j])
}
for j := 0; j < len(clear.ColumnIDs); j++ {
- fmt.Fprintf(&buf, "ClearBit(frame=%q, id=%d, columnID=%d)\n", f.Frame(), clear.RowIDs[j], (f.Slice()*SliceWidth)+clear.ColumnIDs[j])
+ fmt.Fprintf(&buf, "ClearBit(frame=%q, rowID=%d, columnID=%d)\n", f.Frame(), clear.RowIDs[j], (f.Slice()*SliceWidth)+clear.ColumnIDs[j])
}
// Verify sync is not prematurely closing.
diff --git a/fragment_test.go b/fragment_test.go
index f4d3fc247..5f3da53e2 100644
--- a/fragment_test.go
+++ b/fragment_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/frame.go b/frame.go
index f6af6e80c..6afff11de 100644
--- a/frame.go
+++ b/frame.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -17,7 +31,7 @@ import (
// Default frame settings.
const (
- DefaultRowLabel = "id"
+ DefaultRowLabel = "rowID"
DefaultCacheType = CacheTypeLRU
DefaultInverseEnabled = false
@@ -67,7 +81,8 @@ func NewFrame(path, index, name string) (*Frame, error) {
views: make(map[string]*View),
rowAttrStore: NewAttrStore(filepath.Join(path, ".data")),
- Stats: NopStatsClient,
+ broadcaster: NopBroadcaster,
+ Stats: NopStatsClient,
rowLabel: DefaultRowLabel,
inverseEnabled: DefaultInverseEnabled,
@@ -125,7 +140,7 @@ func (f *Frame) SetRowLabel(v string) error {
}
// Make sure rowLabel is valid name
- err := ValidateName(v)
+ err := ValidateLabel(v)
if err != nil {
return err
}
@@ -423,6 +438,7 @@ func (f *Frame) newView(path, name string) *View {
view.LogOutput = f.LogOutput
view.RowAttrStore = f.rowAttrStore
view.stats = f.Stats.WithTags(fmt.Sprintf("view:%s", name))
+ view.broadcaster = f.broadcaster
return view
}
@@ -529,6 +545,9 @@ func (f *Frame) Import(rowIDs, columnIDs []uint64, timestamps []*time.Time) erro
inverse = []string{ViewInverse}
} else {
standard = ViewsByTime(ViewStandard, *timestamp, q)
+ // In order to match the logic of `SetBit()`, we want bits
+ // with timestamps to write to both time and standard views.
+ standard = append(standard, ViewStandard)
inverse = ViewsByTime(ViewInverse, *timestamp, q)
}
diff --git a/frame_test.go b/frame_test.go
index 669ffcc40..fc5c01570 100644
--- a/frame_test.go
+++ b/frame_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
@@ -66,6 +80,88 @@ func TestFrame_NameRestriction(t *testing.T) {
}
}
+// Ensure that frame name validation is consistent.
+func TestFrame_NameValidation(t *testing.T) {
+ validFrameNames := []string{
+ "foo",
+ "hyphen-ated",
+ "under_score",
+ "abc123",
+ "trailing_",
+ }
+ invalidFrameNames := []string{
+ "",
+ "123abc",
+ "x.y",
+ "_foo",
+ "-bar",
+ "abc def",
+ "camelCase",
+ "UPPERCASE",
+ "a12345678901234567890123456789012345678901234567890123456789012345",
+ }
+
+ path, err := ioutil.TempDir("", "pilosa-frame-")
+ if err != nil {
+ panic(err)
+ }
+ for _, name := range validFrameNames {
+ _, err := pilosa.NewFrame(path, "i", name)
+ if err != nil {
+ t.Fatalf("unexpected frame name: %s %s", name, err)
+ }
+ }
+ for _, name := range invalidFrameNames {
+ _, err := pilosa.NewFrame(path, "i", name)
+ if err == nil {
+ t.Fatalf("expected error on frame name: %s", name)
+ }
+ }
+}
+
+// Ensure that frame RowLable validation is consistent.
+func TestFrame_RowLabelValidation(t *testing.T) {
+ validRowLabels := []string{
+ "",
+ "foo",
+ "hyphen-ated",
+ "under_score",
+ "abc123",
+ "trailing_",
+ "camelCase",
+ "UPPERCASE",
+ }
+ invalidRowLabels := []string{
+ "123abc",
+ "x.y",
+ "_foo",
+ "-bar",
+ "abc def",
+ "a12345678901234567890123456789012345678901234567890123456789012345",
+ }
+
+ path, err := ioutil.TempDir("", "pilosa-frame-")
+ if err != nil {
+ panic(err)
+ }
+ f, err := pilosa.NewFrame(path, "i", "f")
+ if err != nil {
+ t.Fatalf("unexpected frame error: %s", err)
+ }
+
+ for _, label := range validRowLabels {
+ if err := f.SetRowLabel(label); err != nil {
+ t.Fatalf("unexpected row label: %s %s", label, err)
+ }
+ }
+ for _, label := range invalidRowLabels {
+ if err := f.SetRowLabel(label); err == nil {
+ t.Fatalf("expected error on row label: %s", label)
+ }
+ }
+
+}
+
// Frame represents a test wrapper for pilosa.Frame.
type Frame struct {
*pilosa.Frame
diff --git a/glide.lock b/glide.lock
index 7ba5cdab6..a615eac54 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,5 +1,5 @@
-hash: a6889bf5334164aa4aebd670eee45444f1bdfa1888642d290acf33e18e57f293
-updated: 2017-04-19T17:19:43.584486333-05:00
+hash: 5066da70d6f312d032800619798684a693851eb77991b3788f68e89dfaf93925
+updated: 2017-04-25T15:31:49.712775923-05:00
imports:
- name: github.com/armon/go-metrics
version: 97c69685293dce4c0a2d0b19535179bbc976e4d2
@@ -7,8 +7,6 @@ imports:
version: 4b1ebc1869ad66568b313d0dc410e2be72670dda
- name: github.com/BurntSushi/toml
version: 99064174e013895bbd9b025c31100bd1d9b590ca
-- name: github.com/CAFxX/gcnotifier
- version: adea3e70515666981da25214a7d3e377e4841c22
- name: github.com/DataDog/datadog-go
version: 909c02b65dd8a52e8fa6072db9752a112227cf21
subpackages:
@@ -48,10 +46,10 @@ imports:
subpackages:
- hcl/ast
- hcl/parser
- - hcl/token
- - json/parser
- hcl/scanner
- hcl/strconv
+ - hcl/token
+ - json/parser
- json/scanner
- json/token
- name: github.com/hashicorp/memberlist
@@ -68,6 +66,10 @@ imports:
version: c37440a7cf42ac63b919c752ca73a85067e05992
- name: github.com/pelletier/go-toml
version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a
+- name: github.com/rakyll/statik
+ version: 89fe3459b5c829c32e89bdff9c43f18aad728f2f
+ subpackages:
+ - fs
- name: github.com/satori/go.uuid
version: 879c5887cd475cd7864858769793b2ceb0d44feb
- name: github.com/spf13/afero
@@ -103,4 +105,4 @@ imports:
- unicode/norm
- name: gopkg.in/yaml.v2
version: a3f3340b5840cee44f372bddb5880fcbc419b46a
-devImports: []
+testImports: []
diff --git a/glide.yaml b/glide.yaml
index 4b1ff6038..32421ffad 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -31,6 +31,10 @@ import:
- package: github.com/spf13/viper
- package: github.com/gorilla/mux
version: ^1.3.0
+- package: github.com/rakyll/statik
+ version: 89fe3459b5c829c32e89bdff9c43f18aad728f2f
+ subpackages:
+ - fs
- package: github.com/hashicorp/memberlist
- package: golang.org/x/sync
- package: github.com/CAFxX/gcnotifier
diff --git a/gossip/gossip.go b/gossip/gossip.go
index c3ded776a..1b15522a7 100644
--- a/gossip/gossip.go
+++ b/gossip/gossip.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 gossip
import (
@@ -24,12 +38,13 @@ type GossipNodeSet struct {
broadcasts *memberlist.TransmitLimitedQueue
statusHandler pilosa.StatusHandler
- config *GossipConfig
+ config *gossipConfig
// The writer for any logging.
LogOutput io.Writer
}
+// Nodes implements the NodeSet interface and returns a list of nodes in the cluster.
func (g *GossipNodeSet) Nodes() []*pilosa.Node {
a := make([]*pilosa.Node, 0, g.memberlist.NumMembers())
for _, n := range g.memberlist.Members() {
@@ -38,11 +53,13 @@ func (g *GossipNodeSet) Nodes() []*pilosa.Node {
return a
}
+// Start implements the BroadcastReceiver interface and sets the BroadcastHandler
func (g *GossipNodeSet) Start(h pilosa.BroadcastHandler) error {
g.handler = h
return nil
}
+// Open implements the NodeSet interface to start network activity.
func (g *GossipNodeSet) Open() error {
if g.handler == nil {
return fmt.Errorf("opening GossipNodeSet: you must call Start(pilosa.BroadcastHandler) before calling Open()")
@@ -75,7 +92,7 @@ func (g *GossipNodeSet) logger() *log.Logger {
////////////////////////////////////////////////////////////////
-type GossipConfig struct {
+type gossipConfig struct {
gossipSeed string
memberlistConfig *memberlist.Config
}
@@ -87,7 +104,7 @@ func NewGossipNodeSet(name string, gossipHost string, gossipPort int, gossipSeed
}
//TODO: pull memberlist config from pilosa.cfg file
- g.config = &GossipConfig{
+ g.config = &gossipConfig{
memberlistConfig: memberlist.DefaultLocalConfig(),
gossipSeed: gossipSeed,
}
@@ -103,7 +120,7 @@ func NewGossipNodeSet(name string, gossipHost string, gossipPort int, gossipSeed
return g
}
-// SendSync implementation of the Broadcaster interface
+// SendSync implementation of the Broadcaster interface.
func (g *GossipNodeSet) SendSync(pb proto.Message) error {
msg, err := pilosa.MarshalMessage(pb)
if err != nil {
@@ -131,7 +148,7 @@ func (g *GossipNodeSet) SendSync(pb proto.Message) error {
return eg.Wait()
}
-// SendAsync implementation of the Broadcaster interface
+// SendAsync implementation of the Broadcaster interface.
func (g *GossipNodeSet) SendAsync(pb proto.Message) error {
msg, err := pilosa.MarshalMessage(pb)
if err != nil {
@@ -146,11 +163,13 @@ func (g *GossipNodeSet) SendAsync(pb proto.Message) error {
return nil
}
-// implementation of the memberlist.Delegate interface
+// NodeMeta implementation of the memberlist.Delegate interface.
func (g *GossipNodeSet) NodeMeta(limit int) []byte {
return []byte{}
}
+// NotifyMsg implementation of the memberlist.Delegate interface
+// called when a user-data message is received.
func (g *GossipNodeSet) NotifyMsg(b []byte) {
m, err := pilosa.UnmarshalMessage(b)
if err != nil {
@@ -163,10 +182,14 @@ func (g *GossipNodeSet) NotifyMsg(b []byte) {
}
}
+// GetBroadcasts implementation of the memberlist.Delegate interface
+// called when user data messages can be broadcast.
func (g *GossipNodeSet) GetBroadcasts(overhead, limit int) [][]byte {
return g.broadcasts.GetBroadcasts(overhead, limit)
}
+// LocalState implementation of the memberlist.Delegate interface
+// sends this Node's state data.
func (g *GossipNodeSet) LocalState(join bool) []byte {
pb, err := g.statusHandler.LocalStatus()
if err != nil {
@@ -183,6 +206,8 @@ func (g *GossipNodeSet) LocalState(join bool) []byte {
return buf
}
+// MergeRemoteState implementation of the memberlist.Delegate interface
+// receive and process the remote side side's LocalState.
func (g *GossipNodeSet) MergeRemoteState(buf []byte, join bool) {
// Unmarshal nodestate data.
var pb internal.NodeStatus
diff --git a/handler.go b/handler.go
index 1d2faf89f..3f7bb8845 100644
--- a/handler.go
+++ b/handler.go
@@ -1,3 +1,19 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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.
+
+//go:generate statik -src=./webui
+
package pilosa
import (
@@ -23,6 +39,9 @@ import (
"github.com/gorilla/mux"
"github.com/pilosa/pilosa/internal"
"github.com/pilosa/pilosa/pql"
+
+ _ "github.com/pilosa/pilosa/statik"
+ "github.com/rakyll/statik/fs"
)
// Handler represents an HTTP handler.
@@ -58,8 +77,11 @@ func NewHandler() *Handler {
return handler
}
+// NewRouter creates a Gorilla Mux http router.
func NewRouter(handler *Handler) *mux.Router {
router := mux.NewRouter()
+ router.HandleFunc("/", handler.handleWebUI).Methods("GET")
+ router.HandleFunc("/assets/{file}", handler.handleWebUI).Methods("GET")
router.HandleFunc("/index", handler.handleGetIndexes).Methods("GET")
router.HandleFunc("/index/{index}", handler.handleGetIndex).Methods("GET")
router.HandleFunc("/index/{index}", handler.handlePostIndex).Methods("POST")
@@ -83,7 +105,7 @@ func NewRouter(handler *Handler) *mux.Router {
router.HandleFunc("/fragment/data", handler.handlePostFragmentData).Methods("POST")
router.HandleFunc("/fragment/nodes", handler.handleGetFragmentNodes).Methods("GET")
router.HandleFunc("/import", handler.handlePostImport).Methods("POST")
- router.HandleFunc("/nodes", handler.handleGetNodes).Methods("GET")
+ router.HandleFunc("/hosts", handler.handleGetHosts).Methods("GET")
router.HandleFunc("/schema", handler.handleGetSchema).Methods("GET")
router.HandleFunc("/slices/max", handler.handleGetSliceMax).Methods("GET")
router.HandleFunc("/status", handler.handleGetStatus).Methods("GET")
@@ -107,6 +129,21 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.Router.ServeHTTP(w, r)
}
+func (h *Handler) handleWebUI(w http.ResponseWriter, r *http.Request) {
+ // If user is using curl, don't chuck HTML at them
+ if strings.HasPrefix(r.UserAgent(), "curl") {
+ http.Error(w, "Welcome. Pilosa is running. Visit https://www.pilosa.com/docs/ for more information or try the WebUI by visiting this URL in your browser.", http.StatusNotFound)
+ return
+ }
+ statikFS, err := fs.New()
+ if err != nil {
+ h.writeQueryResponse(w, r, &QueryResponse{Err: err})
+ fmt.Println("Pilosa WebUI is not available. Please run `make generate-statik` before building Pilosa with `make install`.")
+ return
+ }
+ http.FileServer(statikFS).ServeHTTP(w, r)
+}
+
// handleGetSchema handles GET /schema requests.
func (h *Handler) handleGetSchema(w http.ResponseWriter, r *http.Request) {
if err := json.NewEncoder(w).Encode(getSchemaResponse{
@@ -1237,8 +1274,8 @@ func (h *Handler) handlePostFrameRestore(w http.ResponseWriter, r *http.Request)
}
}
-// handleGetNodes handles /nodes requests.
-func (h *Handler) handleGetNodes(w http.ResponseWriter, r *http.Request) {
+// handleGetHosts handles /hosts requests.
+func (h *Handler) handleGetHosts(w http.ResponseWriter, r *http.Request) {
if err := json.NewEncoder(w).Encode(h.Cluster.Nodes); err != nil {
h.logger().Printf("write version response error: %s", err)
}
@@ -1324,6 +1361,7 @@ type QueryResponse struct {
Err error
}
+// MarshalJSON marshals QueryResponse into a JSON-encoded byte slice
func (resp *QueryResponse) MarshalJSON() ([]byte, error) {
var output struct {
Results []interface{} `json:"results,omitempty"`
diff --git a/handler_internal_test.go b/handler_internal_test.go
index f749c23aa..169267c38 100644
--- a/handler_internal_test.go
+++ b/handler_internal_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
diff --git a/handler_test.go b/handler_test.go
index d0b467f90..c2aa50927 100644
--- a/handler_test.go
+++ b/handler_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/holder.go b/holder.go
index 8e71ebe5b..59fef1560 100644
--- a/holder.go
+++ b/holder.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -46,7 +60,8 @@ func NewHolder() *Holder {
indexes: make(map[string]*Index),
closing: make(chan struct{}, 0),
- Stats: NopStatsClient,
+ Broadcaster: NopBroadcaster,
+ Stats: NopStatsClient,
CacheFlushInterval: DefaultCacheFlushInterval,
@@ -350,7 +365,7 @@ type HolderSyncer struct {
Closing <-chan struct{}
}
-// Returns true if the syncer has been marked to close.
+// IsClosing returns true if the syncer has been marked to close.
func (s *HolderSyncer) IsClosing() bool {
select {
case <-s.Closing:
diff --git a/holder_test.go b/holder_test.go
index 78c4700f8..d7c90fccd 100644
--- a/holder_test.go
+++ b/holder_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/httpbroadcast/messenger.go b/httpbroadcast/messenger.go
index ebd4b2e6a..387f28585 100644
--- a/httpbroadcast/messenger.go
+++ b/httpbroadcast/messenger.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 httpbroadcast
import (
@@ -62,11 +76,11 @@ func (h *HTTPBroadcaster) SendAsync(pb proto.Message) error {
func (h *HTTPBroadcaster) nodes() ([]*pilosa.Node, error) {
if h.server == nil {
- return nil, errors.New("HTTPBroadcaster has no reference to Server.")
+ return nil, errors.New("HTTPBroadcaster has no reference to Server")
}
nodeset, ok := h.server.Cluster.NodeSet.(*HTTPNodeSet)
if !ok {
- return nil, errors.New("NodeSet cannot be caste to HTTPNodeSet.")
+ return nil, errors.New("NodeSet cannot be caste to HTTPNodeSet")
}
return nodeset.Nodes(), nil
}
@@ -106,12 +120,14 @@ func (h *HTTPBroadcaster) sendNodeMessage(node *pilosa.Node, msg []byte) error {
return nil
}
+// HTTPBroadcastReceiver unmarshals incoming messages over HTTP and passes them on to the handler.
type HTTPBroadcastReceiver struct {
port string
handler pilosa.BroadcastHandler
logOutput io.Writer
}
+// NewHTTPBroadcastReceiver returns a new instance of HTTPBroadcastReceiver.
func NewHTTPBroadcastReceiver(port string, logOutput io.Writer) *HTTPBroadcastReceiver {
return &HTTPBroadcastReceiver{
port: port,
@@ -119,6 +135,7 @@ func NewHTTPBroadcastReceiver(port string, logOutput io.Writer) *HTTPBroadcastRe
}
}
+// Start implements the BroadcastReceiver interface and starts listening for broadcast messages.
func (rec *HTTPBroadcastReceiver) Start(b pilosa.BroadcastHandler) error {
rec.handler = b
go func() {
@@ -166,14 +183,17 @@ func NewHTTPNodeSet() *HTTPNodeSet {
return &HTTPNodeSet{}
}
+// Nodes implements the NodeSet interface and returns a list of nodes in the cluster.
func (h *HTTPNodeSet) Nodes() []*pilosa.Node {
return h.nodes
}
+// Open implements the NodeSet interface to start network activity, but for a HTTPNodeSet it does nothing.
func (h *HTTPNodeSet) Open() error {
return nil
}
+// Join sets the NodeSet nodes to the slice of Nodes passed in.
func (h *HTTPNodeSet) Join(nodes []*pilosa.Node) error {
h.nodes = nodes
return nil
diff --git a/index.go b/index.go
index 516064f64..e400b19ce 100644
--- a/index.go
+++ b/index.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -68,8 +82,9 @@ func NewIndex(path, name string) (*Index, error) {
columnLabel: DefaultColumnLabel,
- Stats: NopStatsClient,
- LogOutput: ioutil.Discard,
+ broadcaster: NopBroadcaster,
+ Stats: NopStatsClient,
+ LogOutput: ioutil.Discard,
}, nil
}
@@ -93,7 +108,7 @@ func (i *Index) SetColumnLabel(v string) error {
}
// Make sure columnLabel is valid name
- err := ValidateName(v)
+ err := ValidateLabel(v)
if err != nil {
return err
}
@@ -248,6 +263,7 @@ func (i *Index) MaxSlice() uint64 {
return max
}
+// SetRemoteMaxSlice sets the remote max slice value received from another node.
func (i *Index) SetRemoteMaxSlice(newmax uint64) {
i.mu.Lock()
defer i.mu.Unlock()
@@ -271,6 +287,7 @@ func (i *Index) MaxInverseSlice() uint64 {
return max
}
+// SetRemoteMaxInverseSlice sets the remote max inverse slice value received from another node.
func (i *Index) SetRemoteMaxInverseSlice(v uint64) {
i.mu.Lock()
defer i.mu.Unlock()
@@ -375,7 +392,11 @@ func (i *Index) createFrame(name string, opt FrameOptions) (*Frame, error) {
}
// Default the time quantum to what is set on the Index.
- if err := f.SetTimeQuantum(i.timeQuantum); err != nil {
+ timeQuantum := i.timeQuantum
+ if opt.TimeQuantum != "" {
+ timeQuantum = opt.TimeQuantum
+ }
+ if err := f.SetTimeQuantum(timeQuantum); err != nil {
f.Close()
return nil, err
}
@@ -519,7 +540,7 @@ func encodeIndex(d *Index) *internal.Index {
ColumnLabel: d.columnLabel,
TimeQuantum: string(d.timeQuantum),
},
- MaxSlice: d.remoteMaxSlice,
+ MaxSlice: d.MaxSlice(),
Frames: encodeFrames(d.Frames()),
}
}
diff --git a/index_test.go b/index_test.go
index 67b5b8b2c..7986855e6 100644
--- a/index_test.go
+++ b/index_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
@@ -34,23 +48,43 @@ func TestIndex_CreateFrameIfNotExists(t *testing.T) {
}
}
-// Ensure index defaults the time quantum on new frames.
+// Ensure index is assigned the correct time quantum on creation.
func TestIndex_CreateFrame_TimeQuantum(t *testing.T) {
- index := MustOpenIndex()
- defer index.Close()
+ t.Run("Explicit", func(t *testing.T) {
+ index := MustOpenIndex()
+ defer index.Close()
- // Set index time quantum.
- if err := index.SetTimeQuantum(pilosa.TimeQuantum("YM")); err != nil {
- t.Fatal(err)
- }
+ // Set index time quantum.
+ if err := index.SetTimeQuantum(pilosa.TimeQuantum("YM")); err != nil {
+ t.Fatal(err)
+ }
- // Create frame.
- f, err := index.CreateFrame("f", pilosa.FrameOptions{})
- if err != nil {
- t.Fatal(err)
- } else if q := f.TimeQuantum(); q != pilosa.TimeQuantum("YM") {
- t.Fatalf("unexpected frame time quantum: %s", q)
- }
+ // Create frame with explicit quantum.
+ f, err := index.CreateFrame("f", pilosa.FrameOptions{TimeQuantum: pilosa.TimeQuantum("YMDH")})
+ if err != nil {
+ t.Fatal(err)
+ } else if q := f.TimeQuantum(); q != pilosa.TimeQuantum("YMDH") {
+ t.Fatalf("unexpected frame time quantum: %s", q)
+ }
+ })
+
+ t.Run("Inherited", func(t *testing.T) {
+ index := MustOpenIndex()
+ defer index.Close()
+
+ // Set index time quantum.
+ if err := index.SetTimeQuantum(pilosa.TimeQuantum("YM")); err != nil {
+ t.Fatal(err)
+ }
+
+ // Create frame.
+ f, err := index.CreateFrame("f", pilosa.FrameOptions{})
+ if err != nil {
+ t.Fatal(err)
+ } else if q := f.TimeQuantum(); q != pilosa.TimeQuantum("YM") {
+ t.Fatalf("unexpected frame time quantum: %s", q)
+ }
+ })
}
// Ensure index can delete a frame.
diff --git a/internal/internal.go b/internal/internal.go
index d727d97e9..e316aba1b 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 internal
import (
diff --git a/internal/private.pb.go b/internal/private.pb.go
index a41df4788..c5d7be01e 100644
--- a/internal/private.pb.go
+++ b/internal/private.pb.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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.
+
// Code generated by protoc-gen-gogo.
// source: private.proto
// DO NOT EDIT!
@@ -126,8 +140,9 @@ func (m *MaxSlicesResponse) GetMaxSlices() map[string]uint64 {
}
type CreateSliceMessage struct {
- Index string `protobuf:"bytes,1,opt,name=Index,proto3" json:"Index,omitempty"`
- Slice uint64 `protobuf:"varint,2,opt,name=Slice,proto3" json:"Slice,omitempty"`
+ Index string `protobuf:"bytes,1,opt,name=Index,proto3" json:"Index,omitempty"`
+ Slice uint64 `protobuf:"varint,2,opt,name=Slice,proto3" json:"Slice,omitempty"`
+ IsInverse bool `protobuf:"varint,3,opt,name=IsInverse,proto3" json:"IsInverse,omitempty"`
}
func (m *CreateSliceMessage) Reset() { *m = CreateSliceMessage{} }
@@ -211,6 +226,7 @@ type Index struct {
Meta *IndexMeta `protobuf:"bytes,2,opt,name=Meta" json:"Meta,omitempty"`
MaxSlice uint64 `protobuf:"varint,3,opt,name=MaxSlice,proto3" json:"MaxSlice,omitempty"`
Frames []*Frame `protobuf:"bytes,4,rep,name=Frames" json:"Frames,omitempty"`
+ Slices []uint64 `protobuf:"varint,5,rep,packed,name=Slices" json:"Slices,omitempty"`
}
func (m *Index) Reset() { *m = Index{} }
@@ -582,6 +598,16 @@ func (m *CreateSliceMessage) MarshalTo(dAtA []byte) (int, error) {
i++
i = encodeVarintPrivate(dAtA, i, uint64(m.Slice))
}
+ if m.IsInverse {
+ dAtA[i] = 0x18
+ i++
+ if m.IsInverse {
+ dAtA[i] = 1
+ } else {
+ dAtA[i] = 0
+ }
+ i++
+ }
return i, nil
}
@@ -795,6 +821,23 @@ func (m *Index) MarshalTo(dAtA []byte) (int, error) {
i += n
}
}
+ if len(m.Slices) > 0 {
+ dAtA12 := make([]byte, len(m.Slices)*10)
+ var j11 int
+ for _, num := range m.Slices {
+ for num >= 1<<7 {
+ dAtA12[j11] = uint8(uint64(num)&0x7f | 0x80)
+ num >>= 7
+ j11++
+ }
+ dAtA12[j11] = uint8(num)
+ j11++
+ }
+ dAtA[i] = 0x2a
+ i++
+ i = encodeVarintPrivate(dAtA, i, uint64(j11))
+ i += copy(dAtA[i:], dAtA12[:j11])
+ }
return i, nil
}
@@ -1026,6 +1069,9 @@ func (m *CreateSliceMessage) Size() (n int) {
if m.Slice != 0 {
n += 1 + sovPrivate(uint64(m.Slice))
}
+ if m.IsInverse {
+ n += 2
+ }
return n
}
@@ -1119,6 +1165,13 @@ func (m *Index) Size() (n int) {
n += 1 + l + sovPrivate(uint64(l))
}
}
+ if len(m.Slices) > 0 {
+ l = 0
+ for _, e := range m.Slices {
+ l += sovPrivate(uint64(e))
+ }
+ n += 1 + sovPrivate(uint64(l)) + l
+ }
return n
}
@@ -2224,6 +2277,26 @@ func (m *CreateSliceMessage) Unmarshal(dAtA []byte) error {
break
}
}
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field IsInverse", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowPrivate
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.IsInverse = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipPrivate(dAtA[iNdEx:])
@@ -2938,6 +3011,68 @@ func (m *Index) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 5:
+ if wireType == 2 {
+ var packedLen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowPrivate
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ packedLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if packedLen < 0 {
+ return ErrInvalidLengthPrivate
+ }
+ postIndex := iNdEx + packedLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ for iNdEx < postIndex {
+ var v uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowPrivate
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Slices = append(m.Slices, v)
+ }
+ } else if wireType == 0 {
+ var v uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowPrivate
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Slices = append(m.Slices, v)
+ } else {
+ return fmt.Errorf("proto: wrong wireType = %d for field Slices", wireType)
+ }
default:
iNdEx = preIndex
skippy, err := skipPrivate(dAtA[iNdEx:])
@@ -3287,44 +3422,45 @@ var (
func init() { proto.RegisterFile("private.proto", fileDescriptorPrivate) }
var fileDescriptorPrivate = []byte{
- // 617 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x54, 0xc1, 0x6e, 0xd3, 0x40,
- 0x10, 0xc5, 0x89, 0x53, 0x9a, 0xa9, 0x5a, 0xda, 0xa5, 0x42, 0xa6, 0x42, 0x51, 0xb4, 0x07, 0x5a,
- 0x7a, 0xe8, 0xa1, 0x5c, 0x10, 0x70, 0xa8, 0x9a, 0x04, 0x35, 0x12, 0x29, 0x62, 0x53, 0x71, 0xdf,
- 0x24, 0x23, 0xb0, 0xe2, 0xd8, 0xc1, 0xbb, 0x4e, 0x1a, 0x0e, 0xdc, 0xf9, 0x03, 0x24, 0xbe, 0x81,
- 0xff, 0xe0, 0xc8, 0x27, 0xa0, 0xf0, 0x23, 0x68, 0xc7, 0x6b, 0x3b, 0xb8, 0x94, 0x0a, 0x6e, 0x3b,
- 0x6f, 0x66, 0xe7, 0xbd, 0x7d, 0x9e, 0x31, 0x6c, 0x4e, 0x63, 0x7f, 0x26, 0x35, 0x1e, 0x4d, 0xe3,
- 0x48, 0x47, 0x6c, 0xdd, 0x0f, 0x35, 0xc6, 0xa1, 0x0c, 0xf8, 0x2b, 0xa8, 0x77, 0xc3, 0x11, 0x5e,
- 0xf6, 0x50, 0x4b, 0xd6, 0x84, 0x8d, 0x56, 0x14, 0x24, 0x93, 0xf0, 0xa5, 0x1c, 0x60, 0xe0, 0x39,
- 0x4d, 0xe7, 0xa0, 0x2e, 0x56, 0x21, 0x53, 0x71, 0xe1, 0x4f, 0xf0, 0x75, 0x22, 0x43, 0x9d, 0x4c,
- 0xbc, 0x4a, 0x5a, 0xb1, 0x02, 0xf1, 0xaf, 0x0e, 0xd4, 0x5f, 0xc4, 0x72, 0x82, 0xd4, 0x71, 0x0f,
- 0xd6, 0x45, 0x34, 0x5f, 0x6d, 0x97, 0xc7, 0xec, 0x21, 0x6c, 0x75, 0xc3, 0x19, 0xc6, 0x0a, 0x3b,
- 0xa1, 0x1c, 0x04, 0x38, 0xa2, 0x76, 0xeb, 0xa2, 0x84, 0xb2, 0x07, 0x50, 0x6f, 0xc9, 0xe1, 0x3b,
- 0xbc, 0x58, 0x4c, 0xd1, 0xab, 0x52, 0x93, 0x02, 0xc8, 0xb3, 0x7d, 0xff, 0x03, 0x7a, 0x6e, 0xd3,
- 0x39, 0xd8, 0x14, 0x05, 0x50, 0xd6, 0x5b, 0xbb, 0xaa, 0x97, 0xc3, 0x56, 0x77, 0x32, 0x8d, 0x62,
- 0x2d, 0x50, 0x4d, 0xa3, 0x50, 0x21, 0xdb, 0x86, 0x6a, 0x27, 0x8e, 0xad, 0x5c, 0x73, 0xe4, 0x1f,
- 0x61, 0xfb, 0x34, 0x88, 0x86, 0xe3, 0xb6, 0xd4, 0x52, 0xe0, 0xfb, 0x04, 0x95, 0x66, 0xbb, 0x50,
- 0x23, 0xe3, 0x6c, 0x5d, 0x1a, 0x18, 0x94, 0x1e, 0x6f, 0x9d, 0x49, 0x03, 0x83, 0xd2, 0x7d, 0x52,
- 0xef, 0x8a, 0x34, 0x30, 0x68, 0x3f, 0xf0, 0x87, 0xa9, 0x6a, 0x57, 0xa4, 0x01, 0x63, 0xe0, 0xbe,
- 0xf1, 0x71, 0x6e, 0xa5, 0xd2, 0x99, 0x77, 0x61, 0x67, 0x85, 0xdf, 0xca, 0xbc, 0x07, 0x6b, 0x22,
- 0x9a, 0x77, 0xdb, 0xca, 0x73, 0x9a, 0xd5, 0x03, 0x57, 0xd8, 0x88, 0x0c, 0xa1, 0x2f, 0x66, 0x52,
- 0x15, 0x4a, 0x15, 0x00, 0xbf, 0x0f, 0x35, 0x72, 0xc7, 0xbc, 0xb2, 0xb8, 0x6b, 0x8e, 0xfc, 0x8b,
- 0x03, 0x3b, 0x3d, 0x79, 0x49, 0x32, 0x54, 0x4e, 0x73, 0x06, 0xf5, 0x1c, 0xa4, 0xea, 0x8d, 0xe3,
- 0xc3, 0xa3, 0x6c, 0x7c, 0x8e, 0xae, 0xd4, 0x17, 0x48, 0x27, 0xd4, 0xf1, 0x42, 0x14, 0x97, 0xf7,
- 0x9e, 0xc3, 0xd6, 0xef, 0x49, 0xa3, 0x61, 0x8c, 0x8b, 0xcc, 0xe9, 0x31, 0x2e, 0x8c, 0x27, 0x33,
- 0x19, 0x24, 0xa9, 0x7f, 0xae, 0x48, 0x83, 0xa7, 0x95, 0x27, 0x0e, 0x3f, 0x01, 0xd6, 0x8a, 0x51,
- 0x6a, 0xa4, 0x06, 0x3d, 0x54, 0x4a, 0xbe, 0xc5, 0xeb, 0xbf, 0x42, 0xea, 0x6c, 0x65, 0xc5, 0x59,
- 0x7e, 0x08, 0xac, 0x8d, 0x01, 0x6a, 0xb4, 0x03, 0xff, 0x97, 0x0e, 0xbc, 0x9f, 0xb1, 0xdd, 0x5c,
- 0xcb, 0xf6, 0xc1, 0x35, 0xb3, 0x4e, 0x64, 0x1b, 0xc7, 0x77, 0x0b, 0x73, 0xf2, 0xc5, 0x12, 0x54,
- 0xc0, 0xfd, 0xac, 0xa9, 0xdd, 0x8f, 0x1b, 0x9e, 0xf0, 0x87, 0x41, 0xca, 0xa8, 0xaa, 0x65, 0xaa,
- 0x7c, 0xe3, 0x2c, 0xd5, 0x49, 0xf6, 0xd6, 0xff, 0xa5, 0xe2, 0x6d, 0x8b, 0x9a, 0x81, 0x3c, 0x37,
- 0xd9, 0xf4, 0x0e, 0x9d, 0xaf, 0x7f, 0x72, 0x59, 0xc7, 0x27, 0xc7, 0x52, 0xfe, 0x5b, 0x9b, 0x92,
- 0x73, 0xe6, 0x37, 0x92, 0x8d, 0x8e, 0xdd, 0xa1, 0x3c, 0x66, 0xfb, 0xb0, 0x46, 0xac, 0xca, 0x73,
- 0x69, 0x3a, 0xef, 0x94, 0xd4, 0x08, 0x9b, 0xe6, 0x12, 0xe0, 0x3c, 0x1a, 0x61, 0x5f, 0x4b, 0x9d,
- 0x28, 0xa3, 0xe7, 0x2c, 0x52, 0x3a, 0xd3, 0x63, 0xce, 0x34, 0x37, 0x5a, 0xea, 0xdc, 0x09, 0x0a,
- 0xd8, 0x23, 0xb8, 0x4d, 0x7a, 0x50, 0x79, 0xd5, 0x32, 0x03, 0x25, 0x44, 0x96, 0xe7, 0xcf, 0x60,
- 0xb3, 0x15, 0x24, 0x4a, 0x63, 0x6c, 0x59, 0x0e, 0xa1, 0x66, 0x38, 0xb3, 0xcd, 0xd9, 0x2d, 0x6e,
- 0x16, 0x52, 0x44, 0x5a, 0x72, 0xba, 0xfd, 0x6d, 0xd9, 0x70, 0xbe, 0x2f, 0x1b, 0xce, 0x8f, 0x65,
- 0xc3, 0xf9, 0xfc, 0xb3, 0x71, 0x6b, 0xb0, 0x46, 0x7f, 0xeb, 0xc7, 0xbf, 0x02, 0x00, 0x00, 0xff,
- 0xff, 0x77, 0x02, 0x29, 0xbb, 0xbe, 0x05, 0x00, 0x00,
+ // 640 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x54, 0xc1, 0x4e, 0x14, 0x4d,
+ 0x10, 0xfe, 0x67, 0x77, 0x96, 0x7f, 0xa7, 0x08, 0x08, 0x2d, 0x31, 0x23, 0x21, 0x9b, 0x4d, 0x1f,
+ 0x04, 0x39, 0x70, 0xc0, 0x8b, 0x51, 0x0f, 0x86, 0x5d, 0x0c, 0x93, 0x08, 0xc6, 0x5e, 0xe2, 0xd1,
+ 0xa4, 0x81, 0x8a, 0x4e, 0x98, 0x9d, 0x59, 0xa7, 0x7b, 0x80, 0xf5, 0xe0, 0x73, 0x98, 0x78, 0xf2,
+ 0x01, 0x7c, 0x0f, 0x8f, 0x3e, 0x82, 0xc1, 0x17, 0x31, 0x5d, 0xdd, 0x33, 0xb3, 0x0e, 0x22, 0xd1,
+ 0x5b, 0xd7, 0x57, 0xd5, 0xf5, 0x7d, 0xfd, 0x4d, 0xd5, 0xc0, 0xc2, 0x24, 0x8f, 0xcf, 0xa4, 0xc6,
+ 0xad, 0x49, 0x9e, 0xe9, 0x8c, 0x75, 0xe3, 0x54, 0x63, 0x9e, 0xca, 0x84, 0xbf, 0x80, 0x20, 0x4a,
+ 0x4f, 0xf0, 0x62, 0x1f, 0xb5, 0x64, 0x7d, 0x98, 0x1f, 0x64, 0x49, 0x31, 0x4e, 0x9f, 0xcb, 0x23,
+ 0x4c, 0x42, 0xaf, 0xef, 0x6d, 0x04, 0x62, 0x16, 0x32, 0x15, 0x87, 0xf1, 0x18, 0x5f, 0x16, 0x32,
+ 0xd5, 0xc5, 0x38, 0x6c, 0xd9, 0x8a, 0x19, 0x88, 0x7f, 0xf1, 0x20, 0x78, 0x96, 0xcb, 0x31, 0x52,
+ 0xc7, 0x55, 0xe8, 0x8a, 0xec, 0x7c, 0xb6, 0x5d, 0x15, 0xb3, 0x7b, 0xb0, 0x18, 0xa5, 0x67, 0x98,
+ 0x2b, 0xdc, 0x4d, 0xe5, 0x51, 0x82, 0x27, 0xd4, 0xae, 0x2b, 0x1a, 0x28, 0x5b, 0x83, 0x60, 0x20,
+ 0x8f, 0xdf, 0xe2, 0xe1, 0x74, 0x82, 0x61, 0x9b, 0x9a, 0xd4, 0x40, 0x95, 0x1d, 0xc5, 0xef, 0x31,
+ 0xf4, 0xfb, 0xde, 0xc6, 0x82, 0xa8, 0x81, 0xa6, 0xde, 0xce, 0x55, 0xbd, 0x1c, 0x16, 0xa3, 0xf1,
+ 0x24, 0xcb, 0xb5, 0x40, 0x35, 0xc9, 0x52, 0x85, 0x6c, 0x09, 0xda, 0xbb, 0x79, 0xee, 0xe4, 0x9a,
+ 0x23, 0xff, 0x00, 0x4b, 0x3b, 0x49, 0x76, 0x7c, 0x3a, 0x94, 0x5a, 0x0a, 0x7c, 0x57, 0xa0, 0xd2,
+ 0x6c, 0x05, 0x3a, 0x64, 0x9c, 0xab, 0xb3, 0x81, 0x41, 0xe9, 0xf1, 0xce, 0x19, 0x1b, 0x18, 0x94,
+ 0xee, 0x93, 0x7a, 0x5f, 0xd8, 0xc0, 0xa0, 0xa3, 0x24, 0x3e, 0xb6, 0xaa, 0x7d, 0x61, 0x03, 0xc6,
+ 0xc0, 0x7f, 0x15, 0xe3, 0xb9, 0x93, 0x4a, 0x67, 0x1e, 0xc1, 0xf2, 0x0c, 0xbf, 0x93, 0x79, 0x07,
+ 0xe6, 0x44, 0x76, 0x1e, 0x0d, 0x55, 0xe8, 0xf5, 0xdb, 0x1b, 0xbe, 0x70, 0x11, 0x19, 0x42, 0x5f,
+ 0xcc, 0xa4, 0x5a, 0x94, 0xaa, 0x01, 0x7e, 0x17, 0x3a, 0xe4, 0x8e, 0x79, 0x65, 0x7d, 0xd7, 0x1c,
+ 0xf9, 0x27, 0x0f, 0x96, 0xf7, 0xe5, 0x05, 0xc9, 0x50, 0x15, 0xcd, 0x1e, 0x04, 0x15, 0x48, 0xd5,
+ 0xf3, 0xdb, 0x9b, 0x5b, 0xe5, 0xf8, 0x6c, 0x5d, 0xa9, 0xaf, 0x91, 0xdd, 0x54, 0xe7, 0x53, 0x51,
+ 0x5f, 0x5e, 0x7d, 0x02, 0x8b, 0xbf, 0x26, 0x8d, 0x86, 0x53, 0x9c, 0x96, 0x4e, 0x9f, 0xe2, 0xd4,
+ 0x78, 0x72, 0x26, 0x93, 0xc2, 0xfa, 0xe7, 0x0b, 0x1b, 0x3c, 0x6a, 0x3d, 0xf4, 0xf8, 0x6b, 0x60,
+ 0x83, 0x1c, 0xa5, 0x46, 0x6a, 0xb0, 0x8f, 0x4a, 0xc9, 0x37, 0x78, 0xfd, 0x57, 0xb0, 0xce, 0xb6,
+ 0x66, 0x9d, 0x5d, 0x83, 0x20, 0x52, 0x6e, 0xb6, 0xe8, 0x4b, 0x74, 0x45, 0x0d, 0xf0, 0x4d, 0x60,
+ 0x43, 0x4c, 0x50, 0xa3, 0x5b, 0x87, 0x3f, 0xf4, 0xe7, 0xa3, 0x52, 0xcb, 0xcd, 0xb5, 0x6c, 0x1d,
+ 0x7c, 0xb3, 0x09, 0x24, 0x65, 0x7e, 0xfb, 0x76, 0x6d, 0x5d, 0xb5, 0x76, 0x82, 0x0a, 0x78, 0x5c,
+ 0x36, 0x75, 0xdb, 0x73, 0xc3, 0x03, 0x7f, 0x33, 0x66, 0x25, 0x55, 0xbb, 0x49, 0x55, 0xed, 0xa3,
+ 0xa3, 0x7a, 0x5a, 0xbe, 0xf5, 0x5f, 0xa9, 0xf8, 0xd0, 0xa1, 0x66, 0x5c, 0x0f, 0x4c, 0xd6, 0xde,
+ 0xa1, 0xf3, 0xf5, 0x4f, 0x6e, 0xea, 0xf8, 0xec, 0x39, 0xca, 0xbf, 0x6b, 0xd3, 0x70, 0xce, 0xfc,
+ 0x64, 0xca, 0xc1, 0x72, 0x1b, 0x56, 0xc5, 0x6c, 0x1d, 0xe6, 0x88, 0x55, 0x85, 0x3e, 0xcd, 0xee,
+ 0xad, 0x86, 0x1a, 0xe1, 0xd2, 0x66, 0x9d, 0xdc, 0x90, 0x77, 0xec, 0x3a, 0xd9, 0x88, 0x4b, 0x80,
+ 0x83, 0xec, 0x04, 0x47, 0x5a, 0xea, 0x42, 0x19, 0x9d, 0x7b, 0x99, 0xd2, 0xa5, 0x4e, 0x73, 0xa6,
+ 0x69, 0xd3, 0x52, 0x57, 0x0e, 0x51, 0xc0, 0xee, 0xc3, 0xff, 0xa4, 0x13, 0x55, 0xd8, 0x6e, 0x32,
+ 0x53, 0x42, 0x94, 0x79, 0xfe, 0x18, 0x16, 0x06, 0x49, 0xa1, 0x34, 0xe6, 0x8e, 0x65, 0x13, 0x3a,
+ 0x86, 0xb3, 0xdc, 0xb7, 0x95, 0xfa, 0x66, 0x2d, 0x45, 0xd8, 0x92, 0x9d, 0xa5, 0xaf, 0x97, 0x3d,
+ 0xef, 0xdb, 0x65, 0xcf, 0xfb, 0x7e, 0xd9, 0xf3, 0x3e, 0xfe, 0xe8, 0xfd, 0x77, 0x34, 0x47, 0xff,
+ 0xf8, 0x07, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x5a, 0x1c, 0x79, 0x08, 0xf4, 0x05, 0x00, 0x00,
}
diff --git a/internal/private.proto b/internal/private.proto
index b83683d51..c7c792f4a 100644
--- a/internal/private.proto
+++ b/internal/private.proto
@@ -43,6 +43,7 @@ message MaxSlicesResponse {
message CreateSliceMessage {
string Index = 1;
uint64 Slice = 2;
+ bool IsInverse = 3;
}
message DeleteIndexMessage {
@@ -75,6 +76,7 @@ message Index {
IndexMeta Meta = 2;
uint64 MaxSlice = 3;
repeated Frame Frames = 4;
+ repeated uint64 Slices = 5;
}
message NodeStatus {
diff --git a/internal/public.pb.go b/internal/public.pb.go
index 9eb005c17..8499042fd 100644
--- a/internal/public.pb.go
+++ b/internal/public.pb.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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.
+
// Code generated by protoc-gen-gogo.
// source: public.proto
// DO NOT EDIT!
@@ -2576,7 +2590,7 @@ func init() { proto.RegisterFile("public.proto", fileDescriptorPublic) }
var fileDescriptorPublic = []byte{
// 576 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x54, 0x4b, 0x8e, 0xd3, 0x40,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x4b, 0x8e, 0xd3, 0x40,
0x10, 0xa5, 0x63, 0xe7, 0x57, 0xf9, 0x28, 0x6a, 0xf1, 0xb1, 0x10, 0x8a, 0x2c, 0x8b, 0x85, 0x57,
0x19, 0x69, 0x38, 0x00, 0xc2, 0x49, 0x46, 0xb2, 0x10, 0x23, 0xa6, 0x33, 0xb0, 0xf7, 0xcc, 0xb4,
0x06, 0x4b, 0xfe, 0xd1, 0xdd, 0x16, 0xe4, 0x00, 0xec, 0x91, 0xd8, 0x70, 0x03, 0x38, 0x0a, 0x4b,
diff --git a/iterator.go b/iterator.go
index 155914491..473e862dd 100644
--- a/iterator.go
+++ b/iterator.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
diff --git a/iterator_test.go b/iterator_test.go
index c9240b6c5..025672f8a 100644
--- a/iterator_test.go
+++ b/iterator_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/pilosa.go b/pilosa.go
index 16739fd7a..dd7402677 100644
--- a/pilosa.go
+++ b/pilosa.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -24,16 +38,19 @@ var (
ErrInvalidView = errors.New("invalid view")
ErrInvalidCacheType = errors.New("invalid cache type")
- ErrName = errors.New("invalid index or frame's name, must match [a-z0-9_-]")
+ ErrName = errors.New("invalid index or frame's name, must match [a-z0-9_-]")
+ ErrLabel = errors.New("invalid row or column label, must match [A-Za-z0-9_-]")
// ErrFragmentNotFound is returned when a fragment does not exist.
ErrFragmentNotFound = errors.New("fragment not found")
ErrQueryRequired = errors.New("query required")
)
-// Regular expression to valuate index and frame's name
-// Todo: remove . when frame doesn't require . for topN
-var nameRegexp = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,64}$`)
+// Regular expression to validate index and frame names.
+var nameRegexp = regexp.MustCompile(`^[a-z][a-z0-9_-]{0,64}$`)
+
+// Regular expression to validate row and column labels.
+var labelRegexp = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_-]{0,64}$`)
// ColumnAttrSet represents a set of attributes for a vertical column in an index.
// Can have a set of attributes attached to it.
@@ -88,11 +105,18 @@ func decodeColumnAttrSet(pb *internal.ColumnAttrSet) *ColumnAttrSet {
// TimeFormat is the go-style time format used to parse string dates.
const TimeFormat = "2006-01-02T15:04"
-// Restrict name using regex
+// ValidateName ensures that the name is a valid format.
func ValidateName(name string) error {
- validName := nameRegexp.Match([]byte(name))
- if validName == false {
+ if nameRegexp.Match([]byte(name)) == false {
return ErrName
}
return nil
}
+
+// ValidateLabel ensures that the label is a valid format.
+func ValidateLabel(label string) error {
+ if labelRegexp.Match([]byte(label)) == false {
+ return ErrLabel
+ }
+ return nil
+}
diff --git a/pql/ast.go b/pql/ast.go
index 6673bb638..b83b35731 100644
--- a/pql/ast.go
+++ b/pql/ast.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql
import (
@@ -156,6 +170,30 @@ func (c *Call) String() string {
return buf.String()
}
+// SupportsInverse indicates that the call may be on an inverse frame.
+func (c *Call) SupportsInverse() bool {
+ if c.Name == "Bitmap" {
+ return true
+ }
+ return false
+}
+
+// IsInverse specifies if the call is for an inverse view.
+// Return defaults to false unless absolutely sure of inversion.
+func (c *Call) IsInverse(rowLabel, columnLabel string) bool {
+ if c.SupportsInverse() {
+ _, rowOK, rowErr := c.UintArg(rowLabel)
+ _, columnOK, columnErr := c.UintArg(columnLabel)
+ if rowErr != nil || columnErr != nil {
+ return false
+ }
+ if !rowOK && columnOK {
+ return true
+ }
+ }
+ return false
+}
+
// CopyArgs returns a copy of m.
func CopyArgs(m map[string]interface{}) map[string]interface{} {
other := make(map[string]interface{}, len(m))
diff --git a/pql/ast_test.go b/pql/ast_test.go
index 653bd5af5..2b8519571 100644
--- a/pql/ast_test.go
+++ b/pql/ast_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql_test
import (
@@ -15,3 +29,69 @@ func TestCall_String(t *testing.T) {
}
})
}
+
+// Ensure call can be converted into a string.
+func TestCall_SupportsInverse(t *testing.T) {
+ t.Run("Bitmap", func(t *testing.T) {
+ q, err := pql.ParseString(`Bitmap()`)
+ if err != nil {
+ t.Fatal(err)
+ } else if q.Calls[0].SupportsInverse() != true {
+ t.Fatalf("call should support inverse: %s", q.Calls[0])
+ }
+ })
+ t.Run("Count Bitmap", func(t *testing.T) {
+ q, err := pql.ParseString(`Count(Bitmap())`)
+ if err != nil {
+ t.Fatal(err)
+ } else if q.Calls[0].SupportsInverse() == true {
+ t.Fatalf("call should not support inverse: %s", q.Calls[0])
+ }
+ })
+ t.Run("Union Bitmaps", func(t *testing.T) {
+ q, err := pql.ParseString(`Union(Bitmap(), Bitmap())`)
+ if err != nil {
+ t.Fatal(err)
+ } else if q.Calls[0].SupportsInverse() == true {
+ t.Fatalf("call should not support inverse: %s", q.Calls[0])
+ }
+ })
+
+}
+
+// Ensure call is correctly determined to be against an inverse view.
+func TestCall_IsInverse(t *testing.T) {
+ t.Run("Bitmap Row", func(t *testing.T) {
+ q, err := pql.ParseString(`Bitmap(frame="f", row=1)`)
+ if err != nil {
+ t.Fatal(err)
+ } else if q.Calls[0].IsInverse("row", "col") != false {
+ t.Fatalf("incorrect call inverse: %s", q.Calls[0])
+ }
+ })
+ t.Run("Bitmap Column", func(t *testing.T) {
+ q, err := pql.ParseString(`Bitmap(frame="f", col=1)`)
+ if err != nil {
+ t.Fatal(err)
+ } else if q.Calls[0].IsInverse("row", "col") != true {
+ t.Fatalf("incorrect call inverse: %s", q.Calls[0])
+ }
+ })
+ t.Run("Bitmap Column No Label", func(t *testing.T) {
+ q, err := pql.ParseString(`Bitmap(frame="f", col=1)`)
+ if err != nil {
+ t.Fatal(err)
+ } else if q.Calls[0].IsInverse("rowX", "colX") != false {
+ t.Fatalf("incorrect call inverse: %s", q.Calls[0])
+ }
+ })
+ t.Run("Count", func(t *testing.T) {
+ q, err := pql.ParseString(`Count(Bitmap(frame="f", col=1))`)
+ if err != nil {
+ t.Fatal(err)
+ } else if q.Calls[0].IsInverse("row", "col") != false {
+ t.Fatalf("incorrect call inverse: %s", q.Calls[0])
+ }
+ })
+
+}
diff --git a/pql/doc.go b/pql/doc.go
index abd8607a1..6150f4012 100644
--- a/pql/doc.go
+++ b/pql/doc.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql defines the Pilosa Query Language.
*/
diff --git a/pql/parser.go b/pql/parser.go
index f7d93ab81..048f6a664 100644
--- a/pql/parser.go
+++ b/pql/parser.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql
import (
diff --git a/pql/parser_test.go b/pql/parser_test.go
index 66b84f6be..f757a7d2e 100644
--- a/pql/parser_test.go
+++ b/pql/parser_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql_test
import (
diff --git a/pql/scanner.go b/pql/scanner.go
index a0146725a..a559d8ccb 100644
--- a/pql/scanner.go
+++ b/pql/scanner.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql
import (
diff --git a/pql/scanner_test.go b/pql/scanner_test.go
index 4dc2f4d8f..5b7a06b2f 100644
--- a/pql/scanner_test.go
+++ b/pql/scanner_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql_test
import (
diff --git a/pql/token.go b/pql/token.go
index 2ffc84400..4d71117af 100644
--- a/pql/token.go
+++ b/pql/token.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pql
import "strings"
diff --git a/roaring/assembly.go b/roaring/assembly.go
index 3ffa6efc7..1e5d49519 100644
--- a/roaring/assembly.go
+++ b/roaring/assembly.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 roaring
func hasAsm() bool
diff --git a/roaring/assembly_asm.go b/roaring/assembly_asm.go
index 88435422d..eec8234b8 100644
--- a/roaring/assembly_asm.go
+++ b/roaring/assembly_asm.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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.
+
// +build amd64
package roaring
diff --git a/roaring/assembly_generic.go b/roaring/assembly_generic.go
index 50c4936d5..746970ba0 100644
--- a/roaring/assembly_generic.go
+++ b/roaring/assembly_generic.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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.
+
// +build !amd64
package roaring
diff --git a/roaring/assembly_test.go b/roaring/assembly_test.go
index 244e5ad16..f18aa65ec 100644
--- a/roaring/assembly_test.go
+++ b/roaring/assembly_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 roaring
import "testing"
diff --git a/roaring/internal_test.go b/roaring/internal_test.go
index 4a39906d3..59fbccf1c 100644
--- a/roaring/internal_test.go
+++ b/roaring/internal_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 roaring
import (
diff --git a/roaring/roaring.go b/roaring/roaring.go
index e74739311..e0810a6c2 100644
--- a/roaring/roaring.go
+++ b/roaring/roaring.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 roaring implements roaring bitmaps with support for incremental changes.
package roaring
diff --git a/roaring/roaring_test.go b/roaring/roaring_test.go
index ccbe3b66c..d28e72354 100644
--- a/roaring/roaring_test.go
+++ b/roaring/roaring_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 roaring_test
import (
@@ -197,7 +211,10 @@ func testBitmapQuick(t *testing.T, n int, min, max uint64) {
}
// Verify slices are equal.
- if got, exp := bm.Slice(), uint64SetSlice(m); !reflect.DeepEqual(got, exp) {
+ // If `got` is nil and `exp` has zero length, don't perform the DeepEqual
+ // because when `a` is empty (`a = []uint64{}`) then `got` is a nil slice
+ // while `exp` is an empty slice. Therefore they will not be considered equal.
+ if got, exp := bm.Slice(), uint64SetSlice(m); !(got == nil && len(exp) == 0) && !reflect.DeepEqual(got, exp) {
t.Fatalf("unexpected values:\n\ngot=%+v\n\nexp=%+v\n\n", got, exp)
}
diff --git a/server.go b/server.go
index 9ccb0ee84..60570bc0f 100644
--- a/server.go
+++ b/server.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -254,9 +268,16 @@ func (s *Server) ReceiveMessage(pb proto.Message) error {
if idx == nil {
return fmt.Errorf("Local Index not found: %s", obj.Index)
}
- idx.SetRemoteMaxSlice(obj.Slice)
+ if obj.IsInverse {
+ idx.SetRemoteMaxInverseSlice(obj.Slice)
+ } else {
+ idx.SetRemoteMaxSlice(obj.Slice)
+ }
case *internal.CreateIndexMessage:
- opt := IndexOptions{ColumnLabel: obj.Meta.ColumnLabel}
+ opt := IndexOptions{
+ ColumnLabel: obj.Meta.ColumnLabel,
+ TimeQuantum: TimeQuantum(obj.Meta.TimeQuantum),
+ }
_, err := s.Holder.CreateIndex(obj.Index, opt)
if err != nil {
return err
@@ -267,7 +288,13 @@ func (s *Server) ReceiveMessage(pb proto.Message) error {
}
case *internal.CreateFrameMessage:
index := s.Holder.Index(obj.Index)
- opt := FrameOptions{RowLabel: obj.Meta.RowLabel}
+ opt := FrameOptions{
+ RowLabel: obj.Meta.RowLabel,
+ InverseEnabled: obj.Meta.InverseEnabled,
+ CacheType: obj.Meta.CacheType,
+ CacheSize: obj.Meta.CacheSize,
+ TimeQuantum: TimeQuantum(obj.Meta.TimeQuantum),
+ }
_, err := index.CreateFrame(obj.Frame, opt)
if err != nil {
return err
@@ -281,19 +308,27 @@ func (s *Server) ReceiveMessage(pb proto.Message) error {
return nil
}
-// Server implements StatusHandler.
// LocalStatus returns the state of the local node as well as the
// holder (indexes/frames) according to the local node.
// In a gossip implementation, memberlist.Delegate.LocalState() uses this.
+// Server implements StatusHandler.
func (s *Server) LocalStatus() (proto.Message, error) {
if s.Holder == nil {
return nil, errors.New("Server.Holder is nil")
}
- return &internal.NodeStatus{
+
+ ns := internal.NodeStatus{
Host: s.Host,
State: NodeStateUp,
Indexes: encodeIndexes(s.Holder.Indexes()),
- }, nil
+ }
+
+ // Append Slice list per this Node's indexes
+ for _, index := range ns.Indexes {
+ index.Slices = s.Cluster.OwnsSlices(index.Name, index.MaxSlice, s.Host)
+ }
+
+ return &ns, nil
}
// ClusterStatus returns the NodeState for all nodes in the cluster.
@@ -308,6 +343,14 @@ func (s *Server) ClusterStatus() (proto.Message, error) {
// Update NodeState for all nodes.
for host, nodeState := range s.Cluster.NodeStates() {
+ // In a default configuration (or single-node) where a StaticNodeSet is used
+ // then all nodes are marked as DOWN. At the very least, we should consider
+ // the local node as UP.
+ // TODO: we should be able to remove this check if/when cluster.Nodes and
+ // cluster.NodeSet are unified.
+ if host == s.Host {
+ nodeState = NodeStateUp
+ }
node := s.Cluster.NodeByHost(host)
node.SetState(nodeState)
}
diff --git a/server/server.go b/server/server.go
index 1f52c0f48..e2360522d 100644
--- a/server/server.go
+++ b/server/server.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 server contains the `pilosa server` subcommand which runs Pilosa
// itself. The purpose of this package is to define an easily tested Command
// object which handles interpreting configuration and setting up all the
diff --git a/server/server_test.go b/server/server_test.go
index 4e3fa5e36..d981a4cf3 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 server_test
import (
@@ -8,16 +22,19 @@ import (
"io"
"io/ioutil"
"math/rand"
+ "net"
"net/http"
"os"
"reflect"
"sort"
+ "strconv"
"strings"
"testing"
"testing/quick"
"github.com/BurntSushi/toml"
"github.com/pilosa/pilosa"
+ "github.com/pilosa/pilosa/httpbroadcast"
"github.com/pilosa/pilosa/server"
)
@@ -45,7 +62,7 @@ func TestMain_Set_Quick(t *testing.T) {
if err := client.CreateFrame(context.Background(), "i", cmd.Frame, pilosa.FrameOptions{}); err != nil && err != pilosa.ErrFrameExists {
t.Fatal(err)
}
- if _, err := m.Query("i", "", fmt.Sprintf(`SetBit(id=%d, frame=%q, columnID=%d)`, cmd.ID, cmd.Frame, cmd.ColumnID)); err != nil {
+ if _, err := m.Query("i", "", fmt.Sprintf(`SetBit(rowID=%d, frame=%q, columnID=%d)`, cmd.ID, cmd.Frame, cmd.ColumnID)); err != nil {
t.Fatal(err)
}
}
@@ -61,7 +78,7 @@ func TestMain_Set_Quick(t *testing.T) {
},
},
}) + "\n"
- if res, err := m.Query("i", "", fmt.Sprintf(`Bitmap(id=%d, frame=%q)`, id, frame)); err != nil {
+ if res, err := m.Query("i", "", fmt.Sprintf(`Bitmap(rowID=%d, frame=%q)`, id, frame)); err != nil {
t.Fatal(err)
} else if res != exp {
t.Fatalf("unexpected result:\n\ngot=%s\n\nexp=%s\n\n", res, exp)
@@ -84,7 +101,7 @@ func TestMain_Set_Quick(t *testing.T) {
},
},
}) + "\n"
- if res, err := m.Query("i", "", fmt.Sprintf(`Bitmap(id=%d, frame=%q)`, id, frame)); err != nil {
+ if res, err := m.Query("i", "", fmt.Sprintf(`Bitmap(rowID=%d, frame=%q)`, id, frame)); err != nil {
t.Fatal(err)
} else if res != exp {
t.Fatalf("unexpected result (reopen):\n\ngot=%s\n\nexp=%s\n\n", res, exp)
@@ -111,7 +128,7 @@ func TestMain_SetRowAttrs(t *testing.T) {
client := m.Client()
if err := client.CreateIndex(context.Background(), "i", pilosa.IndexOptions{}); err != nil && err != pilosa.ErrIndexExists {
t.Fatal(err)
- } else if err := client.CreateFrame(context.Background(), "i", "x.n", pilosa.FrameOptions{}); err != nil {
+ } else if err := client.CreateFrame(context.Background(), "i", "x", pilosa.FrameOptions{}); err != nil {
t.Fatal(err)
} else if err := client.CreateFrame(context.Background(), "i", "z", pilosa.FrameOptions{}); err != nil {
t.Fatal(err)
@@ -120,36 +137,36 @@ func TestMain_SetRowAttrs(t *testing.T) {
}
// Set bits on different rows in different frames.
- if _, err := m.Query("i", "", `SetBit(id=1, frame="x.n", columnID=100)`); err != nil {
+ if _, err := m.Query("i", "", `SetBit(rowID=1, frame="x", columnID=100)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetBit(id=2, frame="x.n", columnID=100)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetBit(rowID=2, frame="x", columnID=100)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetBit(id=2, frame="z", columnID=100)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetBit(rowID=2, frame="z", columnID=100)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetBit(id=3, frame="neg", columnID=100)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetBit(rowID=3, frame="neg", columnID=100)`); err != nil {
t.Fatal(err)
}
// Set row attributes.
- if _, err := m.Query("i", "", `SetRowAttrs(id=1, frame="x.n", x=100)`); err != nil {
+ if _, err := m.Query("i", "", `SetRowAttrs(rowID=1, frame="x", x=100)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetRowAttrs(id=2, frame="x.n", x=-200)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetRowAttrs(rowID=2, frame="x", x=-200)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetRowAttrs(id=2, frame="z", x=300)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetRowAttrs(rowID=2, frame="z", x=300)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetRowAttrs(id=3, frame="neg", x=-0.44)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetRowAttrs(rowID=3, frame="neg", x=-0.44)`); err != nil {
t.Fatal(err)
}
- // Query row x.n/1.
- if res, err := m.Query("i", "", `Bitmap(id=1, frame="x.n")`); err != nil {
+ // Query row x/1.
+ if res, err := m.Query("i", "", `Bitmap(rowID=1, frame="x")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{"x":100},"bits":[100]}]}`+"\n" {
t.Fatalf("unexpected result: %s", res)
}
- // Query row x.n/2.
- if res, err := m.Query("i", "", `Bitmap(id=2, frame="x.n")`); err != nil {
+ // Query row x/2.
+ if res, err := m.Query("i", "", `Bitmap(rowID=2, frame="x")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{"x":-200},"bits":[100]}]}`+"\n" {
t.Fatalf("unexpected result: %s", res)
@@ -160,19 +177,19 @@ func TestMain_SetRowAttrs(t *testing.T) {
}
// Query rows after reopening.
- if res, err := m.Query("i", "columnAttrs=true", `Bitmap(id=1, frame="x.n")`); err != nil {
+ if res, err := m.Query("i", "columnAttrs=true", `Bitmap(rowID=1, frame="x")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{"x":100},"bits":[100]}]}`+"\n" {
t.Fatalf("unexpected result(reopen): %s", res)
}
- if res, err := m.Query("i", "columnAttrs=true", `Bitmap(id=3, frame="neg")`); err != nil {
+ if res, err := m.Query("i", "columnAttrs=true", `Bitmap(rowID=3, frame="neg")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{"x":-0.44},"bits":[100]}]}`+"\n" {
t.Fatalf("unexpected result(reopen): %s", res)
}
- // Query row x.n/2.
- if res, err := m.Query("i", "", `Bitmap(id=2, frame="x.n")`); err != nil {
+ // Query row x/2.
+ if res, err := m.Query("i", "", `Bitmap(rowID=2, frame="x")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{"x":-200},"bits":[100]}]}`+"\n" {
t.Fatalf("unexpected result: %s", res)
@@ -188,14 +205,14 @@ func TestMain_SetColumnAttrs(t *testing.T) {
client := m.Client()
if err := client.CreateIndex(context.Background(), "i", pilosa.IndexOptions{}); err != nil && err != pilosa.ErrIndexExists {
t.Fatal(err)
- } else if err := client.CreateFrame(context.Background(), "i", "x.n", pilosa.FrameOptions{}); err != nil {
+ } else if err := client.CreateFrame(context.Background(), "i", "x", pilosa.FrameOptions{}); err != nil {
t.Fatal(err)
}
// Set bits on row.
- if _, err := m.Query("i", "", `SetBit(id=1, frame="x.n", columnID=100)`); err != nil {
+ if _, err := m.Query("i", "", `SetBit(rowID=1, frame="x", columnID=100)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetBit(id=1, frame="x.n", columnID=101)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetBit(rowID=1, frame="x", columnID=101)`); err != nil {
t.Fatal(err)
}
@@ -205,7 +222,7 @@ func TestMain_SetColumnAttrs(t *testing.T) {
}
// Query row.
- if res, err := m.Query("i", "columnAttrs=true", `Bitmap(id=1, frame="x.n")`); err != nil {
+ if res, err := m.Query("i", "columnAttrs=true", `Bitmap(rowID=1, frame="x")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{},"bits":[100,101]}],"columnAttrs":[{"id":100,"attrs":{"foo":"bar"}}]}`+"\n" {
t.Fatalf("unexpected result: %s", res)
@@ -216,7 +233,7 @@ func TestMain_SetColumnAttrs(t *testing.T) {
}
// Query row after reopening.
- if res, err := m.Query("i", "columnAttrs=true", `Bitmap(id=1, frame="x.n")`); err != nil {
+ if res, err := m.Query("i", "columnAttrs=true", `Bitmap(rowID=1, frame="x")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{},"bits":[100,101]}],"columnAttrs":[{"id":100,"attrs":{"foo":"bar"}}]}`+"\n" {
t.Fatalf("unexpected result(reopen): %s", res)
@@ -232,14 +249,14 @@ func TestMain_SetColumnAttrsWithColumnOption(t *testing.T) {
client := m.Client()
if err := client.CreateIndex(context.Background(), "i", pilosa.IndexOptions{ColumnLabel: "col"}); err != nil && err != pilosa.ErrIndexExists {
t.Fatal(err)
- } else if err := client.CreateFrame(context.Background(), "i", "x.n", pilosa.FrameOptions{}); err != nil {
+ } else if err := client.CreateFrame(context.Background(), "i", "x", pilosa.FrameOptions{}); err != nil {
t.Fatal(err)
}
// Set bits on row.
- if _, err := m.Query("i", "", `SetBit(id=1, frame="x.n", col=100)`); err != nil {
+ if _, err := m.Query("i", "", `SetBit(rowID=1, frame="x", col=100)`); err != nil {
t.Fatal(err)
- } else if _, err := m.Query("i", "", `SetBit(id=1, frame="x.n", col=101)`); err != nil {
+ } else if _, err := m.Query("i", "", `SetBit(rowID=1, frame="x", col=101)`); err != nil {
t.Fatal(err)
}
@@ -249,7 +266,7 @@ func TestMain_SetColumnAttrsWithColumnOption(t *testing.T) {
}
// Query row.
- if res, err := m.Query("i", "columnAttrs=true", `Bitmap(id=1, frame="x.n")`); err != nil {
+ if res, err := m.Query("i", "columnAttrs=true", `Bitmap(rowID=1, frame="x")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{},"bits":[100,101]}],"columnAttrs":[{"id":100,"attrs":{"foo":"bar"}}]}`+"\n" {
t.Fatalf("unexpected result: %s", res)
@@ -282,19 +299,19 @@ func TestMain_FrameRestore(t *testing.T) {
// Write data on first cluster.
if _, err := m0.Query("x", "", `
- SetBit(id=1, frame="f", columnID=100)
- SetBit(id=1, frame="f", columnID=1000)
- SetBit(id=1, frame="f", columnID=100000)
- SetBit(id=1, frame="f", columnID=200000)
- SetBit(id=1, frame="f", columnID=400000)
- SetBit(id=1, frame="f", columnID=600000)
- SetBit(id=1, frame="f", columnID=800000)
+ SetBit(rowID=1, frame="f", columnID=100)
+ SetBit(rowID=1, frame="f", columnID=1000)
+ SetBit(rowID=1, frame="f", columnID=100000)
+ SetBit(rowID=1, frame="f", columnID=200000)
+ SetBit(rowID=1, frame="f", columnID=400000)
+ SetBit(rowID=1, frame="f", columnID=600000)
+ SetBit(rowID=1, frame="f", columnID=800000)
`); err != nil {
t.Fatal(err)
}
// Query row on first cluster.
- if res, err := m0.Query("x", "", `Bitmap(id=1, frame="f")`); err != nil {
+ if res, err := m0.Query("x", "", `Bitmap(rowID=1, frame="f")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{},"bits":[100,1000,100000,200000,400000,600000,800000]}]}`+"\n" {
t.Fatalf("unexpected result: %s", res)
@@ -317,7 +334,7 @@ func TestMain_FrameRestore(t *testing.T) {
}
// Query row on second cluster.
- if res, err := m2.Query("x", "", `Bitmap(id=1, frame="f")`); err != nil {
+ if res, err := m2.Query("x", "", `Bitmap(rowID=1, frame="f")`); err != nil {
t.Fatal(err)
} else if res != `{"results":[{"attrs":{},"bits":[100,1000,100000,200000,400000,600000,800000]}]}`+"\n" {
t.Fatalf("unexpected result: %s", res)
@@ -354,6 +371,160 @@ path = "/path/to/plugins"
}
}
+// Ensure program can send/receive broadcast messages.
+func TestMain_SendReceiveMessage(t *testing.T) {
+ m0 := MustRunMain()
+ defer m0.Close()
+
+ m1 := MustRunMain()
+ defer m1.Close()
+
+ // Get available ports for internal messaging
+ freePorts, err := availablePorts(2)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Update cluster config
+ m0.Server.Cluster.Nodes = []*pilosa.Node{
+ {Host: m0.Server.Host, InternalHost: "localhost:" + freePorts[0]},
+ {Host: m1.Server.Host, InternalHost: "localhost:" + freePorts[1]},
+ }
+ m1.Server.Cluster.Nodes = m0.Server.Cluster.Nodes
+
+ // Configure node0
+ m0.Server.Broadcaster = httpbroadcast.NewHTTPBroadcaster(m0.Server, freePorts[0])
+ m0.Server.Handler.Broadcaster = m0.Server.Broadcaster
+ m0.Server.Holder.Broadcaster = m0.Server.Broadcaster
+ m0.Server.BroadcastReceiver = httpbroadcast.NewHTTPBroadcastReceiver(freePorts[0], nil)
+ m0.Server.Cluster.NodeSet = httpbroadcast.NewHTTPNodeSet()
+ err = m0.Server.Cluster.NodeSet.(*httpbroadcast.HTTPNodeSet).Join(m0.Server.Cluster.Nodes)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := m0.Server.BroadcastReceiver.Start(m0.Server); err != nil {
+ t.Fatal(err)
+ }
+
+ // Configure node1
+ m1.Server.Broadcaster = httpbroadcast.NewHTTPBroadcaster(m1.Server, freePorts[1])
+ m1.Server.Handler.Broadcaster = m1.Server.Broadcaster
+ m1.Server.Holder.Broadcaster = m1.Server.Broadcaster
+ m1.Server.BroadcastReceiver = httpbroadcast.NewHTTPBroadcastReceiver(freePorts[1], nil)
+ m1.Server.Cluster.NodeSet = httpbroadcast.NewHTTPNodeSet()
+ err = m1.Server.Cluster.NodeSet.(*httpbroadcast.HTTPNodeSet).Join(m1.Server.Cluster.Nodes)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := m1.Server.BroadcastReceiver.Start(m1.Server); err != nil {
+ t.Fatal(err)
+ }
+
+ // Expected indexes and Frames
+ expected := map[string][]string{
+ "i": []string{"f"},
+ }
+
+ // Create a client for each node.
+ client0 := m0.Client()
+ client1 := m1.Client()
+
+ // Create indexes and frames on one node.
+ if err := client0.CreateIndex(context.Background(), "i", pilosa.IndexOptions{}); err != nil && err != pilosa.ErrIndexExists {
+ t.Fatal(err)
+ } else if err := client0.CreateFrame(context.Background(), "i", "f", pilosa.FrameOptions{}); err != nil {
+ t.Fatal(err)
+ }
+
+ // Make sure node0 knows about the index and frame created.
+ schema0, err := client0.Schema(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ received0 := map[string][]string{}
+ for _, idx := range schema0 {
+ received0[idx.Name] = []string{}
+ for _, frame := range idx.Frames {
+ received0[idx.Name] = append(received0[idx.Name], frame.Name)
+ }
+ }
+ if !reflect.DeepEqual(received0, expected) {
+ t.Fatalf("unexpected schema on node0: %d", received0)
+ }
+
+ // Make sure node1 knows about the index and frame created.
+ schema1, err := client1.Schema(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ received1 := map[string][]string{}
+ for _, idx := range schema1 {
+ received1[idx.Name] = []string{}
+ for _, frame := range idx.Frames {
+ received1[idx.Name] = append(received1[idx.Name], frame.Name)
+ }
+ }
+ if !reflect.DeepEqual(received1, expected) {
+ t.Fatalf("unexpected schema on node1: %d", received1)
+ }
+
+ // Write data on first node.
+ if _, err := m0.Query("i", "", `
+ SetBit(rowID=1, frame="f", columnID=1)
+ SetBit(rowID=1, frame="f", columnID=2400000)
+ `); err != nil {
+ t.Fatal(err)
+ }
+
+ // Make sure node0 knows about the latest MaxSlice.
+ maxSlices0, err := client0.MaxSliceByIndex(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if maxSlices0["i"] != 2 {
+ t.Fatalf("unexpected maxSlice on node0: %d", maxSlices0["i"])
+ }
+
+ // Make sure node1 knows about the latest MaxSlice.
+ maxSlices1, err := client1.MaxSliceByIndex(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if maxSlices1["i"] != 2 {
+ t.Fatalf("unexpected maxSlice on node1: %d", maxSlices1["i"])
+ }
+}
+
+// availablePorts returns a slice of ports that can be used for testing.
+func availablePorts(cnt int) ([]string, error) {
+ rtn := []string{}
+
+ for i := 0; i < cnt; i++ {
+ port, err := getPort()
+ if err != nil {
+ return nil, err
+ }
+ rtn = append(rtn, strconv.Itoa(port))
+ }
+ return rtn, nil
+}
+
+// Ask the kernel for a free open port that is ready to use
+func getPort() (int, error) {
+ addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
+ if err != nil {
+ return 0, err
+ }
+
+ l, err := net.ListenTCP("tcp", addr)
+ if err != nil {
+ return 0, err
+ }
+ defer l.Close()
+
+ return l.Addr().(*net.TCPAddr).Port, nil
+}
+
// Main represents a test wrapper for main.Main.
type Main struct {
*server.Command
@@ -432,7 +603,6 @@ func (m *Main) Client() *pilosa.Client {
// Query executes a query against the program through the HTTP API.
func (m *Main) Query(index, rawQuery, query string) (string, error) {
- fmt.Println("Query:", index, query)
resp := MustDo("POST", m.URL()+fmt.Sprintf("/index/%s/query?", index)+rawQuery, query)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("invalid status: %d, body=%s", resp.StatusCode, resp.Body)
@@ -482,7 +652,7 @@ func GenerateSetCommands(n int, rand *rand.Rand) []SetCommand {
for i := range cmds {
cmds[i] = SetCommand{
ID: uint64(rand.Intn(1000)),
- Frame: "x.n",
+ Frame: "x",
ColumnID: uint64(rand.Intn(10)),
}
}
diff --git a/statik/.gitignore b/statik/.gitignore
new file mode 100644
index 000000000..514ee40a1
--- /dev/null
+++ b/statik/.gitignore
@@ -0,0 +1 @@
+statik.go
diff --git a/statik/doc.go b/statik/doc.go
new file mode 100644
index 000000000..85edd9e6f
--- /dev/null
+++ b/statik/doc.go
@@ -0,0 +1,3 @@
+// Package statik contains static assets for the Web UI. `go generate` will
+// produce statik.go, which is ignored by git.
+package statik
diff --git a/stats.go b/stats.go
index 057d46320..efaa200d5 100644
--- a/stats.go
+++ b/stats.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -12,7 +26,7 @@ func init() {
NopStatsClient = &nopStatsClient{}
}
-// Global expvar.
+// Expvar global expvar map.
var Expvar = expvar.NewMap("index")
// StatsClient represents a client to a stats server.
@@ -42,9 +56,9 @@ type StatsClient interface {
Timing(name string, value time.Duration)
}
+// NopStatsClient represents a client that doesn't do anything.
var NopStatsClient StatsClient
-// nopStatsClient represents a client that doesn't do anything.
type nopStatsClient struct{}
func (c *nopStatsClient) Tags() []string { return nil }
diff --git a/time.go b/time.go
index b2176285b..ed6f62bf0 100644
--- a/time.go
+++ b/time.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
diff --git a/time_test.go b/time_test.go
index 6de3df364..0602b60de 100644
--- a/time_test.go
+++ b/time_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/view.go b/view.go
index 9f67b516d..3f26185e4 100644
--- a/view.go
+++ b/view.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa
import (
@@ -9,6 +23,8 @@ import (
"strconv"
"strings"
"sync"
+
+ "github.com/pilosa/pilosa/internal"
)
// View layout modes.
@@ -36,7 +52,12 @@ type View struct {
cacheType string // passed in by frame
fragments map[uint64]*Fragment
- stats StatsClient
+ // maxSlice maintains this view's max slice in order to
+ // prevent sending multiple `CreateSliceMessage` messages
+ maxSlice uint64
+
+ broadcaster Broadcaster
+ stats StatsClient
RowAttrStore *AttrStore
LogOutput io.Writer
@@ -54,8 +75,9 @@ func NewView(path, index, frame, name string, cacheSize uint32) *View {
cacheType: DefaultCacheType,
fragments: make(map[uint64]*Fragment),
- stats: NopStatsClient,
- LogOutput: ioutil.Discard,
+ broadcaster: NopBroadcaster,
+ stats: NopStatsClient,
+ LogOutput: ioutil.Discard,
}
}
@@ -207,6 +229,22 @@ func (v *View) createFragmentIfNotExists(slice uint64) (*Fragment, error) {
}
frag.RowAttrStore = v.RowAttrStore
+ // Broadcast a message that a new max slice was just created.
+ if slice > v.maxSlice {
+ v.maxSlice = slice
+
+ // Send the create slice message to all nodes.
+ err := v.broadcaster.SendAsync(
+ &internal.CreateSliceMessage{
+ Index: v.index,
+ Slice: slice,
+ IsInverse: IsInverseView(v.name),
+ })
+ if err != nil {
+ return nil, err
+ }
+ }
+
// Save to lookup.
v.fragments[slice] = frag
diff --git a/view_test.go b/view_test.go
index adaa0ced2..53310c298 100644
--- a/view_test.go
+++ b/view_test.go
@@ -1,3 +1,17 @@
+// Copyright 2017 Pilosa Corp.
+//
+// Licensed 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 pilosa_test
import (
diff --git a/webui/assets/chevron-down.png b/webui/assets/chevron-down.png
new file mode 100644
index 000000000..3312489a2
Binary files /dev/null and b/webui/assets/chevron-down.png differ
diff --git a/webui/assets/main.js b/webui/assets/main.js
new file mode 100644
index 000000000..4e17db19a
--- /dev/null
+++ b/webui/assets/main.js
@@ -0,0 +1,390 @@
+class REPL {
+ constructor(input, output, button) {
+ this.input = input
+ this.output = output
+ this.button = button
+ this.history = []
+ this.history_index = 0
+ this.history_buffer = ''
+ this.result_number = 0
+ }
+ bind_events() {
+ var repl = this
+ var keys = {
+ TAB: 9,
+ ENTER: 13,
+ UP_ARROW: 38,
+ DOWN_ARROW: 40
+ }
+ var keywords = {
+ // keyword: length of substring that comes after cursor
+ "SetBit()": 1,
+ "ClearBit()": 1,
+ "SetBitmapAttrs()": 1,
+ "Bitmap()": 1,
+ "Union()": 1,
+ "Intersect()": 1,
+ "Difference()": 1,
+ "Count()": 1,
+ "Range()": 1,
+ "TopN()": 1,
+ "frame=": 0,
+ }
+
+ this.input.addEventListener("keydown", function(e) {
+ if (e.keyCode == keys.UP_ARROW) {
+ e.preventDefault()
+ if (repl.input.value.substring(0, repl.input.selectionStart).indexOf('\n') == '-1') {
+ if (repl.history_index == 0) {
+ return
+ } else {
+ if (repl.history_index == repl.history.length) {
+ repl.history_buffer = repl.input.value
+ }
+ repl.history_index--
+ repl.input.value = repl.history[repl.history_index]
+ repl.input.setSelectionRange(repl.input.value.length, repl.input.value.length)
+ }
+ }
+ }
+ if (e.keyCode == keys.DOWN_ARROW) {
+ e.preventDefault()
+ if (repl.input.value.substring(repl.input.selectionEnd, repl.input.length).indexOf('\n') == '-1') {
+ if (repl.history_index == repl.history.length) {
+ return
+ } else {
+ repl.history_index++
+ if (repl.history_index == repl.history.length) {
+ repl.input.value = repl.history_buffer
+ } else {
+ repl.input.value = repl.history[repl.history_index]
+ }
+ repl.input.setSelectionRange(repl.input.value.length, repl.input.value.length)
+ }
+ }
+ }
+ if (e.keyCode == keys.ENTER && !e.shiftKey) {
+ e.preventDefault()
+ repl.submit();
+ }
+ if (e.keyCode == keys.TAB) {
+ e.preventDefault()
+
+ // extract word fragment ending at cursor. a word fragment:
+ // - starts with last nonalpha character before cursor (or beginning of string)
+ // - ends at cursor
+ var word_start = repl.input.selectionEnd-1
+ while(word_start>0) {
+ var c = repl.input.value.charCodeAt(word_start)
+ if(!((c>64 && c<91) || (c>96 && c<123))) {
+ word_start++
+ break
+ }
+ word_start--
+ }
+ var input_word = repl.input.value.substring(word_start, repl.input.selectionEnd)
+
+ // check for keyword match and insert
+ // this just stops at the first match
+ for(var keyword in keywords) {
+ if(keyword.startsWith(input_word)){
+ var cursor_pos = repl.input.selectionEnd
+ var completion = keyword.substring(input_word.length)
+ var before = repl.input.value.substring(0, cursor_pos)
+ var after = repl.input.value.substring(cursor_pos)
+ repl.input.value = before + completion + after
+ var new_pos = cursor_pos + completion.length - keywords[keyword]
+ repl.input.setSelectionRange(new_pos, new_pos)
+ break
+ }
+ }
+ }
+ })
+ repl.button.onclick = function() {
+ repl.submit();
+ };
+ }
+
+ submit() {
+ this.history_buffer = ''
+ this.history_index = this.history.length
+ this.history[this.history_index] = this.input.value
+ this.history_index++
+ this.process_query(this.input.value)
+ this.input.value = ""
+ }
+
+ process_query(query) {
+ var xhr = new XMLHttpRequest();
+ var e = document.getElementById("index-dropdown");
+ var indexname = e.options[e.selectedIndex].text;
+ xhr.open('POST', '/index/' + indexname + '/query');
+ xhr.setRequestHeader('Content-Type', 'application/text');
+
+ var repl = this
+ var start_time = new Date().getTime();
+ xhr.send(query)
+ xhr.onload = function() {
+ var end_time = new Date().getTime()
+ repl.result_number++
+ repl.createSingleOutput({
+ "input": query,
+ "output": xhr.responseText,
+ "indexname": indexname,
+ "querytime_ms": end_time - start_time,
+ })
+ }
+
+ }
+
+ createSingleOutput(res) {
+ var node = document.createElement("div");
+ node.classList.add('output');
+ var output_string = res['output']
+ var output_json = JSON.parse(output_string)
+ var result_class = "result-output"
+ var getting_started_errors = [
+ 'index not found',
+ 'frame not found',
+ ]
+
+ if("error" in output_json) {
+ result_class = "result-error"
+ if(getting_started_errors.indexOf(output_json['error']) >= 0) {
+ output_string += `
+
+ Just getting started? Try this:
+ $ curl -XPOST "http://127.0.0.1:10101/index/test" -d '{"options": {"columnLabel": "col"}}' # create index "test"
+ $ curl -XPOST "http://127.0.0.1:10101/index/test/frame/foo" -d '{"options": {"rowLabel": "row"}}' # create frame "foo"
+ # Select "test" in the index dropdown above
+ SetBit(row=0, col=0, frame=foo) # Use PQL to set a bit
+ `
+ }
+ }
+
+
+ var markup =`
+