From 19b127f9f2185de28db4f51d88b762ec8eec4287 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Tue, 30 Jun 2015 12:07:00 -0700 Subject: [PATCH] Godep updating dependencies --- Godeps/Godeps.json | 25 +- .../src/github.com/gocql/gocql/.gitignore | 4 + .../src/github.com/gocql/gocql/.travis.yml | 42 + .../src/github.com/gocql/gocql/AUTHORS | 51 + .../github.com/gocql/gocql/CONTRIBUTING.md | 78 + .../src/github.com/gocql/gocql/LICENSE | 27 + .../src/github.com/gocql/gocql/README.md | 202 + .../github.com/gocql/gocql/cass1batch_test.go | 60 + .../github.com/gocql/gocql/cassandra_test.go | 1981 +++++ .../src/github.com/gocql/gocql/cluster.go | 111 + .../src/github.com/gocql/gocql/compressor.go | 28 + .../github.com/gocql/gocql/compressor_test.go | 40 + .../src/github.com/gocql/gocql/conn.go | 796 ++ .../src/github.com/gocql/gocql/conn_test.go | 769 ++ .../github.com/gocql/gocql/connectionpool.go | 895 +++ .../src/github.com/gocql/gocql/doc.go | 9 + .../src/github.com/gocql/gocql/errors.go | 88 + .../src/github.com/gocql/gocql/errors_test.go | 29 + .../src/github.com/gocql/gocql/frame.go | 1480 ++++ .../src/github.com/gocql/gocql/frame_test.go | 98 + .../src/github.com/gocql/gocql/fuzz.go | 33 + .../src/github.com/gocql/gocql/helpers.go | 180 + .../src/github.com/gocql/gocql/host_source.go | 119 + .../src/github.com/gocql/gocql/integration.sh | 83 + .../src/github.com/gocql/gocql/marshal.go | 1621 ++++ .../github.com/gocql/gocql/marshal_test.go | 796 ++ .../src/github.com/gocql/gocql/metadata.go | 871 +++ .../github.com/gocql/gocql/metadata_test.go | 802 ++ .../src/github.com/gocql/gocql/policies.go | 244 + .../github.com/gocql/gocql/policies_test.go | 125 + .../src/github.com/gocql/gocql/session.go | 1019 +++ .../github.com/gocql/gocql/session_test.go | 255 + .../gocql/gocql/testdata/pki/.keystore | 3 + .../gocql/gocql/testdata/pki/.truststore | 3 + .../gocql/gocql/testdata/pki/ca.crt | 20 + .../gocql/gocql/testdata/pki/ca.key | 30 + .../gocql/gocql/testdata/pki/cassandra.crt | 83 + .../gocql/gocql/testdata/pki/cassandra.key | 27 + .../gocql/gocql/testdata/pki/gocql.crt | 83 + .../gocql/gocql/testdata/pki/gocql.key | 27 + .../src/github.com/gocql/gocql/token.go | 348 + .../src/github.com/gocql/gocql/token_test.go | 474 ++ .../src/github.com/gocql/gocql/topology.go | 74 + .../github.com/gocql/gocql/topology_test.go | 51 + .../src/github.com/gocql/gocql/tuple_test.go | 51 + .../src/github.com/gocql/gocql/udt_test.go | 254 + .../src/github.com/gocql/gocql/uuid.go | 242 + .../src/github.com/gocql/gocql/uuid_test.go | 197 + .../gocql/website/css/bootstrap-theme.css | 384 + .../gocql/website/css/bootstrap-theme.min.css | 1 + .../gocql/gocql/website/css/bootstrap.css | 6805 +++++++++++++++++ .../gocql/gocql/website/css/bootstrap.min.css | 9 + .../gocql/gocql/website/favicon.ico | 3 + .../fonts/glyphicons-halflings-regular.eot | 3 + .../fonts/glyphicons-halflings-regular.svg | 228 + .../fonts/glyphicons-halflings-regular.ttf | 3 + .../fonts/glyphicons-halflings-regular.woff | 3 + .../github.com/gocql/gocql/website/gocql.png | 3 + .../github.com/gocql/gocql/website/index.html | 133 + .../gocql/gocql/website/js/bootstrap.js | 1999 +++++ .../gocql/gocql/website/js/bootstrap.min.js | 6 + .../src/github.com/gocql/gocql/wiki_test.go | 274 + .../github.com/golang/groupcache/lru/lru.go | 121 + .../golang/groupcache/lru/lru_test.go | 73 + .../github.com/golang/snappy/snappy/decode.go | 292 + .../github.com/golang/snappy/snappy/encode.go | 258 + .../github.com/golang/snappy/snappy/snappy.go | 68 + .../golang/snappy/snappy/snappy_test.go | 364 + .../speter.net/go/exp/math/dec/inf/LICENSE | 57 + .../go/exp/math/dec/inf/benchmark_test.go | 210 + .../src/speter.net/go/exp/math/dec/inf/dec.go | 615 ++ .../go/exp/math/dec/inf/dec_go1_2_test.go | 33 + .../go/exp/math/dec/inf/dec_internal_test.go | 40 + .../go/exp/math/dec/inf/dec_test.go | 379 + .../go/exp/math/dec/inf/example_test.go | 62 + .../speter.net/go/exp/math/dec/inf/rounder.go | 145 + .../exp/math/dec/inf/rounder_example_test.go | 72 + .../go/exp/math/dec/inf/rounder_test.go | 109 + 78 files changed, 27672 insertions(+), 8 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/.gitignore create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/.travis.yml create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/AUTHORS create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/CONTRIBUTING.md create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/LICENSE create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/README.md create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/cass1batch_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/cassandra_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/cluster.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/compressor.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/compressor_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/conn.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/conn_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/connectionpool.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/doc.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/errors.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/errors_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/frame.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/frame_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/fuzz.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/helpers.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/host_source.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/integration.sh create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/marshal.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/marshal_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/metadata.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/metadata_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/policies.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/policies_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/session.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/session_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.keystore create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.truststore create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.crt create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.key create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.crt create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.key create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.crt create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.key create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/token.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/token_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/topology.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/topology_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/tuple_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/udt_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/uuid.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/uuid_test.go create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.css create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.min.css create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.css create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.min.css create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/favicon.ico create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.eot create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.svg create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.ttf create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.woff create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/gocql.png create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/index.html create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/js/bootstrap.js create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/website/js/bootstrap.min.js create mode 100644 Godeps/_workspace/src/github.com/gocql/gocql/wiki_test.go create mode 100644 Godeps/_workspace/src/github.com/golang/groupcache/lru/lru.go create mode 100644 Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go create mode 100644 Godeps/_workspace/src/github.com/golang/snappy/snappy/decode.go create mode 100644 Godeps/_workspace/src/github.com/golang/snappy/snappy/encode.go create mode 100644 Godeps/_workspace/src/github.com/golang/snappy/snappy/snappy.go create mode 100644 Godeps/_workspace/src/github.com/golang/snappy/snappy/snappy_test.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/LICENSE create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/benchmark_test.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_go1_2_test.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_internal_test.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_test.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/example_test.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder_example_test.go create mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 3b0d52184..12e6fa2fc 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,9 +1,6 @@ { "ImportPath": "github.com/hashicorp/vault", "GoVersion": "go1.4.2", - "Packages": [ - "./..." - ], "Deps": [ { "ImportPath": "github.com/armon/go-metrics", @@ -67,16 +64,24 @@ "Comment": "v1-14-g406aa05", "Rev": "406aa05eb8272fb8aa201e410afa6f9fdcb2bf68" }, - { - "ImportPath": "github.com/go-ldap/ldap", - "Comment": "v1-14-g406aa05", - "Rev": "406aa05eb8272fb8aa201e410afa6f9fdcb2bf68" - }, { "ImportPath": "github.com/go-sql-driver/mysql", "Comment": "v1.2-112-gfb72997", "Rev": "fb7299726d2e68745a8805b14f2ff44b5c2cfa84" }, + { + "ImportPath": "github.com/gocql/gocql", + "Comment": "1st_gen_framing-187-g80e812a", + "Rev": "80e812acf0ab386dd34271acc10d22514c0a67ba" + }, + { + "ImportPath": "github.com/golang/groupcache/lru", + "Rev": "604ed5785183e59ae2789449d89e73f3a2a77987" + }, + { + "ImportPath": "github.com/golang/snappy/snappy", + "Rev": "eaa750b9bf4dcb7cb20454be850613b66cda3273" + }, { "ImportPath": "github.com/google/go-github/github", "Rev": "fccd5bb66f985db0a0d150342ca0a9529a23488a" @@ -196,6 +201,10 @@ "ImportPath": "gopkg.in/asn1-ber.v1", "Comment": "v1", "Rev": "9eae18c3681ae3d3c677ac2b80a8fe57de45fc09" + }, + { + "ImportPath": "speter.net/go/exp/math/dec/inf", + "Rev": "42ca6cd68aa922bc3f32f1e056e61b65945d9ad7" } ] } diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/.gitignore b/Godeps/_workspace/src/github.com/gocql/gocql/.gitignore new file mode 100644 index 000000000..22f57be40 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/.gitignore @@ -0,0 +1,4 @@ +gocql-fuzz +fuzz-corpus +fuzz-work +gocql.test \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/.travis.yml b/Godeps/_workspace/src/github.com/gocql/gocql/.travis.yml new file mode 100644 index 000000000..5a5602280 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/.travis.yml @@ -0,0 +1,42 @@ +language: go + +sudo: false + +cache: + directories: + - $HOME/.ccm/repository + +matrix: + fast_finish: true + +env: + global: + - GOMAXPROCS=2 + matrix: + - CASS=1.2.19 AUTH=false + - CASS=2.0.14 AUTH=false + - CASS=2.1.5 AUTH=false + - CASS=2.1.5 AUTH=true + +go: + - 1.3 + - 1.4 + +install: + - pip install --user cql PyYAML six + - go get golang.org/x/tools/cmd/vet + - go get golang.org/x/tools/cmd/cover + - git clone https://github.com/pcmanus/ccm.git + - pushd ccm + - ./setup.py install --user + - popd + - go get . + +script: + - set -e + - go test -v -tags unit + - PATH=$PATH:$HOME/.local/bin bash -x integration.sh $CASS $AUTH + - go vet . + +notifications: + - email: false diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/AUTHORS b/Godeps/_workspace/src/github.com/gocql/gocql/AUTHORS new file mode 100644 index 000000000..1fe81f565 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/AUTHORS @@ -0,0 +1,51 @@ +# This source file refers to The gocql Authors for copyright purposes. + +Christoph Hack +Jonathan Rudenberg +Thorsten von Eicken +Matt Robenolt +Phillip Couto +Niklas Korz +Nimi Wariboko Jr +Ghais Issa +Sasha Klizhentas +Konstantin Cherkasov +Ben Hood <0x6e6562@gmail.com> +Pete Hopkins +Chris Bannister +Maxim Bublis +Alex Zorin +Kasper Middelboe Petersen +Harpreet Sawhney +Charlie Andrews +Stanislavs Koikovs +Dan Forest +Miguel Serrano +Stefan Radomski +Josh Wright +Jacob Rhoden +Ben Frye +Fred McCann +Dan Simmons +Muir Manders +Sankar P +Julien Da Silva +Dan Kennedy +Nick Dhupia +Yasuharu Goto +Jeremy Schlatter +Matthias Kadenbach +Dean Elbaz +Mike Berman +Dmitriy Fedorenko +Zach Marcantel +James Maloney +Ashwin Purohit +Dan Kinder +Oliver Beattie +Justin Corpron +Miles Delahunty +Zach Badgett +Maciek Sakrejda +Jeff Mitchell +Baptiste Fontaine diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/gocql/gocql/CONTRIBUTING.md new file mode 100644 index 000000000..e149322bb --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/CONTRIBUTING.md @@ -0,0 +1,78 @@ +# Contributing to gocql + +**TL;DR** - this manifesto sets out the bare mimimum requirements for submitting a patch to gocql. + +This guide outlines the process of landing patches in gocql and the general approach to maintaining the code base. + +## Background + +The goal of the gocql project is to provide a stable and robust CQL driver for Golang. gocql is a community driven project that is coordinated by a small team of core developers. + +## Minimum Requirement Checklist + +The following is a check list of requirements that need to be satisfied in order for us to merge your patch: + +* You should raise a pull request to gocql/gocql on Github +* The pull request has a title that clearly summarizes the purpose of the patch +* The motivation behind the patch is clearly defined in the pull request summary +* Your name and email have been added to the `AUTHORS` file (for copyright purposes) +* The patch will merge cleanly +* The test coverage does not fall below the critical threshold (currently 64%) +* The merge commit passes the regression test suite on Travis +* `go fmt` has been applied to the submitted code +* Functional changes (i.e. new features or changed behavior) are appropriately documented, either as a godoc or in the README (non-functional changes such as bug fixes may not require documentation) + +If there are any requirements that can't be reasonably satifisfied, please state this either on the pull request or as part of discussion on the mailing list. Where appropriate, the core team may apply discretion and make an exception to these requirements. + +## Beyond The Checklist + +In addition to stating the hard requirements, there are a bunch of things that we consider when assessing changes to the library. These soft requirements are helpful pointers of how to get a patch landed quicker and with less fuss. + +### General QA Approach + +The gocql team needs to consider the ongoing maintainability of the library at all times. Patches that look like they will introduce maintenance issues for the team will not be accepted. + +Your patch will get merged quicker if you have decent test cases that provide test coverage for the new behavior you wish to introduce. + +Unit tests are good, integration tests are even better. An example of a unit test is `marshal_test.go` - this tests the serialization code in isolation. `cassandra_test.go` is an integration test suite that is executed against every version of Cassandra that gocql supports as part of the CI process on Travis. + +That said, the point of writing tests is to provide a safety net to catch regressions, so there is no need to go overboard with tests. Remember that the more tests you write, the more code we will have to maintain. So there's a balance to strike there. + +### When It's Too Difficult To Automate Testing + +There are legitimate examples of where it is infeasible to write a regression test for a change. Never fear, we will still consider the patch and quite possibly accept the change without a test. The gocql team takes a pragmatic approach to testing. At the end of the day, you could be addressing an issue that is too difficult to reproduce in a test suite, but still occurs in a real production app. In this case, your production app is the test case, and we will have to trust that your change is good. + +Examples of pull requests that have been accepted without tests include: + +* https://github.com/gocql/gocql/pull/181 - this patch would otherwise require a multi-node cluster to be booted as part of the CI build +* https://github.com/gocql/gocql/pull/179 - this bug can only be reproduced under heavy load in certain circumstances + +### Sign Off Procedure + +Generally speaking, a pull request can get merged by any one of the core gocql team. If your change is minor, chances are that one team member will just go ahead and merge it there and then. As stated earlier, suitable test coverage will increase the likelihood that a single reviewer will assess and merge your change. If your change has no test coverage, or looks like it may have wider implications for the health and stability of the library, the reviewer may elect to refer the change to another team member to acheive consensus before proceeding. Therefore, the tighter and cleaner your patch is, the quicker it will go through the review process. + +### Supported Features + +gocql is a low level wire driver for Cassandra CQL. By and large, we would like to keep the functional scope of the library as narrow as possible. We think that gocql should be tight and focussed, and we will be naturally sceptical of things that could just as easily be implemented in a higher layer. Inevitably you will come accross something that could be implemented in a higher layer, save for a minor change to the core API. In this instance, please strike up a conversation with the gocql team. Chances are we will understand what you are trying to acheive and will try to accommodate this in a maintainable way. + +### Longer Term Evolution + +There are some long term plans for gocql that have to be taken into account when assessing changes. That said, gocql is ultimately a community driven project and we don't have a massive development budget, so sometimes the long term view might need to be de-prioritized ahead of short term changes. + +## Officially Supported Server Versions + +Currently, the officiallly supported versions of the Cassandra server include: + +* 1.2.18 +* 2.0.9 + +Chances are that gocql will work with many other versions. If you would like us to support a particular version of Cassandra, please start a conversation about what version you'd like us to consider. We are more likely to accept a new version if you help out by extending the regression suite to cover the new version to be supported. + +## The Core Dev Team + +The core development team includes: + +* tux21b +* phillipCouto +* Zariel +* 0x6e6562 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/LICENSE b/Godeps/_workspace/src/github.com/gocql/gocql/LICENSE new file mode 100644 index 000000000..18d25f923 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The gocql Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/README.md b/Godeps/_workspace/src/github.com/gocql/gocql/README.md new file mode 100644 index 000000000..635382da5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/README.md @@ -0,0 +1,202 @@ +gocql +===== + +[![Join the chat at https://gitter.im/gocql/gocql](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gocql/gocql?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/gocql/gocql.png?branch=master)](https://travis-ci.org/gocql/gocql) +[![GoDoc](http://godoc.org/github.com/gocql/gocql?status.png)](http://godoc.org/github.com/gocql/gocql) + +Package gocql implements a fast and robust Cassandra client for the +Go programming language. + +Project Website: http://gocql.github.io/
+API documentation: http://godoc.org/github.com/gocql/gocql
+Discussions: https://groups.google.com/forum/#!forum/gocql + +Production Stability +--------- +The underlying framing code was rewritten as part of [#339](https://github.com/gocql/gocql/pull/339) and as such may have +unforseen bugs. If you run into a bug related to wire framing, please raise a ticket and we will try to resolve this as soon as we can. If you require a stable version to pin your production app against, we have tagged the previous stable version in source code, so you can build against this. The tag is called 1st_gen_framing ([180456fef0a3c6d02c51dc7211f49b55e9315867](https://github.com/gocql/gocql/commit/180456fef0a3c6d02c51dc7211f49b55e9315867)). This note will be removed as the new generation framing code base matures. + +Supported Versions +------------------ + +The following matrix shows the versions of Go and Cassandra that are tested with the integration test suite as part of the CI build: + +Go/Cassandra | 1.2.19 | 2.0.14 | 2.1.5 +-------------| -------| ------| --------- +1.3 | yes | yes | yes +1.4 | yes | yes | yes + + +Sunsetting Model +---------------- + +In general, the gocql team will focus on supporting the current and previous versions of Golang. gocql may still work with older versions of Golang, but offical support for these versions will have been sunset. + +Installation +------------ + + go get github.com/gocql/gocql + + +Features +-------- + +* Modern Cassandra client using the native transport +* Automatic type conversations between Cassandra and Go + * Support for all common types including sets, lists and maps + * Custom types can implement a `Marshaler` and `Unmarshaler` interface + * Strict type conversations without any loss of precision + * Built-In support for UUIDs (version 1 and 4) +* Support for logged, unlogged and counter batches +* Cluster management + * Automatic reconnect on connection failures with exponential falloff + * Round robin distribution of queries to different hosts + * Round robin distribution of queries to different connections on a host + * Each connection can execute up to n concurrent queries (whereby n is the limit set by the protocol version the client chooses to use) + * Optional automatic discovery of nodes + * Optional support for periodic node discovery via system.peers + * Policy based connection pool with token aware and round-robin policy implementations +* Support for password authentication +* Iteration over paged results with configurable page size +* Support for TLS/SSL +* Optional frame compression (using snappy) +* Automatic query preparation +* Support for query tracing +* Experimental support for [binary protocol version 3](https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v3.spec) + * Support for up to 32768 streams + * Support for tuple types + * Support for client side timestamps by default + * Support for UDTs via a custom marshaller or struct tags +* An API to access the schema metadata of a given keyspace + +Please visit the [Roadmap](https://github.com/gocql/gocql/wiki/Roadmap) page to see what is on the horizion. + +Important Default Keyspace Changes +---------------------------------- +gocql no longer supports executing "use " statements to simplfy the library. The user still has the +ability to define the default keyspace for connections but now the keyspace can only be defined before a +session is created. Queries can still access keyspaces by indicating the keyspace in the query: +```sql +SELECT * FROM example2.table; +``` + +Example of correct usage: +```go + cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") + cluster.Keyspace = "example" + ... + session, err := cluster.CreateSession() + +``` +Example of incorrect usage: +```go + cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") + cluster.Keyspace = "example" + ... + session, err := cluster.CreateSession() + + if err = session.Query("use example2").Exec(); err != nil { + log.Fatal(err) + } +``` +This will result in an err being returned from the session.Query line as the user is trying to execute a "use" +statement. + +Example +------- + +```go +/* Before you execute the program, Launch `cqlsh` and execute: +create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; +create table example.tweet(timeline text, id UUID, text text, PRIMARY KEY(id)); +create index on example.tweet(timeline); +*/ +package main + +import ( + "fmt" + "log" + + "github.com/gocql/gocql" +) + +func main() { + // connect to the cluster + cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") + cluster.Keyspace = "example" + cluster.Consistency = gocql.Quorum + session, _ := cluster.CreateSession() + defer session.Close() + + // insert a tweet + if err := session.Query(`INSERT INTO tweet (timeline, id, text) VALUES (?, ?, ?)`, + "me", gocql.TimeUUID(), "hello world").Exec(); err != nil { + log.Fatal(err) + } + + var id gocql.UUID + var text string + + /* Search for a specific set of records whose 'timeline' column matches + * the value 'me'. The secondary index that we created earlier will be + * used for optimizing the search */ + if err := session.Query(`SELECT id, text FROM tweet WHERE timeline = ? LIMIT 1`, + "me").Consistency(gocql.One).Scan(&id, &text); err != nil { + log.Fatal(err) + } + fmt.Println("Tweet:", id, text) + + // list all tweets + iter := session.Query(`SELECT id, text FROM tweet WHERE timeline = ?`, "me").Iter() + for iter.Scan(&id, &text) { + fmt.Println("Tweet:", id, text) + } + if err := iter.Close(); err != nil { + log.Fatal(err) + } +} +``` + +Data Binding +------------ + +There are various ways to bind application level data structures to CQL statements: + +* You can write the data binding by hand, as outlined in the Tweet example. This provides you with the greatest flexibility, but it does mean that you need to keep your application code in sync with your Cassandra schema. +* You can dynamically marshal an entire query result into an `[]map[string]interface{}` using the `SliceMap()` API. This returns a slice of row maps keyed by CQL column mames. This method requires no special interaction with the gocql API, but it does require your application to be able to deal with a key value view of your data. +* As a refinement on the `SliceMap()` API you can also call `MapScan()` which returns `map[string]interface{}` instances in a row by row fashion. +* The `Bind()` API provides a client app with a low level mechanism to introspect query meta data and extract appropriate field values from application level data structures. +* Building on top of the gocql driver, [cqlr](https://github.com/relops/cqlr) adds the ability to auto-bind a CQL iterator to a struct or to bind a struct to an INSERT statement. +* Another external project that layers on top of gocql is [cqlc](http://relops.com/cqlc) which generates gocql compliant code from your Cassandra schema so that you can write type safe CQL statements in Go with a natural query syntax. +* [gocassa](https://github.com/hailocab/gocassa) is an external project that layers on top of gocql to provide convenient query building and data binding. +* [gocqltable](https://github.com/elvtechnology/gocqltable) provides an ORM-style convenience layer to make CRUD operations with gocql easier. + +Ecosphere +--------- + +The following community maintained tools are known to integrate with gocql: + +* [migrate](https://github.com/mattes/migrate) is a migration handling tool written in Go with Cassandra support. +* [negronicql](https://github.com/mikebthun/negronicql) is gocql middleware for Negroni. +* [cqlr](https://github.com/relops/cqlr) adds the ability to auto-bind a CQL iterator to a struct or to bind a struct to an INSERT statement. +* [cqlc](http://relops.com/cqlc) which generates gocql compliant code from your Cassandra schema so that you can write type safe CQL statements in Go with a natural query syntax. +* [gocassa](https://github.com/hailocab/gocassa) provides query building, adds data binding, and provides easy-to-use "recipe" tables for common query use-cases. +* [gocqltable](https://github.com/elvtechnology/gocqltable) is a wrapper around gocql that aims to simplify common operations whilst working the library. + +Other Projects +-------------- + +* [gocqldriver](https://github.com/tux21b/gocqldriver) is the predecessor of gocql based on Go's "database/sql" package. This project isn't maintained anymore, because Cassandra wasn't a good fit for the traditional "database/sql" API. Use this package instead. + +SEO +--- + +For some reason, when you google `golang cassandra`, this project doesn't feature very highly in the result list. But if you google `go cassandra`, then we're a bit higher up the list. So this is note to try to convince Google that Golang is an alias for Go. + +License +------- + +> Copyright (c) 2012-2015 The gocql Authors. All rights reserved. +> Use of this source code is governed by a BSD-style +> license that can be found in the LICENSE file. diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/cass1batch_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/cass1batch_test.go new file mode 100644 index 000000000..35750a41d --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/cass1batch_test.go @@ -0,0 +1,60 @@ +// +build all integration + +package gocql + +import ( + "strings" + "testing" +) + +func TestProto1BatchInsert(t *testing.T) { + session := createSession(t) + if err := session.Query("CREATE TABLE large (id int primary key)").Exec(); err != nil { + t.Fatal("create table:", err) + } + defer session.Close() + + begin := "BEGIN BATCH" + end := "APPLY BATCH" + query := "INSERT INTO large (id) VALUES (?)" + fullQuery := strings.Join([]string{begin, query, end}, "\n") + args := []interface{}{5} + if err := session.Query(fullQuery, args...).Consistency(Quorum).Exec(); err != nil { + t.Fatal(err) + } + +} + +func TestShouldPrepareFunction(t *testing.T) { + var shouldPrepareTests = []struct { + Stmt string + Result bool + }{ + {` + BEGIN BATCH + INSERT INTO users (userID, password) + VALUES ('smith', 'secret') + APPLY BATCH + ; + `, true}, + {`INSERT INTO users (userID, password, name) VALUES ('user2', 'ch@ngem3b', 'second user')`, true}, + {`BEGIN COUNTER BATCH UPDATE stats SET views = views + 1 WHERE pageid = 1 APPLY BATCH`, true}, + {`delete name from users where userID = 'smith';`, true}, + {` UPDATE users SET password = 'secret' WHERE userID = 'smith' `, true}, + {`CREATE TABLE users ( + user_name varchar PRIMARY KEY, + password varchar, + gender varchar, + session_token varchar, + state varchar, + birth_year bigint + );`, false}, + } + + for _, test := range shouldPrepareTests { + q := &Query{stmt: test.Stmt} + if got := q.shouldPrepare(); got != test.Result { + t.Fatalf("%q: got %v, expected %v\n", test.Stmt, got, test.Result) + } + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/cassandra_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/cassandra_test.go new file mode 100644 index 000000000..9f34e6e3c --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/cassandra_test.go @@ -0,0 +1,1981 @@ +// +build all integration + +package gocql + +import ( + "bytes" + "flag" + "fmt" + "log" + "math" + "math/big" + "net" + "reflect" + "strconv" + "strings" + "sync" + "testing" + "time" + "unicode" + + "speter.net/go/exp/math/dec/inf" +) + +var ( + flagCluster = flag.String("cluster", "127.0.0.1", "a comma-separated list of host:port tuples") + flagProto = flag.Int("proto", 2, "protcol version") + flagCQL = flag.String("cql", "3.0.0", "CQL version") + flagRF = flag.Int("rf", 1, "replication factor for test keyspace") + clusterSize = flag.Int("clusterSize", 1, "the expected size of the cluster") + flagRetry = flag.Int("retries", 5, "number of times to retry queries") + flagAutoWait = flag.Duration("autowait", 1000*time.Millisecond, "time to wait for autodiscovery to fill the hosts poll") + flagRunSslTest = flag.Bool("runssl", false, "Set to true to run ssl test") + flagRunAuthTest = flag.Bool("runauth", false, "Set to true to run authentication test") + flagCompressTest = flag.String("compressor", "", "compressor to use") + clusterHosts []string +) + +func init() { + flag.Parse() + clusterHosts = strings.Split(*flagCluster, ",") + log.SetFlags(log.Lshortfile | log.LstdFlags) +} + +func addSslOptions(cluster *ClusterConfig) *ClusterConfig { + if *flagRunSslTest { + cluster.SslOpts = &SslOptions{ + CertPath: "testdata/pki/gocql.crt", + KeyPath: "testdata/pki/gocql.key", + CaPath: "testdata/pki/ca.crt", + EnableHostVerification: false, + } + } + return cluster +} + +var initOnce sync.Once + +func createTable(s *Session, table string) error { + err := s.Query(table).Consistency(All).Exec() + if *clusterSize > 1 { + // wait for table definition to propogate + time.Sleep(1 * time.Second) + } + return err +} + +func createCluster() *ClusterConfig { + cluster := NewCluster(clusterHosts...) + cluster.ProtoVersion = *flagProto + cluster.CQLVersion = *flagCQL + cluster.Timeout = 5 * time.Second + cluster.Consistency = Quorum + if *flagRetry > 0 { + cluster.RetryPolicy = &SimpleRetryPolicy{NumRetries: *flagRetry} + } + + switch *flagCompressTest { + case "snappy": + cluster.Compressor = &SnappyCompressor{} + case "": + default: + panic("invalid compressor: " + *flagCompressTest) + } + + cluster = addSslOptions(cluster) + return cluster +} + +func createKeyspace(tb testing.TB, cluster *ClusterConfig, keyspace string) { + session, err := cluster.CreateSession() + if err != nil { + tb.Fatal("createSession:", err) + } + defer session.Close() + if err = session.Query(`DROP KEYSPACE IF EXISTS ` + keyspace).Exec(); err != nil { + tb.Log("drop keyspace:", err) + } + if err := session.Query(fmt.Sprintf(`CREATE KEYSPACE %s + WITH replication = { + 'class' : 'SimpleStrategy', + 'replication_factor' : %d + }`, keyspace, *flagRF)).Consistency(All).Exec(); err != nil { + tb.Fatalf("error creating keyspace %s: %v", keyspace, err) + } + tb.Logf("Created keyspace %s", keyspace) +} + +func createSession(tb testing.TB) *Session { + cluster := createCluster() + + // Drop and re-create the keyspace once. Different tests should use their own + // individual tables, but can assume that the table does not exist before. + initOnce.Do(func() { + createKeyspace(tb, cluster, "gocql_test") + }) + + cluster.Keyspace = "gocql_test" + session, err := cluster.CreateSession() + if err != nil { + tb.Fatal("createSession:", err) + } + + return session +} + +// TestAuthentication verifies that gocql will work with a host configured to only accept authenticated connections +func TestAuthentication(t *testing.T) { + + if *flagProto < 2 { + t.Skip("Authentication is not supported with protocol < 2") + } + + if !*flagRunAuthTest { + t.Skip("Authentication is not configured in the target cluster") + } + + cluster := createCluster() + + cluster.Authenticator = PasswordAuthenticator{ + Username: "cassandra", + Password: "cassandra", + } + + session, err := cluster.CreateSession() + + if err != nil { + t.Fatalf("Authentication error: %s", err) + } + + session.Close() +} + +//TestRingDiscovery makes sure that you can autodiscover other cluster members when you seed a cluster config with just one node +func TestRingDiscovery(t *testing.T) { + cluster := createCluster() + cluster.Hosts = clusterHosts[:1] + cluster.DiscoverHosts = true + + session, err := cluster.CreateSession() + if err != nil { + t.Fatalf("got error connecting to the cluster %v", err) + } + + if *clusterSize > 1 { + // wait for autodiscovery to update the pool with the list of known hosts + time.Sleep(*flagAutoWait) + } + + size := len(session.Pool.(*SimplePool).connPool) + + if *clusterSize != size { + t.Logf("WARN: Expected a cluster size of %d, but actual size was %d", *clusterSize, size) + } + + session.Close() +} + +func TestEmptyHosts(t *testing.T) { + cluster := createCluster() + cluster.Hosts = nil + if session, err := cluster.CreateSession(); err == nil { + session.Close() + t.Error("expected err, got nil") + } +} + +//TestUseStatementError checks to make sure the correct error is returned when the user tries to execute a use statement. +func TestUseStatementError(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := session.Query("USE gocql_test").Exec(); err != nil { + if err != ErrUseStmt { + t.Error("expected ErrUseStmt, got " + err.Error()) + } + } else { + t.Error("expected err, got nil.") + } +} + +//TestInvalidKeyspace checks that an invalid keyspace will return promptly and without a flood of connections +func TestInvalidKeyspace(t *testing.T) { + cluster := createCluster() + cluster.Keyspace = "invalidKeyspace" + session, err := cluster.CreateSession() + if err != nil { + if err != ErrNoConnectionsStarted { + t.Errorf("Expected ErrNoConnections but got %v", err) + } + } else { + session.Close() //Clean up the session + t.Error("expected err, got nil.") + } +} + +func TestTracing(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE trace (id int primary key)`); err != nil { + t.Fatal("create:", err) + } + + buf := &bytes.Buffer{} + trace := NewTraceWriter(session, buf) + + if err := session.Query(`INSERT INTO trace (id) VALUES (?)`, 42).Trace(trace).Exec(); err != nil { + t.Error("insert:", err) + } else if buf.Len() == 0 { + t.Error("insert: failed to obtain any tracing") + } + buf.Reset() + + var value int + if err := session.Query(`SELECT id FROM trace WHERE id = ?`, 42).Trace(trace).Scan(&value); err != nil { + t.Error("select:", err) + } else if value != 42 { + t.Errorf("value: expected %d, got %d", 42, value) + } else if buf.Len() == 0 { + t.Error("select: failed to obtain any tracing") + } +} + +func TestPaging(t *testing.T) { + if *flagProto == 1 { + t.Skip("Paging not supported. Please use Cassandra >= 2.0") + } + + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE paging (id int primary key)"); err != nil { + t.Fatal("create table:", err) + } + for i := 0; i < 100; i++ { + if err := session.Query("INSERT INTO paging (id) VALUES (?)", i).Exec(); err != nil { + t.Fatal("insert:", err) + } + } + + iter := session.Query("SELECT id FROM paging").PageSize(10).Iter() + var id int + count := 0 + for iter.Scan(&id) { + count++ + } + if err := iter.Close(); err != nil { + t.Fatal("close:", err) + } + if count != 100 { + t.Fatalf("expected %d, got %d", 100, count) + } +} + +func TestCAS(t *testing.T) { + if *flagProto == 1 { + t.Skip("lightweight transactions not supported. Please use Cassandra >= 2.0") + } + + session := createSession(t) + defer session.Close() + session.cfg.SerialConsistency = LocalSerial + + if err := createTable(session, `CREATE TABLE cas_table ( + title varchar, + revid timeuuid, + last_modified timestamp, + PRIMARY KEY (title, revid) + )`); err != nil { + t.Fatal("create:", err) + } + + title, revid, modified := "baz", TimeUUID(), time.Now() + var titleCAS string + var revidCAS UUID + var modifiedCAS time.Time + + if applied, err := session.Query(`INSERT INTO cas_table (title, revid, last_modified) + VALUES (?, ?, ?) IF NOT EXISTS`, + title, revid, modified).ScanCAS(&titleCAS, &revidCAS, &modifiedCAS); err != nil { + t.Fatal("insert:", err) + } else if !applied { + t.Fatal("insert should have been applied") + } + + if applied, err := session.Query(`INSERT INTO cas_table (title, revid, last_modified) + VALUES (?, ?, ?) IF NOT EXISTS`, + title, revid, modified).ScanCAS(&titleCAS, &revidCAS, &modifiedCAS); err != nil { + t.Fatal("insert:", err) + } else if applied { + t.Fatal("insert should not have been applied") + } else if title != titleCAS || revid != revidCAS { + t.Fatalf("expected %s/%v/%v but got %s/%v/%v", title, revid, modified, titleCAS, revidCAS, modifiedCAS) + } + + tenSecondsLater := modified.Add(10 * time.Second) + + if applied, err := session.Query(`DELETE FROM cas_table WHERE title = ? and revid = ? IF last_modified = ?`, + title, revid, tenSecondsLater).ScanCAS(&modifiedCAS); err != nil { + t.Fatal("delete:", err) + } else if applied { + t.Fatal("delete should have not been applied") + } + + if modifiedCAS.Unix() != tenSecondsLater.Add(-10*time.Second).Unix() { + t.Fatalf("Was expecting modified CAS to be %v; but was one second later", modifiedCAS.UTC()) + } + + if _, err := session.Query(`DELETE FROM cas_table WHERE title = ? and revid = ? IF last_modified = ?`, + title, revid, tenSecondsLater).ScanCAS(); err.Error() != "count mismatch" { + t.Fatalf("delete: was expecting count mismatch error but got %s", err) + } + + if applied, err := session.Query(`DELETE FROM cas_table WHERE title = ? and revid = ? IF last_modified = ?`, + title, revid, modified).ScanCAS(&modifiedCAS); err != nil { + t.Fatal("delete:", err) + } else if !applied { + t.Fatal("delete should have been applied") + } +} + +func TestMapScanCAS(t *testing.T) { + if *flagProto == 1 { + t.Skip("lightweight transactions not supported. Please use Cassandra >= 2.0") + } + + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE cas_table2 ( + title varchar, + revid timeuuid, + last_modified timestamp, + deleted boolean, + PRIMARY KEY (title, revid) + )`); err != nil { + t.Fatal("create:", err) + } + + title, revid, modified, deleted := "baz", TimeUUID(), time.Now(), false + mapCAS := map[string]interface{}{} + + if applied, err := session.Query(`INSERT INTO cas_table2 (title, revid, last_modified, deleted) + VALUES (?, ?, ?, ?) IF NOT EXISTS`, + title, revid, modified, deleted).MapScanCAS(mapCAS); err != nil { + t.Fatal("insert:", err) + } else if !applied { + t.Fatal("insert should have been applied") + } + + mapCAS = map[string]interface{}{} + if applied, err := session.Query(`INSERT INTO cas_table2 (title, revid, last_modified, deleted) + VALUES (?, ?, ?, ?) IF NOT EXISTS`, + title, revid, modified, deleted).MapScanCAS(mapCAS); err != nil { + t.Fatal("insert:", err) + } else if applied { + t.Fatal("insert should not have been applied") + } else if title != mapCAS["title"] || revid != mapCAS["revid"] || deleted != mapCAS["deleted"] { + t.Fatalf("expected %s/%v/%v/%v but got %s/%v/%v%v", title, revid, modified, false, mapCAS["title"], mapCAS["revid"], mapCAS["last_modified"], mapCAS["deleted"]) + } + +} + +func TestBatch(t *testing.T) { + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE batch_table (id int primary key)`); err != nil { + t.Fatal("create table:", err) + } + + batch := NewBatch(LoggedBatch) + for i := 0; i < 100; i++ { + batch.Query(`INSERT INTO batch_table (id) VALUES (?)`, i) + } + if err := session.ExecuteBatch(batch); err != nil { + t.Fatal("execute batch:", err) + } + + count := 0 + if err := session.Query(`SELECT COUNT(*) FROM batch_table`).Scan(&count); err != nil { + t.Fatal("select count:", err) + } else if count != 100 { + t.Fatalf("count: expected %d, got %d\n", 100, count) + } +} + +func TestUnpreparedBatch(t *testing.T) { + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE batch_unprepared (id int primary key, c counter)`); err != nil { + t.Fatal("create table:", err) + } + + var batch *Batch + if *flagProto == 2 { + batch = NewBatch(CounterBatch) + } else { + batch = NewBatch(UnloggedBatch) + } + + for i := 0; i < 100; i++ { + batch.Query(`UPDATE batch_unprepared SET c = c + 1 WHERE id = 1`) + } + + if err := session.ExecuteBatch(batch); err != nil { + t.Fatal("execute batch:", err) + } + + count := 0 + if err := session.Query(`SELECT COUNT(*) FROM batch_unprepared`).Scan(&count); err != nil { + t.Fatal("select count:", err) + } else if count != 1 { + t.Fatalf("count: expected %d, got %d\n", 100, count) + } + + if err := session.Query(`SELECT c FROM batch_unprepared`).Scan(&count); err != nil { + t.Fatal("select count:", err) + } else if count != 100 { + t.Fatalf("count: expected %d, got %d\n", 100, count) + } +} + +// TestBatchLimit tests gocql to make sure batch operations larger than the maximum +// statement limit are not submitted to a cassandra node. +func TestBatchLimit(t *testing.T) { + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE batch_table2 (id int primary key)`); err != nil { + t.Fatal("create table:", err) + } + + batch := NewBatch(LoggedBatch) + for i := 0; i < 65537; i++ { + batch.Query(`INSERT INTO batch_table2 (id) VALUES (?)`, i) + } + if err := session.ExecuteBatch(batch); err != ErrTooManyStmts { + t.Fatal("gocql attempted to execute a batch larger than the support limit of statements.") + } + +} + +func TestWhereIn(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE where_in_table (id int, cluster int, primary key (id,cluster))`); err != nil { + t.Fatal("create table:", err) + } + + if err := session.Query("INSERT INTO where_in_table (id, cluster) VALUES (?,?)", 100, 200).Exec(); err != nil { + t.Fatal("insert:", err) + } + + iter := session.Query("SELECT * FROM where_in_table WHERE id = ? AND cluster IN (?)", 100, 200).Iter() + var id, cluster int + count := 0 + for iter.Scan(&id, &cluster) { + count++ + } + + if id != 100 || cluster != 200 { + t.Fatalf("Was expecting id and cluster to be (100,200) but were (%d,%d)", id, cluster) + } +} + +// TestTooManyQueryArgs tests to make sure the library correctly handles the application level bug +// whereby too many query arguments are passed to a query +func TestTooManyQueryArgs(t *testing.T) { + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE too_many_query_args (id int primary key, value int)`); err != nil { + t.Fatal("create table:", err) + } + + _, err := session.Query(`SELECT * FROM too_many_query_args WHERE id = ?`, 1, 2).Iter().SliceMap() + + if err == nil { + t.Fatal("'`SELECT * FROM too_many_query_args WHERE id = ?`, 1, 2' should return an ErrQueryArgLength") + } + + if err != ErrQueryArgLength { + t.Fatalf("'`SELECT * FROM too_many_query_args WHERE id = ?`, 1, 2' should return an ErrQueryArgLength, but returned: %s", err) + } + + batch := session.NewBatch(UnloggedBatch) + batch.Query("INSERT INTO too_many_query_args (id, value) VALUES (?, ?)", 1, 2, 3) + err = session.ExecuteBatch(batch) + + if err == nil { + t.Fatal("'`INSERT INTO too_many_query_args (id, value) VALUES (?, ?)`, 1, 2, 3' should return an ErrQueryArgLength") + } + + if err != ErrQueryArgLength { + t.Fatalf("'INSERT INTO too_many_query_args (id, value) VALUES (?, ?)`, 1, 2, 3' should return an ErrQueryArgLength, but returned: %s", err) + } + +} + +// TestNotEnoughQueryArgs tests to make sure the library correctly handles the application level bug +// whereby not enough query arguments are passed to a query +func TestNotEnoughQueryArgs(t *testing.T) { + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE not_enough_query_args (id int, cluster int, value int, primary key (id, cluster))`); err != nil { + t.Fatal("create table:", err) + } + + _, err := session.Query(`SELECT * FROM not_enough_query_args WHERE id = ? and cluster = ?`, 1).Iter().SliceMap() + + if err == nil { + t.Fatal("'`SELECT * FROM not_enough_query_args WHERE id = ? and cluster = ?`, 1' should return an ErrQueryArgLength") + } + + if err != ErrQueryArgLength { + t.Fatalf("'`SELECT * FROM too_few_query_args WHERE id = ? and cluster = ?`, 1' should return an ErrQueryArgLength, but returned: %s", err) + } + + batch := session.NewBatch(UnloggedBatch) + batch.Query("INSERT INTO not_enough_query_args (id, cluster, value) VALUES (?, ?, ?)", 1, 2) + err = session.ExecuteBatch(batch) + + if err == nil { + t.Fatal("'`INSERT INTO not_enough_query_args (id, cluster, value) VALUES (?, ?, ?)`, 1, 2' should return an ErrQueryArgLength") + } + + if err != ErrQueryArgLength { + t.Fatalf("'INSERT INTO not_enough_query_args (id, cluster, value) VALUES (?, ?, ?)`, 1, 2' should return an ErrQueryArgLength, but returned: %s", err) + } +} + +// TestCreateSessionTimeout tests to make sure the CreateSession function timeouts out correctly +// and prevents an infinite loop of connection retries. +func TestCreateSessionTimeout(t *testing.T) { + go func() { + <-time.After(2 * time.Second) + t.Error("no startup timeout") + }() + + cluster := createCluster() + cluster.Hosts = []string{"127.0.0.1:1"} + session, err := cluster.CreateSession() + if err == nil { + session.Close() + t.Fatal("expected ErrNoConnectionsStarted, but no error was returned.") + } + + if err != ErrNoConnectionsStarted { + t.Fatalf("expected ErrNoConnectionsStarted, but received %v", err) + } +} + +type FullName struct { + FirstName string + LastName string +} + +func (n FullName) MarshalCQL(info TypeInfo) ([]byte, error) { + return []byte(n.FirstName + " " + n.LastName), nil +} + +func (n *FullName) UnmarshalCQL(info TypeInfo, data []byte) error { + t := strings.SplitN(string(data), " ", 2) + n.FirstName, n.LastName = t[0], t[1] + return nil +} + +func TestMapScanWithRefMap(t *testing.T) { + session := createSession(t) + defer session.Close() + if err := createTable(session, `CREATE TABLE scan_map_ref_table ( + testtext text PRIMARY KEY, + testfullname text, + testint int, + )`); err != nil { + t.Fatal("create table:", err) + } + m := make(map[string]interface{}) + m["testtext"] = "testtext" + m["testfullname"] = FullName{"John", "Doe"} + m["testint"] = 100 + + if err := session.Query(`INSERT INTO scan_map_ref_table (testtext, testfullname, testint) values (?,?,?)`, m["testtext"], m["testfullname"], m["testint"]).Exec(); err != nil { + t.Fatal("insert:", err) + } + + var testText string + var testFullName FullName + ret := map[string]interface{}{ + "testtext": &testText, + "testfullname": &testFullName, + // testint is not set here. + } + iter := session.Query(`SELECT * FROM scan_map_ref_table`).Iter() + if ok := iter.MapScan(ret); !ok { + t.Fatal("select:", iter.Close()) + } else { + if ret["testtext"] != "testtext" { + t.Fatal("returned testtext did not match") + } + f := ret["testfullname"].(FullName) + if f.FirstName != "John" || f.LastName != "Doe" { + t.Fatal("returned testfullname did not match") + } + if ret["testint"] != 100 { + t.Fatal("returned testinit did not match") + } + } + +} + +func TestSliceMap(t *testing.T) { + session := createSession(t) + defer session.Close() + if err := createTable(session, `CREATE TABLE slice_map_table ( + testuuid timeuuid PRIMARY KEY, + testtimestamp timestamp, + testvarchar varchar, + testbigint bigint, + testblob blob, + testbool boolean, + testfloat float, + testdouble double, + testint int, + testdecimal decimal, + testlist list, + testset set, + testmap map, + testvarint varint, + testinet inet + )`); err != nil { + t.Fatal("create table:", err) + } + m := make(map[string]interface{}) + + bigInt := new(big.Int) + if _, ok := bigInt.SetString("830169365738487321165427203929228", 10); !ok { + t.Fatal("Failed setting bigint by string") + } + + m["testuuid"] = TimeUUID() + m["testvarchar"] = "Test VarChar" + m["testbigint"] = time.Now().Unix() + m["testtimestamp"] = time.Now().Truncate(time.Millisecond).UTC() + m["testblob"] = []byte("test blob") + m["testbool"] = true + m["testfloat"] = float32(4.564) + m["testdouble"] = float64(4.815162342) + m["testint"] = 2343 + m["testdecimal"] = inf.NewDec(100, 0) + m["testlist"] = []string{"quux", "foo", "bar", "baz", "quux"} + m["testset"] = []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + m["testmap"] = map[string]string{"field1": "val1", "field2": "val2", "field3": "val3"} + m["testvarint"] = bigInt + m["testinet"] = "213.212.2.19" + sliceMap := []map[string]interface{}{m} + if err := session.Query(`INSERT INTO slice_map_table (testuuid, testtimestamp, testvarchar, testbigint, testblob, testbool, testfloat, testdouble, testint, testdecimal, testlist, testset, testmap, testvarint, testinet) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + m["testuuid"], m["testtimestamp"], m["testvarchar"], m["testbigint"], m["testblob"], m["testbool"], m["testfloat"], m["testdouble"], m["testint"], m["testdecimal"], m["testlist"], m["testset"], m["testmap"], m["testvarint"], m["testinet"]).Exec(); err != nil { + t.Fatal("insert:", err) + } + if returned, retErr := session.Query(`SELECT * FROM slice_map_table`).Iter().SliceMap(); retErr != nil { + t.Fatal("select:", retErr) + } else { + matchSliceMap(t, sliceMap, returned[0]) + } + + // Test for Iter.MapScan() + { + testMap := make(map[string]interface{}) + if !session.Query(`SELECT * FROM slice_map_table`).Iter().MapScan(testMap) { + t.Fatal("MapScan failed to work with one row") + } + matchSliceMap(t, sliceMap, testMap) + } + + // Test for Query.MapScan() + { + testMap := make(map[string]interface{}) + if session.Query(`SELECT * FROM slice_map_table`).MapScan(testMap) != nil { + t.Fatal("MapScan failed to work with one row") + } + matchSliceMap(t, sliceMap, testMap) + } +} +func matchSliceMap(t *testing.T, sliceMap []map[string]interface{}, testMap map[string]interface{}) { + if sliceMap[0]["testuuid"] != testMap["testuuid"] { + t.Fatal("returned testuuid did not match") + } + if sliceMap[0]["testtimestamp"] != testMap["testtimestamp"] { + t.Fatal("returned testtimestamp did not match") + } + if sliceMap[0]["testvarchar"] != testMap["testvarchar"] { + t.Fatal("returned testvarchar did not match") + } + if sliceMap[0]["testbigint"] != testMap["testbigint"] { + t.Fatal("returned testbigint did not match") + } + if !reflect.DeepEqual(sliceMap[0]["testblob"], testMap["testblob"]) { + t.Fatal("returned testblob did not match") + } + if sliceMap[0]["testbool"] != testMap["testbool"] { + t.Fatal("returned testbool did not match") + } + if sliceMap[0]["testfloat"] != testMap["testfloat"] { + t.Fatal("returned testfloat did not match") + } + if sliceMap[0]["testdouble"] != testMap["testdouble"] { + t.Fatal("returned testdouble did not match") + } + if sliceMap[0]["testinet"] != testMap["testinet"] { + t.Fatal("returned testinet did not match") + } + + expectedDecimal := sliceMap[0]["testdecimal"].(*inf.Dec) + returnedDecimal := testMap["testdecimal"].(*inf.Dec) + + if expectedDecimal.Cmp(returnedDecimal) != 0 { + t.Fatal("returned testdecimal did not match") + } + + if !reflect.DeepEqual(sliceMap[0]["testlist"], testMap["testlist"]) { + t.Fatal("returned testlist did not match") + } + if !reflect.DeepEqual(sliceMap[0]["testset"], testMap["testset"]) { + t.Fatal("returned testset did not match") + } + if !reflect.DeepEqual(sliceMap[0]["testmap"], testMap["testmap"]) { + t.Fatal("returned testmap did not match") + } + if sliceMap[0]["testint"] != testMap["testint"] { + t.Fatal("returned testint did not match") + } +} + +func TestScanWithNilArguments(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE scan_with_nil_arguments ( + foo varchar, + bar int, + PRIMARY KEY (foo, bar) + )`); err != nil { + t.Fatal("create:", err) + } + for i := 1; i <= 20; i++ { + if err := session.Query("INSERT INTO scan_with_nil_arguments (foo, bar) VALUES (?, ?)", + "squares", i*i).Exec(); err != nil { + t.Fatal("insert:", err) + } + } + + iter := session.Query("SELECT * FROM scan_with_nil_arguments WHERE foo = ?", "squares").Iter() + var n int + count := 0 + for iter.Scan(nil, &n) { + count += n + } + if err := iter.Close(); err != nil { + t.Fatal("close:", err) + } + if count != 2870 { + t.Fatalf("expected %d, got %d", 2870, count) + } +} + +func TestScanCASWithNilArguments(t *testing.T) { + if *flagProto == 1 { + t.Skip("lightweight transactions not supported. Please use Cassandra >= 2.0") + } + + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE scan_cas_with_nil_arguments ( + foo varchar, + bar varchar, + PRIMARY KEY (foo, bar) + )`); err != nil { + t.Fatal("create:", err) + } + + foo := "baz" + var cas string + + if applied, err := session.Query(`INSERT INTO scan_cas_with_nil_arguments (foo, bar) + VALUES (?, ?) IF NOT EXISTS`, + foo, foo).ScanCAS(nil, nil); err != nil { + t.Fatal("insert:", err) + } else if !applied { + t.Fatal("insert should have been applied") + } + + if applied, err := session.Query(`INSERT INTO scan_cas_with_nil_arguments (foo, bar) + VALUES (?, ?) IF NOT EXISTS`, + foo, foo).ScanCAS(&cas, nil); err != nil { + t.Fatal("insert:", err) + } else if applied { + t.Fatal("insert should not have been applied") + } else if foo != cas { + t.Fatalf("expected %v but got %v", foo, cas) + } + + if applied, err := session.Query(`INSERT INTO scan_cas_with_nil_arguments (foo, bar) + VALUES (?, ?) IF NOT EXISTS`, + foo, foo).ScanCAS(nil, &cas); err != nil { + t.Fatal("insert:", err) + } else if applied { + t.Fatal("insert should not have been applied") + } else if foo != cas { + t.Fatalf("expected %v but got %v", foo, cas) + } +} + +func TestRebindQueryInfo(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE rebind_query (id int, value text, PRIMARY KEY (id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + if err := session.Query("INSERT INTO rebind_query (id, value) VALUES (?, ?)", 23, "quux").Exec(); err != nil { + t.Fatalf("insert into rebind_query failed, err '%v'", err) + } + + if err := session.Query("INSERT INTO rebind_query (id, value) VALUES (?, ?)", 24, "w00t").Exec(); err != nil { + t.Fatalf("insert into rebind_query failed, err '%v'", err) + } + + q := session.Query("SELECT value FROM rebind_query WHERE ID = ?") + q.Bind(23) + + iter := q.Iter() + var value string + for iter.Scan(&value) { + } + + if value != "quux" { + t.Fatalf("expected %v but got %v", "quux", value) + } + + q.Bind(24) + iter = q.Iter() + + for iter.Scan(&value) { + } + + if value != "w00t" { + t.Fatalf("expected %v but got %v", "quux", value) + } +} + +//TestStaticQueryInfo makes sure that the application can manually bind query parameters using the simplest possible static binding strategy +func TestStaticQueryInfo(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE static_query_info (id int, value text, PRIMARY KEY (id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + if err := session.Query("INSERT INTO static_query_info (id, value) VALUES (?, ?)", 113, "foo").Exec(); err != nil { + t.Fatalf("insert into static_query_info failed, err '%v'", err) + } + + autobinder := func(q *QueryInfo) ([]interface{}, error) { + values := make([]interface{}, 1) + values[0] = 113 + return values, nil + } + + qry := session.Bind("SELECT id, value FROM static_query_info WHERE id = ?", autobinder) + + if err := qry.Exec(); err != nil { + t.Fatalf("expose query info failed, error '%v'", err) + } + + iter := qry.Iter() + + var id int + var value string + + iter.Scan(&id, &value) + + if err := iter.Close(); err != nil { + t.Fatalf("query with exposed info failed, err '%v'", err) + } + + if value != "foo" { + t.Fatalf("Expected value %s, but got %s", "foo", value) + } + +} + +type ClusteredKeyValue struct { + Id int + Cluster int + Value string +} + +func (kv *ClusteredKeyValue) Bind(q *QueryInfo) ([]interface{}, error) { + values := make([]interface{}, len(q.Args)) + + for i, info := range q.Args { + fieldName := upcaseInitial(info.Name) + value := reflect.ValueOf(kv) + field := reflect.Indirect(value).FieldByName(fieldName) + values[i] = field.Addr().Interface() + } + + return values, nil +} + +func upcaseInitial(str string) string { + for i, v := range str { + return string(unicode.ToUpper(v)) + str[i+1:] + } + return "" +} + +//TestBoundQueryInfo makes sure that the application can manually bind query parameters using the query meta data supplied at runtime +func TestBoundQueryInfo(t *testing.T) { + + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE clustered_query_info (id int, cluster int, value text, PRIMARY KEY (id, cluster))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + write := &ClusteredKeyValue{Id: 200, Cluster: 300, Value: "baz"} + + insert := session.Bind("INSERT INTO clustered_query_info (id, cluster, value) VALUES (?, ?,?)", write.Bind) + + if err := insert.Exec(); err != nil { + t.Fatalf("insert into clustered_query_info failed, err '%v'", err) + } + + read := &ClusteredKeyValue{Id: 200, Cluster: 300} + + qry := session.Bind("SELECT id, cluster, value FROM clustered_query_info WHERE id = ? and cluster = ?", read.Bind) + + iter := qry.Iter() + + var id, cluster int + var value string + + iter.Scan(&id, &cluster, &value) + + if err := iter.Close(); err != nil { + t.Fatalf("query with clustered_query_info info failed, err '%v'", err) + } + + if value != "baz" { + t.Fatalf("Expected value %s, but got %s", "baz", value) + } + +} + +//TestBatchQueryInfo makes sure that the application can manually bind query parameters when executing in a batch +func TestBatchQueryInfo(t *testing.T) { + + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE batch_query_info (id int, cluster int, value text, PRIMARY KEY (id, cluster))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + write := func(q *QueryInfo) ([]interface{}, error) { + values := make([]interface{}, 3) + values[0] = 4000 + values[1] = 5000 + values[2] = "bar" + return values, nil + } + + batch := session.NewBatch(LoggedBatch) + batch.Bind("INSERT INTO batch_query_info (id, cluster, value) VALUES (?, ?,?)", write) + + if err := session.ExecuteBatch(batch); err != nil { + t.Fatalf("batch insert into batch_query_info failed, err '%v'", err) + } + + read := func(q *QueryInfo) ([]interface{}, error) { + values := make([]interface{}, 2) + values[0] = 4000 + values[1] = 5000 + return values, nil + } + + qry := session.Bind("SELECT id, cluster, value FROM batch_query_info WHERE id = ? and cluster = ?", read) + + iter := qry.Iter() + + var id, cluster int + var value string + + iter.Scan(&id, &cluster, &value) + + if err := iter.Close(); err != nil { + t.Fatalf("query with batch_query_info info failed, err '%v'", err) + } + + if value != "bar" { + t.Fatalf("Expected value %s, but got %s", "bar", value) + } +} + +func injectInvalidPreparedStatement(t *testing.T, session *Session, table string) (string, *Conn) { + if err := createTable(session, `CREATE TABLE `+table+` ( + foo varchar, + bar int, + PRIMARY KEY (foo, bar) + )`); err != nil { + t.Fatal("create:", err) + } + stmt := "INSERT INTO " + table + " (foo, bar) VALUES (?, 7)" + conn := session.Pool.Pick(nil) + flight := new(inflightPrepare) + stmtsLRU.Lock() + stmtsLRU.lru.Add(conn.addr+stmt, flight) + stmtsLRU.Unlock() + flight.info = &resultPreparedFrame{ + preparedID: []byte{'f', 'o', 'o', 'b', 'a', 'r'}, + reqMeta: resultMetadata{ + columns: []ColumnInfo{ + { + Keyspace: "gocql_test", + Table: table, + Name: "foo", + TypeInfo: NativeType{ + typ: TypeVarchar, + }, + }, + }}, + } + return stmt, conn +} + +func TestMissingSchemaPrepare(t *testing.T) { + s := createSession(t) + conn := s.Pool.Pick(nil) + defer s.Close() + + insertQry := &Query{stmt: "INSERT INTO invalidschemaprep (val) VALUES (?)", values: []interface{}{5}, cons: s.cons, + session: s, pageSize: s.pageSize, trace: s.trace, + prefetch: s.prefetch, rt: s.cfg.RetryPolicy} + + if err := conn.executeQuery(insertQry).err; err == nil { + t.Fatal("expected error, but got nil.") + } + + if err := createTable(s, "CREATE TABLE invalidschemaprep (val int, PRIMARY KEY (val))"); err != nil { + t.Fatal("create table:", err) + } + + if err := conn.executeQuery(insertQry).err; err != nil { + t.Fatal(err) // unconfigured columnfamily + } +} + +func TestReprepareStatement(t *testing.T) { + session := createSession(t) + defer session.Close() + stmt, conn := injectInvalidPreparedStatement(t, session, "test_reprepare_statement") + query := session.Query(stmt, "bar") + if err := conn.executeQuery(query).Close(); err != nil { + t.Fatalf("Failed to execute query for reprepare statement: %v", err) + } +} + +func TestReprepareBatch(t *testing.T) { + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + session := createSession(t) + defer session.Close() + stmt, conn := injectInvalidPreparedStatement(t, session, "test_reprepare_statement_batch") + batch := session.NewBatch(UnloggedBatch) + batch.Query(stmt, "bar") + if err := conn.executeBatch(batch); err != nil { + t.Fatalf("Failed to execute query for reprepare statement: %v", err) + } + +} + +func TestQueryInfo(t *testing.T) { + session := createSession(t) + defer session.Close() + + conn := session.Pool.Pick(nil) + info, err := conn.prepareStatement("SELECT release_version, host_id FROM system.local WHERE key = ?", nil) + + if err != nil { + t.Fatalf("Failed to execute query for preparing statement: %v", err) + } + + if len(info.reqMeta.columns) != 1 { + t.Fatalf("Was not expecting meta data for %d query arguments, but got %d\n", 1, len(info.reqMeta.columns)) + } + + if *flagProto > 1 { + if len(info.respMeta.columns) != 2 { + t.Fatalf("Was not expecting meta data for %d result columns, but got %d\n", 2, len(info.respMeta.columns)) + } + } +} + +//TestPreparedCacheEviction will make sure that the cache size is maintained +func TestPreparedCacheEviction(t *testing.T) { + session := createSession(t) + defer session.Close() + stmtsLRU.Lock() + stmtsLRU.Max(4) + stmtsLRU.Unlock() + + if err := createTable(session, "CREATE TABLE prepcachetest (id int,mod int,PRIMARY KEY (id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + //Fill the table + for i := 0; i < 2; i++ { + if err := session.Query("INSERT INTO prepcachetest (id,mod) VALUES (?, ?)", i, 10000%(i+1)).Exec(); err != nil { + t.Fatalf("insert into prepcachetest failed, err '%v'", err) + } + } + //Populate the prepared statement cache with select statements + var id, mod int + for i := 0; i < 2; i++ { + err := session.Query("SELECT id,mod FROM prepcachetest WHERE id = "+strconv.FormatInt(int64(i), 10)).Scan(&id, &mod) + if err != nil { + t.Fatalf("select from prepcachetest failed, error '%v'", err) + } + } + + //generate an update statement to test they are prepared + err := session.Query("UPDATE prepcachetest SET mod = ? WHERE id = ?", 1, 11).Exec() + if err != nil { + t.Fatalf("update prepcachetest failed, error '%v'", err) + } + + //generate a delete statement to test they are prepared + err = session.Query("DELETE FROM prepcachetest WHERE id = ?", 1).Exec() + if err != nil { + t.Fatalf("delete from prepcachetest failed, error '%v'", err) + } + + //generate an insert statement to test they are prepared + err = session.Query("INSERT INTO prepcachetest (id,mod) VALUES (?, ?)", 3, 11).Exec() + if err != nil { + t.Fatalf("insert into prepcachetest failed, error '%v'", err) + } + + stmtsLRU.Lock() + + //Make sure the cache size is maintained + if stmtsLRU.lru.Len() != stmtsLRU.lru.MaxEntries { + t.Fatalf("expected cache size of %v, got %v", stmtsLRU.lru.MaxEntries, stmtsLRU.lru.Len()) + } + + //Walk through all the configured hosts and test cache retention and eviction + var selFound, insFound, updFound, delFound, selEvict bool + for i := range session.cfg.Hosts { + _, ok := stmtsLRU.lru.Get(session.cfg.Hosts[i] + ":9042gocql_testSELECT id,mod FROM prepcachetest WHERE id = 1") + selFound = selFound || ok + + _, ok = stmtsLRU.lru.Get(session.cfg.Hosts[i] + ":9042gocql_testINSERT INTO prepcachetest (id,mod) VALUES (?, ?)") + insFound = insFound || ok + + _, ok = stmtsLRU.lru.Get(session.cfg.Hosts[i] + ":9042gocql_testUPDATE prepcachetest SET mod = ? WHERE id = ?") + updFound = updFound || ok + + _, ok = stmtsLRU.lru.Get(session.cfg.Hosts[i] + ":9042gocql_testDELETE FROM prepcachetest WHERE id = ?") + delFound = delFound || ok + + _, ok = stmtsLRU.lru.Get(session.cfg.Hosts[i] + ":9042gocql_testSELECT id,mod FROM prepcachetest WHERE id = 0") + selEvict = selEvict || !ok + } + + stmtsLRU.Unlock() + + if !selEvict { + t.Fatalf("expected first select statement to be purged, but statement was found in the cache.") + } + if !selFound { + t.Fatalf("expected second select statement to be cached, but statement was purged or not prepared.") + } + if !insFound { + t.Fatalf("expected insert statement to be cached, but statement was purged or not prepared.") + } + if !updFound { + t.Fatalf("expected update statement to be cached, but statement was purged or not prepared.") + } + if !delFound { + t.Error("expected delete statement to be cached, but statement was purged or not prepared.") + } +} + +func TestPreparedCacheKey(t *testing.T) { + session := createSession(t) + defer session.Close() + + // create a second keyspace + cluster2 := createCluster() + createKeyspace(t, cluster2, "gocql_test2") + cluster2.Keyspace = "gocql_test2" + session2, err := cluster2.CreateSession() + if err != nil { + t.Fatal("create session:", err) + } + defer session2.Close() + + // both keyspaces have a table named "test_stmt_cache_key" + if err := createTable(session, "CREATE TABLE test_stmt_cache_key (id varchar primary key, field varchar)"); err != nil { + t.Fatal("create table:", err) + } + if err := createTable(session2, "CREATE TABLE test_stmt_cache_key (id varchar primary key, field varchar)"); err != nil { + t.Fatal("create table:", err) + } + + // both tables have a single row with the same partition key but different column value + if err = session.Query(`INSERT INTO test_stmt_cache_key (id, field) VALUES (?, ?)`, "key", "one").Exec(); err != nil { + t.Fatal("insert:", err) + } + if err = session2.Query(`INSERT INTO test_stmt_cache_key (id, field) VALUES (?, ?)`, "key", "two").Exec(); err != nil { + t.Fatal("insert:", err) + } + + // should be able to see different values in each keyspace + var value string + if err = session.Query("SELECT field FROM test_stmt_cache_key WHERE id = ?", "key").Scan(&value); err != nil { + t.Fatal("select:", err) + } + if value != "one" { + t.Errorf("Expected one, got %s", value) + } + + if err = session2.Query("SELECT field FROM test_stmt_cache_key WHERE id = ?", "key").Scan(&value); err != nil { + t.Fatal("select:", err) + } + if value != "two" { + t.Errorf("Expected two, got %s", value) + } +} + +//TestMarshalFloat64Ptr tests to see that a pointer to a float64 is marshalled correctly. +func TestMarshalFloat64Ptr(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE float_test (id double, test double, primary key (id))"); err != nil { + t.Fatal("create table:", err) + } + testNum := float64(7500) + if err := session.Query(`INSERT INTO float_test (id,test) VALUES (?,?)`, float64(7500.00), &testNum).Exec(); err != nil { + t.Fatal("insert float64:", err) + } +} + +//TestMarshalInet tests to see that a pointer to a float64 is marshalled correctly. +func TestMarshalInet(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE inet_test (ip inet, name text, primary key (ip))"); err != nil { + t.Fatal("create table:", err) + } + stringIp := "123.34.45.56" + if err := session.Query(`INSERT INTO inet_test (ip,name) VALUES (?,?)`, stringIp, "Test IP 1").Exec(); err != nil { + t.Fatal("insert string inet:", err) + } + var stringResult string + if err := session.Query("SELECT ip FROM inet_test").Scan(&stringResult); err != nil { + t.Fatalf("select for string from inet_test 1 failed: %v", err) + } + if stringResult != stringIp { + t.Errorf("Expected %s, was %s", stringIp, stringResult) + } + + var ipResult net.IP + if err := session.Query("SELECT ip FROM inet_test").Scan(&ipResult); err != nil { + t.Fatalf("select for net.IP from inet_test 1 failed: %v", err) + } + if ipResult.String() != stringIp { + t.Errorf("Expected %s, was %s", stringIp, ipResult.String()) + } + + if err := session.Query(`DELETE FROM inet_test WHERE ip = ?`, stringIp).Exec(); err != nil { + t.Fatal("delete inet table:", err) + } + + netIp := net.ParseIP("222.43.54.65") + if err := session.Query(`INSERT INTO inet_test (ip,name) VALUES (?,?)`, netIp, "Test IP 2").Exec(); err != nil { + t.Fatal("insert netIp inet:", err) + } + + if err := session.Query("SELECT ip FROM inet_test").Scan(&stringResult); err != nil { + t.Fatalf("select for string from inet_test 2 failed: %v", err) + } + if stringResult != netIp.String() { + t.Errorf("Expected %s, was %s", netIp.String(), stringResult) + } + if err := session.Query("SELECT ip FROM inet_test").Scan(&ipResult); err != nil { + t.Fatalf("select for net.IP from inet_test 2 failed: %v", err) + } + if ipResult.String() != netIp.String() { + t.Errorf("Expected %s, was %s", netIp.String(), ipResult.String()) + } + +} + +func TestVarint(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE varint_test (id varchar, test varint, test2 varint, primary key (id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + if err := session.Query(`INSERT INTO varint_test (id, test) VALUES (?, ?)`, "id", 0).Exec(); err != nil { + t.Fatalf("insert varint: %v", err) + } + + var result int + if err := session.Query("SELECT test FROM varint_test").Scan(&result); err != nil { + t.Fatalf("select from varint_test failed: %v", err) + } + + if result != 0 { + t.Errorf("Expected 0, was %d", result) + } + + if err := session.Query(`INSERT INTO varint_test (id, test) VALUES (?, ?)`, "id", -1).Exec(); err != nil { + t.Fatalf("insert varint: %v", err) + } + + if err := session.Query("SELECT test FROM varint_test").Scan(&result); err != nil { + t.Fatalf("select from varint_test failed: %v", err) + } + + if result != -1 { + t.Errorf("Expected -1, was %d", result) + } + + if err := session.Query(`INSERT INTO varint_test (id, test) VALUES (?, ?)`, "id", int64(math.MaxInt32)+1).Exec(); err != nil { + t.Fatalf("insert varint: %v", err) + } + + var result64 int64 + if err := session.Query("SELECT test FROM varint_test").Scan(&result64); err != nil { + t.Fatalf("select from varint_test failed: %v", err) + } + + if result64 != int64(math.MaxInt32)+1 { + t.Errorf("Expected %d, was %d", int64(math.MaxInt32)+1, result64) + } + + biggie := new(big.Int) + biggie.SetString("36893488147419103232", 10) // > 2**64 + if err := session.Query(`INSERT INTO varint_test (id, test) VALUES (?, ?)`, "id", biggie).Exec(); err != nil { + t.Fatalf("insert varint: %v", err) + } + + resultBig := new(big.Int) + if err := session.Query("SELECT test FROM varint_test").Scan(resultBig); err != nil { + t.Fatalf("select from varint_test failed: %v", err) + } + + if resultBig.String() != biggie.String() { + t.Errorf("Expected %s, was %s", biggie.String(), resultBig.String()) + } + + err := session.Query("SELECT test FROM varint_test").Scan(&result64) + if err == nil || strings.Index(err.Error(), "out of range") == -1 { + t.Errorf("expected out of range error since value is too big for int64") + } + + // value not set in cassandra, leave bind variable empty + resultBig = new(big.Int) + if err := session.Query("SELECT test2 FROM varint_test").Scan(resultBig); err != nil { + t.Fatalf("select from varint_test failed: %v", err) + } + + if resultBig.Int64() != 0 { + t.Errorf("Expected %s, was %s", biggie.String(), resultBig.String()) + } + + // can use double pointer to explicitly detect value is not set in cassandra + if err := session.Query("SELECT test2 FROM varint_test").Scan(&resultBig); err != nil { + t.Fatalf("select from varint_test failed: %v", err) + } + + if resultBig != nil { + t.Errorf("Expected %v, was %v", nil, *resultBig) + } +} + +//TestQueryStats confirms that the stats are returning valid data. Accuracy may be questionable. +func TestQueryStats(t *testing.T) { + session := createSession(t) + defer session.Close() + qry := session.Query("SELECT * FROM system.peers") + if err := qry.Exec(); err != nil { + t.Fatalf("query failed. %v", err) + } else { + if qry.Attempts() < 1 { + t.Fatal("expected at least 1 attempt, but got 0") + } + if qry.Latency() <= 0 { + t.Fatalf("expected latency to be greater than 0, but got %v instead.", qry.Latency()) + } + } +} + +//TestBatchStats confirms that the stats are returning valid data. Accuracy may be questionable. +func TestBatchStats(t *testing.T) { + if *flagProto == 1 { + t.Skip("atomic batches not supported. Please use Cassandra >= 2.0") + } + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE batchStats (id int, PRIMARY KEY (id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + b := session.NewBatch(LoggedBatch) + b.Query("INSERT INTO batchStats (id) VALUES (?)", 1) + b.Query("INSERT INTO batchStats (id) VALUES (?)", 2) + + if err := session.ExecuteBatch(b); err != nil { + t.Fatalf("query failed. %v", err) + } else { + if b.Attempts() < 1 { + t.Fatal("expected at least 1 attempt, but got 0") + } + if b.Latency() <= 0 { + t.Fatalf("expected latency to be greater than 0, but got %v instead.", b.Latency()) + } + } +} + +//TestNilInQuery tests to see that a nil value passed to a query is handled by Cassandra +//TODO validate the nil value by reading back the nil. Need to fix Unmarshalling. +func TestNilInQuery(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE testNilInsert (id int, count int, PRIMARY KEY (id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + if err := session.Query("INSERT INTO testNilInsert (id,count) VALUES (?,?)", 1, nil).Exec(); err != nil { + t.Fatalf("failed to insert with err: %v", err) + } + + var id int + + if err := session.Query("SELECT id FROM testNilInsert").Scan(&id); err != nil { + t.Fatalf("failed to select with err: %v", err) + } else if id != 1 { + t.Fatalf("expected id to be 1, got %v", id) + } +} + +// Don't initialize time.Time bind variable if cassandra timestamp column is empty +func TestEmptyTimestamp(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE test_empty_timestamp (id int, time timestamp, num int, PRIMARY KEY (id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + if err := session.Query("INSERT INTO test_empty_timestamp (id, num) VALUES (?,?)", 1, 561).Exec(); err != nil { + t.Fatalf("failed to insert with err: %v", err) + } + + var timeVal time.Time + + if err := session.Query("SELECT time FROM test_empty_timestamp where id = ?", 1).Scan(&timeVal); err != nil { + t.Fatalf("failed to select with err: %v", err) + } + + if !timeVal.IsZero() { + t.Errorf("time.Time bind variable should still be empty (was %s)", timeVal) + } +} + +// Integration test of just querying for data from the system.schema_keyspace table +func TestGetKeyspaceMetadata(t *testing.T) { + session := createSession(t) + defer session.Close() + + keyspaceMetadata, err := getKeyspaceMetadata(session, "gocql_test") + if err != nil { + t.Fatalf("failed to query the keyspace metadata with err: %v", err) + } + if keyspaceMetadata == nil { + t.Fatal("failed to query the keyspace metadata, nil returned") + } + if keyspaceMetadata.Name != "gocql_test" { + t.Errorf("Expected keyspace name to be 'gocql' but was '%s'", keyspaceMetadata.Name) + } + if keyspaceMetadata.StrategyClass != "org.apache.cassandra.locator.SimpleStrategy" { + t.Errorf("Expected replication strategy class to be 'org.apache.cassandra.locator.SimpleStrategy' but was '%s'", keyspaceMetadata.StrategyClass) + } + if keyspaceMetadata.StrategyOptions == nil { + t.Error("Expected replication strategy options map but was nil") + } + rfStr, ok := keyspaceMetadata.StrategyOptions["replication_factor"] + if !ok { + t.Fatalf("Expected strategy option 'replication_factor' but was not found in %v", keyspaceMetadata.StrategyOptions) + } + rfInt, err := strconv.Atoi(rfStr.(string)) + if err != nil { + t.Fatalf("Error converting string to int with err: %v", err) + } + if rfInt != *flagRF { + t.Errorf("Expected replication factor to be %d but was %d", *flagRF, rfInt) + } +} + +// Integration test of just querying for data from the system.schema_columnfamilies table +func TestGetTableMetadata(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE test_table_metadata (first_id int, second_id int, third_id int, PRIMARY KEY (first_id, second_id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + tables, err := getTableMetadata(session, "gocql_test") + if err != nil { + t.Fatalf("failed to query the table metadata with err: %v", err) + } + if tables == nil { + t.Fatal("failed to query the table metadata, nil returned") + } + + var testTable *TableMetadata + + // verify all tables have minimum expected data + for i := range tables { + table := &tables[i] + + if table.Name == "" { + t.Errorf("Expected table name to be set, but it was empty: index=%d metadata=%+v", i, table) + } + if table.Keyspace != "gocql_test" { + t.Errorf("Expected keyspace for '%d' table metadata to be 'gocql_test' but was '%s'", table.Name, table.Keyspace) + } + if table.KeyValidator == "" { + t.Errorf("Expected key validator to be set for table %s", table.Name) + } + if table.Comparator == "" { + t.Errorf("Expected comparator to be set for table %s", table.Name) + } + if table.DefaultValidator == "" { + t.Errorf("Expected default validator to be set for table %s", table.Name) + } + + // these fields are not set until the metadata is compiled + if table.PartitionKey != nil { + t.Errorf("Did not expect partition key for table %s", table.Name) + } + if table.ClusteringColumns != nil { + t.Errorf("Did not expect clustering columns for table %s", table.Name) + } + if table.Columns != nil { + t.Errorf("Did not expect columns for table %s", table.Name) + } + + // for the next part of the test after this loop, find the metadata for the test table + if table.Name == "test_table_metadata" { + testTable = table + } + } + + // verify actual values on the test tables + if testTable == nil { + t.Fatal("Expected table metadata for name 'test_table_metadata'") + } + if testTable.KeyValidator != "org.apache.cassandra.db.marshal.Int32Type" { + t.Errorf("Expected test_table_metadata key validator to be 'org.apache.cassandra.db.marshal.Int32Type' but was '%s'", testTable.KeyValidator) + } + if testTable.Comparator != "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.UTF8Type)" { + t.Errorf("Expected test_table_metadata key validator to be 'org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.UTF8Type)' but was '%s'", testTable.Comparator) + } + if testTable.DefaultValidator != "org.apache.cassandra.db.marshal.BytesType" { + t.Errorf("Expected test_table_metadata key validator to be 'org.apache.cassandra.db.marshal.BytesType' but was '%s'", testTable.DefaultValidator) + } + expectedKeyAliases := []string{"first_id"} + if !reflect.DeepEqual(testTable.KeyAliases, expectedKeyAliases) { + t.Errorf("Expected key aliases %v but was %v", expectedKeyAliases, testTable.KeyAliases) + } + expectedColumnAliases := []string{"second_id"} + if !reflect.DeepEqual(testTable.ColumnAliases, expectedColumnAliases) { + t.Errorf("Expected key aliases %v but was %v", expectedColumnAliases, testTable.ColumnAliases) + } + if testTable.ValueAlias != "" { + t.Errorf("Expected value alias '' but was '%s'", testTable.ValueAlias) + } +} + +// Integration test of just querying for data from the system.schema_columns table +func TestGetColumnMetadata(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE test_column_metadata (first_id int, second_id int, third_id int, PRIMARY KEY (first_id, second_id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + if err := session.Query("CREATE INDEX index_column_metadata ON test_column_metadata ( third_id )").Exec(); err != nil { + t.Fatalf("failed to create index with err: %v", err) + } + + columns, err := getColumnMetadata(session, "gocql_test") + if err != nil { + t.Fatalf("failed to query column metadata with err: %v", err) + } + if columns == nil { + t.Fatal("failed to query column metadata, nil returned") + } + + testColumns := map[string]*ColumnMetadata{} + + // verify actual values on the test columns + for i := range columns { + column := &columns[i] + + if column.Name == "" { + t.Errorf("Expected column name to be set, but it was empty: index=%d metadata=%+v", i, column) + } + if column.Table == "" { + t.Errorf("Expected column %s table name to be set, but it was empty", column.Name) + } + if column.Keyspace != "gocql_test" { + t.Errorf("Expected column %s keyspace name to be 'gocql_test', but it was '%s'", column.Name, column.Keyspace) + } + if column.Kind == "" { + t.Errorf("Expected column %s kind to be set, but it was empty", column.Name) + } + if session.cfg.ProtoVersion == 1 && column.Kind != "regular" { + t.Errorf("Expected column %s kind to be set to 'regular' for proto V1 but it was '%s'", column.Name, column.Kind) + } + if column.Validator == "" { + t.Errorf("Expected column %s validator to be set, but it was empty", column.Name) + } + + // find the test table columns for the next step after this loop + if column.Table == "test_column_metadata" { + testColumns[column.Name] = column + } + } + + if *flagProto == 1 { + // V1 proto only returns "regular columns" + if len(testColumns) != 1 { + t.Errorf("Expected 1 test columns but there were %d", len(testColumns)) + } + thirdID, found := testColumns["third_id"] + if !found { + t.Fatalf("Expected to find column 'third_id' metadata but there was only %v", testColumns) + } + + if thirdID.Kind != REGULAR { + t.Errorf("Expected %s column kind to be '%s' but it was '%s'", thirdID.Name, REGULAR, thirdID.Kind) + } + + if thirdID.Index.Name != "index_column_metadata" { + t.Errorf("Expected %s column index name to be 'index_column_metadata' but it was '%s'", thirdID.Name, thirdID.Index.Name) + } + } else { + if len(testColumns) != 3 { + t.Errorf("Expected 3 test columns but there were %d", len(testColumns)) + } + firstID, found := testColumns["first_id"] + if !found { + t.Fatalf("Expected to find column 'first_id' metadata but there was only %v", testColumns) + } + secondID, found := testColumns["second_id"] + if !found { + t.Fatalf("Expected to find column 'second_id' metadata but there was only %v", testColumns) + } + thirdID, found := testColumns["third_id"] + if !found { + t.Fatalf("Expected to find column 'third_id' metadata but there was only %v", testColumns) + } + + if firstID.Kind != PARTITION_KEY { + t.Errorf("Expected %s column kind to be '%s' but it was '%s'", firstID.Name, PARTITION_KEY, firstID.Kind) + } + if secondID.Kind != CLUSTERING_KEY { + t.Errorf("Expected %s column kind to be '%s' but it was '%s'", secondID.Name, CLUSTERING_KEY, secondID.Kind) + } + if thirdID.Kind != REGULAR { + t.Errorf("Expected %s column kind to be '%s' but it was '%s'", thirdID.Name, REGULAR, thirdID.Kind) + } + + if thirdID.Index.Name != "index_column_metadata" { + t.Errorf("Expected %s column index name to be 'index_column_metadata' but it was '%s'", thirdID.Name, thirdID.Index.Name) + } + } +} + +// Integration test of querying and composition the keyspace metadata +func TestKeyspaceMetadata(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE test_metadata (first_id int, second_id int, third_id int, PRIMARY KEY (first_id, second_id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + if err := session.Query("CREATE INDEX index_metadata ON test_metadata ( third_id )").Exec(); err != nil { + t.Fatalf("failed to create index with err: %v", err) + } + + keyspaceMetadata, err := session.KeyspaceMetadata("gocql_test") + if err != nil { + t.Fatalf("failed to query keyspace metadata with err: %v", err) + } + if keyspaceMetadata == nil { + t.Fatal("expected the keyspace metadata to not be nil, but it was nil") + } + if keyspaceMetadata.Name != session.cfg.Keyspace { + t.Fatalf("Expected the keyspace name to be %s but was %s", session.cfg.Keyspace, keyspaceMetadata.Name) + } + if len(keyspaceMetadata.Tables) == 0 { + t.Errorf("Expected tables but there were none") + } + + tableMetadata, found := keyspaceMetadata.Tables["test_metadata"] + if !found { + t.Fatalf("failed to find the test_metadata table metadata") + } + + if len(tableMetadata.PartitionKey) != 1 { + t.Errorf("expected partition key length of 1, but was %d", len(tableMetadata.PartitionKey)) + } + for i, column := range tableMetadata.PartitionKey { + if column == nil { + t.Errorf("partition key column metadata at index %d was nil", i) + } + } + if tableMetadata.PartitionKey[0].Name != "first_id" { + t.Errorf("Expected the first partition key column to be 'first_id' but was '%s'", tableMetadata.PartitionKey[0].Name) + } + if len(tableMetadata.ClusteringColumns) != 1 { + t.Fatalf("expected clustering columns length of 1, but was %d", len(tableMetadata.ClusteringColumns)) + } + for i, column := range tableMetadata.ClusteringColumns { + if column == nil { + t.Fatalf("clustering column metadata at index %d was nil", i) + } + } + if tableMetadata.ClusteringColumns[0].Name != "second_id" { + t.Errorf("Expected the first clustering column to be 'second_id' but was '%s'", tableMetadata.ClusteringColumns[0].Name) + } + thirdColumn, found := tableMetadata.Columns["third_id"] + if !found { + t.Fatalf("Expected a column definition for 'third_id'") + } + if thirdColumn.Index.Name != "index_metadata" { + t.Errorf("Expected column index named 'index_metadata' but was '%s'", thirdColumn.Index.Name) + } +} + +// Integration test of the routing key calculation +func TestRoutingKey(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, "CREATE TABLE test_single_routing_key (first_id int, second_id int, PRIMARY KEY (first_id, second_id))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + if err := createTable(session, "CREATE TABLE test_composite_routing_key (first_id int, second_id int, PRIMARY KEY ((first_id, second_id)))"); err != nil { + t.Fatalf("failed to create table with error '%v'", err) + } + + routingKeyInfo, err := session.routingKeyInfo("SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?") + if err != nil { + t.Fatalf("failed to get routing key info due to error: %v", err) + } + if routingKeyInfo == nil { + t.Fatal("Expected routing key info, but was nil") + } + if len(routingKeyInfo.indexes) != 1 { + t.Fatalf("Expected routing key indexes length to be 1 but was %d", len(routingKeyInfo.indexes)) + } + if routingKeyInfo.indexes[0] != 1 { + t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0]) + } + if len(routingKeyInfo.types) != 1 { + t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types)) + } + if routingKeyInfo.types[0] == nil { + t.Fatal("Expected routing key types[0] to be non-nil") + } + if routingKeyInfo.types[0].Type() != TypeInt { + t.Fatalf("Expected routing key types[0].Type to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type()) + } + + // verify the cache is working + routingKeyInfo, err = session.routingKeyInfo("SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?") + if err != nil { + t.Fatalf("failed to get routing key info due to error: %v", err) + } + if len(routingKeyInfo.indexes) != 1 { + t.Fatalf("Expected routing key indexes length to be 1 but was %d", len(routingKeyInfo.indexes)) + } + if routingKeyInfo.indexes[0] != 1 { + t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0]) + } + if len(routingKeyInfo.types) != 1 { + t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types)) + } + if routingKeyInfo.types[0] == nil { + t.Fatal("Expected routing key types[0] to be non-nil") + } + if routingKeyInfo.types[0].Type() != TypeInt { + t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type()) + } + cacheSize := session.routingKeyInfoCache.lru.Len() + if cacheSize != 1 { + t.Errorf("Expected cache size to be 1 but was %d", cacheSize) + } + + query := session.Query("SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?", 1, 2) + routingKey, err := query.GetRoutingKey() + if err != nil { + t.Fatalf("Failed to get routing key due to error: %v", err) + } + expectedRoutingKey := []byte{0, 0, 0, 2} + if !reflect.DeepEqual(expectedRoutingKey, routingKey) { + t.Errorf("Expected routing key %v but was %v", expectedRoutingKey, routingKey) + } + + routingKeyInfo, err = session.routingKeyInfo("SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?") + if err != nil { + t.Fatalf("failed to get routing key info due to error: %v", err) + } + if routingKeyInfo == nil { + t.Fatal("Expected routing key info, but was nil") + } + if len(routingKeyInfo.indexes) != 2 { + t.Fatalf("Expected routing key indexes length to be 2 but was %d", len(routingKeyInfo.indexes)) + } + if routingKeyInfo.indexes[0] != 1 { + t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0]) + } + if routingKeyInfo.indexes[1] != 0 { + t.Errorf("Expected routing key index[1] to be 0 but was %d", routingKeyInfo.indexes[1]) + } + if len(routingKeyInfo.types) != 2 { + t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types)) + } + if routingKeyInfo.types[0] == nil { + t.Fatal("Expected routing key types[0] to be non-nil") + } + if routingKeyInfo.types[0].Type() != TypeInt { + t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type()) + } + if routingKeyInfo.types[1] == nil { + t.Fatal("Expected routing key types[1] to be non-nil") + } + if routingKeyInfo.types[1].Type() != TypeInt { + t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[1].Type()) + } + + query = session.Query("SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?", 1, 2) + routingKey, err = query.GetRoutingKey() + if err != nil { + t.Fatalf("Failed to get routing key due to error: %v", err) + } + expectedRoutingKey = []byte{0, 4, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 1, 0} + if !reflect.DeepEqual(expectedRoutingKey, routingKey) { + t.Errorf("Expected routing key %v but was %v", expectedRoutingKey, routingKey) + } + + // verify the cache is working + cacheSize = session.routingKeyInfoCache.lru.Len() + if cacheSize != 2 { + t.Errorf("Expected cache size to be 2 but was %d", cacheSize) + } +} + +// Integration test of the token-aware policy-based connection pool +func TestTokenAwareConnPool(t *testing.T) { + cluster := createCluster() + cluster.ConnPoolType = NewTokenAwareConnPool + cluster.DiscoverHosts = true + + // Drop and re-create the keyspace once. Different tests should use their own + // individual tables, but can assume that the table does not exist before. + initOnce.Do(func() { + createKeyspace(t, cluster, "gocql_test") + }) + + cluster.Keyspace = "gocql_test" + session, err := cluster.CreateSession() + if err != nil { + t.Fatal("createSession:", err) + } + defer session.Close() + + if *clusterSize > 1 { + // wait for autodiscovery to update the pool with the list of known hosts + time.Sleep(*flagAutoWait) + } + + if session.Pool.Size() != cluster.NumConns*len(cluster.Hosts) { + t.Errorf("Expected pool size %d but was %d", cluster.NumConns*len(cluster.Hosts), session.Pool.Size()) + } + + if err := createTable(session, "CREATE TABLE test_token_aware (id int, data text, PRIMARY KEY (id))"); err != nil { + t.Fatalf("failed to create test_token_aware table with err: %v", err) + } + query := session.Query("INSERT INTO test_token_aware (id, data) VALUES (?,?)", 42, "8 * 6 =") + if err := query.Exec(); err != nil { + t.Fatalf("failed to insert with err: %v", err) + } + query = session.Query("SELECT data FROM test_token_aware where id = ?", 42).Consistency(One) + iter := query.Iter() + var data string + if !iter.Scan(&data) { + t.Error("failed to scan data") + } + if err := iter.Close(); err != nil { + t.Errorf("iter failed with err: %v", err) + } + + // TODO add verification that the query went to the correct host +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/cluster.go b/Godeps/_workspace/src/github.com/gocql/gocql/cluster.go new file mode 100644 index 000000000..d0a1f955b --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/cluster.go @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "errors" + "sync" + "time" + + "github.com/golang/groupcache/lru" +) + +const defaultMaxPreparedStmts = 1000 + +//Package global reference to Prepared Statements LRU +var stmtsLRU preparedLRU + +//preparedLRU is the prepared statement cache +type preparedLRU struct { + sync.Mutex + lru *lru.Cache +} + +//Max adjusts the maximum size of the cache and cleans up the oldest records if +//the new max is lower than the previous value. Not concurrency safe. +func (p *preparedLRU) Max(max int) { + for p.lru.Len() > max { + p.lru.RemoveOldest() + } + p.lru.MaxEntries = max +} + +func initStmtsLRU(max int) { + if stmtsLRU.lru != nil { + stmtsLRU.Max(max) + } else { + stmtsLRU.lru = lru.New(max) + } +} + +// To enable periodic node discovery enable DiscoverHosts in ClusterConfig +type DiscoveryConfig struct { + // If not empty will filter all discoverred hosts to a single Data Centre (default: "") + DcFilter string + // If not empty will filter all discoverred hosts to a single Rack (default: "") + RackFilter string + // The interval to check for new hosts (default: 30s) + Sleep time.Duration +} + +// ClusterConfig is a struct to configure the default cluster implementation +// of gocoql. It has a varity of attributes that can be used to modify the +// behavior to fit the most common use cases. Applications that requre a +// different setup must implement their own cluster. +type ClusterConfig struct { + Hosts []string // addresses for the initial connections + CQLVersion string // CQL version (default: 3.0.0) + ProtoVersion int // version of the native protocol (default: 2) + Timeout time.Duration // connection timeout (default: 600ms) + Port int // port (default: 9042) + Keyspace string // initial keyspace (optional) + NumConns int // number of connections per host (default: 2) + NumStreams int // number of streams per connection (default: max per protocol, either 128 or 32768) + Consistency Consistency // default consistency level (default: Quorum) + Compressor Compressor // compression algorithm (default: nil) + Authenticator Authenticator // authenticator (default: nil) + RetryPolicy RetryPolicy // Default retry policy to use for queries (default: 0) + SocketKeepalive time.Duration // The keepalive period to use, enabled if > 0 (default: 0) + ConnPoolType NewPoolFunc // The function used to create the connection pool for the session (default: NewSimplePool) + DiscoverHosts bool // If set, gocql will attempt to automatically discover other members of the Cassandra cluster (default: false) + MaxPreparedStmts int // Sets the maximum cache size for prepared statements globally for gocql (default: 1000) + MaxRoutingKeyInfo int // Sets the maximum cache size for query info about statements for each session (default: 1000) + PageSize int // Default page size to use for created sessions (default: 0) + SerialConsistency SerialConsistency // Sets the consistency for the serial part of queries, values can be either SERIAL or LOCAL_SERIAL (default: unset) + Discovery DiscoveryConfig + SslOpts *SslOptions + DefaultTimestamp bool // Sends a client side timestamp for all requests which overrides the timestamp at which it arrives at the server. (default: true, only enabled for protocol 3 and above) +} + +// NewCluster generates a new config for the default cluster implementation. +func NewCluster(hosts ...string) *ClusterConfig { + cfg := &ClusterConfig{ + Hosts: hosts, + CQLVersion: "3.0.0", + ProtoVersion: 2, + Timeout: 600 * time.Millisecond, + Port: 9042, + NumConns: 2, + Consistency: Quorum, + ConnPoolType: NewSimplePool, + DiscoverHosts: false, + MaxPreparedStmts: defaultMaxPreparedStmts, + MaxRoutingKeyInfo: 1000, + DefaultTimestamp: true, + } + return cfg +} + +// CreateSession initializes the cluster based on this config and returns a +// session object that can be used to interact with the database. +func (cfg *ClusterConfig) CreateSession() (*Session, error) { + return NewSession(*cfg) +} + +var ( + ErrNoHosts = errors.New("no hosts provided") + ErrNoConnectionsStarted = errors.New("no connections were made when creating the session") + ErrHostQueryFailed = errors.New("unable to populate Hosts") +) diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/compressor.go b/Godeps/_workspace/src/github.com/gocql/gocql/compressor.go new file mode 100644 index 000000000..20ba0c2ba --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/compressor.go @@ -0,0 +1,28 @@ +package gocql + +import ( + "github.com/golang/snappy/snappy" +) + +type Compressor interface { + Name() string + Encode(data []byte) ([]byte, error) + Decode(data []byte) ([]byte, error) +} + +// SnappyCompressor implements the Compressor interface and can be used to +// compress incoming and outgoing frames. The snappy compression algorithm +// aims for very high speeds and reasonable compression. +type SnappyCompressor struct{} + +func (s SnappyCompressor) Name() string { + return "snappy" +} + +func (s SnappyCompressor) Encode(data []byte) ([]byte, error) { + return snappy.Encode(nil, data) +} + +func (s SnappyCompressor) Decode(data []byte) ([]byte, error) { + return snappy.Decode(nil, data) +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/compressor_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/compressor_test.go new file mode 100644 index 000000000..28240886b --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/compressor_test.go @@ -0,0 +1,40 @@ +// +build all unit + +package gocql + +import ( + "bytes" + "github.com/golang/snappy/snappy" + "testing" +) + +func TestSnappyCompressor(t *testing.T) { + c := SnappyCompressor{} + if c.Name() != "snappy" { + t.Fatalf("expected name to be 'snappy', got %v", c.Name()) + } + + str := "My Test String" + //Test Encoding + if expected, err := snappy.Encode(nil, []byte(str)); err != nil { + t.Fatalf("failed to encode '%v' with error %v", str, err) + } else if res, err := c.Encode([]byte(str)); err != nil { + t.Fatalf("failed to encode '%v' with error %v", str, err) + } else if bytes.Compare(expected, res) != 0 { + t.Fatal("failed to match the expected encoded value with the result encoded value.") + } + + val, err := c.Encode([]byte(str)) + if err != nil { + t.Fatalf("failed to encode '%v' with error '%v'", str, err) + } + + //Test Decoding + if expected, err := snappy.Decode(nil, val); err != nil { + t.Fatalf("failed to decode '%v' with error %v", val, err) + } else if res, err := c.Decode(val); err != nil { + t.Fatalf("failed to decode '%v' with error %v", val, err) + } else if bytes.Compare(expected, res) != 0 { + t.Fatal("failed to match the expected decoded value with the result decoded value.") + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/conn.go b/Godeps/_workspace/src/github.com/gocql/gocql/conn.go new file mode 100644 index 000000000..c3752148b --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/conn.go @@ -0,0 +1,796 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "bufio" + "crypto/tls" + "errors" + "fmt" + "io" + "log" + "net" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" +) + +//JoinHostPort is a utility to return a address string that can be used +//gocql.Conn to form a connection with a host. +func JoinHostPort(addr string, port int) string { + addr = strings.TrimSpace(addr) + if _, _, err := net.SplitHostPort(addr); err != nil { + addr = net.JoinHostPort(addr, strconv.Itoa(port)) + } + return addr +} + +type Authenticator interface { + Challenge(req []byte) (resp []byte, auth Authenticator, err error) + Success(data []byte) error +} + +type PasswordAuthenticator struct { + Username string + Password string +} + +func (p PasswordAuthenticator) Challenge(req []byte) ([]byte, Authenticator, error) { + if string(req) != "org.apache.cassandra.auth.PasswordAuthenticator" { + return nil, nil, fmt.Errorf("unexpected authenticator %q", req) + } + resp := make([]byte, 2+len(p.Username)+len(p.Password)) + resp[0] = 0 + copy(resp[1:], p.Username) + resp[len(p.Username)+1] = 0 + copy(resp[2+len(p.Username):], p.Password) + return resp, nil, nil +} + +func (p PasswordAuthenticator) Success(data []byte) error { + return nil +} + +type SslOptions struct { + tls.Config + + // CertPath and KeyPath are optional depending on server + // config, but both fields must be omitted to avoid using a + // client certificate + CertPath string + KeyPath string + CaPath string //optional depending on server config + // If you want to verify the hostname and server cert (like a wildcard for cass cluster) then you should turn this on + // This option is basically the inverse of InSecureSkipVerify + // See InSecureSkipVerify in http://golang.org/pkg/crypto/tls/ for more info + EnableHostVerification bool +} + +type ConnConfig struct { + ProtoVersion int + CQLVersion string + Timeout time.Duration + NumStreams int + Compressor Compressor + Authenticator Authenticator + Keepalive time.Duration + tlsConfig *tls.Config +} + +type ConnErrorHandler interface { + HandleError(conn *Conn, err error, closed bool) +} + +// How many timeouts we will allow to occur before the connection is closed +// and restarted. This is to prevent a single query timeout from killing a connection +// which may be serving more queries just fine. +// Default is 10, should not be changed concurrently with queries. +var TimeoutLimit int64 = 10 + +// Conn is a single connection to a Cassandra node. It can be used to execute +// queries, but users are usually advised to use a more reliable, higher +// level API. +type Conn struct { + conn net.Conn + r *bufio.Reader + timeout time.Duration + + headerBuf []byte + + uniq chan int + calls []callReq + + errorHandler ConnErrorHandler + compressor Compressor + auth Authenticator + addr string + version uint8 + currentKeyspace string + started bool + + closed int32 + quit chan struct{} + + timeouts int64 +} + +// Connect establishes a connection to a Cassandra node. +// You must also call the Serve method before you can execute any queries. +func Connect(addr string, cfg ConnConfig, errorHandler ConnErrorHandler) (*Conn, error) { + var ( + err error + conn net.Conn + ) + + dialer := &net.Dialer{ + Timeout: cfg.Timeout, + } + + if cfg.tlsConfig != nil { + // the TLS config is safe to be reused by connections but it must not + // be modified after being used. + conn, err = tls.DialWithDialer(dialer, "tcp", addr, cfg.tlsConfig) + } else { + conn, err = dialer.Dial("tcp", addr) + } + + if err != nil { + return nil, err + } + + // going to default to proto 2 + if cfg.ProtoVersion < protoVersion1 || cfg.ProtoVersion > protoVersion3 { + log.Printf("unsupported protocol version: %d using 2\n", cfg.ProtoVersion) + cfg.ProtoVersion = 2 + } + + headerSize := 8 + + maxStreams := 128 + if cfg.ProtoVersion > protoVersion2 { + maxStreams = 32768 + headerSize = 9 + } + + if cfg.NumStreams <= 0 || cfg.NumStreams > maxStreams { + cfg.NumStreams = maxStreams + } + + c := &Conn{ + conn: conn, + r: bufio.NewReader(conn), + uniq: make(chan int, cfg.NumStreams), + calls: make([]callReq, cfg.NumStreams), + timeout: cfg.Timeout, + version: uint8(cfg.ProtoVersion), + addr: conn.RemoteAddr().String(), + errorHandler: errorHandler, + compressor: cfg.Compressor, + auth: cfg.Authenticator, + headerBuf: make([]byte, headerSize), + quit: make(chan struct{}), + } + + if cfg.Keepalive > 0 { + c.setKeepalive(cfg.Keepalive) + } + + for i := 0; i < cfg.NumStreams; i++ { + c.calls[i].resp = make(chan error, 1) + c.uniq <- i + } + + go c.serve() + + if err := c.startup(&cfg); err != nil { + conn.Close() + return nil, err + } + c.started = true + + return c, nil +} + +func (c *Conn) Write(p []byte) (int, error) { + if c.timeout > 0 { + c.conn.SetWriteDeadline(time.Now().Add(c.timeout)) + } + + return c.conn.Write(p) +} + +func (c *Conn) Read(p []byte) (n int, err error) { + const maxAttempts = 5 + + for i := 0; i < maxAttempts; i++ { + var nn int + if c.timeout > 0 { + c.conn.SetReadDeadline(time.Now().Add(c.timeout)) + } + + nn, err = io.ReadFull(c.r, p[n:]) + n += nn + if err == nil { + break + } + + if verr, ok := err.(net.Error); !ok || !verr.Temporary() { + break + } + } + + return +} + +func (c *Conn) startup(cfg *ConnConfig) error { + m := map[string]string{ + "CQL_VERSION": cfg.CQLVersion, + } + + if c.compressor != nil { + m["COMPRESSION"] = c.compressor.Name() + } + + frame, err := c.exec(&writeStartupFrame{opts: m}, nil) + if err != nil { + return err + } + + switch v := frame.(type) { + case error: + return v + case *readyFrame: + return nil + case *authenticateFrame: + return c.authenticateHandshake(v) + default: + return NewErrProtocol("Unknown type of response to startup frame: %s", v) + } +} + +func (c *Conn) authenticateHandshake(authFrame *authenticateFrame) error { + if c.auth == nil { + return fmt.Errorf("authentication required (using %q)", authFrame.class) + } + + resp, challenger, err := c.auth.Challenge([]byte(authFrame.class)) + if err != nil { + return err + } + + req := &writeAuthResponseFrame{data: resp} + + for { + frame, err := c.exec(req, nil) + if err != nil { + return err + } + + switch v := frame.(type) { + case error: + return v + case *authSuccessFrame: + if challenger != nil { + return challenger.Success(v.data) + } + return nil + case *authChallengeFrame: + resp, challenger, err = challenger.Challenge(v.data) + if err != nil { + return err + } + + req = &writeAuthResponseFrame{ + data: resp, + } + default: + return fmt.Errorf("unknown frame response during authentication: %v", v) + } + } +} + +// Serve starts the stream multiplexer for this connection, which is required +// to execute any queries. This method runs as long as the connection is +// open and is therefore usually called in a separate goroutine. +func (c *Conn) serve() { + var ( + err error + ) + + for { + err = c.recv() + if err != nil { + break + } + } + + c.closeWithError(err) +} + +func (c *Conn) recv() error { + // not safe for concurrent reads + + // read a full header, ignore timeouts, as this is being ran in a loop + // TODO: TCP level deadlines? or just query level deadlines? + if c.timeout > 0 { + c.conn.SetReadDeadline(time.Time{}) + } + + // were just reading headers over and over and copy bodies + head, err := readHeader(c.r, c.headerBuf) + if err != nil { + return err + } + + call := &c.calls[head.stream] + err = call.framer.readFrame(&head) + if err != nil { + // only net errors should cause the connection to be closed. Though + // cassandra returning corrupt frames will be returned here as well. + if _, ok := err.(net.Error); ok { + return err + } + } + + if !atomic.CompareAndSwapInt32(&call.waiting, 1, 0) { + // the waiting thread timed out and is no longer waiting, the stream has + // not yet been readded to the chan so it cant be used again, + c.releaseStream(head.stream) + return nil + } + + // we either, return a response to the caller, the caller timedout, or the + // connection has closed. Either way we should never block indefinatly here + select { + case call.resp <- err: + case <-call.timeout: + c.releaseStream(head.stream) + case <-c.quit: + } + + return nil +} + +type callReq struct { + // could use a waitgroup but this allows us to do timeouts on the read/send + resp chan error + framer *framer + waiting int32 + timeout chan struct{} // indicates to recv() that a call has timedout +} + +func (c *Conn) releaseStream(stream int) { + call := &c.calls[stream] + framerPool.Put(call.framer) + call.framer = nil + + select { + case c.uniq <- stream: + default: + } +} + +func (c *Conn) handleTimeout() { + if atomic.AddInt64(&c.timeouts, 1) > TimeoutLimit { + c.closeWithError(ErrTooManyTimeouts) + } +} + +func (c *Conn) exec(req frameWriter, tracer Tracer) (frame, error) { + // TODO: move tracer onto conn + var stream int + select { + case stream = <-c.uniq: + case <-c.quit: + return nil, ErrConnectionClosed + } + + call := &c.calls[stream] + // resp is basically a waiting semaphore protecting the framer + framer := newFramer(c, c, c.compressor, c.version) + call.framer = framer + call.timeout = make(chan struct{}) + + if tracer != nil { + framer.trace() + } + + if !atomic.CompareAndSwapInt32(&call.waiting, 0, 1) { + return nil, errors.New("gocql: stream is busy or closed") + } + defer atomic.StoreInt32(&call.waiting, 0) + + err := req.writeFrame(framer, stream) + if err != nil { + return nil, err + } + + select { + case err := <-call.resp: + // dont release the stream if detect a timeout as another request can reuse + // that stream and get a response for the old request, which we have no + // easy way of detecting. + defer c.releaseStream(stream) + + if err != nil { + return nil, err + } + case <-time.After(c.timeout): + close(call.timeout) + c.handleTimeout() + return nil, ErrTimeoutNoResponse + case <-c.quit: + return nil, ErrConnectionClosed + } + + if v := framer.header.version.version(); v != c.version { + return nil, NewErrProtocol("unexpected protocol version in response: got %d expected %d", v, c.version) + } + + frame, err := framer.parseFrame() + if err != nil { + return nil, err + } + + if len(framer.traceID) > 0 { + tracer.Trace(framer.traceID) + } + + return frame, nil +} + +func (c *Conn) prepareStatement(stmt string, trace Tracer) (*resultPreparedFrame, error) { + stmtsLRU.Lock() + if stmtsLRU.lru == nil { + initStmtsLRU(defaultMaxPreparedStmts) + } + + stmtCacheKey := c.addr + c.currentKeyspace + stmt + + if val, ok := stmtsLRU.lru.Get(stmtCacheKey); ok { + stmtsLRU.Unlock() + flight := val.(*inflightPrepare) + flight.wg.Wait() + return flight.info, flight.err + } + + flight := new(inflightPrepare) + flight.wg.Add(1) + stmtsLRU.lru.Add(stmtCacheKey, flight) + stmtsLRU.Unlock() + + prep := &writePrepareFrame{ + statement: stmt, + } + + resp, err := c.exec(prep, trace) + if err != nil { + flight.err = err + flight.wg.Done() + return nil, err + } + + switch x := resp.(type) { + case *resultPreparedFrame: + flight.info = x + case error: + flight.err = x + default: + flight.err = NewErrProtocol("Unknown type in response to prepare frame: %s", x) + } + flight.wg.Done() + + if flight.err != nil { + stmtsLRU.Lock() + stmtsLRU.lru.Remove(stmtCacheKey) + stmtsLRU.Unlock() + } + + return flight.info, flight.err +} + +func (c *Conn) executeQuery(qry *Query) *Iter { + params := queryParams{ + consistency: qry.cons, + } + + // frame checks that it is not 0 + params.serialConsistency = qry.serialCons + params.defaultTimestamp = qry.defaultTimestamp + + if len(qry.pageState) > 0 { + params.pagingState = qry.pageState + } + if qry.pageSize > 0 { + params.pageSize = qry.pageSize + } + + var frame frameWriter + if qry.shouldPrepare() { + // Prepare all DML queries. Other queries can not be prepared. + info, err := c.prepareStatement(qry.stmt, qry.trace) + if err != nil { + return &Iter{err: err} + } + + var values []interface{} + + if qry.binding == nil { + values = qry.values + } else { + binding := &QueryInfo{ + Id: info.preparedID, + Args: info.reqMeta.columns, + Rval: info.respMeta.columns, + } + + values, err = qry.binding(binding) + if err != nil { + return &Iter{err: err} + } + } + + if len(values) != len(info.reqMeta.columns) { + return &Iter{err: ErrQueryArgLength} + } + params.values = make([]queryValues, len(values)) + for i := 0; i < len(values); i++ { + val, err := Marshal(info.reqMeta.columns[i].TypeInfo, values[i]) + if err != nil { + return &Iter{err: err} + } + + v := ¶ms.values[i] + v.value = val + // TODO: handle query binding names + } + + frame = &writeExecuteFrame{ + preparedID: info.preparedID, + params: params, + } + } else { + frame = &writeQueryFrame{ + statement: qry.stmt, + params: params, + } + } + + resp, err := c.exec(frame, qry.trace) + if err != nil { + return &Iter{err: err} + } + + switch x := resp.(type) { + case *resultVoidFrame: + return &Iter{} + case *resultRowsFrame: + iter := &Iter{ + meta: x.meta, + rows: x.rows, + } + + if len(x.meta.pagingState) > 0 { + iter.next = &nextIter{ + qry: *qry, + pos: int((1 - qry.prefetch) * float64(len(iter.rows))), + } + + iter.next.qry.pageState = x.meta.pagingState + if iter.next.pos < 1 { + iter.next.pos = 1 + } + } + + return iter + case *resultKeyspaceFrame, *resultSchemaChangeFrame: + return &Iter{} + case *RequestErrUnprepared: + stmtsLRU.Lock() + stmtCacheKey := c.addr + c.currentKeyspace + qry.stmt + if _, ok := stmtsLRU.lru.Get(stmtCacheKey); ok { + stmtsLRU.lru.Remove(stmtCacheKey) + stmtsLRU.Unlock() + return c.executeQuery(qry) + } + stmtsLRU.Unlock() + return &Iter{err: x} + case error: + return &Iter{err: x} + default: + return &Iter{err: NewErrProtocol("Unknown type in response to execute query: %s", x)} + } +} + +func (c *Conn) Pick(qry *Query) *Conn { + if c.Closed() { + return nil + } + return c +} + +func (c *Conn) Closed() bool { + return atomic.LoadInt32(&c.closed) == 1 +} + +func (c *Conn) closeWithError(err error) { + if !atomic.CompareAndSwapInt32(&c.closed, 0, 1) { + return + } + + for id := 0; id < len(c.calls); id++ { + req := &c.calls[id] + // we need to send the error to all waiting queries, put the state + // of this conn into not active so that it can not execute any queries. + atomic.StoreInt32(&req.waiting, -1) + + if err != nil { + select { + case req.resp <- err: + default: + } + } + } + + close(c.quit) + c.conn.Close() + + if c.started && err != nil { + c.errorHandler.HandleError(c, err, true) + } +} + +func (c *Conn) Close() { + c.closeWithError(nil) +} + +func (c *Conn) Address() string { + return c.addr +} + +func (c *Conn) AvailableStreams() int { + return len(c.uniq) +} + +func (c *Conn) UseKeyspace(keyspace string) error { + q := &writeQueryFrame{statement: `USE "` + keyspace + `"`} + q.params.consistency = Any + + resp, err := c.exec(q, nil) + if err != nil { + return err + } + + switch x := resp.(type) { + case *resultKeyspaceFrame: + case error: + return x + default: + return NewErrProtocol("unknown frame in response to USE: %v", x) + } + + c.currentKeyspace = keyspace + + return nil +} + +func (c *Conn) executeBatch(batch *Batch) error { + if c.version == protoVersion1 { + return ErrUnsupported + } + + n := len(batch.Entries) + req := &writeBatchFrame{ + typ: batch.Type, + statements: make([]batchStatment, n), + consistency: batch.Cons, + serialConsistency: batch.serialCons, + defaultTimestamp: batch.defaultTimestamp, + } + + stmts := make(map[string]string) + + for i := 0; i < n; i++ { + entry := &batch.Entries[i] + b := &req.statements[i] + if len(entry.Args) > 0 || entry.binding != nil { + info, err := c.prepareStatement(entry.Stmt, nil) + if err != nil { + return err + } + + var args []interface{} + if entry.binding == nil { + args = entry.Args + } else { + binding := &QueryInfo{ + Id: info.preparedID, + Args: info.reqMeta.columns, + Rval: info.respMeta.columns, + } + args, err = entry.binding(binding) + if err != nil { + return err + } + } + + if len(args) != len(info.reqMeta.columns) { + return ErrQueryArgLength + } + + b.preparedID = info.preparedID + stmts[string(info.preparedID)] = entry.Stmt + + b.values = make([]queryValues, len(info.reqMeta.columns)) + + for j := 0; j < len(info.reqMeta.columns); j++ { + val, err := Marshal(info.reqMeta.columns[j].TypeInfo, args[j]) + if err != nil { + return err + } + + b.values[j].value = val + // TODO: add names + } + } else { + b.statement = entry.Stmt + } + } + + // TODO: should batch support tracing? + resp, err := c.exec(req, nil) + if err != nil { + return err + } + + switch x := resp.(type) { + case *resultVoidFrame: + return nil + case *RequestErrUnprepared: + stmt, found := stmts[string(x.StatementId)] + if found { + stmtsLRU.Lock() + stmtsLRU.lru.Remove(c.addr + c.currentKeyspace + stmt) + stmtsLRU.Unlock() + } + if found { + return c.executeBatch(batch) + } else { + return x + } + case error: + return x + default: + return NewErrProtocol("Unknown type in response to batch statement: %s", x) + } +} + +func (c *Conn) setKeepalive(d time.Duration) error { + if tc, ok := c.conn.(*net.TCPConn); ok { + err := tc.SetKeepAlivePeriod(d) + if err != nil { + return err + } + + return tc.SetKeepAlive(true) + } + + return nil +} + +type inflightPrepare struct { + info *resultPreparedFrame + err error + wg sync.WaitGroup +} + +var ( + ErrQueryArgLength = errors.New("gocql: query argument length mismatch") + ErrTimeoutNoResponse = errors.New("gocql: no response recieved from cassandra within timeout period") + ErrTooManyTimeouts = errors.New("gocql: too many query timeouts on the connection") + ErrConnectionClosed = errors.New("gocql: connection closed waiting for response") +) diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/conn_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/conn_test.go new file mode 100644 index 000000000..650a9ffd6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/conn_test.go @@ -0,0 +1,769 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +build all unit + +package gocql + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io" + "io/ioutil" + "net" + "strings" + "sync" + "sync/atomic" + "testing" + "time" +) + +const ( + defaultProto = protoVersion2 +) + +func TestJoinHostPort(t *testing.T) { + tests := map[string]string{ + "127.0.0.1:0": JoinHostPort("127.0.0.1", 0), + "127.0.0.1:1": JoinHostPort("127.0.0.1:1", 9142), + "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:0": JoinHostPort("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 0), + "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:1": JoinHostPort("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:1", 9142), + } + for k, v := range tests { + if k != v { + t.Fatalf("expected '%v', got '%v'", k, v) + } + } +} + +func TestSimple(t *testing.T) { + srv := NewTestServer(t, defaultProto) + defer srv.Stop() + + cluster := NewCluster(srv.Address) + cluster.ProtoVersion = int(defaultProto) + db, err := cluster.CreateSession() + if err != nil { + t.Errorf("0x%x: NewCluster: %v", defaultProto, err) + return + } + + if err := db.Query("void").Exec(); err != nil { + t.Errorf("0x%x: %v", defaultProto, err) + } +} + +func TestSSLSimple(t *testing.T) { + srv := NewSSLTestServer(t, defaultProto) + defer srv.Stop() + + db, err := createTestSslCluster(srv.Address, defaultProto, true).CreateSession() + if err != nil { + t.Fatalf("0x%x: NewCluster: %v", defaultProto, err) + } + + if err := db.Query("void").Exec(); err != nil { + t.Fatalf("0x%x: %v", defaultProto, err) + } +} + +func TestSSLSimpleNoClientCert(t *testing.T) { + srv := NewSSLTestServer(t, defaultProto) + defer srv.Stop() + + db, err := createTestSslCluster(srv.Address, defaultProto, false).CreateSession() + if err != nil { + t.Fatalf("0x%x: NewCluster: %v", defaultProto, err) + } + + if err := db.Query("void").Exec(); err != nil { + t.Fatalf("0x%x: %v", defaultProto, err) + } +} + +func createTestSslCluster(hosts string, proto uint8, useClientCert bool) *ClusterConfig { + cluster := NewCluster(hosts) + sslOpts := &SslOptions{ + CaPath: "testdata/pki/ca.crt", + EnableHostVerification: false, + } + if useClientCert { + sslOpts.CertPath = "testdata/pki/gocql.crt" + sslOpts.KeyPath = "testdata/pki/gocql.key" + } + cluster.SslOpts = sslOpts + cluster.ProtoVersion = int(proto) + return cluster +} + +func TestClosed(t *testing.T) { + t.Skip("Skipping the execution of TestClosed for now to try to concentrate on more important test failures on Travis") + + srv := NewTestServer(t, defaultProto) + defer srv.Stop() + + cluster := NewCluster(srv.Address) + cluster.ProtoVersion = int(defaultProto) + + session, err := cluster.CreateSession() + defer session.Close() + if err != nil { + t.Errorf("0x%x: NewCluster: %v", defaultProto, err) + return + } + + if err := session.Query("void").Exec(); err != ErrSessionClosed { + t.Errorf("0x%x: expected %#v, got %#v", defaultProto, ErrSessionClosed, err) + return + } +} + +func newTestSession(addr string, proto uint8) (*Session, error) { + cluster := NewCluster(addr) + cluster.ProtoVersion = int(proto) + return cluster.CreateSession() +} + +func TestTimeout(t *testing.T) { + + srv := NewTestServer(t, defaultProto) + defer srv.Stop() + + db, err := newTestSession(srv.Address, defaultProto) + if err != nil { + t.Errorf("NewCluster: %v", err) + return + } + defer db.Close() + + go func() { + <-time.After(2 * time.Second) + t.Errorf("no timeout") + }() + + if err := db.Query("kill").Exec(); err == nil { + t.Errorf("expected error") + } +} + +// TestQueryRetry will test to make sure that gocql will execute +// the exact amount of retry queries designated by the user. +func TestQueryRetry(t *testing.T) { + srv := NewTestServer(t, defaultProto) + defer srv.Stop() + + db, err := newTestSession(srv.Address, defaultProto) + if err != nil { + t.Fatalf("NewCluster: %v", err) + } + defer db.Close() + + go func() { + <-time.After(5 * time.Second) + t.Fatalf("no timeout") + }() + rt := &SimpleRetryPolicy{NumRetries: 1} + + qry := db.Query("kill").RetryPolicy(rt) + if err := qry.Exec(); err == nil { + t.Fatalf("expected error") + } + + requests := atomic.LoadInt64(&srv.nKillReq) + attempts := qry.Attempts() + if requests != int64(attempts) { + t.Fatalf("expected requests %v to match query attemps %v", requests, attempts) + } + + //Minus 1 from the requests variable since there is the initial query attempt + if requests-1 != int64(rt.NumRetries) { + t.Fatalf("failed to retry the query %v time(s). Query executed %v times", rt.NumRetries, requests-1) + } +} + +func TestSimplePoolRoundRobin(t *testing.T) { + servers := make([]*TestServer, 5) + addrs := make([]string, len(servers)) + for n := 0; n < len(servers); n++ { + servers[n] = NewTestServer(t, defaultProto) + addrs[n] = servers[n].Address + defer servers[n].Stop() + } + cluster := NewCluster(addrs...) + cluster.ProtoVersion = defaultProto + + db, err := cluster.CreateSession() + time.Sleep(1 * time.Second) // Sleep to allow the Cluster.fillPool to complete + + if err != nil { + t.Fatalf("NewCluster: %v", err) + } + + var wg sync.WaitGroup + wg.Add(5) + for n := 0; n < 5; n++ { + go func() { + for j := 0; j < 5; j++ { + if err := db.Query("void").Exec(); err != nil { + t.Fatal(err) + } + } + wg.Done() + }() + } + wg.Wait() + + diff := 0 + for n := 1; n < len(servers); n++ { + d := 0 + if servers[n].nreq > servers[n-1].nreq { + d = int(servers[n].nreq - servers[n-1].nreq) + } else { + d = int(servers[n-1].nreq - servers[n].nreq) + } + if d > diff { + diff = d + } + } + + if diff > 0 { + t.Errorf("Expected 0 difference in usage but was %d", diff) + } +} + +func TestConnClosing(t *testing.T) { + t.Skip("Skipping until test can be ran reliably") + + srv := NewTestServer(t, protoVersion2) + defer srv.Stop() + + db, err := NewCluster(srv.Address).CreateSession() + if err != nil { + t.Errorf("NewCluster: %v", err) + } + defer db.Close() + + numConns := db.cfg.NumConns + count := db.cfg.NumStreams * numConns + + wg := &sync.WaitGroup{} + wg.Add(count) + for i := 0; i < count; i++ { + go func(wg *sync.WaitGroup) { + wg.Done() + db.Query("kill").Exec() + }(wg) + } + + wg.Wait() + + time.Sleep(1 * time.Second) //Sleep so the fillPool can complete. + pool := db.Pool.(ConnectionPool) + conns := pool.Size() + + if conns != numConns { + t.Errorf("Expected to have %d connections but have %d", numConns, conns) + } +} + +func TestStreams_Protocol1(t *testing.T) { + srv := NewTestServer(t, protoVersion1) + defer srv.Stop() + + // TODO: these are more like session tests and should instead operate + // on a single Conn + cluster := NewCluster(srv.Address) + cluster.NumConns = 1 + cluster.ProtoVersion = 1 + + db, err := cluster.CreateSession() + if err != nil { + t.Fatal(err) + } + defer db.Close() + + var wg sync.WaitGroup + for i := 0; i < db.cfg.NumStreams; i++ { + // here were just validating that if we send NumStream request we get + // a response for every stream and the lengths for the queries are set + // correctly. + wg.Add(1) + go func() { + defer wg.Done() + if err := db.Query("void").Exec(); err != nil { + t.Error(err) + } + }() + } + wg.Wait() +} + +func TestStreams_Protocol2(t *testing.T) { + srv := NewTestServer(t, protoVersion2) + defer srv.Stop() + + // TODO: these are more like session tests and should instead operate + // on a single Conn + cluster := NewCluster(srv.Address) + cluster.NumConns = 1 + cluster.ProtoVersion = 2 + + db, err := cluster.CreateSession() + if err != nil { + t.Fatal(err) + } + defer db.Close() + + for i := 0; i < db.cfg.NumStreams; i++ { + // the test server processes each conn synchronously + // here were just validating that if we send NumStream request we get + // a response for every stream and the lengths for the queries are set + // correctly. + if err = db.Query("void").Exec(); err != nil { + t.Fatal(err) + } + } +} + +func TestStreams_Protocol3(t *testing.T) { + srv := NewTestServer(t, protoVersion3) + defer srv.Stop() + + // TODO: these are more like session tests and should instead operate + // on a single Conn + cluster := NewCluster(srv.Address) + cluster.NumConns = 1 + cluster.ProtoVersion = 3 + + db, err := cluster.CreateSession() + if err != nil { + t.Fatal(err) + } + defer db.Close() + + for i := 0; i < db.cfg.NumStreams; i++ { + // the test server processes each conn synchronously + // here were just validating that if we send NumStream request we get + // a response for every stream and the lengths for the queries are set + // correctly. + if err = db.Query("void").Exec(); err != nil { + t.Fatal(err) + } + } +} + +func BenchmarkProtocolV3(b *testing.B) { + srv := NewTestServer(b, protoVersion3) + defer srv.Stop() + + // TODO: these are more like session tests and should instead operate + // on a single Conn + cluster := NewCluster(srv.Address) + cluster.NumConns = 1 + cluster.ProtoVersion = 3 + + db, err := cluster.CreateSession() + if err != nil { + b.Fatal(err) + } + defer db.Close() + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if err = db.Query("void").Exec(); err != nil { + b.Fatal(err) + } + } +} + +func TestRoundRobinConnPoolRoundRobin(t *testing.T) { + // create 5 test servers + servers := make([]*TestServer, 5) + addrs := make([]string, len(servers)) + for n := 0; n < len(servers); n++ { + servers[n] = NewTestServer(t, defaultProto) + addrs[n] = servers[n].Address + defer servers[n].Stop() + } + + // create a new cluster using the policy-based round robin conn pool + cluster := NewCluster(addrs...) + cluster.ConnPoolType = NewRoundRobinConnPool + + db, err := cluster.CreateSession() + if err != nil { + t.Fatalf("failed to create a new session: %v", err) + } + + // Sleep to allow the pool to fill + time.Sleep(100 * time.Millisecond) + + // run concurrent queries against the pool, server usage should + // be even + var wg sync.WaitGroup + wg.Add(5) + for n := 0; n < 5; n++ { + go func() { + for j := 0; j < 5; j++ { + if err := db.Query("void").Exec(); err != nil { + t.Errorf("Query failed with error: %v", err) + } + } + wg.Done() + }() + } + wg.Wait() + + db.Close() + + // wait for the pool to drain + time.Sleep(100 * time.Millisecond) + size := db.Pool.Size() + if size != 0 { + t.Errorf("connection pool did not drain, still contains %d connections", size) + } + + // verify that server usage is even + diff := 0 + for n := 1; n < len(servers); n++ { + d := 0 + if servers[n].nreq > servers[n-1].nreq { + d = int(servers[n].nreq - servers[n-1].nreq) + } else { + d = int(servers[n-1].nreq - servers[n].nreq) + } + if d > diff { + diff = d + } + } + + if diff > 0 { + t.Errorf("expected 0 difference in usage but was %d", diff) + } +} + +// This tests that the policy connection pool handles SSL correctly +func TestPolicyConnPoolSSL(t *testing.T) { + srv := NewSSLTestServer(t, defaultProto) + defer srv.Stop() + + cluster := createTestSslCluster(srv.Address, defaultProto, true) + cluster.ConnPoolType = NewRoundRobinConnPool + + db, err := cluster.CreateSession() + if err != nil { + t.Fatalf("failed to create new session: %v", err) + } + + if err := db.Query("void").Exec(); err != nil { + t.Errorf("query failed due to error: %v", err) + } + + db.Close() + + // wait for the pool to drain + time.Sleep(100 * time.Millisecond) + size := db.Pool.Size() + if size != 0 { + t.Errorf("connection pool did not drain, still contains %d connections", size) + } +} + +func TestQueryTimeout(t *testing.T) { + srv := NewTestServer(t, defaultProto) + defer srv.Stop() + + cluster := NewCluster(srv.Address) + // Set the timeout arbitrarily low so that the query hits the timeout in a + // timely manner. + cluster.Timeout = 1 * time.Millisecond + + db, err := cluster.CreateSession() + if err != nil { + t.Errorf("NewCluster: %v", err) + } + defer db.Close() + + ch := make(chan error, 1) + + go func() { + err := db.Query("timeout").Exec() + if err != nil { + ch <- err + return + } + t.Errorf("err was nil, expected to get a timeout after %v", db.cfg.Timeout) + }() + + select { + case err := <-ch: + if err != ErrTimeoutNoResponse { + t.Fatalf("expected to get %v for timeout got %v", ErrTimeoutNoResponse, err) + } + case <-time.After(10*time.Millisecond + db.cfg.Timeout): + // ensure that the query goroutines have been scheduled + t.Fatalf("query did not timeout after %v", db.cfg.Timeout) + } +} + +func TestQueryTimeoutReuseStream(t *testing.T) { + srv := NewTestServer(t, defaultProto) + defer srv.Stop() + + cluster := NewCluster(srv.Address) + // Set the timeout arbitrarily low so that the query hits the timeout in a + // timely manner. + cluster.Timeout = 1 * time.Millisecond + cluster.NumConns = 1 + cluster.NumStreams = 1 + + db, err := cluster.CreateSession() + if err != nil { + t.Fatalf("NewCluster: %v", err) + } + defer db.Close() + + db.Query("slow").Exec() + + err = db.Query("void").Exec() + if err != nil { + t.Fatal(err) + } +} + +func TestQueryTimeoutClose(t *testing.T) { + srv := NewTestServer(t, defaultProto) + defer srv.Stop() + + cluster := NewCluster(srv.Address) + // Set the timeout arbitrarily low so that the query hits the timeout in a + // timely manner. + cluster.Timeout = 1000 * time.Millisecond + cluster.NumConns = 1 + cluster.NumStreams = 1 + + db, err := cluster.CreateSession() + if err != nil { + t.Fatalf("NewCluster: %v", err) + } + + ch := make(chan error) + go func() { + err := db.Query("timeout").Exec() + ch <- err + }() + // ensure that the above goroutine gets sheduled + time.Sleep(50 * time.Millisecond) + + db.Close() + select { + case err = <-ch: + case <-time.After(1 * time.Second): + t.Fatal("timedout waiting to get a response once cluster is closed") + } + + if err != ErrConnectionClosed { + t.Fatalf("expected to get %v got %v", ErrConnectionClosed, err) + } +} + +func NewTestServer(t testing.TB, protocol uint8) *TestServer { + laddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + listen, err := net.ListenTCP("tcp", laddr) + if err != nil { + t.Fatal(err) + } + + headerSize := 8 + if protocol > protoVersion2 { + headerSize = 9 + } + + srv := &TestServer{ + Address: listen.Addr().String(), + listen: listen, + t: t, + protocol: protocol, + headerSize: headerSize, + quit: make(chan struct{}), + } + + go srv.serve() + + return srv +} + +func NewSSLTestServer(t testing.TB, protocol uint8) *TestServer { + pem, err := ioutil.ReadFile("testdata/pki/ca.crt") + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(pem) { + t.Errorf("Failed parsing or appending certs") + } + mycert, err := tls.LoadX509KeyPair("testdata/pki/cassandra.crt", "testdata/pki/cassandra.key") + if err != nil { + t.Errorf("could not load cert") + } + config := &tls.Config{ + Certificates: []tls.Certificate{mycert}, + RootCAs: certPool, + } + listen, err := tls.Listen("tcp", "127.0.0.1:0", config) + if err != nil { + t.Fatal(err) + } + + headerSize := 8 + if protocol > protoVersion2 { + headerSize = 9 + } + + srv := &TestServer{ + Address: listen.Addr().String(), + listen: listen, + t: t, + protocol: protocol, + headerSize: headerSize, + quit: make(chan struct{}), + } + go srv.serve() + return srv +} + +type TestServer struct { + Address string + t testing.TB + nreq uint64 + listen net.Listener + nKillReq int64 + compressor Compressor + + protocol byte + headerSize int + + quit chan struct{} +} + +func (srv *TestServer) serve() { + defer srv.listen.Close() + for { + conn, err := srv.listen.Accept() + if err != nil { + break + } + go func(conn net.Conn) { + defer conn.Close() + for { + framer, err := srv.readFrame(conn) + if err != nil { + if err == io.EOF { + return + } + + srv.t.Error(err) + return + } + + atomic.AddUint64(&srv.nreq, 1) + + go srv.process(framer) + } + }(conn) + } +} + +func (srv *TestServer) Stop() { + srv.listen.Close() + close(srv.quit) +} + +func (srv *TestServer) process(f *framer) { + head := f.header + if head == nil { + srv.t.Error("process frame with a nil header") + return + } + + switch head.op { + case opStartup: + f.writeHeader(0, opReady, head.stream) + case opOptions: + f.writeHeader(0, opSupported, head.stream) + f.writeShort(0) + case opQuery: + query := f.readLongString() + first := query + if n := strings.Index(query, " "); n > 0 { + first = first[:n] + } + switch strings.ToLower(first) { + case "kill": + atomic.AddInt64(&srv.nKillReq, 1) + f.writeHeader(0, opError, head.stream) + f.writeInt(0x1001) + f.writeString("query killed") + case "use": + f.writeInt(resultKindKeyspace) + f.writeString(strings.TrimSpace(query[3:])) + case "void": + f.writeHeader(0, opResult, head.stream) + f.writeInt(resultKindVoid) + case "timeout": + <-srv.quit + return + case "slow": + go func() { + f.writeHeader(0, opResult, head.stream) + f.writeInt(resultKindVoid) + f.wbuf[0] = srv.protocol | 0x80 + select { + case <-srv.quit: + case <-time.After(50 * time.Millisecond): + f.finishWrite() + } + }() + return + default: + f.writeHeader(0, opResult, head.stream) + f.writeInt(resultKindVoid) + } + default: + f.writeHeader(0, opError, head.stream) + f.writeInt(0) + f.writeString("not supported") + } + + f.wbuf[0] = srv.protocol | 0x80 + + if err := f.finishWrite(); err != nil { + srv.t.Error(err) + } +} + +func (srv *TestServer) readFrame(conn net.Conn) (*framer, error) { + buf := make([]byte, srv.headerSize) + head, err := readHeader(conn, buf) + if err != nil { + return nil, err + } + framer := newFramer(conn, conn, nil, srv.protocol) + + err = framer.readFrame(&head) + if err != nil { + return nil, err + } + + // should be a request frame + if head.version.response() { + return nil, fmt.Errorf("expected to read a request frame got version: %v", head.version) + } else if head.version.version() != srv.protocol { + return nil, fmt.Errorf("expected to read protocol version 0x%x got 0x%x", srv.protocol, head.version.version()) + } + + return framer, nil +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/connectionpool.go b/Godeps/_workspace/src/github.com/gocql/gocql/connectionpool.go new file mode 100644 index 000000000..9f9904be1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/connectionpool.go @@ -0,0 +1,895 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "log" + "math/rand" + "net" + "sync" + "time" +) + +/*ConnectionPool represents the interface gocql will use to work with a collection of connections. + +Purpose + +The connection pool in gocql opens and closes connections as well as selects an available connection +for gocql to execute a query against. The pool is also respnsible for handling connection errors that +are caught by the connection experiencing the error. + +A connection pool should make a copy of the variables used from the ClusterConfig provided to the pool +upon creation. ClusterConfig is a pointer and can be modified after the creation of the pool. This can +lead to issues with variables being modified outside the expectations of the ConnectionPool type. + +Example of Single Connection Pool: + + type SingleConnection struct { + conn *Conn + cfg *ClusterConfig + } + + func NewSingleConnection(cfg *ClusterConfig) ConnectionPool { + addr := JoinHostPort(cfg.Hosts[0], cfg.Port) + + connCfg := ConnConfig{ + ProtoVersion: cfg.ProtoVersion, + CQLVersion: cfg.CQLVersion, + Timeout: cfg.Timeout, + NumStreams: cfg.NumStreams, + Compressor: cfg.Compressor, + Authenticator: cfg.Authenticator, + Keepalive: cfg.SocketKeepalive, + } + pool := SingleConnection{cfg:cfg} + pool.conn = Connect(addr,connCfg,pool) + return &pool + } + + func (s *SingleConnection) HandleError(conn *Conn, err error, closed bool) { + if closed { + connCfg := ConnConfig{ + ProtoVersion: cfg.ProtoVersion, + CQLVersion: cfg.CQLVersion, + Timeout: cfg.Timeout, + NumStreams: cfg.NumStreams, + Compressor: cfg.Compressor, + Authenticator: cfg.Authenticator, + Keepalive: cfg.SocketKeepalive, + } + s.conn = Connect(conn.Address(),connCfg,s) + } + } + + func (s *SingleConnection) Pick(qry *Query) *Conn { + if s.conn.isClosed { + return nil + } + return s.conn + } + + func (s *SingleConnection) Size() int { + return 1 + } + + func (s *SingleConnection) Close() { + s.conn.Close() + } + +This is a very simple example of a type that exposes the connection pool interface. To assign +this type as the connection pool to use you would assign it to the ClusterConfig like so: + + cluster := NewCluster("127.0.0.1") + cluster.ConnPoolType = NewSingleConnection + ... + session, err := cluster.CreateSession() + +To see a more complete example of a ConnectionPool implementation please see the SimplePool type. +*/ +type ConnectionPool interface { + SetHosts + Pick(*Query) *Conn + Size() int + Close() +} + +// interface to implement to receive the host information +type SetHosts interface { + SetHosts(hosts []HostInfo) +} + +// interface to implement to receive the partitioner value +type SetPartitioner interface { + SetPartitioner(partitioner string) +} + +//NewPoolFunc is the type used by ClusterConfig to create a pool of a specific type. +type NewPoolFunc func(*ClusterConfig) (ConnectionPool, error) + +//SimplePool is the current implementation of the connection pool inside gocql. This +//pool is meant to be a simple default used by gocql so users can get up and running +//quickly. +type SimplePool struct { + cfg *ClusterConfig + hostPool *RoundRobin + connPool map[string]*RoundRobin + conns map[*Conn]struct{} + keyspace string + + hostMu sync.RWMutex + // this is the set of current hosts which the pool will attempt to connect to + hosts map[string]*HostInfo + + // protects hostpool, connPoll, conns, quit + mu sync.Mutex + + cFillingPool chan int + + quit bool + quitWait chan bool + quitOnce sync.Once + + tlsConfig *tls.Config +} + +func setupTLSConfig(sslOpts *SslOptions) (*tls.Config, error) { + // ca cert is optional + if sslOpts.CaPath != "" { + if sslOpts.RootCAs == nil { + sslOpts.RootCAs = x509.NewCertPool() + } + + pem, err := ioutil.ReadFile(sslOpts.CaPath) + if err != nil { + return nil, fmt.Errorf("connectionpool: unable to open CA certs: %v", err) + } + + if !sslOpts.RootCAs.AppendCertsFromPEM(pem) { + return nil, errors.New("connectionpool: failed parsing or CA certs") + } + } + + if sslOpts.CertPath != "" || sslOpts.KeyPath != "" { + mycert, err := tls.LoadX509KeyPair(sslOpts.CertPath, sslOpts.KeyPath) + if err != nil { + return nil, fmt.Errorf("connectionpool: unable to load X509 key pair: %v", err) + } + sslOpts.Certificates = append(sslOpts.Certificates, mycert) + } + + sslOpts.InsecureSkipVerify = !sslOpts.EnableHostVerification + + return &sslOpts.Config, nil +} + +//NewSimplePool is the function used by gocql to create the simple connection pool. +//This is the default if no other pool type is specified. +func NewSimplePool(cfg *ClusterConfig) (ConnectionPool, error) { + pool := &SimplePool{ + cfg: cfg, + hostPool: NewRoundRobin(), + connPool: make(map[string]*RoundRobin), + conns: make(map[*Conn]struct{}), + quitWait: make(chan bool), + cFillingPool: make(chan int, 1), + keyspace: cfg.Keyspace, + hosts: make(map[string]*HostInfo), + } + + for _, host := range cfg.Hosts { + // seed hosts have unknown topology + // TODO: Handle populating this during SetHosts + pool.hosts[host] = &HostInfo{Peer: host} + } + + if cfg.SslOpts != nil { + config, err := setupTLSConfig(cfg.SslOpts) + if err != nil { + return nil, err + } + pool.tlsConfig = config + } + + //Walk through connecting to hosts. As soon as one host connects + //defer the remaining connections to cluster.fillPool() + for i := 0; i < len(cfg.Hosts); i++ { + addr := JoinHostPort(cfg.Hosts[i], cfg.Port) + + if pool.connect(addr) == nil { + pool.cFillingPool <- 1 + go pool.fillPool() + break + } + } + + return pool, nil +} + +func (c *SimplePool) connect(addr string) error { + + cfg := ConnConfig{ + ProtoVersion: c.cfg.ProtoVersion, + CQLVersion: c.cfg.CQLVersion, + Timeout: c.cfg.Timeout, + NumStreams: c.cfg.NumStreams, + Compressor: c.cfg.Compressor, + Authenticator: c.cfg.Authenticator, + Keepalive: c.cfg.SocketKeepalive, + tlsConfig: c.tlsConfig, + } + + conn, err := Connect(addr, cfg, c) + if err != nil { + log.Printf("connect: failed to connect to %q: %v", addr, err) + return err + } + + return c.addConn(conn) +} + +func (c *SimplePool) addConn(conn *Conn) error { + c.mu.Lock() + defer c.mu.Unlock() + if c.quit { + conn.Close() + return nil + } + + //Set the connection's keyspace if any before adding it to the pool + if c.keyspace != "" { + if err := conn.UseKeyspace(c.keyspace); err != nil { + log.Printf("error setting connection keyspace. %v", err) + conn.Close() + return err + } + } + + connPool := c.connPool[conn.Address()] + if connPool == nil { + connPool = NewRoundRobin() + c.connPool[conn.Address()] = connPool + c.hostPool.AddNode(connPool) + } + + connPool.AddNode(conn) + c.conns[conn] = struct{}{} + + return nil +} + +//fillPool manages the pool of connections making sure that each host has the correct +//amount of connections defined. Also the method will test a host with one connection +//instead of flooding the host with number of connections defined in the cluster config +func (c *SimplePool) fillPool() { + //Debounce large amounts of requests to fill pool + select { + case <-time.After(1 * time.Millisecond): + return + case <-c.cFillingPool: + defer func() { c.cFillingPool <- 1 }() + } + + c.mu.Lock() + isClosed := c.quit + c.mu.Unlock() + //Exit if cluster(session) is closed + if isClosed { + return + } + + c.hostMu.RLock() + + //Walk through list of defined hosts + var wg sync.WaitGroup + for host := range c.hosts { + addr := JoinHostPort(host, c.cfg.Port) + + numConns := 1 + //See if the host already has connections in the pool + c.mu.Lock() + conns, ok := c.connPool[addr] + c.mu.Unlock() + + if ok { + //if the host has enough connections just exit + numConns = conns.Size() + if numConns >= c.cfg.NumConns { + continue + } + } else { + //See if the host is reachable + if err := c.connect(addr); err != nil { + continue + } + } + + //This is reached if the host is responsive and needs more connections + //Create connections for host synchronously to mitigate flooding the host. + wg.Add(1) + go func(a string, conns int) { + defer wg.Done() + for ; conns < c.cfg.NumConns; conns++ { + c.connect(a) + } + }(addr, numConns) + } + + c.hostMu.RUnlock() + + //Wait until we're finished connecting to each host before returning + wg.Wait() +} + +// Should only be called if c.mu is locked +func (c *SimplePool) removeConnLocked(conn *Conn) { + conn.Close() + connPool := c.connPool[conn.addr] + if connPool == nil { + return + } + connPool.RemoveNode(conn) + if connPool.Size() == 0 { + c.hostPool.RemoveNode(connPool) + delete(c.connPool, conn.addr) + } + delete(c.conns, conn) +} + +func (c *SimplePool) removeConn(conn *Conn) { + c.mu.Lock() + defer c.mu.Unlock() + c.removeConnLocked(conn) +} + +//HandleError is called by a Connection object to report to the pool an error has occured. +//Logic is then executed within the pool to clean up the erroroneous connection and try to +//top off the pool. +func (c *SimplePool) HandleError(conn *Conn, err error, closed bool) { + if !closed { + // ignore all non-fatal errors + return + } + c.removeConn(conn) + c.mu.Lock() + poolClosed := c.quit + c.mu.Unlock() + if !poolClosed { + go c.fillPool() // top off pool. + } +} + +//Pick selects a connection to be used by the query. +func (c *SimplePool) Pick(qry *Query) *Conn { + //Check if connections are available + c.mu.Lock() + conns := len(c.conns) + c.mu.Unlock() + + if conns == 0 { + //try to populate the pool before returning. + c.fillPool() + } + + return c.hostPool.Pick(qry) +} + +//Size returns the number of connections currently active in the pool +func (p *SimplePool) Size() int { + p.mu.Lock() + conns := len(p.conns) + p.mu.Unlock() + return conns +} + +//Close kills the pool and all associated connections. +func (c *SimplePool) Close() { + c.quitOnce.Do(func() { + c.mu.Lock() + defer c.mu.Unlock() + c.quit = true + close(c.quitWait) + for conn := range c.conns { + c.removeConnLocked(conn) + } + }) +} + +func (c *SimplePool) SetHosts(hosts []HostInfo) { + + c.hostMu.Lock() + toRemove := make(map[string]struct{}) + for k := range c.hosts { + toRemove[k] = struct{}{} + } + + for _, host := range hosts { + host := host + delete(toRemove, host.Peer) + // we already have it + if _, ok := c.hosts[host.Peer]; ok { + // TODO: Check rack, dc, token range is consistent, trigger topology change + // update stored host + continue + } + + c.hosts[host.Peer] = &host + } + + // can we hold c.mu whilst iterating this loop? + for addr := range toRemove { + c.removeHostLocked(addr) + } + c.hostMu.Unlock() + + c.fillPool() +} + +func (c *SimplePool) removeHostLocked(addr string) { + if _, ok := c.hosts[addr]; !ok { + return + } + delete(c.hosts, addr) + + c.mu.Lock() + defer c.mu.Unlock() + + if _, ok := c.connPool[addr]; !ok { + return + } + + for conn := range c.conns { + if conn.Address() == addr { + c.removeConnLocked(conn) + } + } +} + +//NewRoundRobinConnPool creates a connection pool which selects hosts by +//round-robin, and then selects a connection for that host by round-robin. +func NewRoundRobinConnPool(cfg *ClusterConfig) (ConnectionPool, error) { + return NewPolicyConnPool( + cfg, + NewRoundRobinHostPolicy(), + NewRoundRobinConnPolicy, + ) +} + +//NewTokenAwareConnPool creates a connection pool which selects hosts by +//a token aware policy, and then selects a connection for that host by +//round-robin. +func NewTokenAwareConnPool(cfg *ClusterConfig) (ConnectionPool, error) { + return NewPolicyConnPool( + cfg, + NewTokenAwareHostPolicy(NewRoundRobinHostPolicy()), + NewRoundRobinConnPolicy, + ) +} + +type policyConnPool struct { + port int + numConns int + connCfg ConnConfig + keyspace string + + mu sync.RWMutex + hostPolicy HostSelectionPolicy + connPolicy func() ConnSelectionPolicy + hostConnPools map[string]*hostConnPool +} + +//Creates a policy based connection pool. This func isn't meant to be directly +//used as a NewPoolFunc in ClusterConfig, instead a func should be created +//which satisfies the NewPoolFunc type, which calls this func with the desired +//hostPolicy and connPolicy; see NewRoundRobinConnPool or NewTokenAwareConnPool +//for examples. +func NewPolicyConnPool( + cfg *ClusterConfig, + hostPolicy HostSelectionPolicy, + connPolicy func() ConnSelectionPolicy, +) (ConnectionPool, error) { + var err error + var tlsConfig *tls.Config + + if cfg.SslOpts != nil { + tlsConfig, err = setupTLSConfig(cfg.SslOpts) + if err != nil { + return nil, err + } + } + + // create the pool + pool := &policyConnPool{ + port: cfg.Port, + numConns: cfg.NumConns, + connCfg: ConnConfig{ + ProtoVersion: cfg.ProtoVersion, + CQLVersion: cfg.CQLVersion, + Timeout: cfg.Timeout, + NumStreams: cfg.NumStreams, + Compressor: cfg.Compressor, + Authenticator: cfg.Authenticator, + Keepalive: cfg.SocketKeepalive, + tlsConfig: tlsConfig, + }, + keyspace: cfg.Keyspace, + hostPolicy: hostPolicy, + connPolicy: connPolicy, + hostConnPools: map[string]*hostConnPool{}, + } + + hosts := make([]HostInfo, len(cfg.Hosts)) + for i, hostAddr := range cfg.Hosts { + hosts[i].Peer = hostAddr + } + + pool.SetHosts(hosts) + + return pool, nil +} + +func (p *policyConnPool) SetHosts(hosts []HostInfo) { + p.mu.Lock() + + toRemove := make(map[string]struct{}) + for addr := range p.hostConnPools { + toRemove[addr] = struct{}{} + } + + // TODO connect to hosts in parallel, but wait for pools to be + // created before returning + + for i := range hosts { + pool, exists := p.hostConnPools[hosts[i].Peer] + if !exists { + // create a connection pool for the host + pool = newHostConnPool( + hosts[i].Peer, + p.port, + p.numConns, + p.connCfg, + p.keyspace, + p.connPolicy(), + ) + p.hostConnPools[hosts[i].Peer] = pool + } else { + // still have this host, so don't remove it + delete(toRemove, hosts[i].Peer) + } + } + + for addr := range toRemove { + pool := p.hostConnPools[addr] + delete(p.hostConnPools, addr) + pool.Close() + } + + // update the policy + p.hostPolicy.SetHosts(hosts) + + p.mu.Unlock() +} + +func (p *policyConnPool) SetPartitioner(partitioner string) { + p.hostPolicy.SetPartitioner(partitioner) +} + +func (p *policyConnPool) Size() int { + p.mu.RLock() + count := 0 + for _, pool := range p.hostConnPools { + count += pool.Size() + } + p.mu.RUnlock() + + return count +} + +func (p *policyConnPool) Pick(qry *Query) *Conn { + nextHost := p.hostPolicy.Pick(qry) + + p.mu.RLock() + var host *HostInfo + var conn *Conn + for conn == nil { + host = nextHost() + if host == nil { + break + } + conn = p.hostConnPools[host.Peer].Pick(qry) + } + p.mu.RUnlock() + return conn +} + +func (p *policyConnPool) Close() { + p.mu.Lock() + + // remove the hosts from the policy + p.hostPolicy.SetHosts([]HostInfo{}) + + // close the pools + for addr, pool := range p.hostConnPools { + delete(p.hostConnPools, addr) + pool.Close() + } + p.mu.Unlock() +} + +// hostConnPool is a connection pool for a single host. +// Connection selection is based on a provided ConnSelectionPolicy +type hostConnPool struct { + host string + port int + addr string + size int + connCfg ConnConfig + keyspace string + policy ConnSelectionPolicy + // protection for conns, closed, filling + mu sync.RWMutex + conns []*Conn + closed bool + filling bool +} + +func newHostConnPool( + host string, + port int, + size int, + connCfg ConnConfig, + keyspace string, + policy ConnSelectionPolicy, +) *hostConnPool { + + pool := &hostConnPool{ + host: host, + port: port, + addr: JoinHostPort(host, port), + size: size, + connCfg: connCfg, + keyspace: keyspace, + policy: policy, + conns: make([]*Conn, 0, size), + filling: false, + closed: false, + } + + // fill the pool with the initial connections before returning + pool.fill() + + return pool +} + +// Pick a connection from this connection pool for the given query. +func (pool *hostConnPool) Pick(qry *Query) *Conn { + pool.mu.RLock() + if pool.closed { + pool.mu.RUnlock() + return nil + } + + empty := len(pool.conns) == 0 + pool.mu.RUnlock() + + if empty { + // try to fill the empty pool + go pool.fill() + return nil + } + + return pool.policy.Pick(qry) +} + +//Size returns the number of connections currently active in the pool +func (pool *hostConnPool) Size() int { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return len(pool.conns) +} + +//Close the connection pool +func (pool *hostConnPool) Close() { + pool.mu.Lock() + defer pool.mu.Unlock() + + if pool.closed { + return + } + pool.closed = true + + // drain, but don't wait + go pool.drain() +} + +// Fill the connection pool +func (pool *hostConnPool) fill() { + pool.mu.RLock() + // avoid filling a closed pool, or concurrent filling + if pool.closed || pool.filling { + pool.mu.RUnlock() + return + } + + // determine the filling work to be done + startCount := len(pool.conns) + fillCount := pool.size - startCount + + // avoid filling a full (or overfull) pool + if fillCount <= 0 { + pool.mu.RUnlock() + return + } + + // switch from read to write lock + pool.mu.RUnlock() + pool.mu.Lock() + + // double check everything since the lock was released + startCount = len(pool.conns) + fillCount = pool.size - startCount + if pool.closed || pool.filling || fillCount <= 0 { + // looks like another goroutine already beat this + // goroutine to the filling + pool.mu.Unlock() + return + } + + // ok fill the pool + pool.filling = true + + // allow others to access the pool while filling + pool.mu.Unlock() + // only this goroutine should make calls to fill/empty the pool at this + // point until after this routine or its subordinates calls + // fillingStopped + + // fill only the first connection synchronously + if startCount == 0 { + err := pool.connect() + pool.logConnectErr(err) + + if err != nil { + // probably unreachable host + go pool.fillingStopped() + return + } + + // filled one + fillCount-- + + // connect all connections to this host in sync + for fillCount > 0 { + err := pool.connect() + pool.logConnectErr(err) + + // decrement, even on error + fillCount-- + } + + go pool.fillingStopped() + return + } + + // fill the rest of the pool asynchronously + go func() { + for fillCount > 0 { + err := pool.connect() + pool.logConnectErr(err) + + // decrement, even on error + fillCount-- + } + + // mark the end of filling + pool.fillingStopped() + }() +} + +func (pool *hostConnPool) logConnectErr(err error) { + if opErr, ok := err.(*net.OpError); ok && (opErr.Op == "dial" || opErr.Op == "read") { + // connection refused + // these are typical during a node outage so avoid log spam. + } else if err != nil { + // unexpected error + log.Printf("error: failed to connect to %s due to error: %v", pool.addr, err) + } +} + +// transition back to a not-filling state. +func (pool *hostConnPool) fillingStopped() { + // wait for some time to avoid back-to-back filling + // this provides some time between failed attempts + // to fill the pool for the host to recover + time.Sleep(time.Duration(rand.Int31n(100)+31) * time.Millisecond) + + pool.mu.Lock() + pool.filling = false + pool.mu.Unlock() +} + +// create a new connection to the host and add it to the pool +func (pool *hostConnPool) connect() error { + // try to connect + conn, err := Connect(pool.addr, pool.connCfg, pool) + if err != nil { + return err + } + + if pool.keyspace != "" { + // set the keyspace + if err := conn.UseKeyspace(pool.keyspace); err != nil { + conn.Close() + return err + } + } + + // add the Conn to the pool + pool.mu.Lock() + defer pool.mu.Unlock() + + if pool.closed { + conn.Close() + return nil + } + + pool.conns = append(pool.conns, conn) + pool.policy.SetConns(pool.conns) + return nil +} + +// handle any error from a Conn +func (pool *hostConnPool) HandleError(conn *Conn, err error, closed bool) { + if !closed { + // still an open connection, so continue using it + return + } + + pool.mu.Lock() + defer pool.mu.Unlock() + + if pool.closed { + // pool closed + return + } + + // find the connection index + for i, candidate := range pool.conns { + if candidate == conn { + // remove the connection, not preserving order + pool.conns[i], pool.conns = pool.conns[len(pool.conns)-1], pool.conns[:len(pool.conns)-1] + + // update the policy + pool.policy.SetConns(pool.conns) + + // lost a connection, so fill the pool + go pool.fill() + break + } + } +} + +// removes and closes all connections from the pool +func (pool *hostConnPool) drain() { + pool.mu.Lock() + defer pool.mu.Unlock() + + // empty the pool + conns := pool.conns + pool.conns = pool.conns[:0] + + // update the policy + pool.policy.SetConns(pool.conns) + + // close the connections + for _, conn := range conns { + conn.Close() + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/doc.go b/Godeps/_workspace/src/github.com/gocql/gocql/doc.go new file mode 100644 index 000000000..f661cf65f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/doc.go @@ -0,0 +1,9 @@ +// Copyright (c) 2012-2015 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gocql implements a fast and robust Cassandra driver for the +// Go programming language. +package gocql + +// TODO(tux21b): write more docs. diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/errors.go b/Godeps/_workspace/src/github.com/gocql/gocql/errors.go new file mode 100644 index 000000000..1f959dc07 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/errors.go @@ -0,0 +1,88 @@ +package gocql + +import "fmt" + +const ( + errServer = 0x0000 + errProtocol = 0x000A + errCredentials = 0x0100 + errUnavailable = 0x1000 + errOverloaded = 0x1001 + errBootstrapping = 0x1002 + errTruncate = 0x1003 + errWriteTimeout = 0x1100 + errReadTimeout = 0x1200 + errSyntax = 0x2000 + errUnauthorized = 0x2100 + errInvalid = 0x2200 + errConfig = 0x2300 + errAlreadyExists = 0x2400 + errUnprepared = 0x2500 +) + +type RequestError interface { + Code() int + Message() string + Error() string +} + +type errorFrame struct { + frameHeader + + code int + message string +} + +func (e errorFrame) Code() int { + return e.code +} + +func (e errorFrame) Message() string { + return e.message +} + +func (e errorFrame) Error() string { + return e.Message() +} + +func (e errorFrame) String() string { + return fmt.Sprintf("[error code=%x message=%q]", e.code, e.message) +} + +type RequestErrUnavailable struct { + errorFrame + Consistency Consistency + Required int + Alive int +} + +func (e *RequestErrUnavailable) String() string { + return fmt.Sprintf("[request_error_unavailable consistency=%s required=%d alive=%d]", e.Consistency, e.Required, e.Alive) +} + +type RequestErrWriteTimeout struct { + errorFrame + Consistency Consistency + Received int + BlockFor int + WriteType string +} + +type RequestErrReadTimeout struct { + errorFrame + Consistency Consistency + Received int + BlockFor int + DataPresent byte +} + +type RequestErrAlreadyExists struct { + errorFrame + Keyspace string + Table string +} + +type RequestErrUnprepared struct { + errorFrame + StatementId []byte +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/errors_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/errors_test.go new file mode 100644 index 000000000..8c4a588df --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/errors_test.go @@ -0,0 +1,29 @@ +// +build all integration + +package gocql + +import ( + "testing" +) + +func TestErrorsParse(t *testing.T) { + session := createSession(t) + defer session.Close() + + if err := createTable(session, `CREATE TABLE errors_parse (id int primary key)`); err != nil { + t.Fatal("create:", err) + } + + if err := createTable(session, `CREATE TABLE errors_parse (id int primary key)`); err == nil { + t.Fatal("Should have gotten already exists error from cassandra server.") + } else { + switch e := err.(type) { + case *RequestErrAlreadyExists: + if e.Table != "errors_parse" { + t.Fatal("Failed to parse error response from cassandra for ErrAlreadyExists.") + } + default: + t.Fatal("Failed to parse error response from cassandra for ErrAlreadyExists.") + } + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/frame.go b/Godeps/_workspace/src/github.com/gocql/gocql/frame.go new file mode 100644 index 000000000..ba1ad2e47 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/frame.go @@ -0,0 +1,1480 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "runtime" + "sync" + "time" +) + +const ( + protoDirectionMask = 0x80 + protoVersionMask = 0x7F + protoVersion1 = 0x01 + protoVersion2 = 0x02 + protoVersion3 = 0x03 + + maxFrameSize = 256 * 1024 * 1024 +) + +type protoVersion byte + +func (p protoVersion) request() bool { + return p&protoDirectionMask == 0x00 +} + +func (p protoVersion) response() bool { + return p&protoDirectionMask == 0x80 +} + +func (p protoVersion) version() byte { + return byte(p) & protoVersionMask +} + +func (p protoVersion) String() string { + dir := "REQ" + if p.response() { + dir = "RESP" + } + + return fmt.Sprintf("[version=%d direction=%s]", p.version(), dir) +} + +type frameOp byte + +const ( + // header ops + opError frameOp = 0x00 + opStartup = 0x01 + opReady = 0x02 + opAuthenticate = 0x03 + opOptions = 0x05 + opSupported = 0x06 + opQuery = 0x07 + opResult = 0x08 + opPrepare = 0x09 + opExecute = 0x0A + opRegister = 0x0B + opEvent = 0x0C + opBatch = 0x0D + opAuthChallenge = 0x0E + opAuthResponse = 0x0F + opAuthSuccess = 0x10 +) + +func (f frameOp) String() string { + switch f { + case opError: + return "ERROR" + case opStartup: + return "STARTUP" + case opReady: + return "READY" + case opAuthenticate: + return "AUTHENTICATE" + case opOptions: + return "OPTIONS" + case opSupported: + return "SUPPORTED" + case opQuery: + return "QUERY" + case opResult: + return "RESULT" + case opPrepare: + return "PREPARE" + case opExecute: + return "EXECUTE" + case opRegister: + return "REGISTER" + case opEvent: + return "EVENT" + case opBatch: + return "BATCH" + case opAuthChallenge: + return "AUTH_CHALLENGE" + case opAuthResponse: + return "AUTH_RESPONSE" + case opAuthSuccess: + return "AUTH_SUCCESS" + default: + return fmt.Sprintf("UNKNOWN_OP_%d", f) + } +} + +const ( + // result kind + resultKindVoid = 1 + resultKindRows = 2 + resultKindKeyspace = 3 + resultKindPrepared = 4 + resultKindSchemaChanged = 5 + + // rows flags + flagGlobalTableSpec int = 0x01 + flagHasMorePages = 0x02 + flagNoMetaData = 0x04 + + // query flags + flagValues byte = 0x01 + flagSkipMetaData = 0x02 + flagPageSize = 0x04 + flagWithPagingState = 0x08 + flagWithSerialConsistency = 0x10 + flagDefaultTimestamp = 0x20 + flagWithNameValues = 0x40 + + // header flags + flagCompress byte = 0x01 + flagTracing = 0x02 +) + +type Consistency uint16 + +const ( + Any Consistency = 0x00 + One Consistency = 0x01 + Two Consistency = 0x02 + Three Consistency = 0x03 + Quorum Consistency = 0x04 + All Consistency = 0x05 + LocalQuorum Consistency = 0x06 + EachQuorum Consistency = 0x07 + LocalOne Consistency = 0x0A +) + +func (c Consistency) String() string { + switch c { + case Any: + return "ANY" + case One: + return "ONE" + case Two: + return "TWO" + case Three: + return "THREE" + case Quorum: + return "QUORUM" + case All: + return "ALL" + case LocalQuorum: + return "LOCAL_QUORUM" + case EachQuorum: + return "EACH_QUORUM" + case LocalOne: + return "LOCAL_ONE" + default: + return fmt.Sprintf("UNKNOWN_CONS_0x%x", uint16(c)) + } +} + +type SerialConsistency uint16 + +const ( + Serial SerialConsistency = 0x08 + LocalSerial SerialConsistency = 0x09 +) + +func (s SerialConsistency) String() string { + switch s { + case Serial: + return "SERIAL" + case LocalSerial: + return "LOCAL_SERIAL" + default: + return fmt.Sprintf("UNKNOWN_SERIAL_CONS_0x%x", uint16(s)) + } +} + +const ( + apacheCassandraTypePrefix = "org.apache.cassandra.db.marshal." +) + +var ( + ErrFrameTooBig = errors.New("frame length is bigger than the maximum alowed") +) + +func writeInt(p []byte, n int32) { + p[0] = byte(n >> 24) + p[1] = byte(n >> 16) + p[2] = byte(n >> 8) + p[3] = byte(n) +} + +func readInt(p []byte) int32 { + return int32(p[0])<<24 | int32(p[1])<<16 | int32(p[2])<<8 | int32(p[3]) +} + +func writeShort(p []byte, n uint16) { + p[0] = byte(n >> 8) + p[1] = byte(n) +} + +func readShort(p []byte) uint16 { + return uint16(p[0])<<8 | uint16(p[1]) +} + +type frameHeader struct { + version protoVersion + flags byte + stream int + op frameOp + length int +} + +func (f frameHeader) String() string { + return fmt.Sprintf("[header version=%s flags=0x%x stream=%d op=%s length=%d]", f.version, f.flags, f.stream, f.op, f.length) +} + +func (f frameHeader) Header() frameHeader { + return f +} + +const defaultBufSize = 128 + +var framerPool = sync.Pool{ + New: func() interface{} { + return &framer{ + wbuf: make([]byte, defaultBufSize), + readBuffer: make([]byte, defaultBufSize), + } + }, +} + +// a framer is responsible for reading, writing and parsing frames on a single stream +type framer struct { + r io.Reader + w io.Writer + + proto byte + // flags are for outgoing flags, enabling compression and tracing etc + flags byte + compres Compressor + headSize int + // if this frame was read then the header will be here + header *frameHeader + + // if tracing flag is set this is not nil + traceID []byte + + // holds a ref to the whole byte slice for rbuf so that it can be reset to + // 0 after a read. + readBuffer []byte + + rbuf []byte + wbuf []byte +} + +func newFramer(r io.Reader, w io.Writer, compressor Compressor, version byte) *framer { + f := framerPool.Get().(*framer) + var flags byte + if compressor != nil { + flags |= flagCompress + } + + version &= protoVersionMask + + headSize := 8 + if version > protoVersion2 { + headSize = 9 + } + + f.compres = compressor + f.proto = version + f.flags = flags + f.headSize = headSize + + f.r = r + f.rbuf = f.readBuffer[:0] + + f.w = w + f.wbuf = f.wbuf[:0] + + f.header = nil + f.traceID = nil + + return f +} + +type frame interface { + Header() frameHeader +} + +func readHeader(r io.Reader, p []byte) (head frameHeader, err error) { + _, err = io.ReadFull(r, p) + if err != nil { + return + } + + version := p[0] & protoVersionMask + + if version < protoVersion1 || version > protoVersion3 { + err = fmt.Errorf("invalid version: %x", version) + return + } + + head.version = protoVersion(p[0]) + head.flags = p[1] + + if version > protoVersion2 { + if len(p) < 9 { + return frameHeader{}, fmt.Errorf("not enough bytes to read header require 9 got: %d", len(p)) + } + + head.stream = int(int16(p[2])<<8 | int16(p[3])) + head.op = frameOp(p[4]) + head.length = int(readInt(p[5:])) + } else { + if len(p) < 8 { + return frameHeader{}, fmt.Errorf("not enough bytes to read header require 8 got: %d", len(p)) + } + + head.stream = int(int8(p[2])) + head.op = frameOp(p[3]) + head.length = int(readInt(p[4:])) + } + + return +} + +// explicitly enables tracing for the framers outgoing requests +func (f *framer) trace() { + f.flags |= flagTracing +} + +// reads a frame form the wire into the framers buffer +func (f *framer) readFrame(head *frameHeader) error { + if head.length < 0 { + return fmt.Errorf("frame body length can not be less than 0: %d", head.length) + } else if head.length > maxFrameSize { + // need to free up the connection to be used again + _, err := io.CopyN(ioutil.Discard, f.r, int64(head.length)) + if err != nil { + return fmt.Errorf("error whilst trying to discard frame with invalid length: %v", err) + } + return ErrFrameTooBig + } + + if cap(f.readBuffer) >= head.length { + f.rbuf = f.readBuffer[:head.length] + } else { + f.readBuffer = make([]byte, head.length) + f.rbuf = f.readBuffer + } + + // assume the underlying reader takes care of timeouts and retries + _, err := io.ReadFull(f.r, f.rbuf) + if err != nil { + return err + } + + if head.flags&flagCompress == flagCompress { + if f.compres == nil { + return NewErrProtocol("no compressor available with compressed frame body") + } + + f.rbuf, err = f.compres.Decode(f.rbuf) + if err != nil { + return err + } + } + + f.header = head + return nil +} + +func (f *framer) parseFrame() (frame frame, err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(error) + } + }() + + if f.header.version.request() { + return nil, NewErrProtocol("got a request frame from server: %v", f.header.version) + } + + if f.header.flags&flagTracing == flagTracing { + f.readTrace() + } + + // asumes that the frame body has been read into rbuf + switch f.header.op { + case opError: + frame = f.parseErrorFrame() + case opReady: + frame = f.parseReadyFrame() + case opResult: + frame, err = f.parseResultFrame() + case opSupported: + frame = f.parseSupportedFrame() + case opAuthenticate: + frame = f.parseAuthenticateFrame() + case opAuthChallenge: + frame = f.parseAuthChallengeFrame() + case opAuthSuccess: + frame = f.parseAuthSuccessFrame() + default: + return nil, NewErrProtocol("unknown op in frame header: %s", f.header.op) + } + + return +} + +func (f *framer) parseErrorFrame() frame { + code := f.readInt() + msg := f.readString() + + errD := errorFrame{ + frameHeader: *f.header, + code: code, + message: msg, + } + + switch code { + case errUnavailable: + cl := f.readConsistency() + required := f.readInt() + alive := f.readInt() + return &RequestErrUnavailable{ + errorFrame: errD, + Consistency: cl, + Required: required, + Alive: alive, + } + case errWriteTimeout: + cl := f.readConsistency() + received := f.readInt() + blockfor := f.readInt() + writeType := f.readString() + return &RequestErrWriteTimeout{ + errorFrame: errD, + Consistency: cl, + Received: received, + BlockFor: blockfor, + WriteType: writeType, + } + case errReadTimeout: + cl := f.readConsistency() + received := f.readInt() + blockfor := f.readInt() + dataPresent := f.readByte() + return &RequestErrReadTimeout{ + errorFrame: errD, + Consistency: cl, + Received: received, + BlockFor: blockfor, + DataPresent: dataPresent, + } + case errAlreadyExists: + ks := f.readString() + table := f.readString() + return &RequestErrAlreadyExists{ + errorFrame: errD, + Keyspace: ks, + Table: table, + } + case errUnprepared: + stmtId := f.readShortBytes() + return &RequestErrUnprepared{ + errorFrame: errD, + StatementId: stmtId, + } + default: + return &errD + } +} + +func (f *framer) writeHeader(flags byte, op frameOp, stream int) { + f.wbuf = f.wbuf[:0] + f.wbuf = append(f.wbuf, + f.proto, + flags, + ) + + if f.proto > protoVersion2 { + f.wbuf = append(f.wbuf, + byte(stream>>8), + byte(stream), + ) + } else { + f.wbuf = append(f.wbuf, + byte(stream), + ) + } + + // pad out length + f.wbuf = append(f.wbuf, + byte(op), + 0, + 0, + 0, + 0, + ) +} + +func (f *framer) setLength(length int) { + p := 4 + if f.proto > protoVersion2 { + p = 5 + } + + f.wbuf[p+0] = byte(length >> 24) + f.wbuf[p+1] = byte(length >> 16) + f.wbuf[p+2] = byte(length >> 8) + f.wbuf[p+3] = byte(length) +} + +func (f *framer) finishWrite() error { + if len(f.wbuf) > maxFrameSize { + // huge app frame, lets remove it so it doesnt bloat the heap + f.wbuf = make([]byte, defaultBufSize) + return ErrFrameTooBig + } + + if f.wbuf[1]&flagCompress == flagCompress { + if f.compres == nil { + panic("compress flag set with no compressor") + } + + // TODO: only compress frames which are big enough + compressed, err := f.compres.Encode(f.wbuf[f.headSize:]) + if err != nil { + return err + } + + f.wbuf = append(f.wbuf[:f.headSize], compressed...) + } + length := len(f.wbuf) - f.headSize + f.setLength(length) + + _, err := f.w.Write(f.wbuf) + if err != nil { + return err + } + + return nil +} + +func (f *framer) readTrace() { + f.traceID = f.readUUID().Bytes() +} + +type readyFrame struct { + frameHeader +} + +func (f *framer) parseReadyFrame() frame { + return &readyFrame{ + frameHeader: *f.header, + } +} + +type supportedFrame struct { + frameHeader + + supported map[string][]string +} + +// TODO: if we move the body buffer onto the frameHeader then we only need a single +// framer, and can move the methods onto the header. +func (f *framer) parseSupportedFrame() frame { + return &supportedFrame{ + frameHeader: *f.header, + + supported: f.readStringMultiMap(), + } +} + +type writeStartupFrame struct { + opts map[string]string +} + +func (w *writeStartupFrame) writeFrame(framer *framer, streamID int) error { + return framer.writeStartupFrame(streamID, w.opts) +} + +func (f *framer) writeStartupFrame(streamID int, options map[string]string) error { + f.writeHeader(f.flags&^flagCompress, opStartup, streamID) + f.writeStringMap(options) + + return f.finishWrite() +} + +type writePrepareFrame struct { + statement string +} + +func (w *writePrepareFrame) writeFrame(framer *framer, streamID int) error { + return framer.writePrepareFrame(streamID, w.statement) +} + +func (f *framer) writePrepareFrame(stream int, statement string) error { + f.writeHeader(f.flags, opPrepare, stream) + f.writeLongString(statement) + return f.finishWrite() +} + +func (f *framer) readTypeInfo() TypeInfo { + // TODO: factor this out so the same code paths can be used to parse custom + // types and other types, as much of the logic will be duplicated. + id := f.readShort() + + simple := NativeType{ + proto: f.proto, + typ: Type(id), + } + + if simple.typ == TypeCustom { + simple.custom = f.readString() + if cassType := getApacheCassandraType(simple.custom); cassType != TypeCustom { + simple.typ = cassType + } + } + + switch simple.typ { + case TypeTuple: + n := f.readShort() + tuple := TupleTypeInfo{ + NativeType: simple, + Elems: make([]TypeInfo, n), + } + + for i := 0; i < int(n); i++ { + tuple.Elems[i] = f.readTypeInfo() + } + + return tuple + + case TypeUDT: + udt := UDTTypeInfo{ + NativeType: simple, + } + udt.KeySpace = f.readString() + udt.Name = f.readString() + + n := f.readShort() + udt.Elements = make([]UDTField, n) + for i := 0; i < int(n); i++ { + field := &udt.Elements[i] + field.Name = f.readString() + field.Type = f.readTypeInfo() + } + + return udt + case TypeMap, TypeList, TypeSet: + collection := CollectionType{ + NativeType: simple, + } + + if simple.typ == TypeMap { + collection.Key = f.readTypeInfo() + } + + collection.Elem = f.readTypeInfo() + + return collection + } + + return simple +} + +type resultMetadata struct { + flags int + + // only if flagPageState + pagingState []byte + + columns []ColumnInfo + + // this is a count of the total number of columns which can be scanned, + // it is at minimum len(columns) but may be larger, for instance when a column + // is a UDT or tuple. + actualColCount int +} + +func (r resultMetadata) String() string { + return fmt.Sprintf("[metadata flags=0x%x paging_state=% X columns=%v]", r.flags, r.pagingState, r.columns) +} + +func (f *framer) readCol(col *ColumnInfo, meta *resultMetadata, globalSpec bool, keyspace, table string) { + if !globalSpec { + col.Keyspace = f.readString() + col.Table = f.readString() + } else { + col.Keyspace = keyspace + col.Table = table + } + + col.Name = f.readString() + col.TypeInfo = f.readTypeInfo() + switch v := col.TypeInfo.(type) { + // maybe also UDT + case TupleTypeInfo: + // -1 because we already included the tuple column + meta.actualColCount += len(v.Elems) - 1 + } +} + +func (f *framer) parseResultMetadata() resultMetadata { + meta := resultMetadata{ + flags: f.readInt(), + } + + colCount := f.readInt() + if colCount < 0 { + panic(fmt.Errorf("received negative column count: %d", colCount)) + } + meta.actualColCount = colCount + + if meta.flags&flagHasMorePages == flagHasMorePages { + meta.pagingState = f.readBytes() + } + + if meta.flags&flagNoMetaData == flagNoMetaData { + return meta + } + + var keyspace, table string + globalSpec := meta.flags&flagGlobalTableSpec == flagGlobalTableSpec + if globalSpec { + keyspace = f.readString() + table = f.readString() + } + + var cols []ColumnInfo + if colCount < 1000 { + // preallocate columninfo to avoid excess copying + cols = make([]ColumnInfo, colCount) + for i := 0; i < colCount; i++ { + f.readCol(&cols[i], &meta, globalSpec, keyspace, table) + } + + } else { + // use append, huge number of columns usually indicates a corrupt frame or + // just a huge row. + for i := 0; i < colCount; i++ { + var col ColumnInfo + f.readCol(&col, &meta, globalSpec, keyspace, table) + cols = append(cols, col) + } + } + + meta.columns = cols + + return meta +} + +type resultVoidFrame struct { + frameHeader +} + +func (f *resultVoidFrame) String() string { + return "[result_void]" +} + +func (f *framer) parseResultFrame() (frame, error) { + kind := f.readInt() + + switch kind { + case resultKindVoid: + return &resultVoidFrame{frameHeader: *f.header}, nil + case resultKindRows: + return f.parseResultRows(), nil + case resultKindKeyspace: + return f.parseResultSetKeyspace(), nil + case resultKindPrepared: + return f.parseResultPrepared(), nil + case resultKindSchemaChanged: + return f.parseResultSchemaChange(), nil + } + + return nil, NewErrProtocol("unknown result kind: %x", kind) +} + +type resultRowsFrame struct { + frameHeader + + meta resultMetadata + rows [][][]byte +} + +func (f *resultRowsFrame) String() string { + return fmt.Sprintf("[result_rows meta=%v]", f.meta) +} + +func (f *framer) parseResultRows() frame { + meta := f.parseResultMetadata() + + numRows := f.readInt() + if numRows < 0 { + panic(fmt.Errorf("invalid row_count in result frame: %d", numRows)) + } + + colCount := len(meta.columns) + + rows := make([][][]byte, numRows) + for i := 0; i < numRows; i++ { + rows[i] = make([][]byte, colCount) + for j := 0; j < colCount; j++ { + rows[i][j] = f.readBytes() + } + } + + return &resultRowsFrame{ + frameHeader: *f.header, + meta: meta, + rows: rows, + } +} + +type resultKeyspaceFrame struct { + frameHeader + keyspace string +} + +func (r *resultKeyspaceFrame) String() string { + return fmt.Sprintf("[result_keyspace keyspace=%s]", r.keyspace) +} + +func (f *framer) parseResultSetKeyspace() frame { + return &resultKeyspaceFrame{ + frameHeader: *f.header, + keyspace: f.readString(), + } +} + +type resultPreparedFrame struct { + frameHeader + + preparedID []byte + reqMeta resultMetadata + respMeta resultMetadata +} + +func (f *framer) parseResultPrepared() frame { + frame := &resultPreparedFrame{ + frameHeader: *f.header, + preparedID: f.readShortBytes(), + reqMeta: f.parseResultMetadata(), + } + + if f.proto < protoVersion2 { + return frame + } + + frame.respMeta = f.parseResultMetadata() + + return frame +} + +type resultSchemaChangeFrame struct { + frameHeader + + change string + keyspace string + table string +} + +func (s *resultSchemaChangeFrame) String() string { + return fmt.Sprintf("[result_schema_change change=%s keyspace=%s table=%s]", s.change, s.keyspace, s.table) +} + +func (f *framer) parseResultSchemaChange() frame { + frame := &resultSchemaChangeFrame{ + frameHeader: *f.header, + } + + if f.proto < protoVersion3 { + frame.change = f.readString() + frame.keyspace = f.readString() + frame.table = f.readString() + } else { + // TODO: improve type representation of this + frame.change = f.readString() + target := f.readString() + switch target { + case "KEYSPACE": + frame.keyspace = f.readString() + case "TABLE", "TYPE": + frame.keyspace = f.readString() + frame.table = f.readString() + } + } + + return frame +} + +type authenticateFrame struct { + frameHeader + + class string +} + +func (a *authenticateFrame) String() string { + return fmt.Sprintf("[authenticate class=%q]", a.class) +} + +func (f *framer) parseAuthenticateFrame() frame { + return &authenticateFrame{ + frameHeader: *f.header, + class: f.readString(), + } +} + +type authSuccessFrame struct { + frameHeader + + data []byte +} + +func (a *authSuccessFrame) String() string { + return fmt.Sprintf("[auth_success data=%q]", a.data) +} + +func (f *framer) parseAuthSuccessFrame() frame { + return &authSuccessFrame{ + frameHeader: *f.header, + data: f.readBytes(), + } +} + +type authChallengeFrame struct { + frameHeader + + data []byte +} + +func (a *authChallengeFrame) String() string { + return fmt.Sprintf("[auth_challenge data=%q]", a.data) +} + +func (f *framer) parseAuthChallengeFrame() frame { + return &authChallengeFrame{ + frameHeader: *f.header, + data: f.readBytes(), + } +} + +type writeAuthResponseFrame struct { + data []byte +} + +func (a *writeAuthResponseFrame) String() string { + return fmt.Sprintf("[auth_response data=%q]", a.data) +} + +func (a *writeAuthResponseFrame) writeFrame(framer *framer, streamID int) error { + return framer.writeAuthResponseFrame(streamID, a.data) +} + +func (f *framer) writeAuthResponseFrame(streamID int, data []byte) error { + f.writeHeader(f.flags, opAuthResponse, streamID) + f.writeBytes(data) + return f.finishWrite() +} + +type queryValues struct { + value []byte + // optional name, will set With names for values flag + name string +} + +type queryParams struct { + consistency Consistency + // v2+ + skipMeta bool + values []queryValues + pageSize int + pagingState []byte + serialConsistency SerialConsistency + // v3+ + defaultTimestamp bool +} + +func (q queryParams) String() string { + return fmt.Sprintf("[query_params consistency=%v skip_meta=%v page_size=%d paging_state=%q serial_consistency=%v default_timestamp=%v values=%v]", + q.consistency, q.skipMeta, q.pageSize, q.pagingState, q.serialConsistency, q.defaultTimestamp, q.values) +} + +func (f *framer) writeQueryParams(opts *queryParams) { + f.writeConsistency(opts.consistency) + + if f.proto == protoVersion1 { + return + } + + var flags byte + if len(opts.values) > 0 { + flags |= flagValues + } + if opts.skipMeta { + flags |= flagSkipMetaData + } + if opts.pageSize > 0 { + flags |= flagPageSize + } + if len(opts.pagingState) > 0 { + flags |= flagWithPagingState + } + if opts.serialConsistency > 0 { + flags |= flagWithSerialConsistency + } + + names := false + + // protoV3 specific things + if f.proto > protoVersion2 { + if opts.defaultTimestamp { + flags |= flagDefaultTimestamp + } + + if len(opts.values) > 0 && opts.values[0].name != "" { + flags |= flagWithNameValues + names = true + } + } + + f.writeByte(flags) + + if n := len(opts.values); n > 0 { + f.writeShort(uint16(n)) + for i := 0; i < n; i++ { + if names { + f.writeString(opts.values[i].name) + } + f.writeBytes(opts.values[i].value) + } + } + + if opts.pageSize > 0 { + f.writeInt(int32(opts.pageSize)) + } + + if len(opts.pagingState) > 0 { + f.writeBytes(opts.pagingState) + } + + if opts.serialConsistency > 0 { + f.writeConsistency(Consistency(opts.serialConsistency)) + } + + if f.proto > protoVersion2 && opts.defaultTimestamp { + // timestamp in microseconds + ts := time.Now().UnixNano() / 1000 + f.writeLong(ts) + } +} + +type writeQueryFrame struct { + statement string + params queryParams +} + +func (w *writeQueryFrame) String() string { + return fmt.Sprintf("[query statement=%q params=%v]", w.statement, w.params) +} + +func (w *writeQueryFrame) writeFrame(framer *framer, streamID int) error { + return framer.writeQueryFrame(streamID, w.statement, &w.params) +} + +func (f *framer) writeQueryFrame(streamID int, statement string, params *queryParams) error { + f.writeHeader(f.flags, opQuery, streamID) + f.writeLongString(statement) + f.writeQueryParams(params) + + return f.finishWrite() +} + +type frameWriter interface { + writeFrame(framer *framer, streamID int) error +} + +type writeExecuteFrame struct { + preparedID []byte + params queryParams +} + +func (e *writeExecuteFrame) String() string { + return fmt.Sprintf("[execute id=% X params=%v]", e.preparedID, &e.params) +} + +func (e *writeExecuteFrame) writeFrame(fr *framer, streamID int) error { + return fr.writeExecuteFrame(streamID, e.preparedID, &e.params) +} + +func (f *framer) writeExecuteFrame(streamID int, preparedID []byte, params *queryParams) error { + f.writeHeader(f.flags, opExecute, streamID) + f.writeShortBytes(preparedID) + if f.proto > protoVersion1 { + f.writeQueryParams(params) + } else { + n := len(params.values) + f.writeShort(uint16(n)) + for i := 0; i < n; i++ { + f.writeBytes(params.values[i].value) + } + f.writeConsistency(params.consistency) + } + + return f.finishWrite() +} + +// TODO: can we replace BatchStatemt with batchStatement? As they prety much +// duplicate each other +type batchStatment struct { + preparedID []byte + statement string + values []queryValues +} + +type writeBatchFrame struct { + typ BatchType + statements []batchStatment + consistency Consistency + + // v3+ + serialConsistency SerialConsistency + defaultTimestamp bool +} + +func (w *writeBatchFrame) writeFrame(framer *framer, streamID int) error { + return framer.writeBatchFrame(streamID, w) +} + +func (f *framer) writeBatchFrame(streamID int, w *writeBatchFrame) error { + f.writeHeader(f.flags, opBatch, streamID) + f.writeByte(byte(w.typ)) + + n := len(w.statements) + f.writeShort(uint16(n)) + + var flags byte + + for i := 0; i < n; i++ { + b := &w.statements[i] + if len(b.preparedID) == 0 { + f.writeByte(0) + f.writeLongString(b.statement) + } else { + f.writeByte(1) + f.writeShortBytes(b.preparedID) + } + + f.writeShort(uint16(len(b.values))) + for j := range b.values { + col := &b.values[j] + if f.proto > protoVersion2 && col.name != "" { + // TODO: move this check into the caller and set a flag on writeBatchFrame + // to indicate using named values + flags |= flagWithNameValues + f.writeString(col.name) + } + f.writeBytes(col.value) + } + } + + f.writeConsistency(w.consistency) + + if f.proto > protoVersion2 { + if w.serialConsistency > 0 { + flags |= flagWithSerialConsistency + } + if w.defaultTimestamp { + flags |= flagDefaultTimestamp + } + + f.writeByte(flags) + + if w.serialConsistency > 0 { + f.writeConsistency(Consistency(w.serialConsistency)) + } + if w.defaultTimestamp { + now := time.Now().UnixNano() / 1000 + f.writeLong(now) + } + } + + return f.finishWrite() +} + +func (f *framer) readByte() byte { + if len(f.rbuf) < 1 { + panic(fmt.Errorf("not enough bytes in buffer to read byte require 1 got: %d", len(f.rbuf))) + } + + b := f.rbuf[0] + f.rbuf = f.rbuf[1:] + return b +} + +func (f *framer) readInt() (n int) { + if len(f.rbuf) < 4 { + panic(fmt.Errorf("not enough bytes in buffer to read int require 4 got: %d", len(f.rbuf))) + } + + n = int(int32(f.rbuf[0])<<24 | int32(f.rbuf[1])<<16 | int32(f.rbuf[2])<<8 | int32(f.rbuf[3])) + f.rbuf = f.rbuf[4:] + return +} + +func (f *framer) readShort() (n uint16) { + if len(f.rbuf) < 2 { + panic(fmt.Errorf("not enough bytes in buffer to read short require 2 got: %d", len(f.rbuf))) + } + n = uint16(f.rbuf[0])<<8 | uint16(f.rbuf[1]) + f.rbuf = f.rbuf[2:] + return +} + +func (f *framer) readLong() (n int64) { + if len(f.rbuf) < 8 { + panic(fmt.Errorf("not enough bytes in buffer to read long require 8 got: %d", len(f.rbuf))) + } + n = int64(f.rbuf[0])<<56 | int64(f.rbuf[1])<<48 | int64(f.rbuf[2])<<40 | int64(f.rbuf[3])<<32 | + int64(f.rbuf[4])<<24 | int64(f.rbuf[5])<<16 | int64(f.rbuf[6])<<8 | int64(f.rbuf[7]) + f.rbuf = f.rbuf[8:] + return +} + +func (f *framer) readString() (s string) { + size := f.readShort() + + if len(f.rbuf) < int(size) { + panic(fmt.Errorf("not enough bytes in buffer to read string require %d got: %d", size, len(f.rbuf))) + } + + s = string(f.rbuf[:size]) + f.rbuf = f.rbuf[size:] + return +} + +func (f *framer) readLongString() (s string) { + size := f.readInt() + + if len(f.rbuf) < size { + panic(fmt.Errorf("not enough bytes in buffer to read long string require %d got: %d", size, len(f.rbuf))) + } + + s = string(f.rbuf[:size]) + f.rbuf = f.rbuf[size:] + return +} + +func (f *framer) readUUID() *UUID { + if len(f.rbuf) < 16 { + panic(fmt.Errorf("not enough bytes in buffer to read uuid require %d got: %d", 16, len(f.rbuf))) + } + + // TODO: how to handle this error, if it is a uuid, then sureley, problems? + u, _ := UUIDFromBytes(f.rbuf[:16]) + f.rbuf = f.rbuf[16:] + return &u +} + +func (f *framer) readStringList() []string { + size := f.readShort() + + l := make([]string, size) + for i := 0; i < int(size); i++ { + l[i] = f.readString() + } + + return l +} + +func (f *framer) readBytes() []byte { + size := f.readInt() + if size < 0 { + return nil + } + + if len(f.rbuf) < size { + panic(fmt.Errorf("not enough bytes in buffer to read bytes require %d got: %d", size, len(f.rbuf))) + } + + // we cant make assumptions about the length of the life of the supplied byte + // slice so we defensivly copy it out of the underlying buffer. This has the + // downside of increasing allocs per read but will provide much greater memory + // safety. The allocs can hopefully be improved in the future. + // TODO: dont copy into a new slice + l := make([]byte, size) + copy(l, f.rbuf[:size]) + f.rbuf = f.rbuf[size:] + + return l +} + +func (f *framer) readShortBytes() []byte { + size := f.readShort() + if len(f.rbuf) < int(size) { + panic(fmt.Errorf("not enough bytes in buffer to read short bytes: require %d got %d", size, len(f.rbuf))) + } + + l := make([]byte, size) + copy(l, f.rbuf[:size]) + f.rbuf = f.rbuf[size:] + + return l +} + +func (f *framer) readInet() (net.IP, int) { + if len(f.rbuf) < 1 { + panic(fmt.Errorf("not enough bytes in buffer to read inet size require %d got: %d", 1, len(f.rbuf))) + } + + size := f.rbuf[0] + f.rbuf = f.rbuf[1:] + + if !(size == 4 || size == 16) { + panic(fmt.Errorf("invalid IP size: %d", size)) + } + + if len(f.rbuf) < 1 { + panic(fmt.Errorf("not enough bytes in buffer to read inet require %d got: %d", size, len(f.rbuf))) + } + + ip := make([]byte, size) + copy(ip, f.rbuf[:size]) + f.rbuf = f.rbuf[size:] + + port := f.readInt() + return net.IP(ip), port +} + +func (f *framer) readConsistency() Consistency { + return Consistency(f.readShort()) +} + +func (f *framer) readStringMap() map[string]string { + size := f.readShort() + m := make(map[string]string) + + for i := 0; i < int(size); i++ { + k := f.readString() + v := f.readString() + m[k] = v + } + + return m +} + +func (f *framer) readStringMultiMap() map[string][]string { + size := f.readShort() + m := make(map[string][]string) + + for i := 0; i < int(size); i++ { + k := f.readString() + v := f.readStringList() + m[k] = v + } + + return m +} + +func (f *framer) writeByte(b byte) { + f.wbuf = append(f.wbuf, b) +} + +// these are protocol level binary types +func (f *framer) writeInt(n int32) { + f.wbuf = append(f.wbuf, + byte(n>>24), + byte(n>>16), + byte(n>>8), + byte(n), + ) +} + +func (f *framer) writeShort(n uint16) { + f.wbuf = append(f.wbuf, + byte(n>>8), + byte(n), + ) +} + +func (f *framer) writeLong(n int64) { + f.wbuf = append(f.wbuf, + byte(n>>56), + byte(n>>48), + byte(n>>40), + byte(n>>32), + byte(n>>24), + byte(n>>16), + byte(n>>8), + byte(n), + ) +} + +func (f *framer) writeString(s string) { + f.writeShort(uint16(len(s))) + f.wbuf = append(f.wbuf, s...) +} + +func (f *framer) writeLongString(s string) { + f.writeInt(int32(len(s))) + f.wbuf = append(f.wbuf, s...) +} + +func (f *framer) writeUUID(u *UUID) { + f.wbuf = append(f.wbuf, u[:]...) +} + +func (f *framer) writeStringList(l []string) { + f.writeShort(uint16(len(l))) + for _, s := range l { + f.writeString(s) + } +} + +func (f *framer) writeBytes(p []byte) { + // TODO: handle null case correctly, + // [bytes] A [int] n, followed by n bytes if n >= 0. If n < 0, + // no byte should follow and the value represented is `null`. + if p == nil { + f.writeInt(-1) + } else { + f.writeInt(int32(len(p))) + f.wbuf = append(f.wbuf, p...) + } +} + +func (f *framer) writeShortBytes(p []byte) { + f.writeShort(uint16(len(p))) + f.wbuf = append(f.wbuf, p...) +} + +func (f *framer) writeInet(ip net.IP, port int) { + f.wbuf = append(f.wbuf, + byte(len(ip)), + ) + + f.wbuf = append(f.wbuf, + []byte(ip)..., + ) + + f.writeInt(int32(port)) +} + +func (f *framer) writeConsistency(cons Consistency) { + f.writeShort(uint16(cons)) +} + +func (f *framer) writeStringMap(m map[string]string) { + f.writeShort(uint16(len(m))) + for k, v := range m { + f.writeString(k) + f.writeString(v) + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/frame_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/frame_test.go new file mode 100644 index 000000000..87dcfb9a7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/frame_test.go @@ -0,0 +1,98 @@ +package gocql + +import ( + "bytes" + "testing" +) + +func TestFuzzBugs(t *testing.T) { + // these inputs are found using go-fuzz (https://github.com/dvyukov/go-fuzz) + // and should cause a panic unless fixed. + tests := [][]byte{ + []byte("00000\xa0000"), + []byte("\x8000\x0e\x00\x00\x00\x000"), + []byte("\x8000\x00\x00\x00\x00\t0000000000"), + []byte("\xa0\xff\x01\xae\xefqE\xf2\x1a"), + []byte("\x8200\b\x00\x00\x00c\x00\x00\x00\x02000\x01\x00\x00\x00\x03" + + "\x00\n0000000000\x00\x14000000" + + "00000000000000\x00\x020000" + + "\x00\a000000000\x00\x050000000" + + "\xff0000000000000000000" + + "0000000"), + []byte("\x82\xe600\x00\x00\x00\x000"), + []byte("\x8200\b\x00\x00\x00\b0\x00\x00\x00\x040000"), + []byte("\x8200\x00\x00\x00\x00\x100\x00\x00\x12\x00\x00\x0000000" + + "00000"), + []byte("\x83000\b\x00\x00\x00\x14\x00\x00\x00\x020000000" + + "000000000"), + []byte("\x83000\b\x00\x00\x000\x00\x00\x00\x04\x00\x1000000" + + "00000000000000e00000" + + "000\x800000000000000000" + + "0000000000000"), + } + + for i, test := range tests { + t.Logf("test %d input: %q", i, test) + + var bw bytes.Buffer + + r := bytes.NewReader(test) + head, err := readHeader(r, make([]byte, 9)) + if err != nil { + continue + } + + framer := newFramer(r, &bw, nil, byte(head.version)) + err = framer.readFrame(&head) + if err != nil { + continue + } + + _, err = framer.parseFrame() + if err != nil { + continue + } + + t.Errorf("(%d) expected to fail for input %q", i, test) + } +} + +func TestFrameWriteTooLong(t *testing.T) { + w := &bytes.Buffer{} + framer := newFramer(nil, w, nil, 2) + + framer.writeHeader(0, opStartup, 1) + framer.writeBytes(make([]byte, maxFrameSize+1)) + err := framer.finishWrite() + if err != ErrFrameTooBig { + t.Fatalf("expected to get %v got %v", ErrFrameTooBig, err) + } +} + +func TestFrameReadTooLong(t *testing.T) { + r := &bytes.Buffer{} + r.Write(make([]byte, maxFrameSize+1)) + // write a new header right after this frame to verify that we can read it + r.Write([]byte{0x02, 0x00, 0x00, opReady, 0x00, 0x00, 0x00, 0x00}) + + framer := newFramer(r, nil, nil, 2) + + head := frameHeader{ + version: 2, + op: opReady, + length: r.Len() - 8, + } + + err := framer.readFrame(&head) + if err != ErrFrameTooBig { + t.Fatalf("expected to get %v got %v", ErrFrameTooBig, err) + } + + head, err = readHeader(r, make([]byte, 8)) + if err != nil { + t.Fatal(err) + } + if head.op != opReady { + t.Fatalf("expected to get header %v got %v", opReady, head.op) + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/fuzz.go b/Godeps/_workspace/src/github.com/gocql/gocql/fuzz.go new file mode 100644 index 000000000..3606f9381 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/fuzz.go @@ -0,0 +1,33 @@ +// +build gofuzz + +package gocql + +import "bytes" + +func Fuzz(data []byte) int { + var bw bytes.Buffer + + r := bytes.NewReader(data) + + head, err := readHeader(r, make([]byte, 9)) + if err != nil { + return 0 + } + + framer := newFramer(r, &bw, nil, byte(head.version)) + err = framer.readFrame(&head) + if err != nil { + return 0 + } + + frame, err := framer.parseFrame() + if err != nil { + return 0 + } + + if frame != nil { + return 1 + } + + return 2 +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/helpers.go b/Godeps/_workspace/src/github.com/gocql/gocql/helpers.go new file mode 100644 index 000000000..531892a9c --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/helpers.go @@ -0,0 +1,180 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "math/big" + "reflect" + "strings" + "time" + + "speter.net/go/exp/math/dec/inf" +) + +type RowData struct { + Columns []string + Values []interface{} +} + +func goType(t TypeInfo) reflect.Type { + switch t.Type() { + case TypeVarchar, TypeAscii, TypeInet: + return reflect.TypeOf(*new(string)) + case TypeBigInt, TypeCounter: + return reflect.TypeOf(*new(int64)) + case TypeTimestamp: + return reflect.TypeOf(*new(time.Time)) + case TypeBlob: + return reflect.TypeOf(*new([]byte)) + case TypeBoolean: + return reflect.TypeOf(*new(bool)) + case TypeFloat: + return reflect.TypeOf(*new(float32)) + case TypeDouble: + return reflect.TypeOf(*new(float64)) + case TypeInt: + return reflect.TypeOf(*new(int)) + case TypeDecimal: + return reflect.TypeOf(*new(*inf.Dec)) + case TypeUUID, TypeTimeUUID: + return reflect.TypeOf(*new(UUID)) + case TypeList, TypeSet: + return reflect.SliceOf(goType(t.(CollectionType).Elem)) + case TypeMap: + return reflect.MapOf(goType(t.(CollectionType).Key), goType(t.(CollectionType).Elem)) + case TypeVarint: + return reflect.TypeOf(*new(*big.Int)) + case TypeTuple: + // what can we do here? all there is to do is to make a list of interface{} + tuple := t.(TupleTypeInfo) + return reflect.TypeOf(make([]interface{}, len(tuple.Elems))) + default: + return nil + } +} + +func dereference(i interface{}) interface{} { + return reflect.Indirect(reflect.ValueOf(i)).Interface() +} + +func getApacheCassandraType(class string) Type { + switch strings.TrimPrefix(class, apacheCassandraTypePrefix) { + case "AsciiType": + return TypeAscii + case "LongType": + return TypeBigInt + case "BytesType": + return TypeBlob + case "BooleanType": + return TypeBoolean + case "CounterColumnType": + return TypeCounter + case "DecimalType": + return TypeDecimal + case "DoubleType": + return TypeDouble + case "FloatType": + return TypeFloat + case "Int32Type": + return TypeInt + case "DateType", "TimestampType": + return TypeTimestamp + case "UUIDType": + return TypeUUID + case "UTF8Type": + return TypeVarchar + case "IntegerType": + return TypeVarint + case "TimeUUIDType": + return TypeTimeUUID + case "InetAddressType": + return TypeInet + case "MapType": + return TypeMap + case "ListType": + return TypeList + case "SetType": + return TypeSet + case "TupleType": + return TypeTuple + default: + return TypeCustom + } +} + +func (r *RowData) rowMap(m map[string]interface{}) { + for i, column := range r.Columns { + val := dereference(r.Values[i]) + if valVal := reflect.ValueOf(val); valVal.Kind() == reflect.Slice { + valCopy := reflect.MakeSlice(valVal.Type(), valVal.Len(), valVal.Cap()) + reflect.Copy(valCopy, valVal) + m[column] = valCopy.Interface() + } else { + m[column] = val + } + } +} + +func (iter *Iter) RowData() (RowData, error) { + if iter.err != nil { + return RowData{}, iter.err + } + columns := make([]string, 0) + values := make([]interface{}, 0) + for _, column := range iter.Columns() { + val := column.TypeInfo.New() + columns = append(columns, column.Name) + values = append(values, val) + } + rowData := RowData{ + Columns: columns, + Values: values, + } + return rowData, nil +} + +// SliceMap is a helper function to make the API easier to use +// returns the data from the query in the form of []map[string]interface{} +func (iter *Iter) SliceMap() ([]map[string]interface{}, error) { + if iter.err != nil { + return nil, iter.err + } + + // Not checking for the error because we just did + rowData, _ := iter.RowData() + dataToReturn := make([]map[string]interface{}, 0) + for iter.Scan(rowData.Values...) { + m := make(map[string]interface{}) + rowData.rowMap(m) + dataToReturn = append(dataToReturn, m) + } + if iter.err != nil { + return nil, iter.err + } + return dataToReturn, nil +} + +// MapScan takes a map[string]interface{} and populates it with a row +// That is returned from cassandra. +func (iter *Iter) MapScan(m map[string]interface{}) bool { + if iter.err != nil { + return false + } + + // Not checking for the error because we just did + rowData, _ := iter.RowData() + + for i, col := range rowData.Columns { + if dest, ok := m[col]; ok { + rowData.Values[i] = dest + } + } + + if iter.Scan(rowData.Values...) { + rowData.rowMap(m) + return true + } + return false +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/host_source.go b/Godeps/_workspace/src/github.com/gocql/gocql/host_source.go new file mode 100644 index 000000000..801b6f914 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/host_source.go @@ -0,0 +1,119 @@ +package gocql + +import ( + "log" + "net" + "time" +) + +type HostInfo struct { + Peer string + DataCenter string + Rack string + HostId string + Tokens []string +} + +// Polls system.peers at a specific interval to find new hosts +type ringDescriber struct { + dcFilter string + rackFilter string + prevHosts []HostInfo + prevPartitioner string + session *Session + closeChan chan bool +} + +func (r *ringDescriber) GetHosts() ( + hosts []HostInfo, + partitioner string, + err error, +) { + // we need conn to be the same because we need to query system.peers and system.local + // on the same node to get the whole cluster + conn := r.session.Pool.Pick(nil) + if conn == nil { + return r.prevHosts, r.prevPartitioner, nil + } + + query := r.session.Query("SELECT data_center, rack, host_id, tokens, partitioner FROM system.local") + iter := conn.executeQuery(query) + + host := HostInfo{} + iter.Scan(&host.DataCenter, &host.Rack, &host.HostId, &host.Tokens, &partitioner) + + if err = iter.Close(); err != nil { + return nil, "", err + } + + addr, _, err := net.SplitHostPort(conn.Address()) + if err != nil { + // this should not happen, ever, as this is the address that was dialed by conn, here + // a panic makes sense, please report a bug if it occurs. + panic(err) + } + + host.Peer = addr + + hosts = []HostInfo{host} + + query = r.session.Query("SELECT peer, data_center, rack, host_id, tokens FROM system.peers") + iter = conn.executeQuery(query) + + host = HostInfo{} + for iter.Scan(&host.Peer, &host.DataCenter, &host.Rack, &host.HostId, &host.Tokens) { + if r.matchFilter(&host) { + hosts = append(hosts, host) + } + host = HostInfo{} + } + + if err = iter.Close(); err != nil { + return nil, "", err + } + + r.prevHosts = hosts + r.prevPartitioner = partitioner + + return hosts, partitioner, nil +} + +func (r *ringDescriber) matchFilter(host *HostInfo) bool { + + if r.dcFilter != "" && r.dcFilter != host.DataCenter { + return false + } + + if r.rackFilter != "" && r.rackFilter != host.Rack { + return false + } + + return true +} + +func (h *ringDescriber) run(sleep time.Duration) { + if sleep == 0 { + sleep = 30 * time.Second + } + + for { + select { + case <-time.After(sleep): + // if we have 0 hosts this will return the previous list of hosts to + // attempt to reconnect to the cluster otherwise we would never find + // downed hosts again, could possibly have an optimisation to only + // try to add new hosts if GetHosts didnt error and the hosts didnt change. + hosts, partitioner, err := h.GetHosts() + if err != nil { + log.Println("RingDescriber: unable to get ring topology:", err) + } else { + h.session.Pool.SetHosts(hosts) + if v, ok := h.session.Pool.(SetPartitioner); ok { + v.SetPartitioner(partitioner) + } + } + case <-h.closeChan: + return + } + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/integration.sh b/Godeps/_workspace/src/github.com/gocql/gocql/integration.sh new file mode 100644 index 000000000..543b7d1e5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/integration.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +set -e + +function run_tests() { + local clusterSize=3 + local version=$1 + local auth=$2 + + if [ "$auth" = true ]; then + clusterSize=1 + fi + + local keypath="$(pwd)/testdata/pki" + + local conf=( + "client_encryption_options.enabled: true" + "client_encryption_options.keystore: $keypath/.keystore" + "client_encryption_options.keystore_password: cassandra" + "client_encryption_options.require_client_auth: true" + "client_encryption_options.truststore: $keypath/.truststore" + "client_encryption_options.truststore_password: cassandra" + "concurrent_reads: 2" + "concurrent_writes: 2" + "rpc_server_type: sync" + "rpc_min_threads: 2" + "rpc_max_threads: 2" + "write_request_timeout_in_ms: 5000" + "read_request_timeout_in_ms: 5000" + ) + + ccm remove test || true + + ccm create test -v binary:$version -n $clusterSize -d --vnodes --jvm_arg="-Xmx256m -XX:NewSize=100m" + ccm updateconf "${conf[@]}" + + if [ "$auth" = true ] + then + ccm updateconf 'authenticator: PasswordAuthenticator' 'authorizer: CassandraAuthorizer' + rm -rf $HOME/.ccm/test/node1/data/system_auth + fi + + ccm start -v + ccm status + ccm node1 nodetool status + + local proto=2 + if [[ $version == 1.2.* ]]; then + proto=1 + elif [[ $version == 2.1.* ]]; then + proto=3 + fi + + if [ "$auth" = true ] + then + sleep 30s + go test -v . -timeout 15s -run=TestAuthentication -tags integration -runssl -runauth -proto=$proto -cluster=$(ccm liveset) -clusterSize=$clusterSize -autowait=1000ms + else + + go test -timeout 5m -tags integration -cover -v -runssl -proto=$proto -rf=3 -cluster=$(ccm liveset) -clusterSize=$clusterSize -autowait=2000ms -compressor=snappy ./... | tee results.txt + + if [ ${PIPESTATUS[0]} -ne 0 ]; then + echo "--- FAIL: ccm status follows:" + ccm status + ccm node1 nodetool status + ccm node1 showlog > status.log + cat status.log + echo "--- FAIL: Received a non-zero exit code from the go test execution, please investigate this" + exit 1 + fi + + cover=`cat results.txt | grep coverage: | grep -o "[0-9]\{1,3\}" | head -n 1` + + if [[ $cover -lt "55" ]]; then + echo "--- FAIL: expected coverage of at least 60 %, but coverage was $cover %" + exit 1 + fi + fi + + ccm remove +} + +run_tests $1 $2 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/marshal.go b/Godeps/_workspace/src/github.com/gocql/gocql/marshal.go new file mode 100644 index 000000000..72c934925 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/marshal.go @@ -0,0 +1,1621 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "math" + "math/big" + "net" + "reflect" + "strconv" + "strings" + "time" + + "speter.net/go/exp/math/dec/inf" +) + +var ( + bigOne = big.NewInt(1) +) + +var ( + ErrorUDTUnavailable = errors.New("UDT are not available on protocols less than 3, please update config") +) + +// Marshaler is the interface implemented by objects that can marshal +// themselves into values understood by Cassandra. +type Marshaler interface { + MarshalCQL(info TypeInfo) ([]byte, error) +} + +// Unmarshaler is the interface implemented by objects that can unmarshal +// a Cassandra specific description of themselves. +type Unmarshaler interface { + UnmarshalCQL(info TypeInfo, data []byte) error +} + +// Marshal returns the CQL encoding of the value for the Cassandra +// internal type described by the info parameter. +func Marshal(info TypeInfo, value interface{}) ([]byte, error) { + if value == nil { + return nil, nil + } + if info.Version() < protoVersion1 { + panic("protocol version not set") + } + + if valueRef := reflect.ValueOf(value); valueRef.Kind() == reflect.Ptr { + if valueRef.IsNil() { + return nil, nil + } else if v, ok := value.(Marshaler); ok { + return v.MarshalCQL(info) + } else { + return Marshal(info, valueRef.Elem().Interface()) + } + } + + if v, ok := value.(Marshaler); ok { + return v.MarshalCQL(info) + } + + switch info.Type() { + case TypeVarchar, TypeAscii, TypeBlob: + return marshalVarchar(info, value) + case TypeBoolean: + return marshalBool(info, value) + case TypeInt: + return marshalInt(info, value) + case TypeBigInt, TypeCounter: + return marshalBigInt(info, value) + case TypeFloat: + return marshalFloat(info, value) + case TypeDouble: + return marshalDouble(info, value) + case TypeDecimal: + return marshalDecimal(info, value) + case TypeTimestamp: + return marshalTimestamp(info, value) + case TypeList, TypeSet: + return marshalList(info, value) + case TypeMap: + return marshalMap(info, value) + case TypeUUID, TypeTimeUUID: + return marshalUUID(info, value) + case TypeVarint: + return marshalVarint(info, value) + case TypeInet: + return marshalInet(info, value) + case TypeUDT: + return marshalUDT(info, value) + } + + // detect protocol 2 UDT + if strings.HasPrefix(info.Custom(), "org.apache.cassandra.db.marshal.UserType") && info.Version() < 3 { + return nil, ErrorUDTUnavailable + } + + // TODO(tux21b): add the remaining types + return nil, fmt.Errorf("can not marshal %T into %s", value, info) +} + +// Unmarshal parses the CQL encoded data based on the info parameter that +// describes the Cassandra internal data type and stores the result in the +// value pointed by value. +func Unmarshal(info TypeInfo, data []byte, value interface{}) error { + if v, ok := value.(Unmarshaler); ok { + return v.UnmarshalCQL(info, data) + } + if isNullableValue(value) { + return unmarshalNullable(info, data, value) + } + + switch info.Type() { + case TypeVarchar, TypeAscii, TypeBlob: + return unmarshalVarchar(info, data, value) + case TypeBoolean: + return unmarshalBool(info, data, value) + case TypeInt: + return unmarshalInt(info, data, value) + case TypeBigInt, TypeCounter: + return unmarshalBigInt(info, data, value) + case TypeVarint: + return unmarshalVarint(info, data, value) + case TypeFloat: + return unmarshalFloat(info, data, value) + case TypeDouble: + return unmarshalDouble(info, data, value) + case TypeDecimal: + return unmarshalDecimal(info, data, value) + case TypeTimestamp: + return unmarshalTimestamp(info, data, value) + case TypeList, TypeSet: + return unmarshalList(info, data, value) + case TypeMap: + return unmarshalMap(info, data, value) + case TypeTimeUUID: + return unmarshalTimeUUID(info, data, value) + case TypeUUID: + return unmarshalUUID(info, data, value) + case TypeInet: + return unmarshalInet(info, data, value) + case TypeTuple: + return unmarshalTuple(info, data, value) + case TypeUDT: + return unmarshalUDT(info, data, value) + } + + // detect protocol 2 UDT + if strings.HasPrefix(info.Custom(), "org.apache.cassandra.db.marshal.UserType") && info.Version() < 3 { + return ErrorUDTUnavailable + } + + // TODO(tux21b): add the remaining types + return fmt.Errorf("can not unmarshal %s into %T", info, value) +} + +func isNullableValue(value interface{}) bool { + v := reflect.ValueOf(value) + return v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Ptr +} + +func isNullData(info TypeInfo, data []byte) bool { + return len(data) == 0 +} + +func unmarshalNullable(info TypeInfo, data []byte, value interface{}) error { + valueRef := reflect.ValueOf(value) + + if isNullData(info, data) { + nilValue := reflect.Zero(valueRef.Type().Elem()) + valueRef.Elem().Set(nilValue) + return nil + } + + newValue := reflect.New(valueRef.Type().Elem().Elem()) + valueRef.Elem().Set(newValue) + return Unmarshal(info, data, newValue.Interface()) +} + +func marshalVarchar(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case string: + return []byte(v), nil + case []byte: + return v, nil + } + rv := reflect.ValueOf(value) + t := rv.Type() + k := t.Kind() + switch { + case k == reflect.String: + return []byte(rv.String()), nil + case k == reflect.Slice && t.Elem().Kind() == reflect.Uint8: + return rv.Bytes(), nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func unmarshalVarchar(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *string: + *v = string(data) + return nil + case *[]byte: + var dataCopy []byte + if data != nil { + dataCopy = make([]byte, len(data)) + copy(dataCopy, data) + } + *v = dataCopy + return nil + } + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + t := rv.Type() + k := t.Kind() + switch { + case k == reflect.String: + rv.SetString(string(data)) + return nil + case k == reflect.Slice && t.Elem().Kind() == reflect.Uint8: + var dataCopy []byte + if data != nil { + dataCopy = make([]byte, len(data)) + copy(dataCopy, data) + } + rv.SetBytes(dataCopy) + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +func marshalInt(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case int: + if v > math.MaxInt32 || v < math.MinInt32 { + return nil, marshalErrorf("marshal int: value %d out of range", v) + } + return encInt(int32(v)), nil + case uint: + if v > math.MaxInt32 { + return nil, marshalErrorf("marshal int: value %d out of range", v) + } + return encInt(int32(v)), nil + case int64: + if v > math.MaxInt32 || v < math.MinInt32 { + return nil, marshalErrorf("marshal int: value %d out of range", v) + } + return encInt(int32(v)), nil + case uint64: + if v > math.MaxInt32 { + return nil, marshalErrorf("marshal int: value %d out of range", v) + } + return encInt(int32(v)), nil + case int32: + return encInt(v), nil + case uint32: + if v > math.MaxInt32 { + return nil, marshalErrorf("marshal int: value %d out of range", v) + } + return encInt(int32(v)), nil + case int16: + return encInt(int32(v)), nil + case uint16: + return encInt(int32(v)), nil + case int8: + return encInt(int32(v)), nil + case uint8: + return encInt(int32(v)), nil + case string: + i, err := strconv.ParseInt(value.(string), 10, 32) + if err != nil { + return nil, marshalErrorf("can not marshal string to int: %s", err) + } + return encInt(int32(i)), nil + } + rv := reflect.ValueOf(value) + switch rv.Type().Kind() { + case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: + v := rv.Int() + if v > math.MaxInt32 || v < math.MinInt32 { + return nil, marshalErrorf("marshal int: value %d out of range", v) + } + return encInt(int32(v)), nil + case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: + v := rv.Uint() + if v > math.MaxInt32 { + return nil, marshalErrorf("marshal int: value %d out of range", v) + } + return encInt(int32(v)), nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func encInt(x int32) []byte { + return []byte{byte(x >> 24), byte(x >> 16), byte(x >> 8), byte(x)} +} + +func decInt(x []byte) int32 { + if len(x) != 4 { + return 0 + } + return int32(x[0])<<24 | int32(x[1])<<16 | int32(x[2])<<8 | int32(x[3]) +} + +func marshalBigInt(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case int: + return encBigInt(int64(v)), nil + case uint: + if uint64(v) > math.MaxInt64 { + return nil, marshalErrorf("marshal bigint: value %d out of range", v) + } + return encBigInt(int64(v)), nil + case int64: + return encBigInt(v), nil + case uint64: + if v > math.MaxInt64 { + return nil, marshalErrorf("marshal bigint: value %d out of range", v) + } + return encBigInt(int64(v)), nil + case int32: + return encBigInt(int64(v)), nil + case uint32: + return encBigInt(int64(v)), nil + case int16: + return encBigInt(int64(v)), nil + case uint16: + return encBigInt(int64(v)), nil + case int8: + return encBigInt(int64(v)), nil + case uint8: + return encBigInt(int64(v)), nil + case big.Int: + return encBigInt2C(&v), nil + case string: + i, err := strconv.ParseInt(value.(string), 10, 64) + if err != nil { + return nil, marshalErrorf("can not marshal string to bigint: %s", err) + } + return encBigInt(i), nil + } + rv := reflect.ValueOf(value) + switch rv.Type().Kind() { + case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: + v := rv.Int() + return encBigInt(v), nil + case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: + v := rv.Uint() + if v > math.MaxInt64 { + return nil, marshalErrorf("marshal bigint: value %d out of range", v) + } + return encBigInt(int64(v)), nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func encBigInt(x int64) []byte { + return []byte{byte(x >> 56), byte(x >> 48), byte(x >> 40), byte(x >> 32), + byte(x >> 24), byte(x >> 16), byte(x >> 8), byte(x)} +} + +func bytesToInt64(data []byte) (ret int64) { + for i := range data { + ret |= int64(data[i]) << (8 * uint(len(data)-i-1)) + } + return ret +} + +func unmarshalBigInt(info TypeInfo, data []byte, value interface{}) error { + return unmarshalIntlike(info, decBigInt(data), data, value) +} + +func unmarshalInt(info TypeInfo, data []byte, value interface{}) error { + return unmarshalIntlike(info, int64(decInt(data)), data, value) +} + +func unmarshalVarint(info TypeInfo, data []byte, value interface{}) error { + switch value.(type) { + case *big.Int: + return unmarshalIntlike(info, 0, data, value) + } + + if len(data) > 8 { + return unmarshalErrorf("unmarshal int: varint value %v out of range for %T (use big.Int)", data, value) + } + + int64Val := bytesToInt64(data) + if len(data) < 8 && data[0]&0x80 > 0 { + int64Val -= (1 << uint(len(data)*8)) + } + return unmarshalIntlike(info, int64Val, data, value) +} + +func marshalVarint(info TypeInfo, value interface{}) ([]byte, error) { + var ( + retBytes []byte + err error + ) + + switch v := value.(type) { + case uint64: + if v > uint64(math.MaxInt64) { + retBytes = make([]byte, 9) + binary.BigEndian.PutUint64(retBytes[1:], v) + } else { + retBytes = make([]byte, 8) + binary.BigEndian.PutUint64(retBytes, v) + } + default: + retBytes, err = marshalBigInt(info, value) + } + + if err == nil { + // trim down to most significant byte + i := 0 + for ; i < len(retBytes)-1; i++ { + b0 := retBytes[i] + if b0 != 0 && b0 != 0xFF { + break + } + + b1 := retBytes[i+1] + if b0 == 0 && b1 != 0 { + if b1&0x80 == 0 { + i++ + } + break + } + + if b0 == 0xFF && b1 != 0xFF { + if b1&0x80 > 0 { + i++ + } + break + } + } + retBytes = retBytes[i:] + } + + return retBytes, err +} + +func unmarshalIntlike(info TypeInfo, int64Val int64, data []byte, value interface{}) error { + switch v := value.(type) { + case *int: + if ^uint(0) == math.MaxUint32 && (int64Val < math.MinInt32 || int64Val > math.MaxInt32) { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = int(int64Val) + return nil + case *uint: + if int64Val < 0 || (^uint(0) == math.MaxUint32 && int64Val > math.MaxUint32) { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = uint(int64Val) + return nil + case *int64: + *v = int64Val + return nil + case *uint64: + if int64Val < 0 { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = uint64(int64Val) + return nil + case *int32: + if int64Val < math.MinInt32 || int64Val > math.MaxInt32 { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = int32(int64Val) + return nil + case *uint32: + if int64Val < 0 || int64Val > math.MaxUint32 { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = uint32(int64Val) + return nil + case *int16: + if int64Val < math.MinInt16 || int64Val > math.MaxInt16 { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = int16(int64Val) + return nil + case *uint16: + if int64Val < 0 || int64Val > math.MaxUint16 { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = uint16(int64Val) + return nil + case *int8: + if int64Val < math.MinInt8 || int64Val > math.MaxInt8 { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = int8(int64Val) + return nil + case *uint8: + if int64Val < 0 || int64Val > math.MaxUint8 { + return unmarshalErrorf("unmarshal int: value %d out of range for %T", int64Val, *v) + } + *v = uint8(int64Val) + return nil + case *big.Int: + decBigInt2C(data, v) + return nil + case *string: + *v = strconv.FormatInt(int64Val, 10) + return nil + } + + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + + switch rv.Type().Kind() { + case reflect.Int: + if ^uint(0) == math.MaxUint32 && (int64Val < math.MinInt32 || int64Val > math.MaxInt32) { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetInt(int64Val) + return nil + case reflect.Int64: + rv.SetInt(int64Val) + return nil + case reflect.Int32: + if int64Val < math.MinInt32 || int64Val > math.MaxInt32 { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetInt(int64Val) + return nil + case reflect.Int16: + if int64Val < math.MinInt16 || int64Val > math.MaxInt16 { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetInt(int64Val) + return nil + case reflect.Int8: + if int64Val < math.MinInt8 || int64Val > math.MaxInt8 { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetInt(int64Val) + return nil + case reflect.Uint: + if int64Val < 0 || (^uint(0) == math.MaxUint32 && int64Val > math.MaxUint32) { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetUint(uint64(int64Val)) + return nil + case reflect.Uint64: + if int64Val < 0 { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetUint(uint64(int64Val)) + return nil + case reflect.Uint32: + if int64Val < 0 || int64Val > math.MaxUint32 { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetUint(uint64(int64Val)) + return nil + case reflect.Uint16: + if int64Val < 0 || int64Val > math.MaxUint16 { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetUint(uint64(int64Val)) + return nil + case reflect.Uint8: + if int64Val < 0 || int64Val > math.MaxUint8 { + return unmarshalErrorf("unmarshal int: value %d out of range", int64Val) + } + rv.SetUint(uint64(int64Val)) + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +func decBigInt(data []byte) int64 { + if len(data) != 8 { + return 0 + } + return int64(data[0])<<56 | int64(data[1])<<48 | + int64(data[2])<<40 | int64(data[3])<<32 | + int64(data[4])<<24 | int64(data[5])<<16 | + int64(data[6])<<8 | int64(data[7]) +} + +func marshalBool(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case bool: + return encBool(v), nil + } + rv := reflect.ValueOf(value) + switch rv.Type().Kind() { + case reflect.Bool: + return encBool(rv.Bool()), nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func encBool(v bool) []byte { + if v { + return []byte{1} + } + return []byte{0} +} + +func unmarshalBool(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *bool: + *v = decBool(data) + return nil + } + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + switch rv.Type().Kind() { + case reflect.Bool: + rv.SetBool(decBool(data)) + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +func decBool(v []byte) bool { + if len(v) == 0 { + return false + } + return v[0] != 0 +} + +func marshalFloat(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case float32: + return encInt(int32(math.Float32bits(v))), nil + } + rv := reflect.ValueOf(value) + switch rv.Type().Kind() { + case reflect.Float32: + return encInt(int32(math.Float32bits(float32(rv.Float())))), nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func unmarshalFloat(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *float32: + *v = math.Float32frombits(uint32(decInt(data))) + return nil + } + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + switch rv.Type().Kind() { + case reflect.Float32: + rv.SetFloat(float64(math.Float32frombits(uint32(decInt(data))))) + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +func marshalDouble(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case float64: + return encBigInt(int64(math.Float64bits(v))), nil + } + rv := reflect.ValueOf(value) + switch rv.Type().Kind() { + case reflect.Float64: + return encBigInt(int64(math.Float64bits(rv.Float()))), nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func unmarshalDouble(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *float64: + *v = math.Float64frombits(uint64(decBigInt(data))) + return nil + } + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + switch rv.Type().Kind() { + case reflect.Float64: + rv.SetFloat(math.Float64frombits(uint64(decBigInt(data)))) + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +func marshalDecimal(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case inf.Dec: + unscaled := encBigInt2C(v.UnscaledBig()) + if unscaled == nil { + return nil, marshalErrorf("can not marshal %T into %s", value, info) + } + + buf := make([]byte, 4+len(unscaled)) + copy(buf[0:4], encInt(int32(v.Scale()))) + copy(buf[4:], unscaled) + return buf, nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func unmarshalDecimal(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *inf.Dec: + scale := decInt(data[0:4]) + unscaled := decBigInt2C(data[4:], nil) + *v = *inf.NewDecBig(unscaled, inf.Scale(scale)) + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +// decBigInt2C sets the value of n to the big-endian two's complement +// value stored in the given data. If data[0]&80 != 0, the number +// is negative. If data is empty, the result will be 0. +func decBigInt2C(data []byte, n *big.Int) *big.Int { + if n == nil { + n = new(big.Int) + } + n.SetBytes(data) + if len(data) > 0 && data[0]&0x80 > 0 { + n.Sub(n, new(big.Int).Lsh(bigOne, uint(len(data))*8)) + } + return n +} + +// encBigInt2C returns the big-endian two's complement +// form of n. +func encBigInt2C(n *big.Int) []byte { + switch n.Sign() { + case 0: + return []byte{0} + case 1: + b := n.Bytes() + if b[0]&0x80 > 0 { + b = append([]byte{0}, b...) + } + return b + case -1: + length := uint(n.BitLen()/8+1) * 8 + b := new(big.Int).Add(n, new(big.Int).Lsh(bigOne, length)).Bytes() + // When the most significant bit is on a byte + // boundary, we can get some extra significant + // bits, so strip them off when that happens. + if len(b) >= 2 && b[0] == 0xff && b[1]&0x80 != 0 { + b = b[1:] + } + return b + } + return nil +} + +func marshalTimestamp(info TypeInfo, value interface{}) ([]byte, error) { + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case int64: + return encBigInt(v), nil + case time.Time: + x := v.UnixNano() / int64(1000000) + return encBigInt(x), nil + } + rv := reflect.ValueOf(value) + switch rv.Type().Kind() { + case reflect.Int64: + return encBigInt(rv.Int()), nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func unmarshalTimestamp(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *int64: + *v = decBigInt(data) + return nil + case *time.Time: + if len(data) == 0 { + return nil + } + x := decBigInt(data) + sec := x / 1000 + nsec := (x - sec*1000) * 1000000 + *v = time.Unix(sec, nsec).In(time.UTC) + return nil + } + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + switch rv.Type().Kind() { + case reflect.Int64: + rv.SetInt(decBigInt(data)) + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +func writeCollectionSize(info CollectionType, n int, buf *bytes.Buffer) error { + if info.proto > protoVersion2 { + if n > math.MaxInt32 { + return marshalErrorf("marshal: collection too large") + } + + buf.WriteByte(byte(n >> 24)) + buf.WriteByte(byte(n >> 16)) + buf.WriteByte(byte(n >> 8)) + buf.WriteByte(byte(n)) + } else { + if n > math.MaxUint16 { + return marshalErrorf("marshal: collection too large") + } + + buf.WriteByte(byte(n >> 8)) + buf.WriteByte(byte(n)) + } + + return nil +} + +func marshalList(info TypeInfo, value interface{}) ([]byte, error) { + listInfo, ok := info.(CollectionType) + if !ok { + return nil, marshalErrorf("marshal: can not marshal non collection type into list") + } + + rv := reflect.ValueOf(value) + t := rv.Type() + k := t.Kind() + switch k { + case reflect.Slice, reflect.Array: + if k == reflect.Slice && rv.IsNil() { + return nil, nil + } + buf := &bytes.Buffer{} + n := rv.Len() + + if err := writeCollectionSize(listInfo, n, buf); err != nil { + return nil, err + } + + for i := 0; i < n; i++ { + item, err := Marshal(listInfo.Elem, rv.Index(i).Interface()) + if err != nil { + return nil, err + } + if err := writeCollectionSize(listInfo, len(item), buf); err != nil { + return nil, err + } + buf.Write(item) + } + return buf.Bytes(), nil + case reflect.Map: + elem := t.Elem() + if elem.Kind() == reflect.Struct && elem.NumField() == 0 { + rkeys := rv.MapKeys() + keys := make([]interface{}, len(rkeys)) + for i := 0; i < len(keys); i++ { + keys[i] = rkeys[i].Interface() + } + return marshalList(listInfo, keys) + } + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func readCollectionSize(info CollectionType, data []byte) (size, read int) { + if info.proto > protoVersion2 { + size = int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + read = 4 + } else { + size = int(data[0])<<8 | int(data[1]) + read = 2 + } + return +} + +func unmarshalList(info TypeInfo, data []byte, value interface{}) error { + listInfo, ok := info.(CollectionType) + if !ok { + return unmarshalErrorf("unmarshal: can not unmarshal none collection type into list") + } + + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + t := rv.Type() + k := t.Kind() + + switch k { + case reflect.Slice, reflect.Array: + if data == nil { + if k == reflect.Array { + return unmarshalErrorf("unmarshal list: can not store nil in array value") + } + rv.Set(reflect.Zero(t)) + return nil + } + if len(data) < 2 { + return unmarshalErrorf("unmarshal list: unexpected eof") + } + n, p := readCollectionSize(listInfo, data) + data = data[p:] + if k == reflect.Array { + if rv.Len() != n { + return unmarshalErrorf("unmarshal list: array with wrong size") + } + } else { + rv.Set(reflect.MakeSlice(t, n, n)) + } + for i := 0; i < n; i++ { + if len(data) < 2 { + return unmarshalErrorf("unmarshal list: unexpected eof") + } + m, p := readCollectionSize(listInfo, data) + data = data[p:] + if err := Unmarshal(listInfo.Elem, data[:m], rv.Index(i).Addr().Interface()); err != nil { + return err + } + data = data[m:] + } + return nil + } + return unmarshalErrorf("can not unmarshal %s into %T", info, value) +} + +func marshalMap(info TypeInfo, value interface{}) ([]byte, error) { + mapInfo, ok := info.(CollectionType) + if !ok { + return nil, marshalErrorf("marshal: can not marshal none collection type into map") + } + + rv := reflect.ValueOf(value) + t := rv.Type() + if t.Kind() != reflect.Map { + return nil, marshalErrorf("can not marshal %T into %s", value, info) + } + if rv.IsNil() { + return nil, nil + } + buf := &bytes.Buffer{} + n := rv.Len() + + if err := writeCollectionSize(mapInfo, n, buf); err != nil { + return nil, err + } + + keys := rv.MapKeys() + for _, key := range keys { + item, err := Marshal(mapInfo.Key, key.Interface()) + if err != nil { + return nil, err + } + if err := writeCollectionSize(mapInfo, len(item), buf); err != nil { + return nil, err + } + buf.Write(item) + + item, err = Marshal(mapInfo.Elem, rv.MapIndex(key).Interface()) + if err != nil { + return nil, err + } + if err := writeCollectionSize(mapInfo, len(item), buf); err != nil { + return nil, err + } + buf.Write(item) + } + return buf.Bytes(), nil +} + +func unmarshalMap(info TypeInfo, data []byte, value interface{}) error { + mapInfo, ok := info.(CollectionType) + if !ok { + return unmarshalErrorf("unmarshal: can not unmarshal none collection type into map") + } + + rv := reflect.ValueOf(value) + if rv.Kind() != reflect.Ptr { + return unmarshalErrorf("can not unmarshal into non-pointer %T", value) + } + rv = rv.Elem() + t := rv.Type() + if t.Kind() != reflect.Map { + return unmarshalErrorf("can not unmarshal %s into %T", info, value) + } + if data == nil { + rv.Set(reflect.Zero(t)) + return nil + } + rv.Set(reflect.MakeMap(t)) + if len(data) < 2 { + return unmarshalErrorf("unmarshal map: unexpected eof") + } + n, p := readCollectionSize(mapInfo, data) + data = data[p:] + for i := 0; i < n; i++ { + if len(data) < 2 { + return unmarshalErrorf("unmarshal list: unexpected eof") + } + m, p := readCollectionSize(mapInfo, data) + data = data[p:] + key := reflect.New(t.Key()) + if err := Unmarshal(mapInfo.Key, data[:m], key.Interface()); err != nil { + return err + } + data = data[m:] + + m, p = readCollectionSize(mapInfo, data) + data = data[p:] + val := reflect.New(t.Elem()) + if err := Unmarshal(mapInfo.Elem, data[:m], val.Interface()); err != nil { + return err + } + data = data[m:] + + rv.SetMapIndex(key.Elem(), val.Elem()) + } + return nil +} + +func marshalUUID(info TypeInfo, value interface{}) ([]byte, error) { + switch val := value.(type) { + case UUID: + return val.Bytes(), nil + case []byte: + if len(val) == 16 { + return val, nil + } + case string: + b, err := ParseUUID(val) + if err != nil { + return nil, err + } + return b[:], nil + } + return nil, marshalErrorf("can not marshal %T into %s", value, info) +} + +func unmarshalUUID(info TypeInfo, data []byte, value interface{}) error { + if data == nil || len(data) == 0 { + switch v := value.(type) { + case *string: + *v = "" + case *[]byte: + *v = nil + case *UUID: + *v = UUID{} + default: + return unmarshalErrorf("can not unmarshal X %s into %T", info, value) + } + + return nil + } + + u, err := UUIDFromBytes(data) + if err != nil { + return unmarshalErrorf("Unable to parse UUID: %s", err) + } + + switch v := value.(type) { + case *string: + *v = u.String() + return nil + case *[]byte: + *v = u[:] + return nil + case *UUID: + *v = u + return nil + } + return unmarshalErrorf("can not unmarshal X %s into %T", info, value) +} + +func unmarshalTimeUUID(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *time.Time: + id, err := UUIDFromBytes(data) + if err != nil { + return err + } else if id.Version() != 1 { + return unmarshalErrorf("invalid timeuuid") + } + *v = id.Time() + return nil + default: + return unmarshalUUID(info, data, value) + } +} + +func marshalInet(info TypeInfo, value interface{}) ([]byte, error) { + // we return either the 4 or 16 byte representation of an + // ip address here otherwise the db value will be prefixed + // with the remaining byte values e.g. ::ffff:127.0.0.1 and not 127.0.0.1 + switch val := value.(type) { + case net.IP: + t := val.To4() + if t == nil { + return val.To16(), nil + } + return t, nil + case string: + b := net.ParseIP(val) + if b != nil { + t := b.To4() + if t == nil { + return b.To16(), nil + } + return t, nil + } + return nil, marshalErrorf("cannot marshal. invalid ip string %s", val) + } + return nil, marshalErrorf("cannot marshal %T into %s", value, info) +} + +func unmarshalInet(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case *net.IP: + ip := net.IP(data) + if v4 := ip.To4(); v4 != nil { + *v = v4 + return nil + } + *v = ip + return nil + case *string: + if len(data) == 0 { + *v = "" + return nil + } + ip := net.IP(data) + if v4 := ip.To4(); v4 != nil { + *v = v4.String() + return nil + } + *v = ip.String() + return nil + } + return unmarshalErrorf("cannot unmarshal %s into %T", info, value) +} + +// currently only support unmarshal into a list of values, this makes it possible +// to support tuples without changing the query API. In the future this can be extend +// to allow unmarshalling into custom tuple types. +func unmarshalTuple(info TypeInfo, data []byte, value interface{}) error { + if v, ok := value.(Unmarshaler); ok { + return v.UnmarshalCQL(info, data) + } + + tuple := info.(TupleTypeInfo) + switch v := value.(type) { + case []interface{}: + for i, elem := range tuple.Elems { + // each element inside data is a [bytes] + size := readInt(data) + data = data[4:] + + err := Unmarshal(elem, data[:size], v[i]) + if err != nil { + return err + } + data = data[size:] + } + + return nil + } + + return unmarshalErrorf("cannot unmarshal %s into %T", info, value) +} + +// UDTMarshaler is an interface which should be implemented by users wishing to +// handle encoding UDT types to sent to Cassandra. Note: due to current implentations +// methods defined for this interface must be value receivers not pointer receivers. +type UDTMarshaler interface { + // MarshalUDT will be called for each field in the the UDT returned by Cassandra, + // the implementor should marshal the type to return by for example calling + // Marshal. + MarshalUDT(name string, info TypeInfo) ([]byte, error) +} + +// UDTUnmarshaler should be implemented by users wanting to implement custom +// UDT unmarshaling. +type UDTUnmarshaler interface { + // UnmarshalUDT will be called for each field in the UDT return by Cassandra, + // the implementor should unmarshal the data into the value of their chosing, + // for example by calling Unmarshal. + UnmarshalUDT(name string, info TypeInfo, data []byte) error +} + +func marshalUDT(info TypeInfo, value interface{}) ([]byte, error) { + udt := info.(UDTTypeInfo) + + switch v := value.(type) { + case Marshaler: + return v.MarshalCQL(info) + case UDTMarshaler: + var buf []byte + for _, e := range udt.Elements { + data, err := v.MarshalUDT(e.Name, e.Type) + if err != nil { + return nil, err + } + + n := len(data) + buf = append(buf, byte(n>>24), + byte(n>>16), + byte(n>>8), + byte(n)) + + buf = append(buf, data...) + } + + return buf, nil + case map[string]interface{}: + var buf []byte + for _, e := range udt.Elements { + val, ok := v[e.Name] + if !ok { + return nil, marshalErrorf("missing UDT field in map: %s", e.Name) + } + + data, err := Marshal(e.Type, val) + if err != nil { + return nil, err + } + + n := len(data) + buf = append(buf, byte(n>>24), + byte(n>>16), + byte(n>>8), + byte(n)) + + buf = append(buf, data...) + } + + return buf, nil + } + + k := reflect.ValueOf(value) + if k.Kind() == reflect.Ptr { + if k.IsNil() { + return nil, marshalErrorf("cannot marshal %T into %s", value, info) + } + k = k.Elem() + } + + if k.Kind() != reflect.Struct || !k.IsValid() { + return nil, marshalErrorf("cannot marshal %T into %s", value, info) + } + + fields := make(map[string]reflect.Value) + t := reflect.TypeOf(value) + for i := 0; i < t.NumField(); i++ { + sf := t.Field(i) + + if tag := sf.Tag.Get("cql"); tag != "" { + fields[tag] = k.Field(i) + } + } + + var buf []byte + for _, e := range udt.Elements { + f, ok := fields[e.Name] + if !ok { + f = k.FieldByName(e.Name) + } + + if !f.IsValid() { + return nil, marshalErrorf("cannot marshal %T into %s", value, info) + } else if f.Kind() == reflect.Ptr { + f = f.Elem() + } + + data, err := Marshal(e.Type, f.Interface()) + if err != nil { + return nil, err + } + + n := len(data) + buf = append(buf, byte(n>>24), + byte(n>>16), + byte(n>>8), + byte(n)) + + buf = append(buf, data...) + } + + return buf, nil + +} + +func unmarshalUDT(info TypeInfo, data []byte, value interface{}) error { + switch v := value.(type) { + case Unmarshaler: + return v.UnmarshalCQL(info, data) + case UDTUnmarshaler: + udt := info.(UDTTypeInfo) + + for _, e := range udt.Elements { + size := readInt(data[:4]) + data = data[4:] + + var err error + if size < 0 { + err = v.UnmarshalUDT(e.Name, e.Type, nil) + } else { + err = v.UnmarshalUDT(e.Name, e.Type, data[:size]) + data = data[size:] + } + + if err != nil { + return err + } + } + + return nil + } + + k := reflect.ValueOf(value).Elem() + if k.Kind() != reflect.Struct || !k.IsValid() { + return unmarshalErrorf("cannot unmarshal %s into %T", info, value) + } + + fields := make(map[string]reflect.Value) + t := k.Type() + for i := 0; i < t.NumField(); i++ { + sf := t.Field(i) + + if tag := sf.Tag.Get("cql"); tag != "" { + fields[tag] = k.Field(i) + } + } + + if len(data) == 0 { + if k.CanSet() { + k.Set(reflect.Zero(k.Type())) + } + + return nil + } + + udt := info.(UDTTypeInfo) + for _, e := range udt.Elements { + size := readInt(data[:4]) + data = data[4:] + + var err error + if size >= 0 { + f, ok := fields[e.Name] + if !ok { + f = k.FieldByName(e.Name) + } + + if !f.IsValid() || !f.CanAddr() { + return unmarshalErrorf("cannot unmarshal %s into %T", info, value) + } + + fk := f.Addr().Interface() + if err := Unmarshal(e.Type, data[:size], fk); err != nil { + return err + } + data = data[size:] + } + + if err != nil { + return err + } + } + + return nil +} + +// TypeInfo describes a Cassandra specific data type. +type TypeInfo interface { + Type() Type + Version() byte + Custom() string + + // New creates a pointer to an empty version of whatever type + // is referenced by the TypeInfo receiver + New() interface{} +} + +type NativeType struct { + proto byte + typ Type + custom string // only used for TypeCustom +} + +func (t NativeType) New() interface{} { + return reflect.New(goType(t)).Interface() +} + +func (s NativeType) Type() Type { + return s.typ +} + +func (s NativeType) Version() byte { + return s.proto +} + +func (s NativeType) Custom() string { + return s.custom +} + +func (s NativeType) String() string { + switch s.typ { + case TypeCustom: + return fmt.Sprintf("%s(%s)", s.typ, s.custom) + default: + return s.typ.String() + } +} + +type CollectionType struct { + NativeType + Key TypeInfo // only used for TypeMap + Elem TypeInfo // only used for TypeMap, TypeList and TypeSet +} + +func (t CollectionType) New() interface{} { + return reflect.New(goType(t)).Interface() +} + +func (c CollectionType) String() string { + switch c.typ { + case TypeMap: + return fmt.Sprintf("%s(%s, %s)", c.typ, c.Key, c.Elem) + case TypeList, TypeSet: + return fmt.Sprintf("%s(%s)", c.typ, c.Elem) + case TypeCustom: + return fmt.Sprintf("%s(%s)", c.typ, c.custom) + default: + return c.typ.String() + } +} + +type TupleTypeInfo struct { + NativeType + Elems []TypeInfo +} + +type UDTField struct { + Name string + Type TypeInfo +} + +type UDTTypeInfo struct { + NativeType + KeySpace string + Name string + Elements []UDTField +} + +func (u UDTTypeInfo) String() string { + buf := &bytes.Buffer{} + + fmt.Fprintf(buf, "%s.%s{", u.KeySpace, u.Name) + first := true + for _, e := range u.Elements { + if !first { + fmt.Fprint(buf, ",") + } else { + first = false + } + + fmt.Fprintf(buf, "%s=%v", e.Name, e.Type) + } + fmt.Fprint(buf, "}") + + return buf.String() +} + +// String returns a human readable name for the Cassandra datatype +// described by t. +// Type is the identifier of a Cassandra internal datatype. +type Type int + +const ( + TypeCustom Type = 0x0000 + TypeAscii = 0x0001 + TypeBigInt = 0x0002 + TypeBlob = 0x0003 + TypeBoolean = 0x0004 + TypeCounter = 0x0005 + TypeDecimal = 0x0006 + TypeDouble = 0x0007 + TypeFloat = 0x0008 + TypeInt = 0x0009 + TypeTimestamp = 0x000B + TypeUUID = 0x000C + TypeVarchar = 0x000D + TypeVarint = 0x000E + TypeTimeUUID = 0x000F + TypeInet = 0x0010 + TypeList = 0x0020 + TypeMap = 0x0021 + TypeSet = 0x0022 + TypeUDT = 0x0030 + TypeTuple = 0x0031 +) + +// String returns the name of the identifier. +func (t Type) String() string { + switch t { + case TypeCustom: + return "custom" + case TypeAscii: + return "ascii" + case TypeBigInt: + return "bigint" + case TypeBlob: + return "blob" + case TypeBoolean: + return "boolean" + case TypeCounter: + return "counter" + case TypeDecimal: + return "decimal" + case TypeDouble: + return "double" + case TypeFloat: + return "float" + case TypeInt: + return "int" + case TypeTimestamp: + return "timestamp" + case TypeUUID: + return "uuid" + case TypeVarchar: + return "varchar" + case TypeTimeUUID: + return "timeuuid" + case TypeInet: + return "inet" + case TypeList: + return "list" + case TypeMap: + return "map" + case TypeSet: + return "set" + case TypeVarint: + return "varint" + case TypeTuple: + return "tuple" + default: + return fmt.Sprintf("unknown_type_%d", t) + } +} + +type MarshalError string + +func (m MarshalError) Error() string { + return string(m) +} + +func marshalErrorf(format string, args ...interface{}) MarshalError { + return MarshalError(fmt.Sprintf(format, args...)) +} + +type UnmarshalError string + +func (m UnmarshalError) Error() string { + return string(m) +} + +func unmarshalErrorf(format string, args ...interface{}) UnmarshalError { + return UnmarshalError(fmt.Sprintf(format, args...)) +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/marshal_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/marshal_test.go new file mode 100644 index 000000000..6022ba652 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/marshal_test.go @@ -0,0 +1,796 @@ +// +build all unit + +package gocql + +import ( + "bytes" + "math" + "math/big" + "net" + "reflect" + "strings" + "testing" + "time" + + "speter.net/go/exp/math/dec/inf" +) + +var marshalTests = []struct { + Info TypeInfo + Data []byte + Value interface{} +}{ + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte("hello world"), + []byte("hello world"), + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte("hello world"), + "hello world", + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte(nil), + []byte(nil), + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte("hello world"), + MyString("hello world"), + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte("HELLO WORLD"), + CustomString("hello world"), + }, + { + NativeType{proto: 2, typ: TypeBlob}, + []byte("hello\x00"), + []byte("hello\x00"), + }, + { + NativeType{proto: 2, typ: TypeBlob}, + []byte(nil), + []byte(nil), + }, + { + NativeType{proto: 2, typ: TypeTimeUUID}, + []byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0}, + func() UUID { + x, _ := UUIDFromBytes([]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0}) + return x + }(), + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x00\x00\x00\x00"), + 0, + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x01\x02\x03\x04"), + int(16909060), + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x80\x00\x00\x00"), + int32(math.MinInt32), + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x7f\xff\xff\xff"), + int32(math.MaxInt32), + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x00\x00\x00\x00"), + "0", + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x01\x02\x03\x04"), + "16909060", + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x80\x00\x00\x00"), + "-2147483648", // math.MinInt32 + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x7f\xff\xff\xff"), + "2147483647", // math.MaxInt32 + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), + 0, + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x01\x02\x03\x04\x05\x06\x07\x08"), + 72623859790382856, + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), + int64(math.MinInt64), + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), + int64(math.MaxInt64), + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), + "0", + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x01\x02\x03\x04\x05\x06\x07\x08"), + "72623859790382856", + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), + "-9223372036854775808", // math.MinInt64 + }, + { + NativeType{proto: 2, typ: TypeBigInt}, + []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), + "9223372036854775807", // math.MaxInt64 + }, + { + NativeType{proto: 2, typ: TypeBoolean}, + []byte("\x00"), + false, + }, + { + NativeType{proto: 2, typ: TypeBoolean}, + []byte("\x01"), + true, + }, + { + NativeType{proto: 2, typ: TypeFloat}, + []byte("\x40\x49\x0f\xdb"), + float32(3.14159265), + }, + { + NativeType{proto: 2, typ: TypeDouble}, + []byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"), + float64(3.14159265), + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x00\x00"), + inf.NewDec(0, 0), + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x00\x64"), + inf.NewDec(100, 0), + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x02\x19"), + decimalize("0.25"), + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x13\xD5\a;\x20\x14\xA2\x91"), + decimalize("-0.0012095473475870063"), // From the iconara/cql-rb test suite + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x13*\xF8\xC4\xDF\xEB]o"), + decimalize("0.0012095473475870063"), // From the iconara/cql-rb test suite + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x12\xF2\xD8\x02\xB6R\x7F\x99\xEE\x98#\x99\xA9V"), + decimalize("-1042342234234.123423435647768234"), // From the iconara/cql-rb test suite + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\r\nJ\x04\"^\x91\x04\x8a\xb1\x18\xfe"), + decimalize("1243878957943.1234124191998"), // From the datastax/python-driver test suite + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x06\xe5\xde]\x98Y"), + decimalize("-112233.441191"), // From the datastax/python-driver test suite + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x14\x00\xfa\xce"), + decimalize("0.00000000000000064206"), // From the datastax/python-driver test suite + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\x00\x00\x00\x14\xff\x052"), + decimalize("-0.00000000000000064206"), // From the datastax/python-driver test suite + }, + { + NativeType{proto: 2, typ: TypeDecimal}, + []byte("\xff\xff\xff\x9c\x00\xfa\xce"), + inf.NewDec(64206, -100), // From the datastax/python-driver test suite + }, + { + NativeType{proto: 2, typ: TypeTimestamp}, + []byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"), + time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC), + }, + { + NativeType{proto: 2, typ: TypeTimestamp}, + []byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"), + int64(1376387523000), + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeList}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"), + []int{1, 2}, + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeList}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"), + [2]int{1, 2}, + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeSet}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"), + []int{1, 2}, + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeSet}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte(nil), + []int(nil), + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeMap}, + Key: NativeType{proto: 2, typ: TypeVarchar}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"), + map[string]int{"foo": 1}, + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeMap}, + Key: NativeType{proto: 2, typ: TypeVarchar}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte(nil), + map[string]int(nil), + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeList}, + Elem: NativeType{proto: 2, typ: TypeVarchar}, + }, + bytes.Join([][]byte{ + []byte("\x00\x01\xFF\xFF"), + bytes.Repeat([]byte("X"), 65535)}, []byte("")), + []string{strings.Repeat("X", 65535)}, + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeMap}, + Key: NativeType{proto: 2, typ: TypeVarchar}, + Elem: NativeType{proto: 2, typ: TypeVarchar}, + }, + bytes.Join([][]byte{ + []byte("\x00\x01\xFF\xFF"), + bytes.Repeat([]byte("X"), 65535), + []byte("\xFF\xFF"), + bytes.Repeat([]byte("Y"), 65535)}, []byte("")), + map[string]string{ + strings.Repeat("X", 65535): strings.Repeat("Y", 65535), + }, + }, + { + NativeType{proto: 2, typ: TypeVarint}, + []byte("\x00"), + 0, + }, + { + NativeType{proto: 2, typ: TypeVarint}, + []byte("\x37\xE2\x3C\xEC"), + int32(937573612), + }, + { + NativeType{proto: 2, typ: TypeVarint}, + []byte("\x37\xE2\x3C\xEC"), + big.NewInt(937573612), + }, + { + NativeType{proto: 2, typ: TypeVarint}, + []byte("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a["), + bigintize("1231312312331283012830129382342342412123"), // From the iconara/cql-rb test suite + }, + { + NativeType{proto: 2, typ: TypeVarint}, + []byte("\xC9v\x8D:\x86"), + big.NewInt(-234234234234), // From the iconara/cql-rb test suite + }, + { + NativeType{proto: 2, typ: TypeVarint}, + []byte("f\x1e\xfd\xf2\xe3\xb1\x9f|\x04_\x15"), + bigintize("123456789123456789123456789"), // From the datastax/python-driver test suite + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\x7F\x00\x00\x01"), + net.ParseIP("127.0.0.1").To4(), + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\xFF\xFF\xFF\xFF"), + net.ParseIP("255.255.255.255").To4(), + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\x7F\x00\x00\x01"), + "127.0.0.1", + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\xFF\xFF\xFF\xFF"), + "255.255.255.255", + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\x21\xDA\x00\xd3\x00\x00\x2f\x3b\x02\xaa\x00\xff\xfe\x28\x9c\x5a"), + "21da:d3:0:2f3b:2aa:ff:fe28:9c5a", + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"), + "fe80::202:b3ff:fe1e:8329", + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\x21\xDA\x00\xd3\x00\x00\x2f\x3b\x02\xaa\x00\xff\xfe\x28\x9c\x5a"), + net.ParseIP("21da:d3:0:2f3b:2aa:ff:fe28:9c5a"), + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"), + net.ParseIP("fe80::202:b3ff:fe1e:8329"), + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte(nil), + nil, + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte("nullable string"), + func() *string { + value := "nullable string" + return &value + }(), + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte{}, + (*string)(nil), + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte("\x7f\xff\xff\xff"), + func() *int { + var value int = math.MaxInt32 + return &value + }(), + }, + { + NativeType{proto: 2, typ: TypeInt}, + []byte(nil), + (*int)(nil), + }, + { + NativeType{proto: 2, typ: TypeTimeUUID}, + []byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0}, + &UUID{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0}, + }, + { + NativeType{proto: 2, typ: TypeTimeUUID}, + []byte{}, + (*UUID)(nil), + }, + { + NativeType{proto: 2, typ: TypeTimestamp}, + []byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"), + func() *time.Time { + t := time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC) + return &t + }(), + }, + { + NativeType{proto: 2, typ: TypeTimestamp}, + []byte(nil), + (*time.Time)(nil), + }, + { + NativeType{proto: 2, typ: TypeBoolean}, + []byte("\x00"), + func() *bool { + b := false + return &b + }(), + }, + { + NativeType{proto: 2, typ: TypeBoolean}, + []byte("\x01"), + func() *bool { + b := true + return &b + }(), + }, + { + NativeType{proto: 2, typ: TypeBoolean}, + []byte(nil), + (*bool)(nil), + }, + { + NativeType{proto: 2, typ: TypeFloat}, + []byte("\x40\x49\x0f\xdb"), + func() *float32 { + f := float32(3.14159265) + return &f + }(), + }, + { + NativeType{proto: 2, typ: TypeFloat}, + []byte(nil), + (*float32)(nil), + }, + { + NativeType{proto: 2, typ: TypeDouble}, + []byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"), + func() *float64 { + d := float64(3.14159265) + return &d + }(), + }, + { + NativeType{proto: 2, typ: TypeDouble}, + []byte(nil), + (*float64)(nil), + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte("\x7F\x00\x00\x01"), + func() *net.IP { + ip := net.ParseIP("127.0.0.1").To4() + return &ip + }(), + }, + { + NativeType{proto: 2, typ: TypeInet}, + []byte(nil), + (*net.IP)(nil), + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeList}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"), + func() *[]int { + l := []int{1, 2} + return &l + }(), + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeList}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte(nil), + (*[]int)(nil), + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeMap}, + Key: NativeType{proto: 2, typ: TypeVarchar}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"), + func() *map[string]int { + m := map[string]int{"foo": 1} + return &m + }(), + }, + { + CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeMap}, + Key: NativeType{proto: 2, typ: TypeVarchar}, + Elem: NativeType{proto: 2, typ: TypeInt}, + }, + []byte(nil), + (*map[string]int)(nil), + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte("HELLO WORLD"), + func() *CustomString { + customString := CustomString("hello world") + return &customString + }(), + }, + { + NativeType{proto: 2, typ: TypeVarchar}, + []byte(nil), + (*CustomString)(nil), + }, +} + +func decimalize(s string) *inf.Dec { + i, _ := new(inf.Dec).SetString(s) + return i +} + +func bigintize(s string) *big.Int { + i, _ := new(big.Int).SetString(s, 10) + return i +} + +func TestMarshal(t *testing.T) { + for i, test := range marshalTests { + data, err := Marshal(test.Info, test.Value) + if err != nil { + t.Errorf("marshalTest[%d]: %v", i, err) + continue + } + if !bytes.Equal(data, test.Data) { + t.Errorf("marshalTest[%d]: expected %q, got %q (%#v)", i, test.Data, data, test.Value) + } + } +} + +func TestUnmarshal(t *testing.T) { + for i, test := range marshalTests { + if test.Value != nil { + v := reflect.New(reflect.TypeOf(test.Value)) + err := Unmarshal(test.Info, test.Data, v.Interface()) + if err != nil { + t.Errorf("unmarshalTest[%d]: %v", i, err) + continue + } + if !reflect.DeepEqual(v.Elem().Interface(), test.Value) { + t.Errorf("unmarshalTest[%d]: expected %#v, got %#v.", i, test.Value, v.Elem().Interface()) + } + } else { + if err := Unmarshal(test.Info, test.Data, test.Value); nil == err { + t.Errorf("unmarshalTest[%d]: %#v not return error.", i, test.Value) + } + } + } +} + +func TestMarshalVarint(t *testing.T) { + varintTests := []struct { + Value interface{} + Marshaled []byte + Unmarshaled *big.Int + }{ + { + Value: int8(0), + Marshaled: []byte("\x00"), + Unmarshaled: big.NewInt(0), + }, + { + Value: uint8(255), + Marshaled: []byte("\x00\xFF"), + Unmarshaled: big.NewInt(255), + }, + { + Value: int8(-1), + Marshaled: []byte("\xFF"), + Unmarshaled: big.NewInt(-1), + }, + { + Value: big.NewInt(math.MaxInt32), + Marshaled: []byte("\x7F\xFF\xFF\xFF"), + Unmarshaled: big.NewInt(math.MaxInt32), + }, + { + Value: big.NewInt(int64(math.MaxInt32) + 1), + Marshaled: []byte("\x00\x80\x00\x00\x00"), + Unmarshaled: big.NewInt(int64(math.MaxInt32) + 1), + }, + { + Value: big.NewInt(math.MinInt32), + Marshaled: []byte("\x80\x00\x00\x00"), + Unmarshaled: big.NewInt(math.MinInt32), + }, + { + Value: big.NewInt(int64(math.MinInt32) - 1), + Marshaled: []byte("\xFF\x7F\xFF\xFF\xFF"), + Unmarshaled: big.NewInt(int64(math.MinInt32) - 1), + }, + { + Value: math.MinInt64, + Marshaled: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), + Unmarshaled: big.NewInt(math.MinInt64), + }, + { + Value: uint64(math.MaxInt64) + 1, + Marshaled: []byte("\x00\x80\x00\x00\x00\x00\x00\x00\x00"), + Unmarshaled: bigintize("9223372036854775808"), + }, + { + Value: bigintize("2361183241434822606848"), // 2**71 + Marshaled: []byte("\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00"), + Unmarshaled: bigintize("2361183241434822606848"), + }, + { + Value: bigintize("-9223372036854775809"), // -2**63 - 1 + Marshaled: []byte("\xFF\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF"), + Unmarshaled: bigintize("-9223372036854775809"), + }, + } + + for i, test := range varintTests { + data, err := Marshal(NativeType{proto: 2, typ: TypeVarint}, test.Value) + if err != nil { + t.Errorf("error marshaling varint: %v (test #%d)", err, i) + } + + if !bytes.Equal(test.Marshaled, data) { + t.Errorf("marshaled varint mismatch: expected %v, got %v (test #%d)", test.Marshaled, data, i) + } + + binder := new(big.Int) + err = Unmarshal(NativeType{proto: 2, typ: TypeVarint}, test.Marshaled, binder) + if err != nil { + t.Errorf("error unmarshaling varint: %v (test #%d)", err, i) + } + + if test.Unmarshaled.Cmp(binder) != 0 { + t.Errorf("unmarshaled varint mismatch: expected %v, got %v (test #%d)", test.Unmarshaled, binder, i) + } + } +} + +func equalStringSlice(leftList, rightList []string) bool { + if len(leftList) != len(rightList) { + return false + } + for index := range leftList { + if rightList[index] != leftList[index] { + return false + } + } + return true +} + +func TestMarshalList(t *testing.T) { + typeInfo := CollectionType{ + NativeType: NativeType{proto: 2, typ: TypeList}, + Elem: NativeType{proto: 2, typ: TypeVarchar}, + } + + sourceLists := [][]string{ + []string{"valueA"}, + []string{"valueA", "valueB"}, + []string{"valueB"}, + } + + listDatas := [][]byte{} + + for _, list := range sourceLists { + listData, marshalErr := Marshal(typeInfo, list) + if nil != marshalErr { + t.Errorf("Error marshal %+v of type %+v: %s", list, typeInfo, marshalErr) + } + listDatas = append(listDatas, listData) + } + + outputLists := [][]string{} + + var outputList []string + + for _, listData := range listDatas { + if unmarshalErr := Unmarshal(typeInfo, listData, &outputList); nil != unmarshalErr { + t.Error(unmarshalErr) + } + outputLists = append(outputLists, outputList) + } + + for index, sourceList := range sourceLists { + outputList := outputLists[index] + if !equalStringSlice(sourceList, outputList) { + t.Errorf("Lists %+v not equal to lists %+v, but should", sourceList, outputList) + } + } +} + +type CustomString string + +func (c CustomString) MarshalCQL(info TypeInfo) ([]byte, error) { + return []byte(strings.ToUpper(string(c))), nil +} +func (c *CustomString) UnmarshalCQL(info TypeInfo, data []byte) error { + *c = CustomString(strings.ToLower(string(data))) + return nil +} + +type MyString string + +type MyInt int + +var typeLookupTest = []struct { + TypeName string + ExpectedType Type +}{ + {"AsciiType", TypeAscii}, + {"LongType", TypeBigInt}, + {"BytesType", TypeBlob}, + {"BooleanType", TypeBoolean}, + {"CounterColumnType", TypeCounter}, + {"DecimalType", TypeDecimal}, + {"DoubleType", TypeDouble}, + {"FloatType", TypeFloat}, + {"Int32Type", TypeInt}, + {"DateType", TypeTimestamp}, + {"TimestampType", TypeTimestamp}, + {"UUIDType", TypeUUID}, + {"UTF8Type", TypeVarchar}, + {"IntegerType", TypeVarint}, + {"TimeUUIDType", TypeTimeUUID}, + {"InetAddressType", TypeInet}, + {"MapType", TypeMap}, + {"ListType", TypeList}, + {"SetType", TypeSet}, + {"unknown", TypeCustom}, +} + +func testType(t *testing.T, cassType string, expectedType Type) { + if computedType := getApacheCassandraType(apacheCassandraTypePrefix + cassType); computedType != expectedType { + t.Errorf("Cassandra custom type lookup for %s failed. Expected %s, got %s.", cassType, expectedType.String(), computedType.String()) + } +} + +func TestLookupCassType(t *testing.T) { + for _, lookupTest := range typeLookupTest { + testType(t, lookupTest.TypeName, lookupTest.ExpectedType) + } +} + +type MyPointerMarshaler struct{} + +func (m *MyPointerMarshaler) MarshalCQL(_ TypeInfo) ([]byte, error) { + return []byte{42}, nil +} + +func TestMarshalPointer(t *testing.T) { + m := &MyPointerMarshaler{} + typ := NativeType{proto: 2, typ: TypeInt} + + data, err := Marshal(typ, m) + + if err != nil { + t.Errorf("Pointer marshaling failed. Error: %s", err) + } + if len(data) != 1 || data[0] != 42 { + t.Errorf("Pointer marshaling failed. Expected %+v, got %+v", []byte{42}, data) + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/metadata.go b/Godeps/_workspace/src/github.com/gocql/gocql/metadata.go new file mode 100644 index 000000000..e2ddd763f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/metadata.go @@ -0,0 +1,871 @@ +// Copyright (c) 2015 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "log" + "strconv" + "strings" + "sync" +) + +// schema metadata for a keyspace +type KeyspaceMetadata struct { + Name string + DurableWrites bool + StrategyClass string + StrategyOptions map[string]interface{} + Tables map[string]*TableMetadata +} + +// schema metadata for a table (a.k.a. column family) +type TableMetadata struct { + Keyspace string + Name string + KeyValidator string + Comparator string + DefaultValidator string + KeyAliases []string + ColumnAliases []string + ValueAlias string + PartitionKey []*ColumnMetadata + ClusteringColumns []*ColumnMetadata + Columns map[string]*ColumnMetadata +} + +// schema metadata for a column +type ColumnMetadata struct { + Keyspace string + Table string + Name string + ComponentIndex int + Kind string + Validator string + Type TypeInfo + Order ColumnOrder + Index ColumnIndexMetadata +} + +// the ordering of the column with regard to its comparator +type ColumnOrder bool + +const ( + ASC ColumnOrder = false + DESC = true +) + +type ColumnIndexMetadata struct { + Name string + Type string + Options map[string]interface{} +} + +// Column kind values +const ( + PARTITION_KEY = "partition_key" + CLUSTERING_KEY = "clustering_key" + REGULAR = "regular" +) + +// default alias values +const ( + DEFAULT_KEY_ALIAS = "key" + DEFAULT_COLUMN_ALIAS = "column" + DEFAULT_VALUE_ALIAS = "value" +) + +// queries the cluster for schema information for a specific keyspace +type schemaDescriber struct { + session *Session + mu sync.Mutex + + cache map[string]*KeyspaceMetadata +} + +// creates a session bound schema describer which will query and cache +// keyspace metadata +func newSchemaDescriber(session *Session) *schemaDescriber { + return &schemaDescriber{ + session: session, + cache: map[string]*KeyspaceMetadata{}, + } +} + +// returns the cached KeyspaceMetadata held by the describer for the named +// keyspace. +func (s *schemaDescriber) getSchema(keyspaceName string) (*KeyspaceMetadata, error) { + s.mu.Lock() + defer s.mu.Unlock() + + // TODO handle schema change events + + metadata, found := s.cache[keyspaceName] + if !found { + // refresh the cache for this keyspace + err := s.refreshSchema(keyspaceName) + if err != nil { + return nil, err + } + + metadata = s.cache[keyspaceName] + } + + return metadata, nil +} + +// forcibly updates the current KeyspaceMetadata held by the schema describer +// for a given named keyspace. +func (s *schemaDescriber) refreshSchema(keyspaceName string) error { + var err error + + // query the system keyspace for schema data + // TODO retrieve concurrently + keyspace, err := getKeyspaceMetadata(s.session, keyspaceName) + if err != nil { + return err + } + tables, err := getTableMetadata(s.session, keyspaceName) + if err != nil { + return err + } + columns, err := getColumnMetadata(s.session, keyspaceName) + if err != nil { + return err + } + + // organize the schema data + compileMetadata(s.session.cfg.ProtoVersion, keyspace, tables, columns) + + // update the cache + s.cache[keyspaceName] = keyspace + + return nil +} + +// "compiles" derived information about keyspace, table, and column metadata +// for a keyspace from the basic queried metadata objects returned by +// getKeyspaceMetadata, getTableMetadata, and getColumnMetadata respectively; +// Links the metadata objects together and derives the column composition of +// the partition key and clustering key for a table. +func compileMetadata( + protoVersion int, + keyspace *KeyspaceMetadata, + tables []TableMetadata, + columns []ColumnMetadata, +) { + keyspace.Tables = make(map[string]*TableMetadata) + for i := range tables { + tables[i].Columns = make(map[string]*ColumnMetadata) + + keyspace.Tables[tables[i].Name] = &tables[i] + } + + // add columns from the schema data + for i := range columns { + // decode the validator for TypeInfo and order + validatorParsed := parseType(columns[i].Validator) + columns[i].Type = validatorParsed.types[0] + columns[i].Order = ASC + if validatorParsed.reversed[0] { + columns[i].Order = DESC + } + + table := keyspace.Tables[columns[i].Table] + table.Columns[columns[i].Name] = &columns[i] + } + + if protoVersion == 1 { + compileV1Metadata(tables) + } else { + compileV2Metadata(tables) + } +} + +// Compiles derived information from TableMetadata which have had +// ColumnMetadata added already. V1 protocol does not return as much +// column metadata as V2+ (because V1 doesn't support the "type" column in the +// system.schema_columns table) so determining PartitionKey and ClusterColumns +// is more complex. +func compileV1Metadata(tables []TableMetadata) { + for i := range tables { + table := &tables[i] + + // decode the key validator + keyValidatorParsed := parseType(table.KeyValidator) + // decode the comparator + comparatorParsed := parseType(table.Comparator) + + // the partition key length is the same as the number of types in the + // key validator + table.PartitionKey = make([]*ColumnMetadata, len(keyValidatorParsed.types)) + + // V1 protocol only returns "regular" columns from + // system.schema_columns (there is no type field for columns) + // so the alias information is used to + // create the partition key and clustering columns + + // construct the partition key from the alias + for i := range table.PartitionKey { + var alias string + if len(table.KeyAliases) > i { + alias = table.KeyAliases[i] + } else if i == 0 { + alias = DEFAULT_KEY_ALIAS + } else { + alias = DEFAULT_KEY_ALIAS + strconv.Itoa(i+1) + } + + column := &ColumnMetadata{ + Keyspace: table.Keyspace, + Table: table.Name, + Name: alias, + Type: keyValidatorParsed.types[i], + Kind: PARTITION_KEY, + ComponentIndex: i, + } + + table.PartitionKey[i] = column + table.Columns[alias] = column + } + + // determine the number of clustering columns + size := len(comparatorParsed.types) + if comparatorParsed.isComposite { + if len(comparatorParsed.collections) != 0 || + (len(table.ColumnAliases) == size-1 && + comparatorParsed.types[size-1].Type() == TypeVarchar) { + size = size - 1 + } + } else { + if !(len(table.ColumnAliases) != 0 || len(table.Columns) == 0) { + size = 0 + } + } + + table.ClusteringColumns = make([]*ColumnMetadata, size) + + for i := range table.ClusteringColumns { + var alias string + if len(table.ColumnAliases) > i { + alias = table.ColumnAliases[i] + } else if i == 0 { + alias = DEFAULT_COLUMN_ALIAS + } else { + alias = DEFAULT_COLUMN_ALIAS + strconv.Itoa(i+1) + } + + order := ASC + if comparatorParsed.reversed[i] { + order = DESC + } + + column := &ColumnMetadata{ + Keyspace: table.Keyspace, + Table: table.Name, + Name: alias, + Type: comparatorParsed.types[i], + Order: order, + Kind: CLUSTERING_KEY, + ComponentIndex: i, + } + + table.ClusteringColumns[i] = column + table.Columns[alias] = column + } + + if size != len(comparatorParsed.types)-1 { + alias := DEFAULT_VALUE_ALIAS + if len(table.ValueAlias) > 0 { + alias = table.ValueAlias + } + // decode the default validator + defaultValidatorParsed := parseType(table.DefaultValidator) + column := &ColumnMetadata{ + Keyspace: table.Keyspace, + Table: table.Name, + Name: alias, + Type: defaultValidatorParsed.types[0], + Kind: REGULAR, + } + table.Columns[alias] = column + } + } +} + +// The simpler compile case for V2+ protocol +func compileV2Metadata(tables []TableMetadata) { + for i := range tables { + table := &tables[i] + + partitionColumnCount := countColumnsOfKind(table.Columns, PARTITION_KEY) + table.PartitionKey = make([]*ColumnMetadata, partitionColumnCount) + + clusteringColumnCount := countColumnsOfKind(table.Columns, CLUSTERING_KEY) + table.ClusteringColumns = make([]*ColumnMetadata, clusteringColumnCount) + + for _, column := range table.Columns { + if column.Kind == PARTITION_KEY { + table.PartitionKey[column.ComponentIndex] = column + } else if column.Kind == CLUSTERING_KEY { + table.ClusteringColumns[column.ComponentIndex] = column + } + } + + } +} + +// returns the count of coluns with the given "kind" value. +func countColumnsOfKind(columns map[string]*ColumnMetadata, kind string) int { + count := 0 + for _, column := range columns { + if column.Kind == kind { + count++ + } + } + return count +} + +// query only for the keyspace metadata for the specified keyspace from system.schema_keyspace +func getKeyspaceMetadata( + session *Session, + keyspaceName string, +) (*KeyspaceMetadata, error) { + query := session.Query( + ` + SELECT durable_writes, strategy_class, strategy_options + FROM system.schema_keyspaces + WHERE keyspace_name = ? + `, + keyspaceName, + ) + // Set a routing key to avoid GetRoutingKey from computing the routing key + // TODO use a separate connection (pool) for system keyspace queries. + query.RoutingKey([]byte{}) + + keyspace := &KeyspaceMetadata{Name: keyspaceName} + var strategyOptionsJSON []byte + + err := query.Scan( + &keyspace.DurableWrites, + &keyspace.StrategyClass, + &strategyOptionsJSON, + ) + if err != nil { + return nil, fmt.Errorf("Error querying keyspace schema: %v", err) + } + + err = json.Unmarshal(strategyOptionsJSON, &keyspace.StrategyOptions) + if err != nil { + return nil, fmt.Errorf( + "Invalid JSON value '%s' as strategy_options for in keyspace '%s': %v", + strategyOptionsJSON, keyspace.Name, err, + ) + } + + return keyspace, nil +} + +// query for only the table metadata in the specified keyspace from system.schema_columnfamilies +func getTableMetadata( + session *Session, + keyspaceName string, +) ([]TableMetadata, error) { + query := session.Query( + ` + SELECT + columnfamily_name, + key_validator, + comparator, + default_validator, + key_aliases, + column_aliases, + value_alias + FROM system.schema_columnfamilies + WHERE keyspace_name = ? + `, + keyspaceName, + ) + // Set a routing key to avoid GetRoutingKey from computing the routing key + // TODO use a separate connection (pool) for system keyspace queries. + query.RoutingKey([]byte{}) + iter := query.Iter() + + tables := []TableMetadata{} + table := TableMetadata{Keyspace: keyspaceName} + + var keyAliasesJSON []byte + var columnAliasesJSON []byte + for iter.Scan( + &table.Name, + &table.KeyValidator, + &table.Comparator, + &table.DefaultValidator, + &keyAliasesJSON, + &columnAliasesJSON, + &table.ValueAlias, + ) { + var err error + + // decode the key aliases + if keyAliasesJSON != nil { + table.KeyAliases = []string{} + err = json.Unmarshal(keyAliasesJSON, &table.KeyAliases) + if err != nil { + iter.Close() + return nil, fmt.Errorf( + "Invalid JSON value '%s' as key_aliases for in table '%s': %v", + keyAliasesJSON, table.Name, err, + ) + } + } + + // decode the column aliases + if columnAliasesJSON != nil { + table.ColumnAliases = []string{} + err = json.Unmarshal(columnAliasesJSON, &table.ColumnAliases) + if err != nil { + iter.Close() + return nil, fmt.Errorf( + "Invalid JSON value '%s' as column_aliases for in table '%s': %v", + columnAliasesJSON, table.Name, err, + ) + } + } + + tables = append(tables, table) + table = TableMetadata{Keyspace: keyspaceName} + } + + err := iter.Close() + if err != nil && err != ErrNotFound { + return nil, fmt.Errorf("Error querying table schema: %v", err) + } + + return tables, nil +} + +// query for only the column metadata in the specified keyspace from system.schema_columns +func getColumnMetadata( + session *Session, + keyspaceName string, +) ([]ColumnMetadata, error) { + // Deal with differences in protocol versions + var stmt string + var scan func(*Iter, *ColumnMetadata, *[]byte) bool + if session.cfg.ProtoVersion == 1 { + // V1 does not support the type column, and all returned rows are + // of kind "regular". + stmt = ` + SELECT + columnfamily_name, + column_name, + component_index, + validator, + index_name, + index_type, + index_options + FROM system.schema_columns + WHERE keyspace_name = ? + ` + scan = func( + iter *Iter, + column *ColumnMetadata, + indexOptionsJSON *[]byte, + ) bool { + // all columns returned by V1 are regular + column.Kind = REGULAR + return iter.Scan( + &column.Table, + &column.Name, + &column.ComponentIndex, + &column.Validator, + &column.Index.Name, + &column.Index.Type, + &indexOptionsJSON, + ) + } + } else { + // V2+ supports the type column + stmt = ` + SELECT + columnfamily_name, + column_name, + component_index, + validator, + index_name, + index_type, + index_options, + type + FROM system.schema_columns + WHERE keyspace_name = ? + ` + scan = func( + iter *Iter, + column *ColumnMetadata, + indexOptionsJSON *[]byte, + ) bool { + return iter.Scan( + &column.Table, + &column.Name, + &column.ComponentIndex, + &column.Validator, + &column.Index.Name, + &column.Index.Type, + &indexOptionsJSON, + &column.Kind, + ) + } + } + + // get the columns metadata + columns := []ColumnMetadata{} + column := ColumnMetadata{Keyspace: keyspaceName} + + var indexOptionsJSON []byte + + query := session.Query(stmt, keyspaceName) + // Set a routing key to avoid GetRoutingKey from computing the routing key + // TODO use a separate connection (pool) for system keyspace queries. + query.RoutingKey([]byte{}) + iter := query.Iter() + + for scan(iter, &column, &indexOptionsJSON) { + var err error + + // decode the index options + if indexOptionsJSON != nil { + err = json.Unmarshal(indexOptionsJSON, &column.Index.Options) + if err != nil { + iter.Close() + return nil, fmt.Errorf( + "Invalid JSON value '%s' as index_options for column '%s' in table '%s': %v", + indexOptionsJSON, + column.Name, + column.Table, + err, + ) + } + } + + columns = append(columns, column) + column = ColumnMetadata{Keyspace: keyspaceName} + } + + err := iter.Close() + if err != nil && err != ErrNotFound { + return nil, fmt.Errorf("Error querying column schema: %v", err) + } + + return columns, nil +} + +// type definition parser state +type typeParser struct { + input string + index int +} + +// the type definition parser result +type typeParserResult struct { + isComposite bool + types []TypeInfo + reversed []bool + collections map[string]TypeInfo +} + +// Parse the type definition used for validator and comparator schema data +func parseType(def string) typeParserResult { + parser := &typeParser{input: def} + return parser.parse() +} + +const ( + REVERSED_TYPE = "org.apache.cassandra.db.marshal.ReversedType" + COMPOSITE_TYPE = "org.apache.cassandra.db.marshal.CompositeType" + COLLECTION_TYPE = "org.apache.cassandra.db.marshal.ColumnToCollectionType" + LIST_TYPE = "org.apache.cassandra.db.marshal.ListType" + SET_TYPE = "org.apache.cassandra.db.marshal.SetType" + MAP_TYPE = "org.apache.cassandra.db.marshal.MapType" +) + +// represents a class specification in the type def AST +type typeParserClassNode struct { + name string + params []typeParserParamNode + // this is the segment of the input string that defined this node + input string +} + +// represents a class parameter in the type def AST +type typeParserParamNode struct { + name *string + class typeParserClassNode +} + +func (t *typeParser) parse() typeParserResult { + // parse the AST + ast, ok := t.parseClassNode() + if !ok { + // treat this is a custom type + return typeParserResult{ + isComposite: false, + types: []TypeInfo{ + NativeType{ + typ: TypeCustom, + custom: t.input, + }, + }, + reversed: []bool{false}, + collections: nil, + } + } + + // interpret the AST + if strings.HasPrefix(ast.name, COMPOSITE_TYPE) { + count := len(ast.params) + + // look for a collections param + last := ast.params[count-1] + collections := map[string]TypeInfo{} + if strings.HasPrefix(last.class.name, COLLECTION_TYPE) { + count-- + + for _, param := range last.class.params { + // decode the name + var name string + decoded, err := hex.DecodeString(*param.name) + if err != nil { + log.Printf( + "Error parsing type '%s', contains collection name '%s' with an invalid format: %v", + t.input, + *param.name, + err, + ) + // just use the provided name + name = *param.name + } else { + name = string(decoded) + } + collections[name] = param.class.asTypeInfo() + } + } + + types := make([]TypeInfo, count) + reversed := make([]bool, count) + + for i, param := range ast.params[:count] { + class := param.class + reversed[i] = strings.HasPrefix(class.name, REVERSED_TYPE) + if reversed[i] { + class = class.params[0].class + } + types[i] = class.asTypeInfo() + } + + return typeParserResult{ + isComposite: true, + types: types, + reversed: reversed, + collections: collections, + } + } else { + // not composite, so one type + class := *ast + reversed := strings.HasPrefix(class.name, REVERSED_TYPE) + if reversed { + class = class.params[0].class + } + typeInfo := class.asTypeInfo() + + return typeParserResult{ + isComposite: false, + types: []TypeInfo{typeInfo}, + reversed: []bool{reversed}, + } + } +} + +func (class *typeParserClassNode) asTypeInfo() TypeInfo { + if strings.HasPrefix(class.name, LIST_TYPE) { + elem := class.params[0].class.asTypeInfo() + return CollectionType{ + NativeType: NativeType{ + typ: TypeList, + }, + Elem: elem, + } + } + if strings.HasPrefix(class.name, SET_TYPE) { + elem := class.params[0].class.asTypeInfo() + return CollectionType{ + NativeType: NativeType{ + typ: TypeSet, + }, + Elem: elem, + } + } + if strings.HasPrefix(class.name, MAP_TYPE) { + key := class.params[0].class.asTypeInfo() + elem := class.params[1].class.asTypeInfo() + return CollectionType{ + NativeType: NativeType{ + typ: TypeMap, + }, + Key: key, + Elem: elem, + } + } + + // must be a simple type or custom type + info := NativeType{typ: getApacheCassandraType(class.name)} + if info.typ == TypeCustom { + // add the entire class definition + info.custom = class.input + } + return info +} + +// CLASS := ID [ PARAMS ] +func (t *typeParser) parseClassNode() (node *typeParserClassNode, ok bool) { + t.skipWhitespace() + + startIndex := t.index + + name, ok := t.nextIdentifier() + if !ok { + return nil, false + } + + params, ok := t.parseParamNodes() + if !ok { + return nil, false + } + + endIndex := t.index + + node = &typeParserClassNode{ + name: name, + params: params, + input: t.input[startIndex:endIndex], + } + return node, true +} + +// PARAMS := "(" PARAM { "," PARAM } ")" +// PARAM := [ PARAM_NAME ":" ] CLASS +// PARAM_NAME := ID +func (t *typeParser) parseParamNodes() (params []typeParserParamNode, ok bool) { + t.skipWhitespace() + + // the params are optional + if t.index == len(t.input) || t.input[t.index] != '(' { + return nil, true + } + + params = []typeParserParamNode{} + + // consume the '(' + t.index++ + + t.skipWhitespace() + + for t.input[t.index] != ')' { + // look for a named param, but if no colon, then we want to backup + backupIndex := t.index + + // name will be a hex encoded version of a utf-8 string + name, ok := t.nextIdentifier() + if !ok { + return nil, false + } + hasName := true + + // TODO handle '=>' used for DynamicCompositeType + + t.skipWhitespace() + + if t.input[t.index] == ':' { + // there is a name for this parameter + + // consume the ':' + t.index++ + + t.skipWhitespace() + } else { + // no name, backup + hasName = false + t.index = backupIndex + } + + // parse the next full parameter + classNode, ok := t.parseClassNode() + if !ok { + return nil, false + } + + if hasName { + params = append( + params, + typeParserParamNode{name: &name, class: *classNode}, + ) + } else { + params = append( + params, + typeParserParamNode{class: *classNode}, + ) + } + + t.skipWhitespace() + + if t.input[t.index] == ',' { + // consume the comma + t.index++ + + t.skipWhitespace() + } + } + + // consume the ')' + t.index++ + + return params, true +} + +func (t *typeParser) skipWhitespace() { + for t.index < len(t.input) && isWhitespaceChar(t.input[t.index]) { + t.index++ + } +} + +func isWhitespaceChar(c byte) bool { + return c == ' ' || c == '\n' || c == '\t' +} + +// ID := LETTER { LETTER } +// LETTER := "0"..."9" | "a"..."z" | "A"..."Z" | "-" | "+" | "." | "_" | "&" +func (t *typeParser) nextIdentifier() (id string, found bool) { + startIndex := t.index + for t.index < len(t.input) && isIdentifierChar(t.input[t.index]) { + t.index++ + } + if startIndex == t.index { + return "", false + } + return t.input[startIndex:t.index], true +} + +func isIdentifierChar(c byte) bool { + return (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '-' || + c == '+' || + c == '.' || + c == '_' || + c == '&' +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/metadata_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/metadata_test.go new file mode 100644 index 000000000..5d6e96688 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/metadata_test.go @@ -0,0 +1,802 @@ +// Copyright (c) 2015 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "strconv" + "testing" +) + +// Tests V1 and V2 metadata "compilation" from example data which might be returned +// from metadata schema queries (see getKeyspaceMetadata, getTableMetadata, and getColumnMetadata) +func TestCompileMetadata(t *testing.T) { + // V1 tests - these are all based on real examples from the integration test ccm cluster + keyspace := &KeyspaceMetadata{ + Name: "V1Keyspace", + } + tables := []TableMetadata{ + TableMetadata{ + // This table, found in the system keyspace, has no key aliases or column aliases + Keyspace: "V1Keyspace", + Name: "Schema", + KeyValidator: "org.apache.cassandra.db.marshal.BytesType", + Comparator: "org.apache.cassandra.db.marshal.UTF8Type", + DefaultValidator: "org.apache.cassandra.db.marshal.BytesType", + KeyAliases: []string{}, + ColumnAliases: []string{}, + ValueAlias: "", + }, + TableMetadata{ + // This table, found in the system keyspace, has key aliases, column aliases, and a value alias. + Keyspace: "V1Keyspace", + Name: "hints", + KeyValidator: "org.apache.cassandra.db.marshal.UUIDType", + Comparator: "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.TimeUUIDType,org.apache.cassandra.db.marshal.Int32Type)", + DefaultValidator: "org.apache.cassandra.db.marshal.BytesType", + KeyAliases: []string{"target_id"}, + ColumnAliases: []string{"hint_id", "message_version"}, + ValueAlias: "mutation", + }, + TableMetadata{ + // This table, found in the system keyspace, has a comparator with collections, but no column aliases + Keyspace: "V1Keyspace", + Name: "peers", + KeyValidator: "org.apache.cassandra.db.marshal.InetAddressType", + Comparator: "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.ColumnToCollectionType(746f6b656e73:org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)))", + DefaultValidator: "org.apache.cassandra.db.marshal.BytesType", + KeyAliases: []string{"peer"}, + ColumnAliases: []string{}, + ValueAlias: "", + }, + TableMetadata{ + // This table, found in the system keyspace, has a column alias, but not a composite comparator + Keyspace: "V1Keyspace", + Name: "IndexInfo", + KeyValidator: "org.apache.cassandra.db.marshal.UTF8Type", + Comparator: "org.apache.cassandra.db.marshal.ReversedType(org.apache.cassandra.db.marshal.UTF8Type)", + DefaultValidator: "org.apache.cassandra.db.marshal.BytesType", + KeyAliases: []string{"table_name"}, + ColumnAliases: []string{"index_name"}, + ValueAlias: "", + }, + TableMetadata{ + // This table, found in the gocql_test keyspace following an integration test run, has a composite comparator with collections as well as a column alias + Keyspace: "V1Keyspace", + Name: "wiki_page", + KeyValidator: "org.apache.cassandra.db.marshal.UTF8Type", + Comparator: "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.TimeUUIDType,org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.ColumnToCollectionType(74616773:org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type),6174746163686d656e7473:org.apache.cassandra.db.marshal.MapType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.BytesType)))", + DefaultValidator: "org.apache.cassandra.db.marshal.BytesType", + KeyAliases: []string{"title"}, + ColumnAliases: []string{"revid"}, + ValueAlias: "", + }, + TableMetadata{ + // This is a made up example with multiple unnamed aliases + Keyspace: "V1Keyspace", + Name: "no_names", + KeyValidator: "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UUIDType,org.apache.cassandra.db.marshal.UUIDType)", + Comparator: "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.Int32Type)", + DefaultValidator: "org.apache.cassandra.db.marshal.BytesType", + KeyAliases: []string{}, + ColumnAliases: []string{}, + ValueAlias: "", + }, + } + columns := []ColumnMetadata{ + // Here are the regular columns from the peers table for testing regular columns + ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "data_center", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type"}, + ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "host_id", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType"}, + ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rack", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type"}, + ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "release_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type"}, + ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rpc_address", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.InetAddressType"}, + ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "schema_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType"}, + ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "tokens", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)"}, + } + compileMetadata(1, keyspace, tables, columns) + assertKeyspaceMetadata( + t, + keyspace, + &KeyspaceMetadata{ + Name: "V1Keyspace", + Tables: map[string]*TableMetadata{ + "Schema": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "key", + Type: NativeType{typ: TypeBlob}, + }, + }, + ClusteringColumns: []*ColumnMetadata{}, + Columns: map[string]*ColumnMetadata{ + "key": &ColumnMetadata{ + Name: "key", + Type: NativeType{typ: TypeBlob}, + Kind: PARTITION_KEY, + }, + }, + }, + "hints": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "target_id", + Type: NativeType{typ: TypeUUID}, + }, + }, + ClusteringColumns: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "hint_id", + Type: NativeType{typ: TypeTimeUUID}, + Order: ASC, + }, + &ColumnMetadata{ + Name: "message_version", + Type: NativeType{typ: TypeInt}, + Order: ASC, + }, + }, + Columns: map[string]*ColumnMetadata{ + "target_id": &ColumnMetadata{ + Name: "target_id", + Type: NativeType{typ: TypeUUID}, + Kind: PARTITION_KEY, + }, + "hint_id": &ColumnMetadata{ + Name: "hint_id", + Type: NativeType{typ: TypeTimeUUID}, + Order: ASC, + Kind: CLUSTERING_KEY, + }, + "message_version": &ColumnMetadata{ + Name: "message_version", + Type: NativeType{typ: TypeInt}, + Order: ASC, + Kind: CLUSTERING_KEY, + }, + "mutation": &ColumnMetadata{ + Name: "mutation", + Type: NativeType{typ: TypeBlob}, + Kind: REGULAR, + }, + }, + }, + "peers": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "peer", + Type: NativeType{typ: TypeInet}, + }, + }, + ClusteringColumns: []*ColumnMetadata{}, + Columns: map[string]*ColumnMetadata{ + "peer": &ColumnMetadata{ + Name: "peer", + Type: NativeType{typ: TypeInet}, + Kind: PARTITION_KEY, + }, + "data_center": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "data_center", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}}, + "host_id": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "host_id", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: NativeType{typ: TypeUUID}}, + "rack": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rack", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}}, + "release_version": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "release_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}}, + "rpc_address": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rpc_address", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.InetAddressType", Type: NativeType{typ: TypeInet}}, + "schema_version": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "schema_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: NativeType{typ: TypeUUID}}, + "tokens": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "tokens", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)", Type: CollectionType{NativeType: NativeType{typ: TypeSet}}}, + }, + }, + "IndexInfo": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "table_name", + Type: NativeType{typ: TypeVarchar}, + }, + }, + ClusteringColumns: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "index_name", + Type: NativeType{typ: TypeVarchar}, + Order: DESC, + }, + }, + Columns: map[string]*ColumnMetadata{ + "table_name": &ColumnMetadata{ + Name: "table_name", + Type: NativeType{typ: TypeVarchar}, + Kind: PARTITION_KEY, + }, + "index_name": &ColumnMetadata{ + Name: "index_name", + Type: NativeType{typ: TypeVarchar}, + Order: DESC, + Kind: CLUSTERING_KEY, + }, + "value": &ColumnMetadata{ + Name: "value", + Type: NativeType{typ: TypeBlob}, + Kind: REGULAR, + }, + }, + }, + "wiki_page": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "title", + Type: NativeType{typ: TypeVarchar}, + }, + }, + ClusteringColumns: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "revid", + Type: NativeType{typ: TypeTimeUUID}, + Order: ASC, + }, + }, + Columns: map[string]*ColumnMetadata{ + "title": &ColumnMetadata{ + Name: "title", + Type: NativeType{typ: TypeVarchar}, + Kind: PARTITION_KEY, + }, + "revid": &ColumnMetadata{ + Name: "revid", + Type: NativeType{typ: TypeTimeUUID}, + Kind: CLUSTERING_KEY, + }, + }, + }, + "no_names": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "key", + Type: NativeType{typ: TypeUUID}, + }, + &ColumnMetadata{ + Name: "key2", + Type: NativeType{typ: TypeUUID}, + }, + }, + ClusteringColumns: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "column", + Type: NativeType{typ: TypeInt}, + Order: ASC, + }, + &ColumnMetadata{ + Name: "column2", + Type: NativeType{typ: TypeInt}, + Order: ASC, + }, + &ColumnMetadata{ + Name: "column3", + Type: NativeType{typ: TypeInt}, + Order: ASC, + }, + }, + Columns: map[string]*ColumnMetadata{ + "key": &ColumnMetadata{ + Name: "key", + Type: NativeType{typ: TypeUUID}, + Kind: PARTITION_KEY, + }, + "key2": &ColumnMetadata{ + Name: "key2", + Type: NativeType{typ: TypeUUID}, + Kind: PARTITION_KEY, + }, + "column": &ColumnMetadata{ + Name: "column", + Type: NativeType{typ: TypeInt}, + Order: ASC, + Kind: CLUSTERING_KEY, + }, + "column2": &ColumnMetadata{ + Name: "column2", + Type: NativeType{typ: TypeInt}, + Order: ASC, + Kind: CLUSTERING_KEY, + }, + "column3": &ColumnMetadata{ + Name: "column3", + Type: NativeType{typ: TypeInt}, + Order: ASC, + Kind: CLUSTERING_KEY, + }, + "value": &ColumnMetadata{ + Name: "value", + Type: NativeType{typ: TypeBlob}, + Kind: REGULAR, + }, + }, + }, + }, + }, + ) + + // V2 test - V2+ protocol is simpler so here are some toy examples to verify that the mapping works + keyspace = &KeyspaceMetadata{ + Name: "V2Keyspace", + } + tables = []TableMetadata{ + TableMetadata{ + Keyspace: "V2Keyspace", + Name: "Table1", + }, + TableMetadata{ + Keyspace: "V2Keyspace", + Name: "Table2", + }, + } + columns = []ColumnMetadata{ + ColumnMetadata{ + Keyspace: "V2Keyspace", + Table: "Table1", + Name: "Key1", + Kind: PARTITION_KEY, + ComponentIndex: 0, + Validator: "org.apache.cassandra.db.marshal.UTF8Type", + }, + ColumnMetadata{ + Keyspace: "V2Keyspace", + Table: "Table2", + Name: "Column1", + Kind: PARTITION_KEY, + ComponentIndex: 0, + Validator: "org.apache.cassandra.db.marshal.UTF8Type", + }, + ColumnMetadata{ + Keyspace: "V2Keyspace", + Table: "Table2", + Name: "Column2", + Kind: CLUSTERING_KEY, + ComponentIndex: 0, + Validator: "org.apache.cassandra.db.marshal.UTF8Type", + }, + ColumnMetadata{ + Keyspace: "V2Keyspace", + Table: "Table2", + Name: "Column3", + Kind: CLUSTERING_KEY, + ComponentIndex: 1, + Validator: "org.apache.cassandra.db.marshal.ReversedType(org.apache.cassandra.db.marshal.UTF8Type)", + }, + ColumnMetadata{ + Keyspace: "V2Keyspace", + Table: "Table2", + Name: "Column4", + Kind: REGULAR, + Validator: "org.apache.cassandra.db.marshal.UTF8Type", + }, + } + compileMetadata(2, keyspace, tables, columns) + assertKeyspaceMetadata( + t, + keyspace, + &KeyspaceMetadata{ + Name: "V2Keyspace", + Tables: map[string]*TableMetadata{ + "Table1": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "Key1", + Type: NativeType{typ: TypeVarchar}, + }, + }, + ClusteringColumns: []*ColumnMetadata{}, + Columns: map[string]*ColumnMetadata{ + "Key1": &ColumnMetadata{ + Name: "Key1", + Type: NativeType{typ: TypeVarchar}, + Kind: PARTITION_KEY, + }, + }, + }, + "Table2": &TableMetadata{ + PartitionKey: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "Column1", + Type: NativeType{typ: TypeVarchar}, + }, + }, + ClusteringColumns: []*ColumnMetadata{ + &ColumnMetadata{ + Name: "Column2", + Type: NativeType{typ: TypeVarchar}, + Order: ASC, + }, + &ColumnMetadata{ + Name: "Column3", + Type: NativeType{typ: TypeVarchar}, + Order: DESC, + }, + }, + Columns: map[string]*ColumnMetadata{ + "Column1": &ColumnMetadata{ + Name: "Column1", + Type: NativeType{typ: TypeVarchar}, + Kind: PARTITION_KEY, + }, + "Column2": &ColumnMetadata{ + Name: "Column2", + Type: NativeType{typ: TypeVarchar}, + Order: ASC, + Kind: CLUSTERING_KEY, + }, + "Column3": &ColumnMetadata{ + Name: "Column3", + Type: NativeType{typ: TypeVarchar}, + Order: DESC, + Kind: CLUSTERING_KEY, + }, + "Column4": &ColumnMetadata{ + Name: "Column4", + Type: NativeType{typ: TypeVarchar}, + Kind: REGULAR, + }, + }, + }, + }, + }, + ) +} + +// Helper function for asserting that actual metadata returned was as expected +func assertKeyspaceMetadata(t *testing.T, actual, expected *KeyspaceMetadata) { + if len(expected.Tables) != len(actual.Tables) { + t.Errorf("Expected len(%s.Tables) to be %v but was %v", expected.Name, len(expected.Tables), len(actual.Tables)) + } + for keyT := range expected.Tables { + et := expected.Tables[keyT] + at, found := actual.Tables[keyT] + + if !found { + t.Errorf("Expected %s.Tables[%s] but was not found", expected.Name, keyT) + } else { + if keyT != at.Name { + t.Errorf("Expected %s.Tables[%s].Name to be %v but was %v", expected.Name, keyT, keyT, at.Name) + } + if len(et.PartitionKey) != len(at.PartitionKey) { + t.Errorf("Expected len(%s.Tables[%s].PartitionKey) to be %v but was %v", expected.Name, keyT, len(et.PartitionKey), len(at.PartitionKey)) + } else { + for i := range et.PartitionKey { + if et.PartitionKey[i].Name != at.PartitionKey[i].Name { + t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Name to be '%v' but was '%v'", expected.Name, keyT, i, et.PartitionKey[i].Name, at.PartitionKey[i].Name) + } + if expected.Name != at.PartitionKey[i].Keyspace { + t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Keyspace to be '%v' but was '%v'", expected.Name, keyT, i, expected.Name, at.PartitionKey[i].Keyspace) + } + if keyT != at.PartitionKey[i].Table { + t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Table to be '%v' but was '%v'", expected.Name, keyT, i, keyT, at.PartitionKey[i].Table) + } + if et.PartitionKey[i].Type.Type() != at.PartitionKey[i].Type.Type() { + t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Type.Type to be %v but was %v", expected.Name, keyT, i, et.PartitionKey[i].Type.Type(), at.PartitionKey[i].Type.Type()) + } + if i != at.PartitionKey[i].ComponentIndex { + t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].ComponentIndex to be %v but was %v", expected.Name, keyT, i, i, at.PartitionKey[i].ComponentIndex) + } + if PARTITION_KEY != at.PartitionKey[i].Kind { + t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Kind to be '%v' but was '%v'", expected.Name, keyT, i, PARTITION_KEY, at.PartitionKey[i].Kind) + } + } + } + if len(et.ClusteringColumns) != len(at.ClusteringColumns) { + t.Errorf("Expected len(%s.Tables[%s].ClusteringColumns) to be %v but was %v", expected.Name, keyT, len(et.ClusteringColumns), len(at.ClusteringColumns)) + } else { + for i := range et.ClusteringColumns { + if at.ClusteringColumns[i] == nil { + t.Fatalf("Unexpected nil value: %s.Tables[%s].ClusteringColumns[%d]", expected.Name, keyT, i) + } + if et.ClusteringColumns[i].Name != at.ClusteringColumns[i].Name { + t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Name to be '%v' but was '%v'", expected.Name, keyT, i, et.ClusteringColumns[i].Name, at.ClusteringColumns[i].Name) + } + if expected.Name != at.ClusteringColumns[i].Keyspace { + t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Keyspace to be '%v' but was '%v'", expected.Name, keyT, i, expected.Name, at.ClusteringColumns[i].Keyspace) + } + if keyT != at.ClusteringColumns[i].Table { + t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Table to be '%v' but was '%v'", expected.Name, keyT, i, keyT, at.ClusteringColumns[i].Table) + } + if et.ClusteringColumns[i].Type.Type() != at.ClusteringColumns[i].Type.Type() { + t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Type.Type to be %v but was %v", expected.Name, keyT, i, et.ClusteringColumns[i].Type.Type(), at.ClusteringColumns[i].Type.Type()) + } + if i != at.ClusteringColumns[i].ComponentIndex { + t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].ComponentIndex to be %v but was %v", expected.Name, keyT, i, i, at.ClusteringColumns[i].ComponentIndex) + } + if et.ClusteringColumns[i].Order != at.ClusteringColumns[i].Order { + t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Order to be %v but was %v", expected.Name, keyT, i, et.ClusteringColumns[i].Order, at.ClusteringColumns[i].Order) + } + if CLUSTERING_KEY != at.ClusteringColumns[i].Kind { + t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Kind to be '%v' but was '%v'", expected.Name, keyT, i, CLUSTERING_KEY, at.ClusteringColumns[i].Kind) + } + } + } + if len(et.Columns) != len(at.Columns) { + eKeys := make([]string, 0, len(et.Columns)) + for key := range et.Columns { + eKeys = append(eKeys, key) + } + aKeys := make([]string, 0, len(at.Columns)) + for key := range at.Columns { + aKeys = append(aKeys, key) + } + t.Errorf("Expected len(%s.Tables[%s].Columns) to be %v (keys:%v) but was %v (keys:%v)", expected.Name, keyT, len(et.Columns), eKeys, len(at.Columns), aKeys) + } else { + for keyC := range et.Columns { + ec := et.Columns[keyC] + ac, found := at.Columns[keyC] + + if !found { + t.Errorf("Expected %s.Tables[%s].Columns[%s] but was not found", expected.Name, keyT, keyC) + } else { + if keyC != ac.Name { + t.Errorf("Expected %s.Tables[%s].Columns[%s].Name to be '%v' but was '%v'", expected.Name, keyT, keyC, keyC, at.Name) + } + if expected.Name != ac.Keyspace { + t.Errorf("Expected %s.Tables[%s].Columns[%s].Keyspace to be '%v' but was '%v'", expected.Name, keyT, keyC, expected.Name, ac.Keyspace) + } + if keyT != ac.Table { + t.Errorf("Expected %s.Tables[%s].Columns[%s].Table to be '%v' but was '%v'", expected.Name, keyT, keyC, keyT, ac.Table) + } + if ec.Type.Type() != ac.Type.Type() { + t.Errorf("Expected %s.Tables[%s].Columns[%s].Type.Type to be %v but was %v", expected.Name, keyT, keyC, ec.Type.Type(), ac.Type.Type()) + } + if ec.Order != ac.Order { + t.Errorf("Expected %s.Tables[%s].Columns[%s].Order to be %v but was %v", expected.Name, keyT, keyC, ec.Order, ac.Order) + } + if ec.Kind != ac.Kind { + t.Errorf("Expected %s.Tables[%s].Columns[%s].Kind to be '%v' but was '%v'", expected.Name, keyT, keyC, ec.Kind, ac.Kind) + } + } + } + } + } + } +} + +// Tests the cassandra type definition parser +func TestTypeParser(t *testing.T) { + // native type + assertParseNonCompositeType( + t, + "org.apache.cassandra.db.marshal.UTF8Type", + assertTypeInfo{Type: TypeVarchar}, + ) + + // reversed + assertParseNonCompositeType( + t, + "org.apache.cassandra.db.marshal.ReversedType(org.apache.cassandra.db.marshal.UUIDType)", + assertTypeInfo{Type: TypeUUID, Reversed: true}, + ) + + // set + assertParseNonCompositeType( + t, + "org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.Int32Type)", + assertTypeInfo{ + Type: TypeSet, + Elem: &assertTypeInfo{Type: TypeInt}, + }, + ) + + // list + assertParseNonCompositeType( + t, + "org.apache.cassandra.db.marshal.ListType(org.apache.cassandra.db.marshal.TimeUUIDType)", + assertTypeInfo{ + Type: TypeList, + Elem: &assertTypeInfo{Type: TypeTimeUUID}, + }, + ) + + // map + assertParseNonCompositeType( + t, + " org.apache.cassandra.db.marshal.MapType( org.apache.cassandra.db.marshal.UUIDType , org.apache.cassandra.db.marshal.BytesType ) ", + assertTypeInfo{ + Type: TypeMap, + Key: &assertTypeInfo{Type: TypeUUID}, + Elem: &assertTypeInfo{Type: TypeBlob}, + }, + ) + + // custom + assertParseNonCompositeType( + t, + "org.apache.cassandra.db.marshal.UserType(sandbox,61646472657373,737472656574:org.apache.cassandra.db.marshal.UTF8Type,63697479:org.apache.cassandra.db.marshal.UTF8Type,7a6970:org.apache.cassandra.db.marshal.Int32Type)", + assertTypeInfo{Type: TypeCustom, Custom: "org.apache.cassandra.db.marshal.UserType(sandbox,61646472657373,737472656574:org.apache.cassandra.db.marshal.UTF8Type,63697479:org.apache.cassandra.db.marshal.UTF8Type,7a6970:org.apache.cassandra.db.marshal.Int32Type)"}, + ) + assertParseNonCompositeType( + t, + "org.apache.cassandra.db.marshal.DynamicCompositeType(u=>org.apache.cassandra.db.marshal.UUIDType,d=>org.apache.cassandra.db.marshal.DateType,t=>org.apache.cassandra.db.marshal.TimeUUIDType,b=>org.apache.cassandra.db.marshal.BytesType,s=>org.apache.cassandra.db.marshal.UTF8Type,B=>org.apache.cassandra.db.marshal.BooleanType,a=>org.apache.cassandra.db.marshal.AsciiType,l=>org.apache.cassandra.db.marshal.LongType,i=>org.apache.cassandra.db.marshal.IntegerType,x=>org.apache.cassandra.db.marshal.LexicalUUIDType)", + assertTypeInfo{Type: TypeCustom, Custom: "org.apache.cassandra.db.marshal.DynamicCompositeType(u=>org.apache.cassandra.db.marshal.UUIDType,d=>org.apache.cassandra.db.marshal.DateType,t=>org.apache.cassandra.db.marshal.TimeUUIDType,b=>org.apache.cassandra.db.marshal.BytesType,s=>org.apache.cassandra.db.marshal.UTF8Type,B=>org.apache.cassandra.db.marshal.BooleanType,a=>org.apache.cassandra.db.marshal.AsciiType,l=>org.apache.cassandra.db.marshal.LongType,i=>org.apache.cassandra.db.marshal.IntegerType,x=>org.apache.cassandra.db.marshal.LexicalUUIDType)"}, + ) + + // composite defs + assertParseCompositeType( + t, + "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UTF8Type)", + []assertTypeInfo{ + assertTypeInfo{Type: TypeVarchar}, + }, + nil, + ) + assertParseCompositeType( + t, + "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.ReversedType(org.apache.cassandra.db.marshal.DateType),org.apache.cassandra.db.marshal.UTF8Type)", + []assertTypeInfo{ + assertTypeInfo{Type: TypeTimestamp, Reversed: true}, + assertTypeInfo{Type: TypeVarchar}, + }, + nil, + ) + assertParseCompositeType( + t, + "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.ColumnToCollectionType(726f77735f6d6572676564:org.apache.cassandra.db.marshal.MapType(org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.LongType)))", + []assertTypeInfo{ + assertTypeInfo{Type: TypeVarchar}, + }, + map[string]assertTypeInfo{ + "rows_merged": assertTypeInfo{ + Type: TypeMap, + Key: &assertTypeInfo{Type: TypeInt}, + Elem: &assertTypeInfo{Type: TypeBigInt}, + }, + }, + ) +} + +// expected data holder +type assertTypeInfo struct { + Type Type + Reversed bool + Elem *assertTypeInfo + Key *assertTypeInfo + Custom string +} + +// Helper function for asserting that the type parser returns the expected +// results for the given definition +func assertParseNonCompositeType( + t *testing.T, + def string, + typeExpected assertTypeInfo, +) { + + result := parseType(def) + if len(result.reversed) != 1 { + t.Errorf("%s expected %d reversed values but there were %d", def, 1, len(result.reversed)) + } + + assertParseNonCompositeTypes( + t, + def, + []assertTypeInfo{typeExpected}, + result.types, + ) + + // expect no composite part of the result + if result.isComposite { + t.Errorf("%s: Expected not composite", def) + } + if result.collections != nil { + t.Errorf("%s: Expected nil collections: %v", def, result.collections) + } +} + +// Helper function for asserting that the type parser returns the expected +// results for the given definition +func assertParseCompositeType( + t *testing.T, + def string, + typesExpected []assertTypeInfo, + collectionsExpected map[string]assertTypeInfo, +) { + + result := parseType(def) + if len(result.reversed) != len(typesExpected) { + t.Errorf("%s expected %d reversed values but there were %d", def, len(typesExpected), len(result.reversed)) + } + + assertParseNonCompositeTypes( + t, + def, + typesExpected, + result.types, + ) + + // expect composite part of the result + if !result.isComposite { + t.Errorf("%s: Expected composite", def) + } + if result.collections == nil { + t.Errorf("%s: Expected non-nil collections: %v", def, result.collections) + } + + for name, typeExpected := range collectionsExpected { + // check for an actual type for this name + typeActual, found := result.collections[name] + if !found { + t.Errorf("%s.tcollections: Expected param named %s but there wasn't", def, name) + } else { + // remove the actual from the collection so we can detect extras + delete(result.collections, name) + + // check the type + assertParseNonCompositeTypes( + t, + def+"collections["+name+"]", + []assertTypeInfo{typeExpected}, + []TypeInfo{typeActual}, + ) + } + } + + if len(result.collections) != 0 { + t.Errorf("%s.collections: Expected no more types in collections, but there was %v", def, result.collections) + } +} + +// Helper function for asserting that the type parser returns the expected +// results for the given definition +func assertParseNonCompositeTypes( + t *testing.T, + context string, + typesExpected []assertTypeInfo, + typesActual []TypeInfo, +) { + if len(typesActual) != len(typesExpected) { + t.Errorf("%s: Expected %d types, but there were %d", context, len(typesExpected), len(typesActual)) + } + + for i := range typesExpected { + typeExpected := typesExpected[i] + typeActual := typesActual[i] + + // shadow copy the context for local modification + context := context + if len(typesExpected) > 1 { + context = context + "[" + strconv.Itoa(i) + "]" + } + + // check the type + if typeActual.Type() != typeExpected.Type { + t.Errorf("%s: Expected to parse Type to %s but was %s", context, typeExpected.Type, typeActual.Type()) + } + // check the custom + if typeActual.Custom() != typeExpected.Custom { + t.Errorf("%s: Expected to parse Custom %s but was %s", context, typeExpected.Custom, typeActual.Custom()) + } + + collection, _ := typeActual.(CollectionType) + // check the elem + if typeExpected.Elem != nil { + if collection.Elem == nil { + t.Errorf("%s: Expected to parse Elem, but was nil ", context) + } else { + assertParseNonCompositeTypes( + t, + context+".Elem", + []assertTypeInfo{*typeExpected.Elem}, + []TypeInfo{collection.Elem}, + ) + } + } else if collection.Elem != nil { + t.Errorf("%s: Expected to not parse Elem, but was %+v", context, collection.Elem) + } + + // check the key + if typeExpected.Key != nil { + if collection.Key == nil { + t.Errorf("%s: Expected to parse Key, but was nil ", context) + } else { + assertParseNonCompositeTypes( + t, + context+".Key", + []assertTypeInfo{*typeExpected.Key}, + []TypeInfo{collection.Key}, + ) + } + } else if collection.Key != nil { + t.Errorf("%s: Expected to not parse Key, but was %+v", context, collection.Key) + } + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/policies.go b/Godeps/_workspace/src/github.com/gocql/gocql/policies.go new file mode 100644 index 000000000..cb68d4369 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/policies.go @@ -0,0 +1,244 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +//This file will be the future home for more policies +package gocql + +import ( + "log" + "sync" + "sync/atomic" +) + +//RetryableQuery is an interface that represents a query or batch statement that +//exposes the correct functions for the retry policy logic to evaluate correctly. +type RetryableQuery interface { + Attempts() int + GetConsistency() Consistency +} + +// RetryPolicy interface is used by gocql to determine if a query can be attempted +// again after a retryable error has been received. The interface allows gocql +// users to implement their own logic to determine if a query can be attempted +// again. +// +// See SimpleRetryPolicy as an example of implementing and using a RetryPolicy +// interface. +type RetryPolicy interface { + Attempt(RetryableQuery) bool +} + +// SimpleRetryPolicy has simple logic for attempting a query a fixed number of times. +// +// See below for examples of usage: +// +// //Assign to the cluster +// cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: 3} +// +// //Assign to a query +// query.RetryPolicy(&gocql.SimpleRetryPolicy{NumRetries: 1}) +// +type SimpleRetryPolicy struct { + NumRetries int //Number of times to retry a query +} + +// Attempt tells gocql to attempt the query again based on query.Attempts being less +// than the NumRetries defined in the policy. +func (s *SimpleRetryPolicy) Attempt(q RetryableQuery) bool { + return q.Attempts() <= s.NumRetries +} + +//HostSelectionPolicy is an interface for selecting +//the most appropriate host to execute a given query. +type HostSelectionPolicy interface { + SetHosts + SetPartitioner + //Pick returns an iteration function over selected hosts + Pick(*Query) NextHost +} + +//NextHost is an iteration function over picked hosts +type NextHost func() *HostInfo + +//NewRoundRobinHostPolicy is a round-robin load balancing policy +func NewRoundRobinHostPolicy() HostSelectionPolicy { + return &roundRobinHostPolicy{hosts: []HostInfo{}} +} + +type roundRobinHostPolicy struct { + hosts []HostInfo + pos uint32 + mu sync.RWMutex +} + +func (r *roundRobinHostPolicy) SetHosts(hosts []HostInfo) { + r.mu.Lock() + r.hosts = hosts + r.mu.Unlock() +} + +func (r *roundRobinHostPolicy) SetPartitioner(partitioner string) { + // noop +} + +func (r *roundRobinHostPolicy) Pick(qry *Query) NextHost { + // i is used to limit the number of attempts to find a host + // to the number of hosts known to this policy + var i uint32 = 0 + return func() *HostInfo { + r.mu.RLock() + if len(r.hosts) == 0 { + r.mu.RUnlock() + return nil + } + + var host *HostInfo + // always increment pos to evenly distribute traffic in case of + // failures + pos := atomic.AddUint32(&r.pos, 1) + if int(i) < len(r.hosts) { + host = &r.hosts[(pos)%uint32(len(r.hosts))] + i++ + } + r.mu.RUnlock() + return host + } +} + +//NewTokenAwareHostPolicy is a token aware host selection policy +func NewTokenAwareHostPolicy(fallback HostSelectionPolicy) HostSelectionPolicy { + return &tokenAwareHostPolicy{fallback: fallback, hosts: []HostInfo{}} +} + +type tokenAwareHostPolicy struct { + mu sync.RWMutex + hosts []HostInfo + partitioner string + tokenRing *tokenRing + fallback HostSelectionPolicy +} + +func (t *tokenAwareHostPolicy) SetHosts(hosts []HostInfo) { + t.mu.Lock() + defer t.mu.Unlock() + + // always update the fallback + t.fallback.SetHosts(hosts) + t.hosts = hosts + + t.resetTokenRing() +} + +func (t *tokenAwareHostPolicy) SetPartitioner(partitioner string) { + t.mu.Lock() + defer t.mu.Unlock() + + if t.partitioner != partitioner { + t.fallback.SetPartitioner(partitioner) + t.partitioner = partitioner + + t.resetTokenRing() + } +} + +func (t *tokenAwareHostPolicy) resetTokenRing() { + if t.partitioner == "" { + // partitioner not yet set + return + } + + // create a new token ring + tokenRing, err := newTokenRing(t.partitioner, t.hosts) + if err != nil { + log.Printf("Unable to update the token ring due to error: %s", err) + return + } + + // replace the token ring + t.tokenRing = tokenRing +} + +func (t *tokenAwareHostPolicy) Pick(qry *Query) NextHost { + if qry == nil { + return t.fallback.Pick(qry) + } + + routingKey, err := qry.GetRoutingKey() + if err != nil { + return t.fallback.Pick(qry) + } + if routingKey == nil { + return t.fallback.Pick(qry) + } + + var host *HostInfo + + t.mu.RLock() + // TODO retrieve a list of hosts based on the replication strategy + host = t.tokenRing.GetHostForPartitionKey(routingKey) + t.mu.RUnlock() + + if host == nil { + return t.fallback.Pick(qry) + } + + // scope these variables for the same lifetime as the iterator function + var ( + hostReturned bool + fallbackIter NextHost + ) + return func() *HostInfo { + if !hostReturned { + hostReturned = true + return host + } + + // fallback + if fallbackIter == nil { + fallbackIter = t.fallback.Pick(qry) + } + + fallbackHost := fallbackIter() + + // filter the token aware selected hosts from the fallback hosts + if fallbackHost == host { + fallbackHost = fallbackIter() + } + + return fallbackHost + } +} + +//ConnSelectionPolicy is an interface for selecting an +//appropriate connection for executing a query +type ConnSelectionPolicy interface { + SetConns(conns []*Conn) + Pick(*Query) *Conn +} + +type roundRobinConnPolicy struct { + conns []*Conn + pos uint32 + mu sync.RWMutex +} + +func NewRoundRobinConnPolicy() ConnSelectionPolicy { + return &roundRobinConnPolicy{} +} + +func (r *roundRobinConnPolicy) SetConns(conns []*Conn) { + r.mu.Lock() + r.conns = conns + r.mu.Unlock() +} + +func (r *roundRobinConnPolicy) Pick(qry *Query) *Conn { + pos := atomic.AddUint32(&r.pos, 1) + var conn *Conn + r.mu.RLock() + if len(r.conns) > 0 { + conn = r.conns[pos%uint32(len(r.conns))] + } + r.mu.RUnlock() + return conn +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/policies_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/policies_test.go new file mode 100644 index 000000000..78f147f4c --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/policies_test.go @@ -0,0 +1,125 @@ +// Copyright (c) 2015 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import "testing" + +// Tests of the round-robin host selection policy implementation +func TestRoundRobinHostPolicy(t *testing.T) { + policy := NewRoundRobinHostPolicy() + + hosts := []HostInfo{ + HostInfo{HostId: "0"}, + HostInfo{HostId: "1"}, + } + + policy.SetHosts(hosts) + + // the first host selected is actually at [1], but this is ok for RR + // interleaved iteration should always increment the host + iterA := policy.Pick(nil) + if actual := iterA(); actual != &hosts[1] { + t.Errorf("Expected hosts[1] but was hosts[%s]", actual.HostId) + } + iterB := policy.Pick(nil) + if actual := iterB(); actual != &hosts[0] { + t.Errorf("Expected hosts[0] but was hosts[%s]", actual.HostId) + } + if actual := iterB(); actual != &hosts[1] { + t.Errorf("Expected hosts[1] but was hosts[%s]", actual.HostId) + } + if actual := iterA(); actual != &hosts[0] { + t.Errorf("Expected hosts[0] but was hosts[%s]", actual.HostId) + } + + iterC := policy.Pick(nil) + if actual := iterC(); actual != &hosts[1] { + t.Errorf("Expected hosts[1] but was hosts[%s]", actual.HostId) + } + if actual := iterC(); actual != &hosts[0] { + t.Errorf("Expected hosts[0] but was hosts[%s]", actual.HostId) + } +} + +// Tests of the token-aware host selection policy implementation with a +// round-robin host selection policy fallback. +func TestTokenAwareHostPolicy(t *testing.T) { + policy := NewTokenAwareHostPolicy(NewRoundRobinHostPolicy()) + + query := &Query{} + + iter := policy.Pick(nil) + if iter == nil { + t.Fatal("host iterator was nil") + } + actual := iter() + if actual != nil { + t.Fatalf("expected nil from iterator, but was %v", actual) + } + + // set the hosts + hosts := []HostInfo{ + HostInfo{Peer: "0", Tokens: []string{"00"}}, + HostInfo{Peer: "1", Tokens: []string{"25"}}, + HostInfo{Peer: "2", Tokens: []string{"50"}}, + HostInfo{Peer: "3", Tokens: []string{"75"}}, + } + policy.SetHosts(hosts) + + // the token ring is not setup without the partitioner, but the fallback + // should work + if actual := policy.Pick(nil)(); actual.Peer != "1" { + t.Errorf("Expected peer 1 but was %s", actual.Peer) + } + + query.RoutingKey([]byte("30")) + if actual := policy.Pick(query)(); actual.Peer != "2" { + t.Errorf("Expected peer 2 but was %s", actual.Peer) + } + + policy.SetPartitioner("OrderedPartitioner") + + // now the token ring is configured + query.RoutingKey([]byte("20")) + iter = policy.Pick(query) + if actual := iter(); actual.Peer != "1" { + t.Errorf("Expected peer 1 but was %s", actual.Peer) + } + // rest are round robin + if actual := iter(); actual.Peer != "3" { + t.Errorf("Expected peer 3 but was %s", actual.Peer) + } + if actual := iter(); actual.Peer != "0" { + t.Errorf("Expected peer 0 but was %s", actual.Peer) + } + if actual := iter(); actual.Peer != "2" { + t.Errorf("Expected peer 2 but was %s", actual.Peer) + } +} + +// Tests of the round-robin connection selection policy implementation +func TestRoundRobinConnPolicy(t *testing.T) { + policy := NewRoundRobinConnPolicy() + + conn0 := &Conn{} + conn1 := &Conn{} + conn := []*Conn{ + conn0, + conn1, + } + + policy.SetConns(conn) + + // the first conn selected is actually at [1], but this is ok for RR + if actual := policy.Pick(nil); actual != conn1 { + t.Error("Expected conn1") + } + if actual := policy.Pick(nil); actual != conn0 { + t.Error("Expected conn0") + } + if actual := policy.Pick(nil); actual != conn1 { + t.Error("Expected conn1") + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/session.go b/Godeps/_workspace/src/github.com/gocql/gocql/session.go new file mode 100644 index 000000000..1ca657fe6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/session.go @@ -0,0 +1,1019 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "strings" + "sync" + "time" + "unicode" + + "github.com/golang/groupcache/lru" +) + +// Session is the interface used by users to interact with the database. +// +// It's safe for concurrent use by multiple goroutines and a typical usage +// scenario is to have one global session object to interact with the +// whole Cassandra cluster. +// +// This type extends the Node interface by adding a convinient query builder +// and automatically sets a default consinstency level on all operations +// that do not have a consistency level set. +type Session struct { + Pool ConnectionPool + cons Consistency + pageSize int + prefetch float64 + routingKeyInfoCache routingKeyInfoLRU + schemaDescriber *schemaDescriber + trace Tracer + hostSource *ringDescriber + mu sync.RWMutex + + cfg ClusterConfig + + closeMu sync.RWMutex + isClosed bool +} + +// NewSession wraps an existing Node. +func NewSession(cfg ClusterConfig) (*Session, error) { + //Check that hosts in the ClusterConfig is not empty + if len(cfg.Hosts) < 1 { + return nil, ErrNoHosts + } + + maxStreams := 128 + if cfg.ProtoVersion > protoVersion2 { + maxStreams = 32768 + } + + if cfg.NumStreams <= 0 || cfg.NumStreams > maxStreams { + cfg.NumStreams = maxStreams + } + + pool, err := cfg.ConnPoolType(&cfg) + if err != nil { + return nil, err + } + + //Adjust the size of the prepared statements cache to match the latest configuration + stmtsLRU.Lock() + initStmtsLRU(cfg.MaxPreparedStmts) + stmtsLRU.Unlock() + + s := &Session{ + Pool: pool, + cons: cfg.Consistency, + prefetch: 0.25, + cfg: cfg, + } + + //See if there are any connections in the pool + if pool.Size() > 0 { + s.routingKeyInfoCache.lru = lru.New(cfg.MaxRoutingKeyInfo) + + s.SetConsistency(cfg.Consistency) + s.SetPageSize(cfg.PageSize) + + if cfg.DiscoverHosts { + s.hostSource = &ringDescriber{ + session: s, + dcFilter: cfg.Discovery.DcFilter, + rackFilter: cfg.Discovery.RackFilter, + closeChan: make(chan bool), + } + + go s.hostSource.run(cfg.Discovery.Sleep) + } + + return s, nil + } + + s.Close() + + return nil, ErrNoConnectionsStarted +} + +// SetConsistency sets the default consistency level for this session. This +// setting can also be changed on a per-query basis and the default value +// is Quorum. +func (s *Session) SetConsistency(cons Consistency) { + s.mu.Lock() + s.cons = cons + s.mu.Unlock() +} + +// SetPageSize sets the default page size for this session. A value <= 0 will +// disable paging. This setting can also be changed on a per-query basis. +func (s *Session) SetPageSize(n int) { + s.mu.Lock() + s.pageSize = n + s.mu.Unlock() +} + +// SetPrefetch sets the default threshold for pre-fetching new pages. If +// there are only p*pageSize rows remaining, the next page will be requested +// automatically. This value can also be changed on a per-query basis and +// the default value is 0.25. +func (s *Session) SetPrefetch(p float64) { + s.mu.Lock() + s.prefetch = p + s.mu.Unlock() +} + +// SetTrace sets the default tracer for this session. This setting can also +// be changed on a per-query basis. +func (s *Session) SetTrace(trace Tracer) { + s.mu.Lock() + s.trace = trace + s.mu.Unlock() +} + +// Query generates a new query object for interacting with the database. +// Further details of the query may be tweaked using the resulting query +// value before the query is executed. Query is automatically prepared +// if it has not previously been executed. +func (s *Session) Query(stmt string, values ...interface{}) *Query { + s.mu.RLock() + qry := &Query{stmt: stmt, values: values, cons: s.cons, + session: s, pageSize: s.pageSize, trace: s.trace, + prefetch: s.prefetch, rt: s.cfg.RetryPolicy, serialCons: s.cfg.SerialConsistency, + defaultTimestamp: s.cfg.DefaultTimestamp, + } + s.mu.RUnlock() + return qry +} + +type QueryInfo struct { + Id []byte + Args []ColumnInfo + Rval []ColumnInfo +} + +// Bind generates a new query object based on the query statement passed in. +// The query is automatically prepared if it has not previously been executed. +// The binding callback allows the application to define which query argument +// values will be marshalled as part of the query execution. +// During execution, the meta data of the prepared query will be routed to the +// binding callback, which is responsible for producing the query argument values. +func (s *Session) Bind(stmt string, b func(q *QueryInfo) ([]interface{}, error)) *Query { + s.mu.RLock() + qry := &Query{stmt: stmt, binding: b, cons: s.cons, + session: s, pageSize: s.pageSize, trace: s.trace, + prefetch: s.prefetch, rt: s.cfg.RetryPolicy} + s.mu.RUnlock() + return qry +} + +// Close closes all connections. The session is unusable after this +// operation. +func (s *Session) Close() { + + s.closeMu.Lock() + defer s.closeMu.Unlock() + if s.isClosed { + return + } + s.isClosed = true + + s.Pool.Close() + + if s.hostSource != nil { + close(s.hostSource.closeChan) + } +} + +func (s *Session) Closed() bool { + s.closeMu.RLock() + closed := s.isClosed + s.closeMu.RUnlock() + return closed +} + +func (s *Session) executeQuery(qry *Query) *Iter { + + // fail fast + if s.Closed() { + return &Iter{err: ErrSessionClosed} + } + + var iter *Iter + qry.attempts = 0 + qry.totalLatency = 0 + for { + conn := s.Pool.Pick(qry) + + //Assign the error unavailable to the iterator + if conn == nil { + iter = &Iter{err: ErrNoConnections} + break + } + + t := time.Now() + iter = conn.executeQuery(qry) + qry.totalLatency += time.Now().Sub(t).Nanoseconds() + qry.attempts++ + + //Exit for loop if the query was successful + if iter.err == nil { + break + } + + if qry.rt == nil || !qry.rt.Attempt(qry) { + break + } + } + + return iter +} + +// KeyspaceMetadata returns the schema metadata for the keyspace specified. +func (s *Session) KeyspaceMetadata(keyspace string) (*KeyspaceMetadata, error) { + // fail fast + if s.Closed() { + return nil, ErrSessionClosed + } + + if keyspace == "" { + return nil, ErrNoKeyspace + } + + s.mu.Lock() + // lazy-init schemaDescriber + if s.schemaDescriber == nil { + s.schemaDescriber = newSchemaDescriber(s) + } + s.mu.Unlock() + + return s.schemaDescriber.getSchema(keyspace) +} + +// returns routing key indexes and type info +func (s *Session) routingKeyInfo(stmt string) (*routingKeyInfo, error) { + s.routingKeyInfoCache.mu.Lock() + cacheKey := s.cfg.Keyspace + stmt + + entry, cached := s.routingKeyInfoCache.lru.Get(cacheKey) + if cached { + // done accessing the cache + s.routingKeyInfoCache.mu.Unlock() + // the entry is an inflight struct similiar to that used by + // Conn to prepare statements + inflight := entry.(*inflightCachedEntry) + + // wait for any inflight work + inflight.wg.Wait() + + if inflight.err != nil { + return nil, inflight.err + } + + key, _ := inflight.value.(*routingKeyInfo) + + return key, nil + } + + // create a new inflight entry while the data is created + inflight := new(inflightCachedEntry) + inflight.wg.Add(1) + defer inflight.wg.Done() + s.routingKeyInfoCache.lru.Add(cacheKey, inflight) + s.routingKeyInfoCache.mu.Unlock() + + var ( + prepared *resultPreparedFrame + partitionKey []*ColumnMetadata + ) + + // get the query info for the statement + conn := s.Pool.Pick(nil) + if conn == nil { + // no connections + inflight.err = ErrNoConnections + // don't cache this error + s.routingKeyInfoCache.Remove(cacheKey) + return nil, inflight.err + } + + prepared, inflight.err = conn.prepareStatement(stmt, nil) + if inflight.err != nil { + // don't cache this error + s.routingKeyInfoCache.Remove(cacheKey) + return nil, inflight.err + } + + if len(prepared.reqMeta.columns) == 0 { + // no arguments, no routing key, and no error + return nil, nil + } + + // get the table metadata + table := prepared.reqMeta.columns[0].Table + + var keyspaceMetadata *KeyspaceMetadata + keyspaceMetadata, inflight.err = s.KeyspaceMetadata(s.cfg.Keyspace) + if inflight.err != nil { + // don't cache this error + s.routingKeyInfoCache.Remove(cacheKey) + return nil, inflight.err + } + + tableMetadata, found := keyspaceMetadata.Tables[table] + if !found { + // unlikely that the statement could be prepared and the metadata for + // the table couldn't be found, but this may indicate either a bug + // in the metadata code, or that the table was just dropped. + inflight.err = ErrNoMetadata + // don't cache this error + s.routingKeyInfoCache.Remove(cacheKey) + return nil, inflight.err + } + + partitionKey = tableMetadata.PartitionKey + + size := len(partitionKey) + routingKeyInfo := &routingKeyInfo{ + indexes: make([]int, size), + types: make([]TypeInfo, size), + } + for keyIndex, keyColumn := range partitionKey { + // set an indicator for checking if the mapping is missing + routingKeyInfo.indexes[keyIndex] = -1 + + // find the column in the query info + for argIndex, boundColumn := range prepared.reqMeta.columns { + if keyColumn.Name == boundColumn.Name { + // there may be many such bound columns, pick the first + routingKeyInfo.indexes[keyIndex] = argIndex + routingKeyInfo.types[keyIndex] = boundColumn.TypeInfo + break + } + } + + if routingKeyInfo.indexes[keyIndex] == -1 { + // missing a routing key column mapping + // no routing key, and no error + return nil, nil + } + } + + // cache this result + inflight.value = routingKeyInfo + + return routingKeyInfo, nil +} + +// ExecuteBatch executes a batch operation and returns nil if successful +// otherwise an error is returned describing the failure. +func (s *Session) ExecuteBatch(batch *Batch) error { + // fail fast + if s.Closed() { + return ErrSessionClosed + } + + // Prevent the execution of the batch if greater than the limit + // Currently batches have a limit of 65536 queries. + // https://datastax-oss.atlassian.net/browse/JAVA-229 + if batch.Size() > BatchSizeMaximum { + return ErrTooManyStmts + } + + var err error + batch.attempts = 0 + batch.totalLatency = 0 + for { + conn := s.Pool.Pick(nil) + + //Assign the error unavailable and break loop + if conn == nil { + err = ErrNoConnections + break + } + t := time.Now() + err = conn.executeBatch(batch) + batch.totalLatency += time.Now().Sub(t).Nanoseconds() + batch.attempts++ + //Exit loop if operation executed correctly + if err == nil { + return nil + } + + if batch.rt == nil || !batch.rt.Attempt(batch) { + break + } + } + + return err +} + +// Query represents a CQL statement that can be executed. +type Query struct { + stmt string + values []interface{} + cons Consistency + pageSize int + routingKey []byte + pageState []byte + prefetch float64 + trace Tracer + session *Session + rt RetryPolicy + binding func(q *QueryInfo) ([]interface{}, error) + attempts int + totalLatency int64 + serialCons SerialConsistency + defaultTimestamp bool +} + +//Attempts returns the number of times the query was executed. +func (q *Query) Attempts() int { + return q.attempts +} + +//Latency returns the average amount of nanoseconds per attempt of the query. +func (q *Query) Latency() int64 { + if q.attempts > 0 { + return q.totalLatency / int64(q.attempts) + } + return 0 +} + +// Consistency sets the consistency level for this query. If no consistency +// level have been set, the default consistency level of the cluster +// is used. +func (q *Query) Consistency(c Consistency) *Query { + q.cons = c + return q +} + +// GetConsistency returns the currently configured consistency level for +// the query. +func (q *Query) GetConsistency() Consistency { + return q.cons +} + +// Trace enables tracing of this query. Look at the documentation of the +// Tracer interface to learn more about tracing. +func (q *Query) Trace(trace Tracer) *Query { + q.trace = trace + return q +} + +// PageSize will tell the iterator to fetch the result in pages of size n. +// This is useful for iterating over large result sets, but setting the +// page size to low might decrease the performance. This feature is only +// available in Cassandra 2 and onwards. +func (q *Query) PageSize(n int) *Query { + q.pageSize = n + return q +} + +// DefaultTimestamp will enable the with default timestamp flag on the query. +// If enable, this will replace the server side assigned +// timestamp as default timestamp. Note that a timestamp in the query itself +// will still override this timestamp. This is entirely optional. +// +// Only available on protocol >= 3 +func (q *Query) DefaultTimestamp(enable bool) *Query { + q.defaultTimestamp = enable + return q +} + +// RoutingKey sets the routing key to use when a token aware connection +// pool is used to optimize the routing of this query. +func (q *Query) RoutingKey(routingKey []byte) *Query { + q.routingKey = routingKey + return q +} + +// GetRoutingKey gets the routing key to use for routing this query. If +// a routing key has not been explicitly set, then the routing key will +// be constructed if possible using the keyspace's schema and the query +// info for this query statement. If the routing key cannot be determined +// then nil will be returned with no error. On any error condition, +// an error description will be returned. +func (q *Query) GetRoutingKey() ([]byte, error) { + if q.routingKey != nil { + return q.routingKey, nil + } + + // try to determine the routing key + routingKeyInfo, err := q.session.routingKeyInfo(q.stmt) + if err != nil { + return nil, err + } + if routingKeyInfo == nil { + return nil, nil + } + + if len(routingKeyInfo.indexes) == 1 { + // single column routing key + routingKey, err := Marshal( + routingKeyInfo.types[0], + q.values[routingKeyInfo.indexes[0]], + ) + if err != nil { + return nil, err + } + return routingKey, nil + } + + // composite routing key + buf := &bytes.Buffer{} + for i := range routingKeyInfo.indexes { + encoded, err := Marshal( + routingKeyInfo.types[i], + q.values[routingKeyInfo.indexes[i]], + ) + if err != nil { + return nil, err + } + binary.Write(buf, binary.BigEndian, int16(len(encoded))) + buf.Write(encoded) + buf.WriteByte(0x00) + } + routingKey := buf.Bytes() + return routingKey, nil +} + +func (q *Query) shouldPrepare() bool { + + stmt := strings.TrimLeftFunc(strings.TrimRightFunc(q.stmt, func(r rune) bool { + return unicode.IsSpace(r) || r == ';' + }), unicode.IsSpace) + + var stmtType string + if n := strings.IndexFunc(stmt, unicode.IsSpace); n >= 0 { + stmtType = strings.ToLower(stmt[:n]) + } + if stmtType == "begin" { + if n := strings.LastIndexFunc(stmt, unicode.IsSpace); n >= 0 { + stmtType = strings.ToLower(stmt[n+1:]) + } + } + switch stmtType { + case "select", "insert", "update", "delete", "batch": + return true + } + return false +} + +// SetPrefetch sets the default threshold for pre-fetching new pages. If +// there are only p*pageSize rows remaining, the next page will be requested +// automatically. +func (q *Query) Prefetch(p float64) *Query { + q.prefetch = p + return q +} + +// RetryPolicy sets the policy to use when retrying the query. +func (q *Query) RetryPolicy(r RetryPolicy) *Query { + q.rt = r + return q +} + +// Bind sets query arguments of query. This can also be used to rebind new query arguments +// to an existing query instance. +func (q *Query) Bind(v ...interface{}) *Query { + q.values = v + return q +} + +// SerialConsistency sets the consistencyc level for the +// serial phase of conditional updates. That consitency can only be +// either SERIAL or LOCAL_SERIAL and if not present, it defaults to +// SERIAL. This option will be ignored for anything else that a +// conditional update/insert. +func (q *Query) SerialConsistency(cons SerialConsistency) *Query { + q.serialCons = cons + return q +} + +// Exec executes the query without returning any rows. +func (q *Query) Exec() error { + iter := q.Iter() + return iter.err +} + +// Iter executes the query and returns an iterator capable of iterating +// over all results. +func (q *Query) Iter() *Iter { + if strings.Index(strings.ToLower(q.stmt), "use") == 0 { + return &Iter{err: ErrUseStmt} + } + return q.session.executeQuery(q) +} + +// MapScan executes the query, copies the columns of the first selected +// row into the map pointed at by m and discards the rest. If no rows +// were selected, ErrNotFound is returned. +func (q *Query) MapScan(m map[string]interface{}) error { + iter := q.Iter() + if err := iter.checkErrAndNotFound(); err != nil { + return err + } + iter.MapScan(m) + return iter.Close() +} + +// Scan executes the query, copies the columns of the first selected +// row into the values pointed at by dest and discards the rest. If no rows +// were selected, ErrNotFound is returned. +func (q *Query) Scan(dest ...interface{}) error { + iter := q.Iter() + if err := iter.checkErrAndNotFound(); err != nil { + return err + } + iter.Scan(dest...) + return iter.Close() +} + +// ScanCAS executes a lightweight transaction (i.e. an UPDATE or INSERT +// statement containing an IF clause). If the transaction fails because +// the existing values did not match, the previous values will be stored +// in dest. +func (q *Query) ScanCAS(dest ...interface{}) (applied bool, err error) { + iter := q.Iter() + if err := iter.checkErrAndNotFound(); err != nil { + return false, err + } + if len(iter.Columns()) > 1 { + dest = append([]interface{}{&applied}, dest...) + iter.Scan(dest...) + } else { + iter.Scan(&applied) + } + return applied, iter.Close() +} + +// MapScanCAS executes a lightweight transaction (i.e. an UPDATE or INSERT +// statement containing an IF clause). If the transaction fails because +// the existing values did not match, the previous values will be stored +// in dest map. +// +// As for INSERT .. IF NOT EXISTS, previous values will be returned as if +// SELECT * FROM. So using ScanCAS with INSERT is inherently prone to +// column mismatching. MapScanCAS is added to capture them safely. +func (q *Query) MapScanCAS(dest map[string]interface{}) (applied bool, err error) { + iter := q.Iter() + if err := iter.checkErrAndNotFound(); err != nil { + return false, err + } + iter.MapScan(dest) + applied = dest["[applied]"].(bool) + delete(dest, "[applied]") + + return applied, iter.Close() +} + +// Iter represents an iterator that can be used to iterate over all rows that +// were returned by a query. The iterator might send additional queries to the +// database during the iteration if paging was enabled. +type Iter struct { + err error + pos int + rows [][][]byte + meta resultMetadata + next *nextIter +} + +// Columns returns the name and type of the selected columns. +func (iter *Iter) Columns() []ColumnInfo { + return iter.meta.columns +} + +// Scan consumes the next row of the iterator and copies the columns of the +// current row into the values pointed at by dest. Use nil as a dest value +// to skip the corresponding column. Scan might send additional queries +// to the database to retrieve the next set of rows if paging was enabled. +// +// Scan returns true if the row was successfully unmarshaled or false if the +// end of the result set was reached or if an error occurred. Close should +// be called afterwards to retrieve any potential errors. +func (iter *Iter) Scan(dest ...interface{}) bool { + if iter.err != nil { + return false + } + if iter.pos >= len(iter.rows) { + if iter.next != nil { + *iter = *iter.next.fetch() + return iter.Scan(dest...) + } + return false + } + if iter.next != nil && iter.pos == iter.next.pos { + go iter.next.fetch() + } + + // currently only support scanning into an expand tuple, such that its the same + // as scanning in more values from a single column + if len(dest) != iter.meta.actualColCount { + iter.err = errors.New("count mismatch") + return false + } + + // i is the current position in dest, could posible replace it and just use + // slices of dest + i := 0 + for c, col := range iter.meta.columns { + if dest[i] == nil { + i++ + continue + } + + switch col.TypeInfo.Type() { + case TypeTuple: + // this will panic, actually a bug, please report + tuple := col.TypeInfo.(TupleTypeInfo) + + count := len(tuple.Elems) + // here we pass in a slice of the struct which has the number number of + // values as elements in the tuple + iter.err = Unmarshal(col.TypeInfo, iter.rows[iter.pos][c], dest[i:i+count]) + i += count + default: + iter.err = Unmarshal(col.TypeInfo, iter.rows[iter.pos][c], dest[i]) + i++ + } + + if iter.err != nil { + return false + } + } + + iter.pos++ + return true +} + +// Close closes the iterator and returns any errors that happened during +// the query or the iteration. +func (iter *Iter) Close() error { + return iter.err +} + +// checkErrAndNotFound handle error and NotFound in one method. +func (iter *Iter) checkErrAndNotFound() error { + if iter.err != nil { + return iter.err + } else if len(iter.rows) == 0 { + return ErrNotFound + } + return nil +} + +type nextIter struct { + qry Query + pos int + once sync.Once + next *Iter +} + +func (n *nextIter) fetch() *Iter { + n.once.Do(func() { + n.next = n.qry.session.executeQuery(&n.qry) + }) + return n.next +} + +type Batch struct { + Type BatchType + Entries []BatchEntry + Cons Consistency + rt RetryPolicy + attempts int + totalLatency int64 + serialCons SerialConsistency + defaultTimestamp bool +} + +// NewBatch creates a new batch operation without defaults from the cluster +func NewBatch(typ BatchType) *Batch { + return &Batch{Type: typ} +} + +// NewBatch creates a new batch operation using defaults defined in the cluster +func (s *Session) NewBatch(typ BatchType) *Batch { + s.mu.RLock() + batch := &Batch{Type: typ, rt: s.cfg.RetryPolicy, serialCons: s.cfg.SerialConsistency, + Cons: s.cons, defaultTimestamp: s.cfg.DefaultTimestamp} + s.mu.RUnlock() + return batch +} + +// Attempts returns the number of attempts made to execute the batch. +func (b *Batch) Attempts() int { + return b.attempts +} + +//Latency returns the average number of nanoseconds to execute a single attempt of the batch. +func (b *Batch) Latency() int64 { + if b.attempts > 0 { + return b.totalLatency / int64(b.attempts) + } + return 0 +} + +// GetConsistency returns the currently configured consistency level for the batch +// operation. +func (b *Batch) GetConsistency() Consistency { + return b.Cons +} + +// Query adds the query to the batch operation +func (b *Batch) Query(stmt string, args ...interface{}) { + b.Entries = append(b.Entries, BatchEntry{Stmt: stmt, Args: args}) +} + +// Bind adds the query to the batch operation and correlates it with a binding callback +// that will be invoked when the batch is executed. The binding callback allows the application +// to define which query argument values will be marshalled as part of the batch execution. +func (b *Batch) Bind(stmt string, bind func(q *QueryInfo) ([]interface{}, error)) { + b.Entries = append(b.Entries, BatchEntry{Stmt: stmt, binding: bind}) +} + +// RetryPolicy sets the retry policy to use when executing the batch operation +func (b *Batch) RetryPolicy(r RetryPolicy) *Batch { + b.rt = r + return b +} + +// Size returns the number of batch statements to be executed by the batch operation. +func (b *Batch) Size() int { + return len(b.Entries) +} + +// SerialConsistency sets the consistencyc level for the +// serial phase of conditional updates. That consitency can only be +// either SERIAL or LOCAL_SERIAL and if not present, it defaults to +// SERIAL. This option will be ignored for anything else that a +// conditional update/insert. +// +// Only available for protocol 3 and above +func (b *Batch) SerialConsistency(cons SerialConsistency) *Batch { + b.serialCons = cons + return b +} + +// DefaultTimestamp will enable the with default timestamp flag on the query. +// If enable, this will replace the server side assigned +// timestamp as default timestamp. Note that a timestamp in the query itself +// will still override this timestamp. This is entirely optional. +// +// Only available on protocol >= 3 +func (b *Batch) DefaultTimestamp(enable bool) *Batch { + b.defaultTimestamp = enable + return b +} + +type BatchType byte + +const ( + LoggedBatch BatchType = 0 + UnloggedBatch = 1 + CounterBatch = 2 +) + +type BatchEntry struct { + Stmt string + Args []interface{} + binding func(q *QueryInfo) ([]interface{}, error) +} + +type ColumnInfo struct { + Keyspace string + Table string + Name string + TypeInfo TypeInfo +} + +func (c ColumnInfo) String() string { + return fmt.Sprintf("[column keyspace=%s table=%s name=%s type=%v]", c.Keyspace, c.Table, c.Name, c.TypeInfo) +} + +// routing key indexes LRU cache +type routingKeyInfoLRU struct { + lru *lru.Cache + mu sync.Mutex +} + +type routingKeyInfo struct { + indexes []int + types []TypeInfo +} + +func (r *routingKeyInfoLRU) Remove(key string) { + r.mu.Lock() + r.lru.Remove(key) + r.mu.Unlock() +} + +//Max adjusts the maximum size of the cache and cleans up the oldest records if +//the new max is lower than the previous value. Not concurrency safe. +func (r *routingKeyInfoLRU) Max(max int) { + r.mu.Lock() + for r.lru.Len() > max { + r.lru.RemoveOldest() + } + r.lru.MaxEntries = max + r.mu.Unlock() +} + +type inflightCachedEntry struct { + wg sync.WaitGroup + err error + value interface{} +} + +// Tracer is the interface implemented by query tracers. Tracers have the +// ability to obtain a detailed event log of all events that happened during +// the execution of a query from Cassandra. Gathering this information might +// be essential for debugging and optimizing queries, but this feature should +// not be used on production systems with very high load. +type Tracer interface { + Trace(traceId []byte) +} + +type traceWriter struct { + session *Session + w io.Writer + mu sync.Mutex +} + +// NewTraceWriter returns a simple Tracer implementation that outputs +// the event log in a textual format. +func NewTraceWriter(session *Session, w io.Writer) Tracer { + return &traceWriter{session: session, w: w} +} + +func (t *traceWriter) Trace(traceId []byte) { + var ( + coordinator string + duration int + ) + t.session.Query(`SELECT coordinator, duration + FROM system_traces.sessions + WHERE session_id = ?`, traceId). + Consistency(One).Scan(&coordinator, &duration) + + iter := t.session.Query(`SELECT event_id, activity, source, source_elapsed + FROM system_traces.events + WHERE session_id = ?`, traceId). + Consistency(One).Iter() + var ( + timestamp time.Time + activity string + source string + elapsed int + ) + t.mu.Lock() + defer t.mu.Unlock() + fmt.Fprintf(t.w, "Tracing session %016x (coordinator: %s, duration: %v):\n", + traceId, coordinator, time.Duration(duration)*time.Microsecond) + for iter.Scan(×tamp, &activity, &source, &elapsed) { + fmt.Fprintf(t.w, "%s: %s (source: %s, elapsed: %d)\n", + timestamp.Format("2006/01/02 15:04:05.999999"), activity, source, elapsed) + } + if err := iter.Close(); err != nil { + fmt.Fprintln(t.w, "Error:", err) + } +} + +type Error struct { + Code int + Message string +} + +func (e Error) Error() string { + return e.Message +} + +var ( + ErrNotFound = errors.New("not found") + ErrUnavailable = errors.New("unavailable") + ErrUnsupported = errors.New("feature not supported") + ErrTooManyStmts = errors.New("too many statements") + ErrUseStmt = errors.New("use statements aren't supported. Please see https://github.com/gocql/gocql for explaination.") + ErrSessionClosed = errors.New("session has been closed") + ErrNoConnections = errors.New("no connections available") + ErrNoKeyspace = errors.New("no keyspace provided") + ErrNoMetadata = errors.New("no metadata available") +) + +type ErrProtocol struct{ error } + +func NewErrProtocol(format string, args ...interface{}) error { + return ErrProtocol{fmt.Errorf(format, args...)} +} + +// BatchSizeMaximum is the maximum number of statements a batch operation can have. +// This limit is set by cassandra and could change in the future. +const BatchSizeMaximum = 65535 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/session_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/session_test.go new file mode 100644 index 000000000..fa41b6689 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/session_test.go @@ -0,0 +1,255 @@ +// +build all integration + +package gocql + +import ( + "fmt" + "testing" +) + +func TestSessionAPI(t *testing.T) { + + cfg := &ClusterConfig{} + pool, err := NewSimplePool(cfg) + if err != nil { + t.Fatal(err) + } + + s := &Session{ + Pool: pool, + cfg: *cfg, + cons: Quorum, + } + + defer s.Close() + + s.SetConsistency(All) + if s.cons != All { + t.Fatalf("expected consistency 'All', got '%v'", s.cons) + } + + s.SetPageSize(100) + if s.pageSize != 100 { + t.Fatalf("expected pageSize 100, got %v", s.pageSize) + } + + s.SetPrefetch(0.75) + if s.prefetch != 0.75 { + t.Fatalf("expceted prefetch 0.75, got %v", s.prefetch) + } + + trace := &traceWriter{} + + s.SetTrace(trace) + if s.trace != trace { + t.Fatalf("expected traceWriter '%v',got '%v'", trace, s.trace) + } + + qry := s.Query("test", 1) + if v, ok := qry.values[0].(int); !ok { + t.Fatalf("expected qry.values[0] to be an int, got %v", qry.values[0]) + } else if v != 1 { + t.Fatalf("expceted qry.values[0] to be 1, got %v", v) + } else if qry.stmt != "test" { + t.Fatalf("expected qry.stmt to be 'test', got '%v'", qry.stmt) + } + + boundQry := s.Bind("test", func(q *QueryInfo) ([]interface{}, error) { + return nil, nil + }) + if boundQry.binding == nil { + t.Fatal("expected qry.binding to be defined, got nil") + } else if boundQry.stmt != "test" { + t.Fatalf("expected qry.stmt to be 'test', got '%v'", boundQry.stmt) + } + + itr := s.executeQuery(qry) + if itr.err != ErrNoConnections { + t.Fatalf("expected itr.err to be '%v', got '%v'", ErrNoConnections, itr.err) + } + + testBatch := s.NewBatch(LoggedBatch) + testBatch.Query("test") + err = s.ExecuteBatch(testBatch) + + if err != ErrNoConnections { + t.Fatalf("expected session.ExecuteBatch to return '%v', got '%v'", ErrNoConnections, err) + } + + s.Close() + if !s.Closed() { + t.Fatal("expected s.Closed() to be true, got false") + } + //Should just return cleanly + s.Close() + + err = s.ExecuteBatch(testBatch) + if err != ErrSessionClosed { + t.Fatalf("expected session.ExecuteBatch to return '%v', got '%v'", ErrSessionClosed, err) + } +} + +func TestQueryBasicAPI(t *testing.T) { + qry := &Query{} + + if qry.Latency() != 0 { + t.Fatalf("expected Query.Latency() to return 0, got %v", qry.Latency()) + } + + qry.attempts = 2 + qry.totalLatency = 4 + if qry.Attempts() != 2 { + t.Fatalf("expected Query.Attempts() to return 2, got %v", qry.Attempts()) + } + if qry.Latency() != 2 { + t.Fatalf("expected Query.Latency() to return 2, got %v", qry.Latency()) + } + + qry.Consistency(All) + if qry.GetConsistency() != All { + t.Fatalf("expected Query.GetConsistency to return 'All', got '%s'", qry.GetConsistency()) + } + + trace := &traceWriter{} + qry.Trace(trace) + if qry.trace != trace { + t.Fatalf("expected Query.Trace to be '%v', got '%v'", trace, qry.trace) + } + + qry.PageSize(10) + if qry.pageSize != 10 { + t.Fatalf("expected Query.PageSize to be 10, got %v", qry.pageSize) + } + + qry.Prefetch(0.75) + if qry.prefetch != 0.75 { + t.Fatalf("expected Query.Prefetch to be 0.75, got %v", qry.prefetch) + } + + rt := &SimpleRetryPolicy{NumRetries: 3} + if qry.RetryPolicy(rt); qry.rt != rt { + t.Fatalf("expected Query.RetryPolicy to be '%v', got '%v'", rt, qry.rt) + } + + qry.Bind(qry) + if qry.values[0] != qry { + t.Fatalf("expected Query.Values[0] to be '%v', got '%v'", qry, qry.values[0]) + } +} + +func TestQueryShouldPrepare(t *testing.T) { + toPrepare := []string{"select * ", "INSERT INTO", "update table", "delete from", "begin batch"} + cantPrepare := []string{"create table", "USE table", "LIST keyspaces", "alter table", "drop table", "grant user", "revoke user"} + q := &Query{} + + for i := 0; i < len(toPrepare); i++ { + q.stmt = toPrepare[i] + if !q.shouldPrepare() { + t.Fatalf("expected Query.shouldPrepare to return true, got false for statement '%v'", toPrepare[i]) + } + } + + for i := 0; i < len(cantPrepare); i++ { + q.stmt = cantPrepare[i] + if q.shouldPrepare() { + t.Fatalf("expected Query.shouldPrepare to return false, got true for statement '%v'", cantPrepare[i]) + } + } +} + +func TestBatchBasicAPI(t *testing.T) { + + cfg := &ClusterConfig{RetryPolicy: &SimpleRetryPolicy{NumRetries: 2}} + pool, err := NewSimplePool(cfg) + if err != nil { + t.Fatal(err) + } + + s := &Session{ + Pool: pool, + cfg: *cfg, + cons: Quorum, + } + defer s.Close() + + b := s.NewBatch(UnloggedBatch) + if b.Type != UnloggedBatch { + t.Fatalf("expceted batch.Type to be '%v', got '%v'", UnloggedBatch, b.Type) + } else if b.rt != cfg.RetryPolicy { + t.Fatalf("expceted batch.RetryPolicy to be '%v', got '%v'", cfg.RetryPolicy, b.rt) + } + + b = NewBatch(LoggedBatch) + if b.Type != LoggedBatch { + t.Fatalf("expected batch.Type to be '%v', got '%v'", LoggedBatch, b.Type) + } + + b.attempts = 1 + if b.Attempts() != 1 { + t.Fatalf("expceted batch.Attempts() to return %v, got %v", 1, b.Attempts()) + } + + if b.Latency() != 0 { + t.Fatalf("expected batch.Latency() to be 0, got %v", b.Latency()) + } + + b.totalLatency = 4 + if b.Latency() != 4 { + t.Fatalf("expected batch.Latency() to return %v, got %v", 4, b.Latency()) + } + + b.Cons = One + if b.GetConsistency() != One { + t.Fatalf("expected batch.GetConsistency() to return 'One', got '%s'", b.GetConsistency()) + } + + b.Query("test", 1) + if b.Entries[0].Stmt != "test" { + t.Fatalf("expected batch.Entries[0].Stmt to be 'test', got '%v'", b.Entries[0].Stmt) + } else if b.Entries[0].Args[0].(int) != 1 { + t.Fatalf("expected batch.Entries[0].Args[0] to be 1, got %v", b.Entries[0].Args[0]) + } + + b.Bind("test2", func(q *QueryInfo) ([]interface{}, error) { + return nil, nil + }) + + if b.Entries[1].Stmt != "test2" { + t.Fatalf("expected batch.Entries[1].Stmt to be 'test2', got '%v'", b.Entries[1].Stmt) + } else if b.Entries[1].binding == nil { + t.Fatal("expected batch.Entries[1].binding to be defined, got nil") + } + r := &SimpleRetryPolicy{NumRetries: 4} + + b.RetryPolicy(r) + if b.rt != r { + t.Fatalf("expected batch.RetryPolicy to be '%v', got '%v'", r, b.rt) + } + + if b.Size() != 2 { + t.Fatalf("expected batch.Size() to return 2, got %v", b.Size()) + } + +} + +func TestConsistencyNames(t *testing.T) { + names := map[fmt.Stringer]string{ + Any: "ANY", + One: "ONE", + Two: "TWO", + Three: "THREE", + Quorum: "QUORUM", + All: "ALL", + LocalQuorum: "LOCAL_QUORUM", + EachQuorum: "EACH_QUORUM", + Serial: "SERIAL", + LocalSerial: "LOCAL_SERIAL", + LocalOne: "LOCAL_ONE", + } + + for k, v := range names { + if k.String() != v { + t.Fatalf("expected '%v', got '%v'", v, k.String()) + } + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.keystore b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.keystore new file mode 100644 index 000000000..0bbf1929f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.keystore @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9d7522bb1c517241b555d66e3ba3e98922309c6378e625e991ee69372c97b7e +size 2178 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.truststore b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.truststore new file mode 100644 index 000000000..e7f20ba11 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.truststore @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02b50c1f6bb4f5a6fb013c2c9ebe47ecd08db683dca48045f08e9e9ca98d8963 +size 882 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.crt b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.crt new file mode 100644 index 000000000..fc3ba63f2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgIJAIKbAXgemwsjMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV +BAMTCWNhc3NhbmRyYTAeFw0xNDA5MTkyMTE4MTNaFw0yNDA5MTYyMTE4MTNaMBQx +EjAQBgNVBAMTCWNhc3NhbmRyYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5fX0l1WDNa+mO1krxw7k8lfUQn+Ec4L3Mqv6IstGoNdCPq4YRA+SXRD5YC +k/UXrFBWh9Hbs849GiuTYMPdj9HDLYz40RaQjM9GbieS23iy3UStQ0tKhxaaG6FN +6XBypXFKCTsanu0TkEoDGhAkSzAMcCAC3gkFBzMrZ5qt4HEzjY9rasZ2gthN+xop +nq3t4dDkE8HGaiFJcFvqTor7xmrnAaPjrPzUpvOF/ObIC09omwg/KXdPRx4DKPon +gCMKEE3ckebKnJvbsRX3WO8H5nTHBYZ6v1JxLZz5pqmV+P0NGxldCARM0gCQUBz5 +wjMJkD/3e1ETC+q6uwfnAG0hlD8CAwEAAaOBgzCBgDAdBgNVHQ4EFgQUjHzn0nYF +iXEaI1vUWbRR4lwKXOgwRAYDVR0jBD0wO4AUjHzn0nYFiXEaI1vUWbRR4lwKXOih +GKQWMBQxEjAQBgNVBAMTCWNhc3NhbmRyYYIJAIKbAXgemwsjMAwGA1UdEwQFMAMB +Af8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBCYDdIhtf/Y12Et947 +am1B8TzSX+/iQ1V1J3JtvgD5F4fvNjfArat/I3D277WREUTAc76o16BCp2OBGqzO +zf9MvZPkjkAUoyU0TtPUEHyqxq4gZxbWKugIZGYkmQ1hCvSIgA5UnjRL3dylMmZb +Y33JJA2QY63FZwnhmWsM8FYZwh+8MzVCQx3mgXC/k/jS6OuYyIT/KjxQHHjyr5ZS +zAAQln1IcZycLfh1w5MtCFahCIethFcVDnWUWYPcPGDGgMJW7WBpNZdHbLxYY8cI +eCc3Hcrbdc/CG5CaLJeqUidBayjnlUIO/NNgglkJ1KhQzkM6bd+37e0AX1hLIqx7 +gIZR +-----END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.key b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.key new file mode 100644 index 000000000..4360c17a2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,54C8072C0FF3B3A3 + +27eijmHdgB+s3beNPmU0+iz+muxMD0BVvWkDzyec/uawMv/Cn4c3mYXOcsFxS3BL ++qLT9MEttOmjqhHSaVrDYOPKoJIMpn+bVeKiR08V89icO36shEPy1feGqanagKtw +ecgzFDBTA8ZbqjAhftXlhTwxADebvNms/2aDh5Aw04vIcbo8nQ/8z1Wz8O7Firsn +kaseSTMTC6lxc+pa2V1X6mN0/2UpDi55bZbx1Z/mQ3+1CsdHOx0p7m/KY2m3ysov +XluaC0sqmzHkcwNgDhUs3Jh+apE33vXzLGU+W4BDOwrYJiL6KpspZW/mJj3OEx8B +8xdAZU3a/ei8NUA/lDStGmcYX+dOysExwJ6GMrCBm9iufZiefDQCQ8yRqWnr6Zop +lsFd+CqHNWYxfWDI1pSUBw3bsgIjevI0f0B7PxkFEF0DmIhCgB324/uqToRzGsOF +4MSVg6cSK7Sjo/u3r8r75A3aUAcY8NbR3peiZfAPMsTiUcfp4DoU+MJTqkX5PyQq +FNxHOJoARZqjjQ2IhZiUQWfIINHvZ8F9G2K7VaES8A0EATyUghqaRyeLbyI3IYdW +pGZBzrpGtdFlk9AVetHDDlY+gQiurtYhxOsxvlxJJuTj8FV+A5NWSElfPele0OiR +iprE3xkFSk3whHu5L1vnzamvdSlnBWOAE7pQD7kQA6NmcEw/tqnXK0dVdAw8RIFh +4BKgv0sNrXzBgnzE8+bKLUf1a2Byc/YKuBrI7EpSZ9/VHYvOcgmOxNxMmRS6NYd1 +Ly+agQn0AyvsDmSlBZBp8GCzVp6JYBMDKSXyPVN8+wjK9OQM0PZdEdXouMwPCOVN +oNSjhmMtfjOsnG2SZ9tRas3p0qFdfh/N/E6Q7QHG3WD3cUIEweFV9ji1FTSRUrIa +shuKug8MUfNjvDJNMsdGyf6Hi/7Iik++42Rq3ZdTy0ZVkj5snv5yBN77pr2M/J4b +M+dsXjyXPO4SDW3kP/e3RnLRlWmUv1PNdOmNDdjBBUTKgVZ3ur+4HmSY1iDvhlUF +/hz2tz3/XUKQwYuv3KJVlBhLrniXeES36GK+JQadIszrjwb5N4q4p6xrIdIR7XgR +TJCSL1NGPLeQyjK6byWLNPRcCGrvnxWs0k0ev6trMRJL1EjsIFDCJam9szhcXkZP +iYl1d7ZMKPS3cAqCjdaFRSe65cZ+qI/cqxiv122orq/jkDY7ZSA9rWywY4YnYQ7A +BqvcPzC/6K0bteXqmMQkIy/84aSnEts6ecb/4s5e5xXLhHe0dchG0HkasC/Gb+v/ +m9NOqACTerWvSD+Ecv9OvnBjP+GTlA1g7xTiRANLXsTJuiJomtxewXcV6kGZEMmZ +QWerGtPJGGUx36WRWrMiPeBfWZoIbjYGPmOO5mYNXMTjABGGWcFnKAqWUKsFihi9 +pC0OpZ7A0dtc9uSm0ZmsHUc3XENMHTeeEN+qgWxVKcMzRKEcnapu/0OcHrOUHDZf +qPoG4EkNnG9kPMq3HzvFPx3qbQ017yl87vAkWy/Edo+ojfHoNghRBVGCw1zt/BMN +eJbFFHop+rQ87omz8WIL4K+zVf91rJ0REVAJssQVDo16O5wrMo+f+c8v2GANQks5 +-----END RSA PRIVATE KEY----- diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.crt b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.crt new file mode 100644 index 000000000..9d2facf9d --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.crt @@ -0,0 +1,83 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=cassandra + Validity + Not Before: Sep 19 21:18:48 2014 GMT + Not After : Sep 16 21:18:48 2024 GMT + Subject: CN=cassandra + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:e5:9c:20:9e:de:98:73:44:41:0d:37:4c:62:c3: + 9f:87:5f:9b:4f:aa:cf:f6:90:6e:a5:e0:89:88:7a: + 00:c6:bb:d7:80:87:69:2e:fa:f0:35:59:80:6e:82: + 25:c8:b3:6c:f6:a4:97:97:93:93:ea:f0:70:70:a4: + e1:b7:aa:da:c1:99:66:9b:93:04:3a:ce:0b:83:07: + 06:22:3d:a6:db:7f:68:0f:49:80:bd:86:a8:bb:54: + 6d:38:5f:0f:b0:fa:1b:97:24:ae:cc:9d:37:98:7e: + 76:cc:e3:1b:45:1b:21:25:17:02:c0:1a:c5:fb:76: + c3:8b:93:d7:c5:85:14:0a:5c:a4:12:e7:18:69:98: + f5:76:cd:78:cd:99:5a:29:65:f1:68:20:97:d3:be: + 09:b3:68:1b:f2:a3:a2:9a:73:58:53:7e:ed:86:32: + a3:5a:d5:46:03:f9:b3:b4:ec:63:71:ba:bb:fb:6f: + f9:82:63:e4:55:47:7a:7a:e4:7b:17:6b:d7:e6:cf: + 3b:c9:ab:0c:30:15:c9:ed:c7:d6:fc:b6:72:b2:14: + 7d:c7:f3:7f:8a:f4:63:70:64:8e:0f:db:e8:3a:45: + 47:cd:b9:7b:ae:c8:31:c1:52:d1:3e:34:12:b7:73: + e7:ba:89:86:9a:36:ed:a0:5a:69:d0:d4:e3:b6:16: + 85:af + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 4A:D3:EC:63:07:E0:8F:1A:4E:F5:09:43:90:9F:7A:C5:31:D1:8F:D8 + X509v3 Authority Key Identifier: + keyid:8C:7C:E7:D2:76:05:89:71:1A:23:5B:D4:59:B4:51:E2:5C:0A:5C:E8 + DirName:/CN=cassandra + serial:82:9B:01:78:1E:9B:0B:23 + + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Key Usage: + Digital Signature, Key Encipherment + Signature Algorithm: sha256WithRSAEncryption + ac:bc:80:82:2d:6d:f1:a0:46:eb:00:05:d2:25:9a:83:66:57: + 40:51:6e:ff:db:e3:28:04:7b:16:63:74:ec:55:a0:c0:5b:47: + 13:e1:5a:a5:6d:22:d0:e5:fe:c1:51:e8:f6:c6:9c:f9:be:b7: + be:82:14:e4:a0:b2:0b:9f:ee:68:bc:ac:17:0d:13:50:c6:9e: + 52:91:8c:a0:98:db:4e:2d:f6:3d:6e:85:0a:bb:b9:dd:01:bf: + ad:52:dd:6e:e4:41:01:a5:93:58:dd:3f:cf:bf:15:e6:25:aa: + a0:4f:98:0d:75:8a:3f:5b:ba:67:37:f6:b1:0b:3f:21:34:97: + 50:9a:85:97:2b:b6:05:41:9a:f3:cf:c4:92:23:06:ab:3e:87: + 98:30:eb:cb:d3:83:ab:04:7d:5c:b9:f0:12:d1:43:b3:c5:7d: + 33:9a:2e:2b:80:3a:66:be:f1:8c:08:37:7a:93:9c:9b:60:60: + 53:71:16:70:86:df:ca:5f:a9:0b:e2:8b:3d:af:02:62:3b:61: + 30:da:53:89:e3:d8:0b:88:04:9a:93:6a:f6:28:f8:dd:0d:8f: + 0c:82:5b:c0:e5:f8:0d:ad:06:76:a7:3b:4b:ae:54:37:25:15: + f5:0c:67:0f:77:c5:c4:97:68:09:c3:02:a7:a0:46:10:1c:d1: + 95:3a:4c:94 +-----BEGIN CERTIFICATE----- +MIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwljYXNz +YW5kcmEwHhcNMTQwOTE5MjExODQ4WhcNMjQwOTE2MjExODQ4WjAUMRIwEAYDVQQD +EwljYXNzYW5kcmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlnCCe +3phzREENN0xiw5+HX5tPqs/2kG6l4ImIegDGu9eAh2ku+vA1WYBugiXIs2z2pJeX +k5Pq8HBwpOG3qtrBmWabkwQ6zguDBwYiPabbf2gPSYC9hqi7VG04Xw+w+huXJK7M +nTeYfnbM4xtFGyElFwLAGsX7dsOLk9fFhRQKXKQS5xhpmPV2zXjNmVopZfFoIJfT +vgmzaBvyo6Kac1hTfu2GMqNa1UYD+bO07GNxurv7b/mCY+RVR3p65HsXa9fmzzvJ +qwwwFcntx9b8tnKyFH3H83+K9GNwZI4P2+g6RUfNuXuuyDHBUtE+NBK3c+e6iYaa +Nu2gWmnQ1OO2FoWvAgMBAAGjgZUwgZIwCQYDVR0TBAIwADAdBgNVHQ4EFgQUStPs +YwfgjxpO9QlDkJ96xTHRj9gwRAYDVR0jBD0wO4AUjHzn0nYFiXEaI1vUWbRR4lwK +XOihGKQWMBQxEjAQBgNVBAMTCWNhc3NhbmRyYYIJAIKbAXgemwsjMBMGA1UdJQQM +MAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQsFAAOCAQEArLyA +gi1t8aBG6wAF0iWag2ZXQFFu/9vjKAR7FmN07FWgwFtHE+FapW0i0OX+wVHo9sac ++b63voIU5KCyC5/uaLysFw0TUMaeUpGMoJjbTi32PW6FCru53QG/rVLdbuRBAaWT +WN0/z78V5iWqoE+YDXWKP1u6Zzf2sQs/ITSXUJqFlyu2BUGa88/EkiMGqz6HmDDr +y9ODqwR9XLnwEtFDs8V9M5ouK4A6Zr7xjAg3epOcm2BgU3EWcIbfyl+pC+KLPa8C +YjthMNpTiePYC4gEmpNq9ij43Q2PDIJbwOX4Da0Gdqc7S65UNyUV9QxnD3fFxJdo +CcMCp6BGEBzRlTpMlA== +-----END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.key b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.key new file mode 100644 index 000000000..6878e8209 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA5Zwgnt6Yc0RBDTdMYsOfh1+bT6rP9pBupeCJiHoAxrvXgIdp +LvrwNVmAboIlyLNs9qSXl5OT6vBwcKTht6rawZlmm5MEOs4LgwcGIj2m239oD0mA +vYaou1RtOF8PsPoblySuzJ03mH52zOMbRRshJRcCwBrF+3bDi5PXxYUUClykEucY +aZj1ds14zZlaKWXxaCCX074Js2gb8qOimnNYU37thjKjWtVGA/mztOxjcbq7+2/5 +gmPkVUd6euR7F2vX5s87yasMMBXJ7cfW/LZyshR9x/N/ivRjcGSOD9voOkVHzbl7 +rsgxwVLRPjQSt3PnuomGmjbtoFpp0NTjthaFrwIDAQABAoIBAQChjdjl73kUoVGk +GuSEGWCFv59nzqfEtJsl23bpr+4b5s8agCxiAe5Bm1fiaXBsZtKkN+rxm8TX6ZUz +rM+ki3KgBW9Mx4SSW6d96dNHBFoC1wJAv1b2A2l1ZVHz9+7ydwgysHzNO1GC2nh8 +cM8fMJeBoU8uG6hx5n5wFvYa5CfVoUQh8+Oq0b+mVxEFKHmRPnWp9/jPzL5eBIdr +ulbDt9S3dKJtouHgHBUNdkq/7Ex3QeHrUOahX6Y4eX1rzLnfLYY+0J4EA2PCKvgQ +bfKCxVnnzL6ywviH8eS3ql6OvTfnbK9kCRw7WxX9CC50qKj3EmwC/51MPhWohWlq +jw3qf38BAoGBAPPNyb3vUiyUqoErZxxIPFc2ob3vCjj06cvi7uKpOgrkdgC3iBhz +aCFQ28r7LrxLAHaKvNvwp71Lc7WYo8WWkLI1DVn0dx+GiQYW3DbNcwZOS40ZQz5L +zsjEcG4+cnZmuqGZBMNvQ+xUjkuucxvxPWKpEKM18GfDjgEkKbmDr+uNAoGBAPEY +kVSfSZGtP0MoXIfRkrxBlhvCj9m+p60P37pyHrJBrlrwvxB7x3Oz8S70D6kV8s2g +vVHgOS3VPj17VaQG8a3jBLKjzp5JLe34G8D1Ny8GqDc2wzOBtZySpJbifXuSUSPk +cqF7yiu1cD/wRPlwyWxBX9ZbaxvxnIUwLLd3ygkrAoGBAKQaw42uVkCdvPr/DQOT +d9I4erxO9zGJYQmU8bjtsZz9VJR89QWIQPIT7C3/zuB9F42zKxZcMXwQGo2EddAc +3b6mSRtgmwJEW10W7BmTRrZa4y3RcFqxSjoHR6pdLEyYL01woy0taqnb7H/yp5aK +VghfxkwllXEyxxXrko5FnpdNAoGBANeJLBunz2BxrnW+doJhZDnytFya4nk6TbKU +12FaNoEL4PCh+12kGtogSwS74eg6m/citT2mI9gKpHrYcOaT4qmeo4uEj+nH6Eyv +Gzi0wCHFZMr/pSC92/teyc+uKZo4Y1ugFq6w+Tt8GB7BERiisR+bji8XSTkRFemn ++MIIUFFDAoGAM8Va2Q5aTUkfg2mYlNLqT2tUAXVEhbmzjPA6laSo25PQEYWmX7vj +hiU0DPCDJQ/PlPI23xYtDDLNk83Zbx+Oj29GO5pawJY9NvFI8n60EFXfLbP1nEdG +j077QZNZOKfcgJirWi3+RrHSAK4tFftCe7rkV8ZmlMRBY3SDxzKOGcc= +-----END RSA PRIVATE KEY----- diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.crt b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.crt new file mode 100644 index 000000000..22bf19f56 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.crt @@ -0,0 +1,83 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=cassandra + Validity + Not Before: Sep 19 21:18:33 2014 GMT + Not After : Sep 16 21:18:33 2024 GMT + Subject: CN=gocql + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:ae:e9:fa:9e:fd:e2:69:85:1d:08:0f:35:68:bc: + 63:7b:92:50:7f:73:50:fc:42:43:35:06:b3:5c:9e: + 27:1e:16:05:69:ec:88:d5:9c:4f:ef:e8:13:69:7a: + b5:b3:7f:66:6d:14:00:2e:d6:af:5b:ff:2c:90:91: + a6:11:07:72:5e:b0:37:c0:6d:ff:7b:76:2b:fe:de: + 4c:d2:8d:ce:43:3b:1a:c4:1d:de:b6:d8:26:08:25: + 89:59:a1:4b:94:a3:57:9e:19:46:28:6e:97:11:7c: + e6:b7:41:96:8f:42:dd:66:da:86:d2:53:dd:d8:f5: + 20:cd:24:8b:0f:ab:df:c4:10:b2:64:20:1d:e0:0f: + f4:2d:f6:ca:94:be:83:ac:3e:a8:4a:77:b6:08:97: + 3a:7e:7b:e0:3e:ab:68:cf:ee:f6:a1:8e:bf:ec:be: + 06:d1:ad:6c:ed:4f:35:d1:04:97:08:33:b1:65:5b: + 61:32:8d:4b:f0:30:35:4b:8b:6b:06:f2:1a:72:8c: + 69:bd:f3:b2:c4:a4:a4:70:45:e3:67:a2:7a:9f:2e: + cb:28:2d:9f:68:03:f1:c7:d9:4f:83:c9:3d:8c:34: + 04:0a:3b:13:87:92:e1:f7:e3:79:7e:ab:c0:25:b1: + e5:38:09:44:3e:31:df:12:d4:dc:7b:0e:35:bf:ee: + 25:5f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 9F:F1:B2:C4:82:34:D0:2F:FF:E9:7F:19:F1:3B:51:57:BF:E8:95:BB + X509v3 Authority Key Identifier: + keyid:8C:7C:E7:D2:76:05:89:71:1A:23:5B:D4:59:B4:51:E2:5C:0A:5C:E8 + DirName:/CN=cassandra + serial:82:9B:01:78:1E:9B:0B:23 + + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Key Usage: + Digital Signature + Signature Algorithm: sha256WithRSAEncryption + 12:aa:1b:a6:58:27:52:32:c9:46:19:32:d3:69:ae:95:ad:23: + 55:ad:12:65:da:2c:4c:72:f3:29:bd:2b:5a:97:3b:b7:68:8b: + 68:80:77:55:e6:32:81:f1:f5:20:54:ba:0e:2b:86:90:d8:44: + cf:f2:9f:ec:4d:39:67:4e:36:6c:9b:49:4a:80:e6:c1:ed:a4: + 41:39:19:16:d2:88:df:17:0c:46:5a:b9:88:53:f5:67:19:f0: + 1f:9a:51:40:1b:40:12:bc:57:db:de:dd:d3:f5:a8:93:68:30: + ac:ba:4e:ee:6b:af:f8:13:3d:11:1a:fa:90:93:d0:68:ce:77: + 5f:85:8b:a4:95:2a:4c:25:7b:53:9c:44:43:b1:d9:fe:0c:83: + b8:19:2a:88:cc:d8:d1:d9:b3:04:eb:45:9b:30:5e:cb:61:e0: + e1:88:23:9c:b0:34:79:62:82:0d:f8:10:ed:96:bb:a0:fd:0d: + 02:cb:c5:d3:47:1f:35:a7:e3:39:31:56:d5:b3:eb:2f:93:8f: + 18:b4:b7:3c:00:03:a7:b4:1c:17:72:91:7e:b6:f6:36:17:3d: + f6:54:3b:87:84:d1:9b:43:d1:88:42:64:20:7a:e3:cc:f7:05: + 98:0e:1c:51:da:20:b7:9b:49:88:e8:c6:e1:de:0d:f5:56:4f: + 79:41:d0:7f +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwljYXNz +YW5kcmEwHhcNMTQwOTE5MjExODMzWhcNMjQwOTE2MjExODMzWjAQMQ4wDAYDVQQD +EwVnb2NxbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7p+p794mmF +HQgPNWi8Y3uSUH9zUPxCQzUGs1yeJx4WBWnsiNWcT+/oE2l6tbN/Zm0UAC7Wr1v/ +LJCRphEHcl6wN8Bt/3t2K/7eTNKNzkM7GsQd3rbYJggliVmhS5SjV54ZRihulxF8 +5rdBlo9C3WbahtJT3dj1IM0kiw+r38QQsmQgHeAP9C32ypS+g6w+qEp3tgiXOn57 +4D6raM/u9qGOv+y+BtGtbO1PNdEElwgzsWVbYTKNS/AwNUuLawbyGnKMab3zssSk +pHBF42eiep8uyygtn2gD8cfZT4PJPYw0BAo7E4eS4ffjeX6rwCWx5TgJRD4x3xLU +3HsONb/uJV8CAwEAAaOBlTCBkjAJBgNVHRMEAjAAMB0GA1UdDgQWBBSf8bLEgjTQ +L//pfxnxO1FXv+iVuzBEBgNVHSMEPTA7gBSMfOfSdgWJcRojW9RZtFHiXApc6KEY +pBYwFDESMBAGA1UEAxMJY2Fzc2FuZHJhggkAgpsBeB6bCyMwEwYDVR0lBAwwCgYI +KwYBBQUHAwIwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQASqhumWCdS +MslGGTLTaa6VrSNVrRJl2ixMcvMpvStalzu3aItogHdV5jKB8fUgVLoOK4aQ2ETP +8p/sTTlnTjZsm0lKgObB7aRBORkW0ojfFwxGWrmIU/VnGfAfmlFAG0ASvFfb3t3T +9aiTaDCsuk7ua6/4Ez0RGvqQk9BozndfhYuklSpMJXtTnERDsdn+DIO4GSqIzNjR +2bME60WbMF7LYeDhiCOcsDR5YoIN+BDtlrug/Q0Cy8XTRx81p+M5MVbVs+svk48Y +tLc8AAOntBwXcpF+tvY2Fz32VDuHhNGbQ9GIQmQgeuPM9wWYDhxR2iC3m0mI6Mbh +3g31Vk95QdB/ +-----END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.key b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.key new file mode 100644 index 000000000..0d701f43e --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArun6nv3iaYUdCA81aLxje5JQf3NQ/EJDNQazXJ4nHhYFaeyI +1ZxP7+gTaXq1s39mbRQALtavW/8skJGmEQdyXrA3wG3/e3Yr/t5M0o3OQzsaxB3e +ttgmCCWJWaFLlKNXnhlGKG6XEXzmt0GWj0LdZtqG0lPd2PUgzSSLD6vfxBCyZCAd +4A/0LfbKlL6DrD6oSne2CJc6fnvgPqtoz+72oY6/7L4G0a1s7U810QSXCDOxZVth +Mo1L8DA1S4trBvIacoxpvfOyxKSkcEXjZ6J6ny7LKC2faAPxx9lPg8k9jDQECjsT +h5Lh9+N5fqvAJbHlOAlEPjHfEtTcew41v+4lXwIDAQABAoIBAQCCP9XSwzfwX6Fo +uPqKjY5/HEs5PQPXdPha6ixyEYsLilZptCuI9adI/MZHy4q2qW36V+Ry/IcEuJXU +6cCB+cue2xYJA2A17Z+BYMRQHiy0P7UEyUFpYrefZWRMDCIeAyxhnGxz+zYfXaTo +Xbzh3WbFCoFO6gjPYGoWmNm8x74PXyunNaMa/gWFECX5MMBXoOk5xSFGbHzI2Cds +iT7sdCQJVbBs7yidYwNqPWQuOwrskFinPIFSc7bZ0Sx9wO3XTIrQFCE94v/AN6yR +9Q37ida54g5tgtoeg/5EGsUM++i4wqJVoT3tWUHv1jBozO4Lm65uWR/1HcrusVnr +x0TM9SaBAoGBAOMeaZdUrCJXnIiSoqCGDvZmylTAeOo6n2RAiviOYxVB4GP/SSjh +8VeddFhYT1GCmZ+YjIXnRWK+dSqVukzCuf5xW5mWY7PDNGZe2P6O78lXnY4cb8Nc +Uo9/S2aPnNmNHL2TYVBYUiZj+t2azIQEFvRth4Vu/AHRUG41/USxpwm/AoGBAMUo +GX0xgSFAVpHnTLdzWrHNRrzHgYN8ywPKFgNOASvdgW0BFoqXEvVGc1Ak6uW82m1/ +L9ChOzWjCY7CoT+LPmdUVyGT9/UAPtWeLfo8Owl4tG91jQjePmJFvLoXErryCFRt +SOOvCsTTTq2gN3PREHxY3dj2kJqaCBLCEzx3cYxhAoGBAIUxdrc6/t/9BV3KsPj2 +5Zt3WL0vSzoCOyut9lIiHtV+lrvOIPeK2eCKBIsy7wFcV/+SlQaKRNTN4SSiPml5 +4V3o2NFPsxTfK8HFafiPluw7J7kJ0Dl/0SM6gduZ6WBkMzCyV+WohjTheWOwvrPF +OjkKaunD1qKyQDsCCo/Yp589AoGAdKgnfNZf68bf8nEECcBtt6sY4fbCgYTDszhO +EiKDuurT/CWaquJ9SzgmXxOZEdrO+9838aCVIkWYECrFso23nPhgnfOp0gQVKdzw +o5Ij9JTBXvoVO1wVWZyd8RZZ9Nflad9IM8CNBK1rbnzQkuzvbkQ+8HPkWDYv9Ll1 +HGAohcECgYBQeirIumumj1B17WD/KmNe0U0qCHHp+oSW4W2r7pjlEVZzeQmggX4O +anbEngyQaZKeUiUOj9snBDmzLv7S+j5p7Us4d1fbp70sCKuK6tcAnROU8gK8IGiI +I01ypD8Z1Mb556qek56eRWlr71sy6wI1lbQa856cUBvePajUOKsKsw== +-----END RSA PRIVATE KEY----- diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/token.go b/Godeps/_workspace/src/github.com/gocql/gocql/token.go new file mode 100644 index 000000000..00541707d --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/token.go @@ -0,0 +1,348 @@ +// Copyright (c) 2015 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "bytes" + "crypto/md5" + "fmt" + "math/big" + "sort" + "strconv" + "strings" + "unsafe" +) + +// a token partitioner +type partitioner interface { + Name() string + Hash([]byte) token + ParseString(string) token +} + +// a token +type token interface { + fmt.Stringer + Less(token) bool +} + +// murmur3 partitioner and token +type murmur3Partitioner struct{} +type murmur3Token int64 + +func (p murmur3Partitioner) Name() string { + return "Murmur3Partitioner" +} + +func (p murmur3Partitioner) Hash(partitionKey []byte) token { + h1 := murmur3H1(partitionKey) + return murmur3Token(int64(h1)) +} + +// murmur3 little-endian, 128-bit hash, but returns only h1 +func murmur3H1(data []byte) uint64 { + length := len(data) + + var h1, h2, k1, k2 uint64 + + const ( + c1 = 0x87c37b91114253d5 + c2 = 0x4cf5ad432745937f + ) + + // body + nBlocks := length / 16 + for i := 0; i < nBlocks; i++ { + block := (*[2]uint64)(unsafe.Pointer(&data[i*16])) + + k1 = block[0] + k2 = block[1] + + k1 *= c1 + k1 = (k1 << 31) | (k1 >> 33) // ROTL64(k1, 31) + k1 *= c2 + h1 ^= k1 + + h1 = (h1 << 27) | (h1 >> 37) // ROTL64(h1, 27) + h1 += h2 + h1 = h1*5 + 0x52dce729 + + k2 *= c2 + k2 = (k2 << 33) | (k2 >> 31) // ROTL64(k2, 33) + k2 *= c1 + h2 ^= k2 + + h2 = (h2 << 31) | (h2 >> 33) // ROTL64(h2, 31) + h2 += h1 + h2 = h2*5 + 0x38495ab5 + } + + // tail + tail := data[nBlocks*16:] + k1 = 0 + k2 = 0 + switch length & 15 { + case 15: + k2 ^= uint64(tail[14]) << 48 + fallthrough + case 14: + k2 ^= uint64(tail[13]) << 40 + fallthrough + case 13: + k2 ^= uint64(tail[12]) << 32 + fallthrough + case 12: + k2 ^= uint64(tail[11]) << 24 + fallthrough + case 11: + k2 ^= uint64(tail[10]) << 16 + fallthrough + case 10: + k2 ^= uint64(tail[9]) << 8 + fallthrough + case 9: + k2 ^= uint64(tail[8]) + + k2 *= c2 + k2 = (k2 << 33) | (k2 >> 31) // ROTL64(k2, 33) + k2 *= c1 + h2 ^= k2 + + fallthrough + case 8: + k1 ^= uint64(tail[7]) << 56 + fallthrough + case 7: + k1 ^= uint64(tail[6]) << 48 + fallthrough + case 6: + k1 ^= uint64(tail[5]) << 40 + fallthrough + case 5: + k1 ^= uint64(tail[4]) << 32 + fallthrough + case 4: + k1 ^= uint64(tail[3]) << 24 + fallthrough + case 3: + k1 ^= uint64(tail[2]) << 16 + fallthrough + case 2: + k1 ^= uint64(tail[1]) << 8 + fallthrough + case 1: + k1 ^= uint64(tail[0]) + + k1 *= c1 + k1 = (k1 << 31) | (k1 >> 33) // ROTL64(k1, 31) + k1 *= c2 + h1 ^= k1 + } + + h1 ^= uint64(length) + h2 ^= uint64(length) + + h1 += h2 + h2 += h1 + + // finalizer + const ( + fmix1 = 0xff51afd7ed558ccd + fmix2 = 0xc4ceb9fe1a85ec53 + ) + + // fmix64(h1) + h1 ^= h1 >> 33 + h1 *= fmix1 + h1 ^= h1 >> 33 + h1 *= fmix2 + h1 ^= h1 >> 33 + + // fmix64(h2) + h2 ^= h2 >> 33 + h2 *= fmix1 + h2 ^= h2 >> 33 + h2 *= fmix2 + h2 ^= h2 >> 33 + + h1 += h2 + // the following is extraneous since h2 is discarded + // h2 += h1 + + return h1 +} + +func (p murmur3Partitioner) ParseString(str string) token { + val, _ := strconv.ParseInt(str, 10, 64) + return murmur3Token(val) +} + +func (m murmur3Token) String() string { + return strconv.FormatInt(int64(m), 10) +} + +func (m murmur3Token) Less(token token) bool { + return m < token.(murmur3Token) +} + +// order preserving partitioner and token +type orderedPartitioner struct{} +type orderedToken []byte + +func (p orderedPartitioner) Name() string { + return "OrderedPartitioner" +} + +func (p orderedPartitioner) Hash(partitionKey []byte) token { + // the partition key is the token + return orderedToken(partitionKey) +} + +func (p orderedPartitioner) ParseString(str string) token { + return orderedToken([]byte(str)) +} + +func (o orderedToken) String() string { + return string([]byte(o)) +} + +func (o orderedToken) Less(token token) bool { + return -1 == bytes.Compare(o, token.(orderedToken)) +} + +// random partitioner and token +type randomPartitioner struct{} +type randomToken big.Int + +func (r randomPartitioner) Name() string { + return "RandomPartitioner" +} + +func (p randomPartitioner) Hash(partitionKey []byte) token { + hash := md5.New() + sum := hash.Sum(partitionKey) + + val := new(big.Int) + val = val.SetBytes(sum) + val = val.Abs(val) + + return (*randomToken)(val) +} + +func (p randomPartitioner) ParseString(str string) token { + val := new(big.Int) + val.SetString(str, 10) + return (*randomToken)(val) +} + +func (r *randomToken) String() string { + return (*big.Int)(r).String() +} + +func (r *randomToken) Less(token token) bool { + return -1 == (*big.Int)(r).Cmp((*big.Int)(token.(*randomToken))) +} + +// a data structure for organizing the relationship between tokens and hosts +type tokenRing struct { + partitioner partitioner + tokens []token + hosts []*HostInfo +} + +func newTokenRing(partitioner string, hosts []HostInfo) (*tokenRing, error) { + tokenRing := &tokenRing{ + tokens: []token{}, + hosts: []*HostInfo{}, + } + + if strings.HasSuffix(partitioner, "Murmur3Partitioner") { + tokenRing.partitioner = murmur3Partitioner{} + } else if strings.HasSuffix(partitioner, "OrderedPartitioner") { + tokenRing.partitioner = orderedPartitioner{} + } else if strings.HasSuffix(partitioner, "RandomPartitioner") { + tokenRing.partitioner = randomPartitioner{} + } else { + return nil, fmt.Errorf("Unsupported partitioner '%s'", partitioner) + } + + for i := range hosts { + host := &hosts[i] + for _, strToken := range host.Tokens { + token := tokenRing.partitioner.ParseString(strToken) + tokenRing.tokens = append(tokenRing.tokens, token) + tokenRing.hosts = append(tokenRing.hosts, host) + } + } + + sort.Sort(tokenRing) + + return tokenRing, nil +} + +func (t *tokenRing) Len() int { + return len(t.tokens) +} + +func (t *tokenRing) Less(i, j int) bool { + return t.tokens[i].Less(t.tokens[j]) +} + +func (t *tokenRing) Swap(i, j int) { + t.tokens[i], t.hosts[i], t.tokens[j], t.hosts[j] = + t.tokens[j], t.hosts[j], t.tokens[i], t.hosts[i] +} + +func (t *tokenRing) String() string { + + buf := &bytes.Buffer{} + buf.WriteString("TokenRing(") + if t.partitioner != nil { + buf.WriteString(t.partitioner.Name()) + } + buf.WriteString("){") + sep := "" + for i := range t.tokens { + buf.WriteString(sep) + sep = "," + buf.WriteString("\n\t[") + buf.WriteString(strconv.Itoa(i)) + buf.WriteString("]") + buf.WriteString(t.tokens[i].String()) + buf.WriteString(":") + buf.WriteString(t.hosts[i].Peer) + } + buf.WriteString("\n}") + return string(buf.Bytes()) +} + +func (t *tokenRing) GetHostForPartitionKey(partitionKey []byte) *HostInfo { + if t == nil { + return nil + } + + token := t.partitioner.Hash(partitionKey) + return t.GetHostForToken(token) +} + +func (t *tokenRing) GetHostForToken(token token) *HostInfo { + if t == nil { + return nil + } + + // find the primary replica + ringIndex := sort.Search( + len(t.tokens), + func(i int) bool { + return !t.tokens[i].Less(token) + }, + ) + if ringIndex == len(t.tokens) { + // wrap around to the first in the ring + ringIndex = 0 + } + host := t.hosts[ringIndex] + return host +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/token_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/token_test.go new file mode 100644 index 000000000..c17585ed4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/token_test.go @@ -0,0 +1,474 @@ +// Copyright (c) 2015 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "bytes" + "math/big" + "sort" + "strconv" + "testing" +) + +// Test the implementation of murmur3 +func TestMurmur3H1(t *testing.T) { + // these examples are based on adding a index number to a sample string in + // a loop. The expected values were generated by the java datastax murmur3 + // implementation. The number of examples here of increasing lengths ensure + // test coverage of all tail-length branches in the murmur3 algorithm + seriesExpected := [...]uint64{ + 0x0000000000000000, // "" + 0x2ac9debed546a380, // "0" + 0x649e4eaa7fc1708e, // "01" + 0xce68f60d7c353bdb, // "012" + 0x0f95757ce7f38254, // "0123" + 0x0f04e459497f3fc1, // "01234" + 0x88c0a92586be0a27, // "012345" + 0x13eb9fb82606f7a6, // "0123456" + 0x8236039b7387354d, // "01234567" + 0x4c1e87519fe738ba, // "012345678" + 0x3f9652ac3effeb24, // "0123456789" + 0x3f33760ded9006c6, // "01234567890" + 0xaed70a6631854cb1, // "012345678901" + 0x8a299a8f8e0e2da7, // "0123456789012" + 0x624b675c779249a6, // "01234567890123" + 0xa4b203bb1d90b9a3, // "012345678901234" + 0xa3293ad698ecb99a, // "0123456789012345" + 0xbc740023dbd50048, // "01234567890123456" + 0x3fe5ab9837d25cdd, // "012345678901234567" + 0x2d0338c1ca87d132, // "0123456789012345678" + } + sample := "" + for i, expected := range seriesExpected { + assertMurmur3H1(t, []byte(sample), expected) + + sample = sample + strconv.Itoa(i%10) + } + + // Here are some test examples from other driver implementations + assertMurmur3H1(t, []byte("hello"), 0xcbd8a7b341bd9b02) + assertMurmur3H1(t, []byte("hello, world"), 0x342fac623a5ebc8e) + assertMurmur3H1(t, []byte("19 Jan 2038 at 3:14:07 AM"), 0xb89e5988b737affc) + assertMurmur3H1(t, []byte("The quick brown fox jumps over the lazy dog."), 0xcd99481f9ee902c9) +} + +// helper function for testing the murmur3 implementation +func assertMurmur3H1(t *testing.T, data []byte, expected uint64) { + actual := murmur3H1(data) + if actual != expected { + t.Errorf("Expected h1 = %x for data = %x, but was %x", expected, data, actual) + } +} + +// Benchmark of the performance of the murmur3 implementation +func BenchmarkMurmur3H1(b *testing.B) { + var h1 uint64 + var data [1024]byte + for i := 0; i < 1024; i++ { + data[i] = byte(i) + } + for i := 0; i < b.N; i++ { + b.ResetTimer() + h1 = murmur3H1(data[:]) + _ = murmur3Token(int64(h1)) + } +} + +// Tests of the murmur3Patitioner +func TestMurmur3Partitioner(t *testing.T) { + token := murmur3Partitioner{}.ParseString("-1053604476080545076") + + if "-1053604476080545076" != token.String() { + t.Errorf("Expected '-1053604476080545076' but was '%s'", token) + } + + // at least verify that the partitioner + // doesn't return nil + pk, _ := marshalInt(nil, 1) + token = murmur3Partitioner{}.Hash(pk) + if token == nil { + t.Fatal("token was nil") + } +} + +// Tests of the murmur3Token +func TestMurmur3Token(t *testing.T) { + if murmur3Token(42).Less(murmur3Token(42)) { + t.Errorf("Expected Less to return false, but was true") + } + if !murmur3Token(-42).Less(murmur3Token(42)) { + t.Errorf("Expected Less to return true, but was false") + } + if murmur3Token(42).Less(murmur3Token(-42)) { + t.Errorf("Expected Less to return false, but was true") + } +} + +// Tests of the orderedPartitioner +func TestOrderedPartitioner(t *testing.T) { + // at least verify that the partitioner + // doesn't return nil + p := orderedPartitioner{} + pk, _ := marshalInt(nil, 1) + token := p.Hash(pk) + if token == nil { + t.Fatal("token was nil") + } + + str := token.String() + parsedToken := p.ParseString(str) + + if !bytes.Equal([]byte(token.(orderedToken)), []byte(parsedToken.(orderedToken))) { + t.Errorf("Failed to convert to and from a string %s expected %x but was %x", + str, + []byte(token.(orderedToken)), + []byte(parsedToken.(orderedToken)), + ) + } +} + +// Tests of the orderedToken +func TestOrderedToken(t *testing.T) { + if orderedToken([]byte{0, 0, 4, 2}).Less(orderedToken([]byte{0, 0, 4, 2})) { + t.Errorf("Expected Less to return false, but was true") + } + if !orderedToken([]byte{0, 0, 3}).Less(orderedToken([]byte{0, 0, 4, 2})) { + t.Errorf("Expected Less to return true, but was false") + } + if orderedToken([]byte{0, 0, 4, 2}).Less(orderedToken([]byte{0, 0, 3})) { + t.Errorf("Expected Less to return false, but was true") + } +} + +// Tests of the randomPartitioner +func TestRandomPartitioner(t *testing.T) { + // at least verify that the partitioner + // doesn't return nil + p := randomPartitioner{} + pk, _ := marshalInt(nil, 1) + token := p.Hash(pk) + if token == nil { + t.Fatal("token was nil") + } + + str := token.String() + parsedToken := p.ParseString(str) + + if (*big.Int)(token.(*randomToken)).Cmp((*big.Int)(parsedToken.(*randomToken))) != 0 { + t.Errorf("Failed to convert to and from a string %s expected %v but was %v", + str, + token, + parsedToken, + ) + } +} + +// Tests of the randomToken +func TestRandomToken(t *testing.T) { + if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(42))) { + t.Errorf("Expected Less to return false, but was true") + } + if !((*randomToken)(big.NewInt(41))).Less((*randomToken)(big.NewInt(42))) { + t.Errorf("Expected Less to return true, but was false") + } + if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(41))) { + t.Errorf("Expected Less to return false, but was true") + } +} + +type intToken int + +func (i intToken) String() string { + return strconv.Itoa(int(i)) +} + +func (i intToken) Less(token token) bool { + return i < token.(intToken) +} + +// Test of the token ring implementation based on example at the start of this +// page of documentation: +// http://www.datastax.com/docs/0.8/cluster_architecture/partitioning +func TestIntTokenRing(t *testing.T) { + host0 := &HostInfo{} + host25 := &HostInfo{} + host50 := &HostInfo{} + host75 := &HostInfo{} + ring := &tokenRing{ + partitioner: nil, + // these tokens and hosts are out of order to test sorting + tokens: []token{ + intToken(0), + intToken(50), + intToken(75), + intToken(25), + }, + hosts: []*HostInfo{ + host0, + host50, + host75, + host25, + }, + } + + sort.Sort(ring) + + if ring.GetHostForToken(intToken(0)) != host0 { + t.Error("Expected host 0 for token 0") + } + if ring.GetHostForToken(intToken(1)) != host25 { + t.Error("Expected host 25 for token 1") + } + if ring.GetHostForToken(intToken(24)) != host25 { + t.Error("Expected host 25 for token 24") + } + if ring.GetHostForToken(intToken(25)) != host25 { + t.Error("Expected host 25 for token 25") + } + if ring.GetHostForToken(intToken(26)) != host50 { + t.Error("Expected host 50 for token 26") + } + if ring.GetHostForToken(intToken(49)) != host50 { + t.Error("Expected host 50 for token 49") + } + if ring.GetHostForToken(intToken(50)) != host50 { + t.Error("Expected host 50 for token 50") + } + if ring.GetHostForToken(intToken(51)) != host75 { + t.Error("Expected host 75 for token 51") + } + if ring.GetHostForToken(intToken(74)) != host75 { + t.Error("Expected host 75 for token 74") + } + if ring.GetHostForToken(intToken(75)) != host75 { + t.Error("Expected host 75 for token 75") + } + if ring.GetHostForToken(intToken(76)) != host0 { + t.Error("Expected host 0 for token 76") + } + if ring.GetHostForToken(intToken(99)) != host0 { + t.Error("Expected host 0 for token 99") + } + if ring.GetHostForToken(intToken(100)) != host0 { + t.Error("Expected host 0 for token 100") + } +} + +// Test for the behavior of a nil pointer to tokenRing +func TestNilTokenRing(t *testing.T) { + var ring *tokenRing = nil + + if ring.GetHostForToken(nil) != nil { + t.Error("Expected nil for nil token ring") + } + if ring.GetHostForPartitionKey(nil) != nil { + t.Error("Expected nil for nil token ring") + } +} + +// Test of the recognition of the partitioner class +func TestUnknownTokenRing(t *testing.T) { + _, err := newTokenRing("UnknownPartitioner", nil) + if err == nil { + t.Error("Expected error for unknown partitioner value, but was nil") + } +} + +// Test of the tokenRing with the Murmur3Partitioner +func TestMurmur3TokenRing(t *testing.T) { + // Note, strings are parsed directly to int64, they are not murmur3 hashed + var hosts []HostInfo = []HostInfo{ + HostInfo{ + Peer: "0", + Tokens: []string{"0"}, + }, + HostInfo{ + Peer: "1", + Tokens: []string{"25"}, + }, + HostInfo{ + Peer: "2", + Tokens: []string{"50"}, + }, + HostInfo{ + Peer: "3", + Tokens: []string{"75"}, + }, + } + ring, err := newTokenRing("Murmur3Partitioner", hosts) + if err != nil { + t.Fatalf("Failed to create token ring due to error: %v", err) + } + + p := murmur3Partitioner{} + + var actual *HostInfo + actual = ring.GetHostForToken(p.ParseString("0")) + if actual.Peer != "0" { + t.Errorf("Expected peer 0 for token \"0\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("25")) + if actual.Peer != "1" { + t.Errorf("Expected peer 1 for token \"25\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("50")) + if actual.Peer != "2" { + t.Errorf("Expected peer 2 for token \"50\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("75")) + if actual.Peer != "3" { + t.Errorf("Expected peer 3 for token \"01\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("12")) + if actual.Peer != "1" { + t.Errorf("Expected peer 1 for token \"12\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("24324545443332")) + if actual.Peer != "0" { + t.Errorf("Expected peer 0 for token \"24324545443332\", but was %s", actual.Peer) + } +} + +// Test of the tokenRing with the OrderedPartitioner +func TestOrderedTokenRing(t *testing.T) { + // Tokens here more or less are similar layout to the int tokens above due + // to each numeric character translating to a consistently offset byte. + var hosts []HostInfo = []HostInfo{ + HostInfo{ + Peer: "0", + Tokens: []string{ + "00", + }, + }, + HostInfo{ + Peer: "1", + Tokens: []string{ + "25", + }, + }, + HostInfo{ + Peer: "2", + Tokens: []string{ + "50", + }, + }, + HostInfo{ + Peer: "3", + Tokens: []string{ + "75", + }, + }, + } + ring, err := newTokenRing("OrderedPartitioner", hosts) + if err != nil { + t.Fatalf("Failed to create token ring due to error: %v", err) + } + + p := orderedPartitioner{} + + var actual *HostInfo + actual = ring.GetHostForToken(p.ParseString("0")) + if actual.Peer != "0" { + t.Errorf("Expected peer 0 for token \"0\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("25")) + if actual.Peer != "1" { + t.Errorf("Expected peer 1 for token \"25\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("50")) + if actual.Peer != "2" { + t.Errorf("Expected peer 2 for token \"50\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("75")) + if actual.Peer != "3" { + t.Errorf("Expected peer 3 for token \"01\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("12")) + if actual.Peer != "1" { + t.Errorf("Expected peer 1 for token \"12\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("24324545443332")) + if actual.Peer != "1" { + t.Errorf("Expected peer 1 for token \"24324545443332\", but was %s", actual.Peer) + } +} + +// Test of the tokenRing with the RandomPartitioner +func TestRandomTokenRing(t *testing.T) { + // String tokens are parsed into big.Int in base 10 + var hosts []HostInfo = []HostInfo{ + HostInfo{ + Peer: "0", + Tokens: []string{ + "00", + }, + }, + HostInfo{ + Peer: "1", + Tokens: []string{ + "25", + }, + }, + HostInfo{ + Peer: "2", + Tokens: []string{ + "50", + }, + }, + HostInfo{ + Peer: "3", + Tokens: []string{ + "75", + }, + }, + } + ring, err := newTokenRing("RandomPartitioner", hosts) + if err != nil { + t.Fatalf("Failed to create token ring due to error: %v", err) + } + + p := randomPartitioner{} + + var actual *HostInfo + actual = ring.GetHostForToken(p.ParseString("0")) + if actual.Peer != "0" { + t.Errorf("Expected peer 0 for token \"0\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("25")) + if actual.Peer != "1" { + t.Errorf("Expected peer 1 for token \"25\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("50")) + if actual.Peer != "2" { + t.Errorf("Expected peer 2 for token \"50\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("75")) + if actual.Peer != "3" { + t.Errorf("Expected peer 3 for token \"01\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("12")) + if actual.Peer != "1" { + t.Errorf("Expected peer 1 for token \"12\", but was %s", actual.Peer) + } + + actual = ring.GetHostForToken(p.ParseString("24324545443332")) + if actual.Peer != "0" { + t.Errorf("Expected peer 0 for token \"24324545443332\", but was %s", actual.Peer) + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/topology.go b/Godeps/_workspace/src/github.com/gocql/gocql/topology.go new file mode 100644 index 000000000..08ac094a1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/topology.go @@ -0,0 +1,74 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocql + +import ( + "sync" + "sync/atomic" +) + +type Node interface { + Pick(qry *Query) *Conn + Close() +} + +type RoundRobin struct { + pool []Node + pos uint32 + mu sync.RWMutex +} + +func NewRoundRobin() *RoundRobin { + return &RoundRobin{} +} + +func (r *RoundRobin) AddNode(node Node) { + r.mu.Lock() + r.pool = append(r.pool, node) + r.mu.Unlock() +} + +func (r *RoundRobin) RemoveNode(node Node) { + r.mu.Lock() + n := len(r.pool) + for i := 0; i < n; i++ { + if r.pool[i] == node { + r.pool[i], r.pool[n-1] = r.pool[n-1], r.pool[i] + r.pool = r.pool[:n-1] + break + } + } + r.mu.Unlock() +} + +func (r *RoundRobin) Size() int { + r.mu.RLock() + n := len(r.pool) + r.mu.RUnlock() + return n +} + +func (r *RoundRobin) Pick(qry *Query) *Conn { + pos := atomic.AddUint32(&r.pos, 1) + var node Node + r.mu.RLock() + if len(r.pool) > 0 { + node = r.pool[pos%uint32(len(r.pool))] + } + r.mu.RUnlock() + if node == nil { + return nil + } + return node.Pick(qry) +} + +func (r *RoundRobin) Close() { + r.mu.Lock() + for i := 0; i < len(r.pool); i++ { + r.pool[i].Close() + } + r.pool = nil + r.mu.Unlock() +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/topology_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/topology_test.go new file mode 100644 index 000000000..8384824ab --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/topology_test.go @@ -0,0 +1,51 @@ +// +build all unit + +package gocql + +import ( + "testing" +) + +// fakeNode is used as a simple structure to test the RoundRobin API +type fakeNode struct { + conn *Conn + closed bool +} + +// Pick is needed to satisfy the Node interface +func (n *fakeNode) Pick(qry *Query) *Conn { + if n.conn == nil { + n.conn = &Conn{} + } + return n.conn +} + +//Close is needed to satisfy the Node interface +func (n *fakeNode) Close() { + n.closed = true +} + +//TestRoundRobinAPI tests the exported methods of the RoundRobin struct +//to make sure the API behaves accordingly. +func TestRoundRobinAPI(t *testing.T) { + node := &fakeNode{} + rr := NewRoundRobin() + rr.AddNode(node) + + if rr.Size() != 1 { + t.Fatalf("expected size to be 1, got %v", rr.Size()) + } + + if c := rr.Pick(nil); c != node.conn { + t.Fatalf("expected conn %v, got %v", node.conn, c) + } + + rr.Close() + if rr.pool != nil { + t.Fatalf("expected rr.pool to be nil, got %v", rr.pool) + } + + if !node.closed { + t.Fatal("expected node.closed to be true, got false") + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/tuple_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/tuple_test.go new file mode 100644 index 000000000..bf94191f6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/tuple_test.go @@ -0,0 +1,51 @@ +// +build all integration + +package gocql + +import "testing" + +func TestTupleSimple(t *testing.T) { + if *flagProto < protoVersion3 { + t.Skip("tuple types are only available of proto>=3") + } + + session := createSession(t) + defer session.Close() + + err := createTable(session, `CREATE TABLE tuple_test( + id int, + coord frozen>, + + primary key(id))`) + if err != nil { + t.Fatal(err) + } + + err = session.Query("INSERT INTO tuple_test(id, coord) VALUES(?, (?, ?))", 1, 100, -100).Exec() + if err != nil { + t.Fatal(err) + } + + var ( + id int + coord struct { + x int + y int + } + ) + + iter := session.Query("SELECT id, coord FROM tuple_test WHERE id=?", 1) + if err := iter.Scan(&id, &coord.x, &coord.y); err != nil { + t.Fatal(err) + } + + if id != 1 { + t.Errorf("expected to get id=1 got: %v", id) + } + if coord.x != 100 { + t.Errorf("expected to get coord.x=100 got: %v", coord.x) + } + if coord.y != -100 { + t.Errorf("expected to get coord.y=-100 got: %v", coord.y) + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/udt_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/udt_test.go new file mode 100644 index 000000000..1e0a9fcd1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/udt_test.go @@ -0,0 +1,254 @@ +// +build all integration + +package gocql + +import ( + "fmt" + "strings" + "testing" +) + +type position struct { + Lat int `cql:"lat"` + Lon int `cql:"lon"` + Padding string `json:"padding"` +} + +// NOTE: due to current implementation details it is not currently possible to use +// a pointer receiver type for the UDTMarshaler interface to handle UDT's +func (p position) MarshalUDT(name string, info TypeInfo) ([]byte, error) { + switch name { + case "lat": + return Marshal(info, p.Lat) + case "lon": + return Marshal(info, p.Lon) + case "padding": + return Marshal(info, p.Padding) + default: + return nil, fmt.Errorf("unknown column for position: %q", name) + } +} + +func (p *position) UnmarshalUDT(name string, info TypeInfo, data []byte) error { + switch name { + case "lat": + return Unmarshal(info, data, &p.Lat) + case "lon": + return Unmarshal(info, data, &p.Lon) + case "padding": + return Unmarshal(info, data, &p.Padding) + default: + return fmt.Errorf("unknown column for position: %q", name) + } +} + +func TestUDT_Marshaler(t *testing.T) { + if *flagProto < protoVersion3 { + t.Skip("UDT are only available on protocol >= 3") + } + + session := createSession(t) + defer session.Close() + + err := createTable(session, `CREATE TYPE position( + lat int, + lon int, + padding text);`) + if err != nil { + t.Fatal(err) + } + + err = createTable(session, `CREATE TABLE houses( + id int, + name text, + loc frozen, + + primary key(id) + );`) + if err != nil { + t.Fatal(err) + } + + const ( + expLat = -1 + expLon = 2 + ) + pad := strings.Repeat("X", 1000) + + err = session.Query("INSERT INTO houses(id, name, loc) VALUES(?, ?, ?)", 1, "test", &position{expLat, expLon, pad}).Exec() + if err != nil { + t.Fatal(err) + } + + pos := &position{} + + err = session.Query("SELECT loc FROM houses WHERE id = ?", 1).Scan(pos) + if err != nil { + t.Fatal(err) + } + + if pos.Lat != expLat { + t.Errorf("expeceted lat to be be %d got %d", expLat, pos.Lat) + } + if pos.Lon != expLon { + t.Errorf("expeceted lon to be be %d got %d", expLon, pos.Lon) + } + if pos.Padding != pad { + t.Errorf("expected to get padding %q got %q\n", pad, pos.Padding) + } +} + +func TestUDT_Reflect(t *testing.T) { + if *flagProto < protoVersion3 { + t.Skip("UDT are only available on protocol >= 3") + } + + // Uses reflection instead of implementing the marshaling type + session := createSession(t) + defer session.Close() + + err := createTable(session, `CREATE TYPE horse( + name text, + owner text);`) + if err != nil { + t.Fatal(err) + } + + err = createTable(session, `CREATE TABLE horse_race( + position int, + horse frozen, + + primary key(position) + );`) + if err != nil { + t.Fatal(err) + } + + type horse struct { + Name string `cql:"name"` + Owner string `cql:"owner"` + } + + insertedHorse := &horse{ + Name: "pony", + Owner: "jim", + } + + err = session.Query("INSERT INTO horse_race(position, horse) VALUES(?, ?)", 1, insertedHorse).Exec() + if err != nil { + t.Fatal(err) + } + + retrievedHorse := &horse{} + err = session.Query("SELECT horse FROM horse_race WHERE position = ?", 1).Scan(retrievedHorse) + if err != nil { + t.Fatal(err) + } + + if *retrievedHorse != *insertedHorse { + t.Fatal("exepcted to get %+v got %+v", insertedHorse, retrievedHorse) + } +} + +func TestUDT_Proto2error(t *testing.T) { + if *flagProto < protoVersion3 { + t.Skip("UDT are only available on protocol >= 3") + } + + cluster := createCluster() + cluster.ProtoVersion = 2 + cluster.Keyspace = "gocql_test" + + // Uses reflection instead of implementing the marshaling type + session, err := cluster.CreateSession() + if err != nil { + t.Fatal(err) + } + defer session.Close() + + err = createTable(session, `CREATE TYPE fish( + name text, + owner text);`) + if err != nil { + t.Fatal(err) + } + + err = createTable(session, `CREATE TABLE fish_race( + position int, + fish frozen, + + primary key(position) + );`) + if err != nil { + t.Fatal(err) + } + + type fish struct { + Name string `cql:"name"` + Owner string `cql:"owner"` + } + + insertedFish := &fish{ + Name: "pony", + Owner: "jim", + } + + err = session.Query("INSERT INTO fish_race(position, fish) VALUES(?, ?)", 1, insertedFish).Exec() + if err != ErrorUDTUnavailable { + t.Fatalf("expected to get %v got %v", ErrorUDTUnavailable, err) + } +} + +func TestUDT_NullObject(t *testing.T) { + if *flagProto < protoVersion3 { + t.Skip("UDT are only available on protocol >= 3") + } + + session := createSession(t) + defer session.Close() + + err := createTable(session, `CREATE TYPE udt_null_type( + name text, + owner text);`) + if err != nil { + t.Fatal(err) + } + + err = createTable(session, `CREATE TABLE udt_null_table( + id uuid, + udt_col frozen, + + primary key(id) + );`) + if err != nil { + t.Fatal(err) + } + + type col struct { + Name string `cql:"name"` + Owner string `cql:"owner"` + } + + id := TimeUUID() + err = session.Query("INSERT INTO udt_null_table(id) VALUES(?)", id).Exec() + if err != nil { + t.Fatal(err) + } + + readCol := &col{ + Name: "temp", + Owner: "temp", + } + + err = session.Query("SELECT udt_col FROM udt_null_table WHERE id = ?", id).Scan(readCol) + if err != nil { + t.Fatal(err) + } + + if readCol.Name != "" { + t.Errorf("expected empty string to be returned for null udt: got %q", readCol.Name) + } + if readCol.Owner != "" { + t.Errorf("expected empty string to be returned for null udt: got %q", readCol.Owner) + } +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/uuid.go b/Godeps/_workspace/src/github.com/gocql/gocql/uuid.go new file mode 100644 index 000000000..0017d99a9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/uuid.go @@ -0,0 +1,242 @@ +// Copyright (c) 2012 The gocql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The uuid package can be used to generate and parse universally unique +// identifiers, a standardized format in the form of a 128 bit number. +// +// http://tools.ietf.org/html/rfc4122 +package gocql + +import ( + "crypto/rand" + "errors" + "fmt" + "io" + "net" + "strings" + "sync/atomic" + "time" +) + +type UUID [16]byte + +var hardwareAddr []byte +var clockSeq uint32 + +const ( + VariantNCSCompat = 0 + VariantIETF = 2 + VariantMicrosoft = 6 + VariantFuture = 7 +) + +func init() { + if interfaces, err := net.Interfaces(); err == nil { + for _, i := range interfaces { + if i.Flags&net.FlagLoopback == 0 && len(i.HardwareAddr) > 0 { + hardwareAddr = i.HardwareAddr + break + } + } + } + if hardwareAddr == nil { + // If we failed to obtain the MAC address of the current computer, + // we will use a randomly generated 6 byte sequence instead and set + // the multicast bit as recommended in RFC 4122. + hardwareAddr = make([]byte, 6) + _, err := io.ReadFull(rand.Reader, hardwareAddr) + if err != nil { + panic(err) + } + hardwareAddr[0] = hardwareAddr[0] | 0x01 + } + + // initialize the clock sequence with a random number + var clockSeqRand [2]byte + io.ReadFull(rand.Reader, clockSeqRand[:]) + clockSeq = uint32(clockSeqRand[1])<<8 | uint32(clockSeqRand[0]) +} + +// ParseUUID parses a 32 digit hexadecimal number (that might contain hypens) +// representing an UUID. +func ParseUUID(input string) (UUID, error) { + var u UUID + j := 0 + for _, r := range input { + switch { + case r == '-' && j&1 == 0: + continue + case r >= '0' && r <= '9' && j < 32: + u[j/2] |= byte(r-'0') << uint(4-j&1*4) + case r >= 'a' && r <= 'f' && j < 32: + u[j/2] |= byte(r-'a'+10) << uint(4-j&1*4) + case r >= 'A' && r <= 'F' && j < 32: + u[j/2] |= byte(r-'A'+10) << uint(4-j&1*4) + default: + return UUID{}, fmt.Errorf("invalid UUID %q", input) + } + j += 1 + } + if j != 32 { + return UUID{}, fmt.Errorf("invalid UUID %q", input) + } + return u, nil +} + +// UUIDFromBytes converts a raw byte slice to an UUID. +func UUIDFromBytes(input []byte) (UUID, error) { + var u UUID + if len(input) != 16 { + return u, errors.New("UUIDs must be exactly 16 bytes long") + } + + copy(u[:], input) + return u, nil +} + +// RandomUUID generates a totally random UUID (version 4) as described in +// RFC 4122. +func RandomUUID() (UUID, error) { + var u UUID + _, err := io.ReadFull(rand.Reader, u[:]) + if err != nil { + return u, err + } + u[6] &= 0x0F // clear version + u[6] |= 0x40 // set version to 4 (random uuid) + u[8] &= 0x3F // clear variant + u[8] |= 0x80 // set to IETF variant + return u, nil +} + +var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix() + +// TimeUUID generates a new time based UUID (version 1) using the current +// time as the timestamp. +func TimeUUID() UUID { + return UUIDFromTime(time.Now()) +} + +// UUIDFromTime generates a new time based UUID (version 1) as described in +// RFC 4122. This UUID contains the MAC address of the node that generated +// the UUID, the given timestamp and a sequence number. +func UUIDFromTime(aTime time.Time) UUID { + var u UUID + + utcTime := aTime.In(time.UTC) + t := uint64(utcTime.Unix()-timeBase)*10000000 + uint64(utcTime.Nanosecond()/100) + u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t) + u[4], u[5] = byte(t>>40), byte(t>>32) + u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48) + + clock := atomic.AddUint32(&clockSeq, 1) + u[8] = byte(clock >> 8) + u[9] = byte(clock) + + copy(u[10:], hardwareAddr) + + u[6] |= 0x10 // set version to 1 (time based uuid) + u[8] &= 0x3F // clear variant + u[8] |= 0x80 // set to IETF variant + + return u +} + +// String returns the UUID in it's canonical form, a 32 digit hexadecimal +// number in the form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (u UUID) String() string { + var offsets = [...]int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} + const hexString = "0123456789abcdef" + r := make([]byte, 36) + for i, b := range u { + r[offsets[i]] = hexString[b>>4] + r[offsets[i]+1] = hexString[b&0xF] + } + r[8] = '-' + r[13] = '-' + r[18] = '-' + r[23] = '-' + return string(r) + +} + +// Bytes returns the raw byte slice for this UUID. A UUID is always 128 bits +// (16 bytes) long. +func (u UUID) Bytes() []byte { + return u[:] +} + +// Variant returns the variant of this UUID. This package will only generate +// UUIDs in the IETF variant. +func (u UUID) Variant() int { + x := u[8] + if x&0x80 == 0 { + return VariantNCSCompat + } + if x&0x40 == 0 { + return VariantIETF + } + if x&0x20 == 0 { + return VariantMicrosoft + } + return VariantFuture +} + +// Version extracts the version of this UUID variant. The RFC 4122 describes +// five kinds of UUIDs. +func (u UUID) Version() int { + return int(u[6] & 0xF0 >> 4) +} + +// Node extracts the MAC address of the node who generated this UUID. It will +// return nil if the UUID is not a time based UUID (version 1). +func (u UUID) Node() []byte { + if u.Version() != 1 { + return nil + } + return u[10:] +} + +// Timestamp extracts the timestamp information from a time based UUID +// (version 1). +func (u UUID) Timestamp() int64 { + if u.Version() != 1 { + return 0 + } + return int64(uint64(u[0])<<24|uint64(u[1])<<16| + uint64(u[2])<<8|uint64(u[3])) + + int64(uint64(u[4])<<40|uint64(u[5])<<32) + + int64(uint64(u[6]&0x0F)<<56|uint64(u[7])<<48) +} + +// Time is like Timestamp, except that it returns a time.Time. +func (u UUID) Time() time.Time { + if u.Version() != 1 { + return time.Time{} + } + t := u.Timestamp() + sec := t / 1e7 + nsec := (t % 1e7) * 100 + return time.Unix(sec+timeBase, nsec).UTC() +} + +// Marshaling for JSON +func (u UUID) MarshalJSON() ([]byte, error) { + return []byte(`"` + u.String() + `"`), nil +} + +// Unmarshaling for JSON +func (u *UUID) UnmarshalJSON(data []byte) error { + str := strings.Trim(string(data), `"`) + if len(str) > 36 { + return fmt.Errorf("invalid JSON UUID %s", str) + } + + parsed, err := ParseUUID(str) + if err == nil { + copy(u[:], parsed[:]) + } + + return err +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/uuid_test.go b/Godeps/_workspace/src/github.com/gocql/gocql/uuid_test.go new file mode 100644 index 000000000..ec6a69ff9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/uuid_test.go @@ -0,0 +1,197 @@ +// +build all unit + +package gocql + +import ( + "bytes" + "strings" + "testing" + "time" +) + +func TestUUIDNil(t *testing.T) { + var uuid UUID + want, got := "00000000-0000-0000-0000-000000000000", uuid.String() + if want != got { + t.Fatalf("TestNil: expected %q got %q", want, got) + } +} + +var testsUUID = []struct { + input string + variant int + version int +}{ + {"b4f00409-cef8-4822-802c-deb20704c365", VariantIETF, 4}, + {"B4F00409-CEF8-4822-802C-DEB20704C365", VariantIETF, 4}, //Use capital letters + {"f81d4fae-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1}, + {"00000000-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1}, + {"3051a8d7-aea7-1801-e0bf-bc539dd60cf3", VariantFuture, 1}, + {"3051a8d7-aea7-2801-e0bf-bc539dd60cf3", VariantFuture, 2}, + {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 3}, + {"3051a8d7-aea7-4801-e0bf-bc539dd60cf3", VariantFuture, 4}, + {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 5}, + {"d0e817e1-e4b1-1801-3fe6-b4b60ccecf9d", VariantNCSCompat, 0}, + {"d0e817e1-e4b1-1801-bfe6-b4b60ccecf9d", VariantIETF, 1}, + {"d0e817e1-e4b1-1801-dfe6-b4b60ccecf9d", VariantMicrosoft, 0}, + {"d0e817e1-e4b1-1801-ffe6-b4b60ccecf9d", VariantFuture, 0}, +} + +func TestPredefinedUUID(t *testing.T) { + for i := range testsUUID { + uuid, err := ParseUUID(testsUUID[i].input) + if err != nil { + t.Errorf("ParseUUID #%d: %v", i, err) + continue + } + + if str := uuid.String(); str != strings.ToLower(testsUUID[i].input) { + t.Errorf("String #%d: expected %q got %q", i, testsUUID[i].input, str) + continue + } + + if variant := uuid.Variant(); variant != testsUUID[i].variant { + t.Errorf("Variant #%d: expected %d got %d", i, testsUUID[i].variant, variant) + } + + if testsUUID[i].variant == VariantIETF { + if version := uuid.Version(); version != testsUUID[i].version { + t.Errorf("Version #%d: expected %d got %d", i, testsUUID[i].version, version) + } + } + + json, err := uuid.MarshalJSON() + if err != nil { + t.Errorf("MarshalJSON #%d: %v", i, err) + } + expectedJson := `"` + strings.ToLower(testsUUID[i].input) + `"` + if string(json) != expectedJson { + t.Errorf("MarshalJSON #%d: expected %v got %v", i, expectedJson, string(json)) + } + + var unmarshaled UUID + err = unmarshaled.UnmarshalJSON(json) + if err != nil { + t.Errorf("UnmarshalJSON #%d: %v", i, err) + } + if unmarshaled != uuid { + t.Errorf("UnmarshalJSON #%d: expected %v got %v", i, uuid, unmarshaled) + } + } +} + +func TestInvalidUUIDCharacter(t *testing.T) { + _, err := ParseUUID("z4f00409-cef8-4822-802c-deb20704c365") + if err == nil || !strings.Contains(err.Error(), "invalid UUID") { + t.Fatalf("expected invalid UUID error, got '%v' ", err) + } +} + +func TestInvalidUUIDLength(t *testing.T) { + _, err := ParseUUID("4f00") + if err == nil || !strings.Contains(err.Error(), "invalid UUID") { + t.Fatalf("expected invalid UUID error, got '%v' ", err) + } + + _, err = UUIDFromBytes(TimeUUID().Bytes()[:15]) + if err == nil || err.Error() != "UUIDs must be exactly 16 bytes long" { + t.Fatalf("expected error '%v', got '%v'", "UUIDs must be exactly 16 bytes long", err) + } +} + +func TestRandomUUID(t *testing.T) { + for i := 0; i < 20; i++ { + uuid, err := RandomUUID() + if err != nil { + t.Errorf("RandomUUID: %v", err) + } + if variant := uuid.Variant(); variant != VariantIETF { + t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant) + } + if version := uuid.Version(); version != 4 { + t.Errorf("wrong version. expected %d got %d", 4, version) + } + } +} + +func TestRandomUUIDInvalidAPICalls(t *testing.T) { + uuid, err := RandomUUID() + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + if node := uuid.Node(); node != nil { + t.Fatalf("expected nil, got %v", node) + } + + if stamp := uuid.Timestamp(); stamp != 0 { + t.Fatalf("expceted 0, got %v", stamp) + } + zeroT := time.Time{} + if to := uuid.Time(); to != zeroT { + t.Fatalf("expected %v, got %v", zeroT, to) + } +} + +func TestUUIDFromTime(t *testing.T) { + date := time.Date(1982, 5, 5, 12, 34, 56, 400, time.UTC) + uuid := UUIDFromTime(date) + + if uuid.Time() != date { + t.Errorf("embedded time incorrect. Expected %v got %v", date, uuid.Time()) + } +} + +func TestParseUUID(t *testing.T) { + uuid, _ := ParseUUID("486f3a88-775b-11e3-ae07-d231feb1dc81") + if uuid.Time() != time.Date(2014, 1, 7, 5, 19, 29, 222516000, time.UTC) { + t.Errorf("Expected date of 1/7/2014 at 5:19:29.222516, got %v", uuid.Time()) + } +} + +func TestTimeUUID(t *testing.T) { + var node []byte + timestamp := int64(0) + for i := 0; i < 20; i++ { + uuid := TimeUUID() + + if variant := uuid.Variant(); variant != VariantIETF { + t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant) + } + if version := uuid.Version(); version != 1 { + t.Errorf("wrong version. expected %d got %d", 1, version) + } + + if n := uuid.Node(); !bytes.Equal(n, node) && i > 0 { + t.Errorf("wrong node. expected %x, got %x", node, n) + } else if i == 0 { + node = n + } + + ts := uuid.Timestamp() + if ts < timestamp { + t.Errorf("timestamps must grow") + } + timestamp = ts + } +} + +func TestUnmarshalJSON(t *testing.T) { + var withHyphens, withoutHypens, tooLong UUID + + withHyphens.UnmarshalJSON([]byte(`"486f3a88-775b-11e3-ae07-d231feb1dc81"`)) + if withHyphens.Time().Truncate(time.Second) != time.Date(2014, 1, 7, 5, 19, 29, 0, time.UTC) { + t.Errorf("Expected date of 1/7/2014 at 5:19:29, got %v", withHyphens.Time()) + } + + withoutHypens.UnmarshalJSON([]byte(`"486f3a88775b11e3ae07d231feb1dc81"`)) + if withoutHypens.Time().Truncate(time.Second) != time.Date(2014, 1, 7, 5, 19, 29, 0, time.UTC) { + t.Errorf("Expected date of 1/7/2014 at 5:19:29, got %v", withoutHypens.Time()) + } + + err := tooLong.UnmarshalJSON([]byte(`"486f3a88-775b-11e3-ae07-d231feb1dc81486f3a88"`)) + if err == nil { + t.Errorf("no error for invalid JSON UUID") + } + +} diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.css b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.css new file mode 100644 index 000000000..ad1173569 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.css @@ -0,0 +1,384 @@ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} + +.btn:active, +.btn.active { + background-image: none; +} + +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%); + background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%); + background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%); + background-repeat: repeat-x; + border-color: #e0e0e0; + border-color: #ccc; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); +} + +.btn-default:active, +.btn-default.active { + background-color: #e6e6e6; + border-color: #e0e0e0; +} + +.btn-primary { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); + background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); + background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); + background-repeat: repeat-x; + border-color: #2d6ca2; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); +} + +.btn-primary:active, +.btn-primary.active { + background-color: #3071a9; + border-color: #2d6ca2; +} + +.btn-success { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); + background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); + background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + background-repeat: repeat-x; + border-color: #419641; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); +} + +.btn-success:active, +.btn-success.active { + background-color: #449d44; + border-color: #419641; +} + +.btn-warning { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); + background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); + background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + background-repeat: repeat-x; + border-color: #eb9316; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); +} + +.btn-warning:active, +.btn-warning.active { + background-color: #ec971f; + border-color: #eb9316; +} + +.btn-danger { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); + background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); + background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + background-repeat: repeat-x; + border-color: #c12e2a; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); +} + +.btn-danger:active, +.btn-danger.active { + background-color: #c9302c; + border-color: #c12e2a; +} + +.btn-info { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); + background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); + background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + background-repeat: repeat-x; + border-color: #2aabd2; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); +} + +.btn-info:active, +.btn-info.active { + background-color: #31b0d5; + border-color: #2aabd2; +} + +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #357ebd; + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); + background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); + background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); +} + +.navbar { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#f8f8f8)); + background-image: -webkit-linear-gradient(top, #ffffff, 0%, #f8f8f8, 100%); + background-image: -moz-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); + background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); + background-repeat: repeat-x; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); +} + +.navbar .navbar-nav > .active > a { + background-color: #f8f8f8; +} + +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); +} + +.navbar-inverse { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#3c3c3c), to(#222222)); + background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222222, 100%); + background-image: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); +} + +.navbar-inverse .navbar-nav > .active > a { + background-color: #222222; +} + +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} + +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.alert-success { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#c8e5bc)); + background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%); + background-image: -moz-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + background-repeat: repeat-x; + border-color: #b2dba1; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); +} + +.alert-info { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#b9def0)); + background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #b9def0, 100%); + background-image: -moz-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + background-repeat: repeat-x; + border-color: #9acfea; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); +} + +.alert-warning { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#f8efc0)); + background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%); + background-image: -moz-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + background-repeat: repeat-x; + border-color: #f5e79e; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); +} + +.alert-danger { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#e7c3c3)); + background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%); + background-image: -moz-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + background-repeat: repeat-x; + border-color: #dca7a7; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); +} + +.progress { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ebebeb), to(#f5f5f5)); + background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%); + background-image: -moz-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); +} + +.progress-bar { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); + background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); + background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); +} + +.progress-bar-success { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); + background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); + background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); +} + +.progress-bar-info { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); + background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); + background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); +} + +.progress-bar-warning { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); + background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); + background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); +} + +.progress-bar-danger { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); + background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); + background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); +} + +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); +} + +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #3071a9; + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3278b3)); + background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%); + background-image: -moz-linear-gradient(top, #428bca 0%, #3278b3 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); + background-repeat: repeat-x; + border-color: #3278b3; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); +} + +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.panel-default > .panel-heading { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f5f5f5), to(#e8e8e8)); + background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%); + background-image: -moz-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); +} + +.panel-primary > .panel-heading { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); + background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); + background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); +} + +.panel-success > .panel-heading { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#d0e9c6)); + background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%); + background-image: -moz-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); +} + +.panel-info > .panel-heading { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#c4e3f3)); + background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%); + background-image: -moz-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); +} + +.panel-warning > .panel-heading { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#faf2cc)); + background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%); + background-image: -moz-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); +} + +.panel-danger > .panel-heading { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#ebcccc)); + background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%); + background-image: -moz-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); +} + +.well { + background-image: -webkit-gradient(linear, left 0%, left 100%, from(#e8e8e8), to(#f5f5f5)); + background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%); + background-image: -moz-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + background-repeat: repeat-x; + border-color: #dcdcdc; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.min.css b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.min.css new file mode 100644 index 000000000..cad36b4e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap-theme.min.css @@ -0,0 +1 @@ +.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,0%,#e6e6e6,100%);background-image:-moz-linear-gradient(top,#fff 0,#e6e6e6 100%);background-image:linear-gradient(to bottom,#fff 0,#e6e6e6 100%);background-repeat:repeat-x;border-color:#e0e0e0;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0)}.btn-default:active,.btn-default.active{background-color:#e6e6e6;border-color:#e0e0e0}.btn-primary{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;border-color:#2d6ca2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.btn-primary:active,.btn-primary.active{background-color:#3071a9;border-color:#2d6ca2}.btn-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;border-color:#419641;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.btn-success:active,.btn-success.active{background-color:#449d44;border-color:#419641}.btn-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;border-color:#eb9316;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.btn-warning:active,.btn-warning.active{background-color:#ec971f;border-color:#eb9316}.btn-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;border-color:#c12e2a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.btn-danger:active,.btn-danger.active{background-color:#c9302c;border-color:#c12e2a}.btn-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;border-color:#2aabd2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.btn-info:active,.btn-info.active{background-color:#31b0d5;border-color:#2aabd2}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#f8f8f8));background-image:-webkit-linear-gradient(top,#fff,0%,#f8f8f8,100%);background-image:-moz-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar .navbar-nav>.active>a{background-color:#f8f8f8}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-gradient(linear,left 0,left 100%,from(#3c3c3c),to(#222));background-image:-webkit-linear-gradient(top,#3c3c3c,0%,#222,100%);background-image:-moz-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0)}.navbar-inverse .navbar-nav>.active>a{background-color:#222}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#c8e5bc));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#c8e5bc,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#b9def0));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#b9def0,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#f8efc0));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#f8efc0,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#e7c3c3));background-image:-webkit-linear-gradient(top,#f2dede,0%,#e7c3c3,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-gradient(linear,left 0,left 100%,from(#ebebeb),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#ebebeb,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3278b3));background-image:-webkit-linear-gradient(top,#428bca,0%,#3278b3,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f5f5f5),to(#e8e8e8));background-image:-webkit-linear-gradient(top,#f5f5f5,0%,#e8e8e8,100%);background-image:-moz-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#d0e9c6));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#d0e9c6,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#c4e3f3));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#c4e3f3,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#faf2cc));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#faf2cc,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#ebcccc));background-image:-webkit-linear-gradient(top,#f2dede,0%,#ebcccc,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-gradient(linear,left 0,left 100%,from(#e8e8e8),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#e8e8e8,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.css b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.css new file mode 100644 index 000000000..bbda4eed4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.css @@ -0,0 +1,6805 @@ +/*! + * Bootstrap v3.0.0 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + */ + +/*! normalize.css v2.1.0 | MIT License | git.io/normalize */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +audio, +canvas, +video { + display: inline-block; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +[hidden] { + display: none; +} + +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body { + margin: 0; +} + +a:focus { + outline: thin dotted; +} + +a:active, +a:hover { + outline: 0; +} + +h1 { + margin: 0.67em 0; + font-size: 2em; +} + +abbr[title] { + border-bottom: 1px dotted; +} + +b, +strong { + font-weight: bold; +} + +dfn { + font-style: italic; +} + +hr { + height: 0; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +mark { + color: #000; + background: #ff0; +} + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +pre { + white-space: pre-wrap; +} + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + border: 0; +} + +svg:not(:root) { + overflow: hidden; +} + +figure { + margin: 0; +} + +fieldset { + padding: 0.35em 0.625em 0.75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} + +legend { + padding: 0; + border: 0; +} + +button, +input, +select, +textarea { + margin: 0; + font-family: inherit; + font-size: 100%; +} + +button, +input { + line-height: normal; +} + +button, +select { + text-transform: none; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +button[disabled], +html input[disabled] { + cursor: default; +} + +input[type="checkbox"], +input[type="radio"] { + padding: 0; + box-sizing: border-box; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 2cm .5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .table td, + .table th { + background-color: #fff !important; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} + +*, +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +html { + font-size: 62.5%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.428571429; + color: #333333; + background-color: #ffffff; +} + +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +input, +select[multiple], +textarea { + background-image: none; +} + +a { + color: #428bca; + text-decoration: none; +} + +a:hover, +a:focus { + color: #2a6496; + text-decoration: underline; +} + +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +img { + vertical-align: middle; +} + +.img-responsive { + display: block; + height: auto; + max-width: 100%; +} + +.img-rounded { + border-radius: 6px; +} + +.img-thumbnail { + display: inline-block; + height: auto; + max-width: 100%; + padding: 4px; + line-height: 1.428571429; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.img-circle { + border-radius: 50%; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0 0 0 0); + border: 0; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 16.099999999999998px; + font-weight: 200; + line-height: 1.4; +} + +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} + +small { + font-size: 85%; +} + +cite { + font-style: normal; +} + +.text-muted { + color: #999999; +} + +.text-primary { + color: #428bca; +} + +.text-warning { + color: #c09853; +} + +.text-danger { + color: #b94a48; +} + +.text-success { + color: #468847; +} + +.text-info { + color: #3a87ad; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 500; + line-height: 1.1; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} + +h1, +h2, +h3 { + margin-top: 20px; + margin-bottom: 10px; +} + +h4, +h5, +h6 { + margin-top: 10px; + margin-bottom: 10px; +} + +h1, +.h1 { + font-size: 36px; +} + +h2, +.h2 { + font-size: 30px; +} + +h3, +.h3 { + font-size: 24px; +} + +h4, +.h4 { + font-size: 18px; +} + +h5, +.h5 { + font-size: 14px; +} + +h6, +.h6 { + font-size: 12px; +} + +h1 small, +.h1 small { + font-size: 24px; +} + +h2 small, +.h2 small { + font-size: 18px; +} + +h3 small, +.h3 small, +h4 small, +.h4 small { + font-size: 14px; +} + +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eeeeee; +} + +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} + +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} + +dl { + margin-bottom: 20px; +} + +dt, +dd { + line-height: 1.428571429; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 0; +} + +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } + .dl-horizontal dd:before, + .dl-horizontal dd:after { + display: table; + content: " "; + } + .dl-horizontal dd:after { + clear: both; + } + .dl-horizontal dd:before, + .dl-horizontal dd:after { + display: table; + content: " "; + } + .dl-horizontal dd:after { + clear: both; + } +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + font-size: 17.5px; + font-weight: 300; + line-height: 1.25; +} + +blockquote p:last-child { + margin-bottom: 0; +} + +blockquote small { + display: block; + line-height: 1.428571429; + color: #999999; +} + +blockquote small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} + +blockquote.pull-right small:before { + content: ''; +} + +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} + +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 1.428571429; +} + +code, +pre { + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; +} + +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + white-space: nowrap; + background-color: #f9f2f4; + border-radius: 4px; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.428571429; + color: #333333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #cccccc; + border-radius: 4px; +} + +pre.prettyprint { + margin-bottom: 20px; +} + +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.container:before, +.container:after { + display: table; + content: " "; +} + +.container:after { + clear: both; +} + +.container:before, +.container:after { + display: table; + content: " "; +} + +.container:after { + clear: both; +} + +.row { + margin-right: -15px; + margin-left: -15px; +} + +.row:before, +.row:after { + display: table; + content: " "; +} + +.row:after { + clear: both; +} + +.row:before, +.row:after { + display: table; + content: " "; +} + +.row:after { + clear: both; +} + +.col-xs-1, +.col-xs-2, +.col-xs-3, +.col-xs-4, +.col-xs-5, +.col-xs-6, +.col-xs-7, +.col-xs-8, +.col-xs-9, +.col-xs-10, +.col-xs-11, +.col-xs-12, +.col-sm-1, +.col-sm-2, +.col-sm-3, +.col-sm-4, +.col-sm-5, +.col-sm-6, +.col-sm-7, +.col-sm-8, +.col-sm-9, +.col-sm-10, +.col-sm-11, +.col-sm-12, +.col-md-1, +.col-md-2, +.col-md-3, +.col-md-4, +.col-md-5, +.col-md-6, +.col-md-7, +.col-md-8, +.col-md-9, +.col-md-10, +.col-md-11, +.col-md-12, +.col-lg-1, +.col-lg-2, +.col-lg-3, +.col-lg-4, +.col-lg-5, +.col-lg-6, +.col-lg-7, +.col-lg-8, +.col-lg-9, +.col-lg-10, +.col-lg-11, +.col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-xs-1, +.col-xs-2, +.col-xs-3, +.col-xs-4, +.col-xs-5, +.col-xs-6, +.col-xs-7, +.col-xs-8, +.col-xs-9, +.col-xs-10, +.col-xs-11 { + float: left; +} + +.col-xs-1 { + width: 8.333333333333332%; +} + +.col-xs-2 { + width: 16.666666666666664%; +} + +.col-xs-3 { + width: 25%; +} + +.col-xs-4 { + width: 33.33333333333333%; +} + +.col-xs-5 { + width: 41.66666666666667%; +} + +.col-xs-6 { + width: 50%; +} + +.col-xs-7 { + width: 58.333333333333336%; +} + +.col-xs-8 { + width: 66.66666666666666%; +} + +.col-xs-9 { + width: 75%; +} + +.col-xs-10 { + width: 83.33333333333334%; +} + +.col-xs-11 { + width: 91.66666666666666%; +} + +.col-xs-12 { + width: 100%; +} + +@media (min-width: 768px) { + .container { + max-width: 750px; + } + .col-sm-1, + .col-sm-2, + .col-sm-3, + .col-sm-4, + .col-sm-5, + .col-sm-6, + .col-sm-7, + .col-sm-8, + .col-sm-9, + .col-sm-10, + .col-sm-11 { + float: left; + } + .col-sm-1 { + width: 8.333333333333332%; + } + .col-sm-2 { + width: 16.666666666666664%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-4 { + width: 33.33333333333333%; + } + .col-sm-5 { + width: 41.66666666666667%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-7 { + width: 58.333333333333336%; + } + .col-sm-8 { + width: 66.66666666666666%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-10 { + width: 83.33333333333334%; + } + .col-sm-11 { + width: 91.66666666666666%; + } + .col-sm-12 { + width: 100%; + } + .col-sm-push-1 { + left: 8.333333333333332%; + } + .col-sm-push-2 { + left: 16.666666666666664%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-4 { + left: 33.33333333333333%; + } + .col-sm-push-5 { + left: 41.66666666666667%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-7 { + left: 58.333333333333336%; + } + .col-sm-push-8 { + left: 66.66666666666666%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-10 { + left: 83.33333333333334%; + } + .col-sm-push-11 { + left: 91.66666666666666%; + } + .col-sm-pull-1 { + right: 8.333333333333332%; + } + .col-sm-pull-2 { + right: 16.666666666666664%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-4 { + right: 33.33333333333333%; + } + .col-sm-pull-5 { + right: 41.66666666666667%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-7 { + right: 58.333333333333336%; + } + .col-sm-pull-8 { + right: 66.66666666666666%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-10 { + right: 83.33333333333334%; + } + .col-sm-pull-11 { + right: 91.66666666666666%; + } + .col-sm-offset-1 { + margin-left: 8.333333333333332%; + } + .col-sm-offset-2 { + margin-left: 16.666666666666664%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-4 { + margin-left: 33.33333333333333%; + } + .col-sm-offset-5 { + margin-left: 41.66666666666667%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-7 { + margin-left: 58.333333333333336%; + } + .col-sm-offset-8 { + margin-left: 66.66666666666666%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-10 { + margin-left: 83.33333333333334%; + } + .col-sm-offset-11 { + margin-left: 91.66666666666666%; + } +} + +@media (min-width: 992px) { + .container { + max-width: 970px; + } + .col-md-1, + .col-md-2, + .col-md-3, + .col-md-4, + .col-md-5, + .col-md-6, + .col-md-7, + .col-md-8, + .col-md-9, + .col-md-10, + .col-md-11 { + float: left; + } + .col-md-1 { + width: 8.333333333333332%; + } + .col-md-2 { + width: 16.666666666666664%; + } + .col-md-3 { + width: 25%; + } + .col-md-4 { + width: 33.33333333333333%; + } + .col-md-5 { + width: 41.66666666666667%; + } + .col-md-6 { + width: 50%; + } + .col-md-7 { + width: 58.333333333333336%; + } + .col-md-8 { + width: 66.66666666666666%; + } + .col-md-9 { + width: 75%; + } + .col-md-10 { + width: 83.33333333333334%; + } + .col-md-11 { + width: 91.66666666666666%; + } + .col-md-12 { + width: 100%; + } + .col-md-push-0 { + left: auto; + } + .col-md-push-1 { + left: 8.333333333333332%; + } + .col-md-push-2 { + left: 16.666666666666664%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-4 { + left: 33.33333333333333%; + } + .col-md-push-5 { + left: 41.66666666666667%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-7 { + left: 58.333333333333336%; + } + .col-md-push-8 { + left: 66.66666666666666%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-10 { + left: 83.33333333333334%; + } + .col-md-push-11 { + left: 91.66666666666666%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-pull-1 { + right: 8.333333333333332%; + } + .col-md-pull-2 { + right: 16.666666666666664%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-4 { + right: 33.33333333333333%; + } + .col-md-pull-5 { + right: 41.66666666666667%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-7 { + right: 58.333333333333336%; + } + .col-md-pull-8 { + right: 66.66666666666666%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-10 { + right: 83.33333333333334%; + } + .col-md-pull-11 { + right: 91.66666666666666%; + } + .col-md-offset-0 { + margin-left: 0; + } + .col-md-offset-1 { + margin-left: 8.333333333333332%; + } + .col-md-offset-2 { + margin-left: 16.666666666666664%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-4 { + margin-left: 33.33333333333333%; + } + .col-md-offset-5 { + margin-left: 41.66666666666667%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-7 { + margin-left: 58.333333333333336%; + } + .col-md-offset-8 { + margin-left: 66.66666666666666%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-10 { + margin-left: 83.33333333333334%; + } + .col-md-offset-11 { + margin-left: 91.66666666666666%; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1170px; + } + .col-lg-1, + .col-lg-2, + .col-lg-3, + .col-lg-4, + .col-lg-5, + .col-lg-6, + .col-lg-7, + .col-lg-8, + .col-lg-9, + .col-lg-10, + .col-lg-11 { + float: left; + } + .col-lg-1 { + width: 8.333333333333332%; + } + .col-lg-2 { + width: 16.666666666666664%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-4 { + width: 33.33333333333333%; + } + .col-lg-5 { + width: 41.66666666666667%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-7 { + width: 58.333333333333336%; + } + .col-lg-8 { + width: 66.66666666666666%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-10 { + width: 83.33333333333334%; + } + .col-lg-11 { + width: 91.66666666666666%; + } + .col-lg-12 { + width: 100%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-push-1 { + left: 8.333333333333332%; + } + .col-lg-push-2 { + left: 16.666666666666664%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-4 { + left: 33.33333333333333%; + } + .col-lg-push-5 { + left: 41.66666666666667%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-7 { + left: 58.333333333333336%; + } + .col-lg-push-8 { + left: 66.66666666666666%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-10 { + left: 83.33333333333334%; + } + .col-lg-push-11 { + left: 91.66666666666666%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-pull-1 { + right: 8.333333333333332%; + } + .col-lg-pull-2 { + right: 16.666666666666664%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-4 { + right: 33.33333333333333%; + } + .col-lg-pull-5 { + right: 41.66666666666667%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-7 { + right: 58.333333333333336%; + } + .col-lg-pull-8 { + right: 66.66666666666666%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-10 { + right: 83.33333333333334%; + } + .col-lg-pull-11 { + right: 91.66666666666666%; + } + .col-lg-offset-0 { + margin-left: 0; + } + .col-lg-offset-1 { + margin-left: 8.333333333333332%; + } + .col-lg-offset-2 { + margin-left: 16.666666666666664%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-4 { + margin-left: 33.33333333333333%; + } + .col-lg-offset-5 { + margin-left: 41.66666666666667%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-7 { + margin-left: 58.333333333333336%; + } + .col-lg-offset-8 { + margin-left: 66.66666666666666%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-10 { + margin-left: 83.33333333333334%; + } + .col-lg-offset-11 { + margin-left: 91.66666666666666%; + } +} + +table { + max-width: 100%; + background-color: transparent; +} + +th { + text-align: left; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table thead > tr > th, +.table tbody > tr > th, +.table tfoot > tr > th, +.table thead > tr > td, +.table tbody > tr > td, +.table tfoot > tr > td { + padding: 8px; + line-height: 1.428571429; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #dddddd; +} + +.table caption + thead tr:first-child th, +.table colgroup + thead tr:first-child th, +.table thead:first-child tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child td { + border-top: 0; +} + +.table tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table .table { + background-color: #ffffff; +} + +.table-condensed thead > tr > th, +.table-condensed tbody > tr > th, +.table-condensed tfoot > tr > th, +.table-condensed thead > tr > td, +.table-condensed tbody > tr > td, +.table-condensed tfoot > tr > td { + padding: 5px; +} + +.table-bordered { + border: 1px solid #dddddd; +} + +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #dddddd; +} + +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} + +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} + +.table-hover > tbody > tr:hover > td, +.table-hover > tbody > tr:hover > th { + background-color: #f5f5f5; +} + +table col[class*="col-"] { + display: table-column; + float: none; +} + +table td[class*="col-"], +table th[class*="col-"] { + display: table-cell; + float: none; +} + +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} + +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td { + background-color: #d0e9c6; + border-color: #c9e2b3; +} + +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; + border-color: #eed3d7; +} + +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td { + background-color: #ebcccc; + border-color: #e6c1c7; +} + +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; + border-color: #fbeed5; +} + +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td { + background-color: #faf2cc; + border-color: #f8e5be; +} + +@media (max-width: 768px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-x: scroll; + overflow-y: hidden; + border: 1px solid #dddddd; + } + .table-responsive > .table { + margin-bottom: 0; + background-color: #fff; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > thead > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > thead > tr:last-child > td, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +label { + display: inline-block; + margin-bottom: 5px; + font-weight: bold; +} + +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + /* IE8-9 */ + + line-height: normal; +} + +input[type="file"] { + display: block; +} + +select[multiple], +select[size] { + height: auto; +} + +select optgroup { + font-family: inherit; + font-size: inherit; + font-style: inherit; +} + +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +input[type="number"]::-webkit-outer-spin-button, +input[type="number"]::-webkit-inner-spin-button { + height: auto; +} + +.form-control:-moz-placeholder { + color: #999999; +} + +.form-control::-moz-placeholder { + color: #999999; +} + +.form-control:-ms-input-placeholder { + color: #999999; +} + +.form-control::-webkit-input-placeholder { + color: #999999; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + color: #555555; + vertical-align: middle; + background-color: #ffffff; + border: 1px solid #cccccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +} + +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +} + +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eeeeee; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 15px; +} + +.radio, +.checkbox { + display: block; + min-height: 20px; + padding-left: 20px; + margin-top: 10px; + margin-bottom: 10px; + vertical-align: middle; +} + +.radio label, +.checkbox label { + display: inline; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} + +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} + +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} + +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +.radio[disabled], +.radio-inline[disabled], +.checkbox[disabled], +.checkbox-inline[disabled], +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"], +fieldset[disabled] .radio, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} + +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +select.input-sm { + height: 30px; + line-height: 30px; +} + +textarea.input-sm { + height: auto; +} + +.input-lg { + height: 45px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +select.input-lg { + height: 45px; + line-height: 45px; +} + +textarea.input-lg { + height: auto; +} + +.has-warning .help-block, +.has-warning .control-label { + color: #c09853; +} + +.has-warning .form-control { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-warning .form-control:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.has-warning .input-group-addon { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.has-error .help-block, +.has-error .control-label { + color: #b94a48; +} + +.has-error .form-control { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-error .form-control:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.has-error .input-group-addon { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.has-success .help-block, +.has-success .control-label { + color: #468847; +} + +.has-success .form-control { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-success .form-control:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.has-success .input-group-addon { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.form-control-static { + padding-top: 7px; + margin-bottom: 0; +} + +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} + +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } +} + +.form-horizontal .control-label, +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} + +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} + +.form-horizontal .form-group:before, +.form-horizontal .form-group:after { + display: table; + content: " "; +} + +.form-horizontal .form-group:after { + clear: both; +} + +.form-horizontal .form-group:before, +.form-horizontal .form-group:after { + display: table; + content: " "; +} + +.form-horizontal .form-group:after { + clear: both; +} + +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + } +} + +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.428571429; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + border: 1px solid transparent; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; +} + +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} + +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-default { + color: #333333; + background-color: #ffffff; + border-color: #cccccc; +} + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #333333; + background-color: #ebebeb; + border-color: #adadad; +} + +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + background-image: none; +} + +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #ffffff; + border-color: #cccccc; +} + +.btn-primary { + color: #ffffff; + background-color: #428bca; + border-color: #357ebd; +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #3276b1; + border-color: #285e8e; +} + +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #428bca; + border-color: #357ebd; +} + +.btn-warning { + color: #ffffff; + background-color: #f0ad4e; + border-color: #eea236; +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #ffffff; + background-color: #ed9c28; + border-color: #d58512; +} + +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + background-image: none; +} + +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} + +.btn-danger { + color: #ffffff; + background-color: #d9534f; + border-color: #d43f3a; +} + +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #ffffff; + background-color: #d2322d; + border-color: #ac2925; +} + +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + background-image: none; +} + +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} + +.btn-success { + color: #ffffff; + background-color: #5cb85c; + border-color: #4cae4c; +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #ffffff; + background-color: #47a447; + border-color: #398439; +} + +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + background-image: none; +} + +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} + +.btn-info { + color: #ffffff; + background-color: #5bc0de; + border-color: #46b8da; +} + +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #ffffff; + background-color: #39b3d7; + border-color: #269abc; +} + +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + background-image: none; +} + +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} + +.btn-link { + font-weight: normal; + color: #428bca; + cursor: pointer; + border-radius: 0; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} + +.btn-link:hover, +.btn-link:focus { + color: #2a6496; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #999999; + text-decoration: none; +} + +.btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +.btn-sm, +.btn-xs { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-xs { + padding: 1px 5px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + display: none; +} + +.collapse.in { + display: block; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + transition: height 0.35s ease; +} + +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + -webkit-font-smoothing: antialiased; + font-style: normal; + font-weight: normal; + line-height: 1; +} + +.glyphicon-asterisk:before { + content: "\2a"; +} + +.glyphicon-plus:before { + content: "\2b"; +} + +.glyphicon-euro:before { + content: "\20ac"; +} + +.glyphicon-minus:before { + content: "\2212"; +} + +.glyphicon-cloud:before { + content: "\2601"; +} + +.glyphicon-envelope:before { + content: "\2709"; +} + +.glyphicon-pencil:before { + content: "\270f"; +} + +.glyphicon-glass:before { + content: "\e001"; +} + +.glyphicon-music:before { + content: "\e002"; +} + +.glyphicon-search:before { + content: "\e003"; +} + +.glyphicon-heart:before { + content: "\e005"; +} + +.glyphicon-star:before { + content: "\e006"; +} + +.glyphicon-star-empty:before { + content: "\e007"; +} + +.glyphicon-user:before { + content: "\e008"; +} + +.glyphicon-film:before { + content: "\e009"; +} + +.glyphicon-th-large:before { + content: "\e010"; +} + +.glyphicon-th:before { + content: "\e011"; +} + +.glyphicon-th-list:before { + content: "\e012"; +} + +.glyphicon-ok:before { + content: "\e013"; +} + +.glyphicon-remove:before { + content: "\e014"; +} + +.glyphicon-zoom-in:before { + content: "\e015"; +} + +.glyphicon-zoom-out:before { + content: "\e016"; +} + +.glyphicon-off:before { + content: "\e017"; +} + +.glyphicon-signal:before { + content: "\e018"; +} + +.glyphicon-cog:before { + content: "\e019"; +} + +.glyphicon-trash:before { + content: "\e020"; +} + +.glyphicon-home:before { + content: "\e021"; +} + +.glyphicon-file:before { + content: "\e022"; +} + +.glyphicon-time:before { + content: "\e023"; +} + +.glyphicon-road:before { + content: "\e024"; +} + +.glyphicon-download-alt:before { + content: "\e025"; +} + +.glyphicon-download:before { + content: "\e026"; +} + +.glyphicon-upload:before { + content: "\e027"; +} + +.glyphicon-inbox:before { + content: "\e028"; +} + +.glyphicon-play-circle:before { + content: "\e029"; +} + +.glyphicon-repeat:before { + content: "\e030"; +} + +.glyphicon-refresh:before { + content: "\e031"; +} + +.glyphicon-list-alt:before { + content: "\e032"; +} + +.glyphicon-flag:before { + content: "\e034"; +} + +.glyphicon-headphones:before { + content: "\e035"; +} + +.glyphicon-volume-off:before { + content: "\e036"; +} + +.glyphicon-volume-down:before { + content: "\e037"; +} + +.glyphicon-volume-up:before { + content: "\e038"; +} + +.glyphicon-qrcode:before { + content: "\e039"; +} + +.glyphicon-barcode:before { + content: "\e040"; +} + +.glyphicon-tag:before { + content: "\e041"; +} + +.glyphicon-tags:before { + content: "\e042"; +} + +.glyphicon-book:before { + content: "\e043"; +} + +.glyphicon-print:before { + content: "\e045"; +} + +.glyphicon-font:before { + content: "\e047"; +} + +.glyphicon-bold:before { + content: "\e048"; +} + +.glyphicon-italic:before { + content: "\e049"; +} + +.glyphicon-text-height:before { + content: "\e050"; +} + +.glyphicon-text-width:before { + content: "\e051"; +} + +.glyphicon-align-left:before { + content: "\e052"; +} + +.glyphicon-align-center:before { + content: "\e053"; +} + +.glyphicon-align-right:before { + content: "\e054"; +} + +.glyphicon-align-justify:before { + content: "\e055"; +} + +.glyphicon-list:before { + content: "\e056"; +} + +.glyphicon-indent-left:before { + content: "\e057"; +} + +.glyphicon-indent-right:before { + content: "\e058"; +} + +.glyphicon-facetime-video:before { + content: "\e059"; +} + +.glyphicon-picture:before { + content: "\e060"; +} + +.glyphicon-map-marker:before { + content: "\e062"; +} + +.glyphicon-adjust:before { + content: "\e063"; +} + +.glyphicon-tint:before { + content: "\e064"; +} + +.glyphicon-edit:before { + content: "\e065"; +} + +.glyphicon-share:before { + content: "\e066"; +} + +.glyphicon-check:before { + content: "\e067"; +} + +.glyphicon-move:before { + content: "\e068"; +} + +.glyphicon-step-backward:before { + content: "\e069"; +} + +.glyphicon-fast-backward:before { + content: "\e070"; +} + +.glyphicon-backward:before { + content: "\e071"; +} + +.glyphicon-play:before { + content: "\e072"; +} + +.glyphicon-pause:before { + content: "\e073"; +} + +.glyphicon-stop:before { + content: "\e074"; +} + +.glyphicon-forward:before { + content: "\e075"; +} + +.glyphicon-fast-forward:before { + content: "\e076"; +} + +.glyphicon-step-forward:before { + content: "\e077"; +} + +.glyphicon-eject:before { + content: "\e078"; +} + +.glyphicon-chevron-left:before { + content: "\e079"; +} + +.glyphicon-chevron-right:before { + content: "\e080"; +} + +.glyphicon-plus-sign:before { + content: "\e081"; +} + +.glyphicon-minus-sign:before { + content: "\e082"; +} + +.glyphicon-remove-sign:before { + content: "\e083"; +} + +.glyphicon-ok-sign:before { + content: "\e084"; +} + +.glyphicon-question-sign:before { + content: "\e085"; +} + +.glyphicon-info-sign:before { + content: "\e086"; +} + +.glyphicon-screenshot:before { + content: "\e087"; +} + +.glyphicon-remove-circle:before { + content: "\e088"; +} + +.glyphicon-ok-circle:before { + content: "\e089"; +} + +.glyphicon-ban-circle:before { + content: "\e090"; +} + +.glyphicon-arrow-left:before { + content: "\e091"; +} + +.glyphicon-arrow-right:before { + content: "\e092"; +} + +.glyphicon-arrow-up:before { + content: "\e093"; +} + +.glyphicon-arrow-down:before { + content: "\e094"; +} + +.glyphicon-share-alt:before { + content: "\e095"; +} + +.glyphicon-resize-full:before { + content: "\e096"; +} + +.glyphicon-resize-small:before { + content: "\e097"; +} + +.glyphicon-exclamation-sign:before { + content: "\e101"; +} + +.glyphicon-gift:before { + content: "\e102"; +} + +.glyphicon-leaf:before { + content: "\e103"; +} + +.glyphicon-eye-open:before { + content: "\e105"; +} + +.glyphicon-eye-close:before { + content: "\e106"; +} + +.glyphicon-warning-sign:before { + content: "\e107"; +} + +.glyphicon-plane:before { + content: "\e108"; +} + +.glyphicon-random:before { + content: "\e110"; +} + +.glyphicon-comment:before { + content: "\e111"; +} + +.glyphicon-magnet:before { + content: "\e112"; +} + +.glyphicon-chevron-up:before { + content: "\e113"; +} + +.glyphicon-chevron-down:before { + content: "\e114"; +} + +.glyphicon-retweet:before { + content: "\e115"; +} + +.glyphicon-shopping-cart:before { + content: "\e116"; +} + +.glyphicon-folder-close:before { + content: "\e117"; +} + +.glyphicon-folder-open:before { + content: "\e118"; +} + +.glyphicon-resize-vertical:before { + content: "\e119"; +} + +.glyphicon-resize-horizontal:before { + content: "\e120"; +} + +.glyphicon-hdd:before { + content: "\e121"; +} + +.glyphicon-bullhorn:before { + content: "\e122"; +} + +.glyphicon-certificate:before { + content: "\e124"; +} + +.glyphicon-thumbs-up:before { + content: "\e125"; +} + +.glyphicon-thumbs-down:before { + content: "\e126"; +} + +.glyphicon-hand-right:before { + content: "\e127"; +} + +.glyphicon-hand-left:before { + content: "\e128"; +} + +.glyphicon-hand-up:before { + content: "\e129"; +} + +.glyphicon-hand-down:before { + content: "\e130"; +} + +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} + +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} + +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} + +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} + +.glyphicon-globe:before { + content: "\e135"; +} + +.glyphicon-tasks:before { + content: "\e137"; +} + +.glyphicon-filter:before { + content: "\e138"; +} + +.glyphicon-fullscreen:before { + content: "\e140"; +} + +.glyphicon-dashboard:before { + content: "\e141"; +} + +.glyphicon-heart-empty:before { + content: "\e143"; +} + +.glyphicon-link:before { + content: "\e144"; +} + +.glyphicon-phone:before { + content: "\e145"; +} + +.glyphicon-usd:before { + content: "\e148"; +} + +.glyphicon-gbp:before { + content: "\e149"; +} + +.glyphicon-sort:before { + content: "\e150"; +} + +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} + +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} + +.glyphicon-sort-by-order:before { + content: "\e153"; +} + +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} + +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} + +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} + +.glyphicon-unchecked:before { + content: "\e157"; +} + +.glyphicon-expand:before { + content: "\e158"; +} + +.glyphicon-collapse-down:before { + content: "\e159"; +} + +.glyphicon-collapse-up:before { + content: "\e160"; +} + +.glyphicon-log-in:before { + content: "\e161"; +} + +.glyphicon-flash:before { + content: "\e162"; +} + +.glyphicon-log-out:before { + content: "\e163"; +} + +.glyphicon-new-window:before { + content: "\e164"; +} + +.glyphicon-record:before { + content: "\e165"; +} + +.glyphicon-save:before { + content: "\e166"; +} + +.glyphicon-open:before { + content: "\e167"; +} + +.glyphicon-saved:before { + content: "\e168"; +} + +.glyphicon-import:before { + content: "\e169"; +} + +.glyphicon-export:before { + content: "\e170"; +} + +.glyphicon-send:before { + content: "\e171"; +} + +.glyphicon-floppy-disk:before { + content: "\e172"; +} + +.glyphicon-floppy-saved:before { + content: "\e173"; +} + +.glyphicon-floppy-remove:before { + content: "\e174"; +} + +.glyphicon-floppy-save:before { + content: "\e175"; +} + +.glyphicon-floppy-open:before { + content: "\e176"; +} + +.glyphicon-credit-card:before { + content: "\e177"; +} + +.glyphicon-transfer:before { + content: "\e178"; +} + +.glyphicon-cutlery:before { + content: "\e179"; +} + +.glyphicon-header:before { + content: "\e180"; +} + +.glyphicon-compressed:before { + content: "\e181"; +} + +.glyphicon-earphone:before { + content: "\e182"; +} + +.glyphicon-phone-alt:before { + content: "\e183"; +} + +.glyphicon-tower:before { + content: "\e184"; +} + +.glyphicon-stats:before { + content: "\e185"; +} + +.glyphicon-sd-video:before { + content: "\e186"; +} + +.glyphicon-hd-video:before { + content: "\e187"; +} + +.glyphicon-subtitles:before { + content: "\e188"; +} + +.glyphicon-sound-stereo:before { + content: "\e189"; +} + +.glyphicon-sound-dolby:before { + content: "\e190"; +} + +.glyphicon-sound-5-1:before { + content: "\e191"; +} + +.glyphicon-sound-6-1:before { + content: "\e192"; +} + +.glyphicon-sound-7-1:before { + content: "\e193"; +} + +.glyphicon-copyright-mark:before { + content: "\e194"; +} + +.glyphicon-registration-mark:before { + content: "\e195"; +} + +.glyphicon-cloud-download:before { + content: "\e197"; +} + +.glyphicon-cloud-upload:before { + content: "\e198"; +} + +.glyphicon-tree-conifer:before { + content: "\e199"; +} + +.glyphicon-tree-deciduous:before { + content: "\e200"; +} + +.glyphicon-briefcase:before { + content: "\1f4bc"; +} + +.glyphicon-calendar:before { + content: "\1f4c5"; +} + +.glyphicon-pushpin:before { + content: "\1f4cc"; +} + +.glyphicon-paperclip:before { + content: "\1f4ce"; +} + +.glyphicon-camera:before { + content: "\1f4f7"; +} + +.glyphicon-lock:before { + content: "\1f512"; +} + +.glyphicon-bell:before { + content: "\1f514"; +} + +.glyphicon-bookmark:before { + content: "\1f516"; +} + +.glyphicon-fire:before { + content: "\1f525"; +} + +.glyphicon-wrench:before { + content: "\1f527"; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-bottom: 0 dotted; + border-left: 4px solid transparent; + content: ""; +} + +.dropdown { + position: relative; +} + +.dropdown-toggle:focus { + outline: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + list-style: none; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} + +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.428571429; + color: #333333; + white-space: nowrap; +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #ffffff; + text-decoration: none; + background-color: #428bca; +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + background-color: #428bca; + outline: 0; +} + +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} + +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.open > .dropdown-menu { + display: block; +} + +.open > a { + outline: 0; +} + +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.428571429; + color: #999999; +} + +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0 dotted; + border-bottom: 4px solid #000000; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } +} + +.btn-default .caret { + border-top-color: #333333; +} + +.btn-primary .caret, +.btn-success .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret { + border-top-color: #fff; +} + +.dropup .btn-default .caret { + border-bottom-color: #333333; +} + +.dropup .btn-primary .caret, +.dropup .btn-success .caret, +.dropup .btn-warning .caret, +.dropup .btn-danger .caret, +.dropup .btn-info .caret { + border-bottom-color: #fff; +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} + +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} + +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} + +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus { + outline: none; +} + +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} + +.btn-toolbar:before, +.btn-toolbar:after { + display: table; + content: " "; +} + +.btn-toolbar:after { + clear: both; +} + +.btn-toolbar:before, +.btn-toolbar:after { + display: table; + content: " "; +} + +.btn-toolbar:after { + clear: both; +} + +.btn-toolbar .btn-group { + float: left; +} + +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group, +.btn-toolbar > .btn-group + .btn-group { + margin-left: 5px; +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} + +.btn-group > .btn:first-child { + margin-left: 0; +} + +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.btn-group > .btn-group { + float: left; +} + +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn-group:last-child > .btn:first-child { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group-xs > .btn { + padding: 5px 10px; + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} + +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} + +.btn .caret { + margin-left: 0; +} + +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} + +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} + +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group { + display: block; + float: none; + width: 100%; + max-width: 100%; +} + +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after { + display: table; + content: " "; +} + +.btn-group-vertical > .btn-group:after { + clear: both; +} + +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after { + display: table; + content: " "; +} + +.btn-group-vertical > .btn-group:after { + clear: both; +} + +.btn-group-vertical > .btn-group > .btn { + float: none; +} + +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} + +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-right-radius: 0; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; +} + +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group-vertical > .btn-group:first-child > .btn:last-child, +.btn-group-vertical > .btn-group:first-child > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn-group:last-child > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.btn-group-justified { + display: table; + width: 100%; + border-collapse: separate; + table-layout: fixed; +} + +.btn-group-justified .btn { + display: table-cell; + float: none; + width: 1%; +} + +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + display: none; +} + +.input-group { + position: relative; + display: table; + border-collapse: separate; +} + +.input-group.col { + float: none; + padding-right: 0; + padding-left: 0; +} + +.input-group .form-control { + width: 100%; + margin-bottom: 0; +} + +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 45px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 45px; + line-height: 45px; +} + +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn { + height: auto; +} + +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} + +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn { + height: auto; +} + +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} + +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} + +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + text-align: center; + background-color: #eeeeee; + border: 1px solid #cccccc; + border-radius: 4px; +} + +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} + +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} + +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} + +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group-addon:first-child { + border-right: 0; +} + +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.input-group-addon:last-child { + border-left: 0; +} + +.input-group-btn { + position: relative; + white-space: nowrap; +} + +.input-group-btn > .btn { + position: relative; +} + +.input-group-btn > .btn + .btn { + margin-left: -4px; +} + +.input-group-btn > .btn:hover, +.input-group-btn > .btn:active { + z-index: 2; +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav:before, +.nav:after { + display: table; + content: " "; +} + +.nav:after { + clear: both; +} + +.nav:before, +.nav:after { + display: table; + content: " "; +} + +.nav:after { + clear: both; +} + +.nav > li { + position: relative; + display: block; +} + +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} + +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} + +.nav > li.disabled > a { + color: #999999; +} + +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} + +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: #428bca; +} + +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} + +.nav > li > a > img { + max-width: none; +} + +.nav-tabs { + border-bottom: 1px solid #dddddd; +} + +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} + +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.428571429; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #dddddd; + border-bottom-color: transparent; +} + +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} + +.nav-tabs.nav-justified > li { + float: none; +} + +.nav-tabs.nav-justified > li > a { + text-align: center; +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } +} + +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-bottom: 1px solid #dddddd; +} + +.nav-tabs.nav-justified > .active > a { + border-bottom-color: #ffffff; +} + +.nav-pills > li { + float: left; +} + +.nav-pills > li > a { + border-radius: 5px; +} + +.nav-pills > li + li { + margin-left: 2px; +} + +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: #428bca; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} + +.nav-justified { + width: 100%; +} + +.nav-justified > li { + float: none; +} + +.nav-justified > li > a { + text-align: center; +} + +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } +} + +.nav-tabs-justified { + border-bottom: 0; +} + +.nav-tabs-justified > li > a { + margin-right: 0; + border-bottom: 1px solid #dddddd; +} + +.nav-tabs-justified > .active > a { + border-bottom-color: #ffffff; +} + +.tabbable:before, +.tabbable:after { + display: table; + content: " "; +} + +.tabbable:after { + clear: both; +} + +.tabbable:before, +.tabbable:after { + display: table; + content: " "; +} + +.tabbable:after { + clear: both; +} + +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} + +.tab-content > .active, +.pill-content > .active { + display: block; +} + +.nav .caret { + border-top-color: #428bca; + border-bottom-color: #428bca; +} + +.nav a:hover .caret { + border-top-color: #2a6496; + border-bottom-color: #2a6496; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.navbar { + position: relative; + z-index: 1000; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} + +.navbar:before, +.navbar:after { + display: table; + content: " "; +} + +.navbar:after { + clear: both; +} + +.navbar:before, +.navbar:after { + display: table; + content: " "; +} + +.navbar:after { + clear: both; +} + +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} + +.navbar-header:before, +.navbar-header:after { + display: table; + content: " "; +} + +.navbar-header:after { + clear: both; +} + +.navbar-header:before, +.navbar-header:after { + display: table; + content: " "; +} + +.navbar-header:after { + clear: both; +} + +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} + +.navbar-collapse { + max-height: 340px; + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} + +.navbar-collapse:before, +.navbar-collapse:after { + display: table; + content: " "; +} + +.navbar-collapse:after { + clear: both; +} + +.navbar-collapse:before, +.navbar-collapse:after { + display: table; + content: " "; +} + +.navbar-collapse:after { + clear: both; +} + +.navbar-collapse.in { + overflow-y: auto; +} + +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-collapse .navbar-nav.navbar-left:first-child { + margin-left: -15px; + } + .navbar-collapse .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } + .navbar-collapse .navbar-text:last-child { + margin-right: 0; + } +} + +.container > .navbar-header, +.container > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} + +@media (min-width: 768px) { + .container > .navbar-header, + .container > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} + +.navbar-static-top { + border-width: 0 0 1px; +} + +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + border-width: 0 0 1px; +} + +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} + +.navbar-fixed-top { + top: 0; + z-index: 1030; +} + +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; +} + +.navbar-brand { + float: left; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} + +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} + +@media (min-width: 768px) { + .navbar > .container .navbar-brand { + margin-left: -15px; + } +} + +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + border: 1px solid transparent; + border-radius: 4px; +} + +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} + +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} + +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} + +.navbar-nav { + margin: 7.5px -15px; +} + +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} + +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} + +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} + +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} + +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); +} + +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } +} + +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} + +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} + +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.navbar-nav.pull-right > li > .dropdown-menu, +.navbar-nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} + +.navbar-text { + float: left; + margin-top: 15px; + margin-bottom: 15px; +} + +@media (min-width: 768px) { + .navbar-text { + margin-right: 15px; + margin-left: 15px; + } +} + +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} + +.navbar-default .navbar-brand { + color: #777777; +} + +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} + +.navbar-default .navbar-text { + color: #777777; +} + +.navbar-default .navbar-nav > li > a { + color: #777777; +} + +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333333; + background-color: transparent; +} + +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555555; + background-color: #e7e7e7; +} + +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} + +.navbar-default .navbar-toggle { + border-color: #dddddd; +} + +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #dddddd; +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #cccccc; +} + +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e6e6e6; +} + +.navbar-default .navbar-nav > .dropdown > a:hover .caret, +.navbar-default .navbar-nav > .dropdown > a:focus .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} + +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555555; + background-color: #e7e7e7; +} + +.navbar-default .navbar-nav > .open > a .caret, +.navbar-default .navbar-nav > .open > a:hover .caret, +.navbar-default .navbar-nav > .open > a:focus .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar-default .navbar-nav > .dropdown > a .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} + +.navbar-default .navbar-link { + color: #777777; +} + +.navbar-default .navbar-link:hover { + color: #333333; +} + +.navbar-inverse { + background-color: #222222; + border-color: #080808; +} + +.navbar-inverse .navbar-brand { + color: #999999; +} + +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .navbar-nav > li > a { + color: #999999; +} + +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #080808; +} + +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444444; + background-color: transparent; +} + +.navbar-inverse .navbar-toggle { + border-color: #333333; +} + +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333333; +} + +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #ffffff; +} + +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} + +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #ffffff; + background-color: #080808; +} + +.navbar-inverse .navbar-nav > .dropdown > a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .navbar-nav > .dropdown > a .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} + +.navbar-inverse .navbar-nav > .open > a .caret, +.navbar-inverse .navbar-nav > .open > a:hover .caret, +.navbar-inverse .navbar-nav > .open > a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #999999; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444444; + background-color: transparent; + } +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} + +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; +} + +.breadcrumb > li + li:before { + padding: 0 5px; + color: #cccccc; + content: "/\00a0"; +} + +.breadcrumb > .active { + color: #999999; +} + +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} + +.pagination > li { + display: inline; +} + +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.428571429; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; +} + +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; +} + +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + background-color: #eeeeee; +} + +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + cursor: default; + background-color: #428bca; + border-color: #428bca; +} + +.pagination > .disabled > span, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + cursor: not-allowed; + background-color: #ffffff; + border-color: #dddddd; +} + +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} + +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-bottom-left-radius: 6px; + border-top-left-radius: 6px; +} + +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} + +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} + +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; +} + +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} + +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} + +.pager:before, +.pager:after { + display: table; + content: " "; +} + +.pager:after { + clear: both; +} + +.pager:before, +.pager:after { + display: table; + content: " "; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 15px; +} + +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} + +.pager .next > a, +.pager .next > span { + float: right; +} + +.pager .previous > a, +.pager .previous > span { + float: left; +} + +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + cursor: not-allowed; + background-color: #ffffff; +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} + +.label[href]:hover, +.label[href]:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label:empty { + display: none; +} + +.label-default { + background-color: #999999; +} + +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #808080; +} + +.label-primary { + background-color: #428bca; +} + +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #3071a9; +} + +.label-success { + background-color: #5cb85c; +} + +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} + +.label-info { + background-color: #5bc0de; +} + +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} + +.label-warning { + background-color: #f0ad4e; +} + +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} + +.label-danger { + background-color: #d9534f; +} + +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} + +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; + border-radius: 10px; +} + +.badge:empty { + display: none; +} + +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.btn .badge { + position: relative; + top: -1px; +} + +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #428bca; + background-color: #ffffff; +} + +.nav-pills > li > a > .badge { + margin-left: 3px; +} + +.jumbotron { + padding: 30px; + margin-bottom: 30px; + font-size: 21px; + font-weight: 200; + line-height: 2.1428571435; + color: inherit; + background-color: #eeeeee; +} + +.jumbotron h1 { + line-height: 1; + color: inherit; +} + +.jumbotron p { + line-height: 1.4; +} + +.container .jumbotron { + border-radius: 6px; +} + +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1 { + font-size: 63px; + } +} + +.thumbnail { + display: inline-block; + display: block; + height: auto; + max-width: 100%; + padding: 4px; + line-height: 1.428571429; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.thumbnail > img { + display: block; + height: auto; + max-width: 100%; +} + +a.thumbnail:hover, +a.thumbnail:focus { + border-color: #428bca; +} + +.thumbnail > img { + margin-right: auto; + margin-left: auto; +} + +.thumbnail .caption { + padding: 9px; + color: #333333; +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} + +.alert h4 { + margin-top: 0; + color: inherit; +} + +.alert .alert-link { + font-weight: bold; +} + +.alert > p, +.alert > ul { + margin-bottom: 0; +} + +.alert > p + p { + margin-top: 5px; +} + +.alert-dismissable { + padding-right: 35px; +} + +.alert-dismissable .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} + +.alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success hr { + border-top-color: #c9e2b3; +} + +.alert-success .alert-link { + color: #356635; +} + +.alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info hr { + border-top-color: #a6e1ec; +} + +.alert-info .alert-link { + color: #2d6987; +} + +.alert-warning { + color: #c09853; + background-color: #fcf8e3; + border-color: #fbeed5; +} + +.alert-warning hr { + border-top-color: #f8e5be; +} + +.alert-warning .alert-link { + color: #a47e3c; +} + +.alert-danger { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-danger hr { + border-top-color: #e6c1c7; +} + +.alert-danger .alert-link { + color: #953b39; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + color: #ffffff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress-striped .progress-bar { + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 40px 40px; +} + +.progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-bar-success { + background-color: #5cb85c; +} + +.progress-striped .progress-bar-success { + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-bar-info { + background-color: #5bc0de; +} + +.progress-striped .progress-bar-info { + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-bar-warning { + background-color: #f0ad4e; +} + +.progress-striped .progress-bar-warning { + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-bar-danger { + background-color: #d9534f; +} + +.progress-striped .progress-bar-danger { + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.media, +.media-body { + overflow: hidden; + zoom: 1; +} + +.media, +.media .media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media-object { + display: block; +} + +.media-heading { + margin: 0 0 5px; +} + +.media > .pull-left { + margin-right: 10px; +} + +.media > .pull-right { + margin-left: 10px; +} + +.media-list { + padding-left: 0; + list-style: none; +} + +.list-group { + padding-left: 0; + margin-bottom: 20px; +} + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #ffffff; + border: 1px solid #dddddd; +} + +.list-group-item:first-child { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} + +.list-group-item > .badge { + float: right; +} + +.list-group-item > .badge + .badge { + margin-right: 5px; +} + +a.list-group-item { + color: #555555; +} + +a.list-group-item .list-group-item-heading { + color: #333333; +} + +a.list-group-item:hover, +a.list-group-item:focus { + text-decoration: none; + background-color: #f5f5f5; +} + +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #ffffff; + background-color: #428bca; + border-color: #428bca; +} + +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading { + color: inherit; +} + +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #e1edf7; +} + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} + +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.panel-body { + padding: 15px; +} + +.panel-body:before, +.panel-body:after { + display: table; + content: " "; +} + +.panel-body:after { + clear: both; +} + +.panel-body:before, +.panel-body:after { + display: table; + content: " "; +} + +.panel-body:after { + clear: both; +} + +.panel > .list-group { + margin-bottom: 0; +} + +.panel > .list-group .list-group-item { + border-width: 1px 0; +} + +.panel > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.panel > .list-group .list-group-item:last-child { + border-bottom: 0; +} + +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} + +.panel > .table { + margin-bottom: 0; +} + +.panel > .panel-body + .table { + border-top: 1px solid #dddddd; +} + +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} + +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; +} + +.panel-title > a { + color: inherit; +} + +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #dddddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +.panel-group .panel { + margin-bottom: 0; + overflow: hidden; + border-radius: 4px; +} + +.panel-group .panel + .panel { + margin-top: 5px; +} + +.panel-group .panel-heading { + border-bottom: 0; +} + +.panel-group .panel-heading + .panel-collapse .panel-body { + border-top: 1px solid #dddddd; +} + +.panel-group .panel-footer { + border-top: 0; +} + +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dddddd; +} + +.panel-default { + border-color: #dddddd; +} + +.panel-default > .panel-heading { + color: #333333; + background-color: #f5f5f5; + border-color: #dddddd; +} + +.panel-default > .panel-heading + .panel-collapse .panel-body { + border-top-color: #dddddd; +} + +.panel-default > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #dddddd; +} + +.panel-primary { + border-color: #428bca; +} + +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #428bca; + border-color: #428bca; +} + +.panel-primary > .panel-heading + .panel-collapse .panel-body { + border-top-color: #428bca; +} + +.panel-primary > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #428bca; +} + +.panel-success { + border-color: #d6e9c6; +} + +.panel-success > .panel-heading { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.panel-success > .panel-heading + .panel-collapse .panel-body { + border-top-color: #d6e9c6; +} + +.panel-success > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #d6e9c6; +} + +.panel-warning { + border-color: #fbeed5; +} + +.panel-warning > .panel-heading { + color: #c09853; + background-color: #fcf8e3; + border-color: #fbeed5; +} + +.panel-warning > .panel-heading + .panel-collapse .panel-body { + border-top-color: #fbeed5; +} + +.panel-warning > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #fbeed5; +} + +.panel-danger { + border-color: #eed3d7; +} + +.panel-danger > .panel-heading { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.panel-danger > .panel-heading + .panel-collapse .panel-body { + border-top-color: #eed3d7; +} + +.panel-danger > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #eed3d7; +} + +.panel-info { + border-color: #bce8f1; +} + +.panel-info > .panel-heading { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.panel-info > .panel-heading + .panel-collapse .panel-body { + border-top-color: #bce8f1; +} + +.panel-info > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #bce8f1; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.well-lg { + padding: 24px; + border-radius: 6px; +} + +.well-sm { + padding: 9px; + border-radius: 3px; +} + +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.modal-open { + overflow: hidden; +} + +body.modal-open, +.modal-open .navbar-fixed-top, +.modal-open .navbar-fixed-bottom { + margin-right: 15px; +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + display: none; + overflow: auto; + overflow-y: scroll; +} + +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -moz-transition: -moz-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: transform 0.3s ease-out; +} + +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} + +.modal-dialog { + z-index: 1050; + width: auto; + padding: 10px; + margin-right: auto; + margin-left: auto; +} + +.modal-content { + position: relative; + background-color: #ffffff; + border: 1px solid #999999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + background-clip: padding-box; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} + +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50); +} + +.modal-header { + min-height: 16.428571429px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} + +.modal-header .close { + margin-top: -2px; +} + +.modal-title { + margin: 0; + line-height: 1.428571429; +} + +.modal-body { + position: relative; + padding: 20px; +} + +.modal-footer { + padding: 19px 20px 20px; + margin-top: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} + +@media screen and (min-width: 768px) { + .modal-dialog { + right: auto; + left: 50%; + width: 600px; + padding-top: 30px; + padding-bottom: 30px; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 12px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} + +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} + +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} + +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} + +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.top-right .tooltip-arrow { + right: 5px; + bottom: 0; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + background-clip: padding-box; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.popover .arrow { + border-width: 11px; +} + +.popover .arrow:after { + border-width: 10px; + content: ""; +} + +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} + +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-top-color: #ffffff; + border-bottom-width: 0; + content: " "; +} + +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999999; + border-right-color: rgba(0, 0, 0, 0.25); + border-left-width: 0; +} + +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; + content: " "; +} + +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-bottom-color: #999999; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-top-width: 0; +} + +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-bottom-color: #ffffff; + border-top-width: 0; + content: " "; +} + +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-left-color: #999999; + border-left-color: rgba(0, 0, 0, 0.25); + border-right-width: 0; +} + +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + border-left-color: #ffffff; + border-right-width: 0; + content: " "; +} + +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + height: auto; + max-width: 100%; + line-height: 1; +} + +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} + +.carousel-inner > .active { + left: 0; +} + +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel-inner > .next { + left: 100%; +} + +.carousel-inner > .prev { + left: -100%; +} + +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} + +.carousel-inner > .active.left { + left: -100%; +} + +.carousel-inner > .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.left { + background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%)); + background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); +} + +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%)); + background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); +} + +.carousel-control:hover, +.carousel-control:focus { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + left: 50%; + z-index: 5; + display: inline-block; +} + +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + margin-left: -10px; + font-family: serif; +} + +.carousel-control .icon-prev:before { + content: '\2039'; +} + +.carousel-control .icon-next:before { + content: '\203a'; +} + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} + +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + border: 1px solid #ffffff; + border-radius: 10px; +} + +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #ffffff; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} + +.carousel-caption .btn { + text-shadow: none; +} + +@media screen and (min-width: 768px) { + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + margin-left: -15px; + font-size: 30px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} + +.clearfix:before, +.clearfix:after { + display: table; + content: " "; +} + +.clearfix:after { + clear: both; +} + +.pull-right { + float: right !important; +} + +.pull-left { + float: left !important; +} + +.hide { + display: none !important; +} + +.show { + display: block !important; +} + +.invisible { + visibility: hidden; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.affix { + position: fixed; +} + +@-ms-viewport { + width: device-width; +} + +@media screen and (max-width: 400px) { + @-ms-viewport { + width: 320px; + } +} + +.hidden { + display: none !important; + visibility: hidden !important; +} + +.visible-xs { + display: none !important; +} + +tr.visible-xs { + display: none !important; +} + +th.visible-xs, +td.visible-xs { + display: none !important; +} + +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-xs.visible-sm { + display: block !important; + } + tr.visible-xs.visible-sm { + display: table-row !important; + } + th.visible-xs.visible-sm, + td.visible-xs.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-xs.visible-md { + display: block !important; + } + tr.visible-xs.visible-md { + display: table-row !important; + } + th.visible-xs.visible-md, + td.visible-xs.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-xs.visible-lg { + display: block !important; + } + tr.visible-xs.visible-lg { + display: table-row !important; + } + th.visible-xs.visible-lg, + td.visible-xs.visible-lg { + display: table-cell !important; + } +} + +.visible-sm { + display: none !important; +} + +tr.visible-sm { + display: none !important; +} + +th.visible-sm, +td.visible-sm { + display: none !important; +} + +@media (max-width: 767px) { + .visible-sm.visible-xs { + display: block !important; + } + tr.visible-sm.visible-xs { + display: table-row !important; + } + th.visible-sm.visible-xs, + td.visible-sm.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-sm.visible-md { + display: block !important; + } + tr.visible-sm.visible-md { + display: table-row !important; + } + th.visible-sm.visible-md, + td.visible-sm.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-sm.visible-lg { + display: block !important; + } + tr.visible-sm.visible-lg { + display: table-row !important; + } + th.visible-sm.visible-lg, + td.visible-sm.visible-lg { + display: table-cell !important; + } +} + +.visible-md { + display: none !important; +} + +tr.visible-md { + display: none !important; +} + +th.visible-md, +td.visible-md { + display: none !important; +} + +@media (max-width: 767px) { + .visible-md.visible-xs { + display: block !important; + } + tr.visible-md.visible-xs { + display: table-row !important; + } + th.visible-md.visible-xs, + td.visible-md.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-md.visible-sm { + display: block !important; + } + tr.visible-md.visible-sm { + display: table-row !important; + } + th.visible-md.visible-sm, + td.visible-md.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-md.visible-lg { + display: block !important; + } + tr.visible-md.visible-lg { + display: table-row !important; + } + th.visible-md.visible-lg, + td.visible-md.visible-lg { + display: table-cell !important; + } +} + +.visible-lg { + display: none !important; +} + +tr.visible-lg { + display: none !important; +} + +th.visible-lg, +td.visible-lg { + display: none !important; +} + +@media (max-width: 767px) { + .visible-lg.visible-xs { + display: block !important; + } + tr.visible-lg.visible-xs { + display: table-row !important; + } + th.visible-lg.visible-xs, + td.visible-lg.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-lg.visible-sm { + display: block !important; + } + tr.visible-lg.visible-sm { + display: table-row !important; + } + th.visible-lg.visible-sm, + td.visible-lg.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-lg.visible-md { + display: block !important; + } + tr.visible-lg.visible-md { + display: table-row !important; + } + th.visible-lg.visible-md, + td.visible-lg.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} + +.hidden-xs { + display: block !important; +} + +tr.hidden-xs { + display: table-row !important; +} + +th.hidden-xs, +td.hidden-xs { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } + tr.hidden-xs { + display: none !important; + } + th.hidden-xs, + td.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-xs.hidden-sm { + display: none !important; + } + tr.hidden-xs.hidden-sm { + display: none !important; + } + th.hidden-xs.hidden-sm, + td.hidden-xs.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-xs.hidden-md { + display: none !important; + } + tr.hidden-xs.hidden-md { + display: none !important; + } + th.hidden-xs.hidden-md, + td.hidden-xs.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-xs.hidden-lg { + display: none !important; + } + tr.hidden-xs.hidden-lg { + display: none !important; + } + th.hidden-xs.hidden-lg, + td.hidden-xs.hidden-lg { + display: none !important; + } +} + +.hidden-sm { + display: block !important; +} + +tr.hidden-sm { + display: table-row !important; +} + +th.hidden-sm, +td.hidden-sm { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-sm.hidden-xs { + display: none !important; + } + tr.hidden-sm.hidden-xs { + display: none !important; + } + th.hidden-sm.hidden-xs, + td.hidden-sm.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } + tr.hidden-sm { + display: none !important; + } + th.hidden-sm, + td.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-sm.hidden-md { + display: none !important; + } + tr.hidden-sm.hidden-md { + display: none !important; + } + th.hidden-sm.hidden-md, + td.hidden-sm.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-sm.hidden-lg { + display: none !important; + } + tr.hidden-sm.hidden-lg { + display: none !important; + } + th.hidden-sm.hidden-lg, + td.hidden-sm.hidden-lg { + display: none !important; + } +} + +.hidden-md { + display: block !important; +} + +tr.hidden-md { + display: table-row !important; +} + +th.hidden-md, +td.hidden-md { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-md.hidden-xs { + display: none !important; + } + tr.hidden-md.hidden-xs { + display: none !important; + } + th.hidden-md.hidden-xs, + td.hidden-md.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-md.hidden-sm { + display: none !important; + } + tr.hidden-md.hidden-sm { + display: none !important; + } + th.hidden-md.hidden-sm, + td.hidden-md.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } + tr.hidden-md { + display: none !important; + } + th.hidden-md, + td.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-md.hidden-lg { + display: none !important; + } + tr.hidden-md.hidden-lg { + display: none !important; + } + th.hidden-md.hidden-lg, + td.hidden-md.hidden-lg { + display: none !important; + } +} + +.hidden-lg { + display: block !important; +} + +tr.hidden-lg { + display: table-row !important; +} + +th.hidden-lg, +td.hidden-lg { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-lg.hidden-xs { + display: none !important; + } + tr.hidden-lg.hidden-xs { + display: none !important; + } + th.hidden-lg.hidden-xs, + td.hidden-lg.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-lg.hidden-sm { + display: none !important; + } + tr.hidden-lg.hidden-sm { + display: none !important; + } + th.hidden-lg.hidden-sm, + td.hidden-lg.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-lg.hidden-md { + display: none !important; + } + tr.hidden-lg.hidden-md { + display: none !important; + } + th.hidden-lg.hidden-md, + td.hidden-lg.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } + tr.hidden-lg { + display: none !important; + } + th.hidden-lg, + td.hidden-lg { + display: none !important; + } +} + +.visible-print { + display: none !important; +} + +tr.visible-print { + display: none !important; +} + +th.visible-print, +td.visible-print { + display: none !important; +} + +@media print { + .visible-print { + display: block !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } + .hidden-print { + display: none !important; + } + tr.hidden-print { + display: none !important; + } + th.hidden-print, + td.hidden-print { + display: none !important; + } +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.min.css b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.min.css new file mode 100644 index 000000000..a553c4f5e --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/css/bootstrap.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap v3.0.0 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + *//*! normalize.css v2.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}mark{color:#000;background:#ff0}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid #c0c0c0}legend{padding:0;border:0}button,input,select,textarea{margin:0;font-family:inherit;font-size:100%}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{padding:0;box-sizing:border-box}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:2cm .5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*,*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.428571429;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}button,input,select[multiple],textarea{background-image:none}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}img{vertical-align:middle}.img-responsive{display:block;height:auto;max-width:100%}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;height:auto;max-width:100%;padding:4px;line-height:1.428571429;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);border:0}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16.099999999999998px;font-weight:200;line-height:1.4}@media(min-width:768px){.lead{font-size:21px}}small{font-size:85%}cite{font-style:normal}.text-muted{color:#999}.text-primary{color:#428bca}.text-warning{color:#c09853}.text-danger{color:#b94a48}.text-success{color:#468847}.text-info{color:#3a87ad}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{margin-top:20px;margin-bottom:10px}h4,h5,h6{margin-top:10px;margin-bottom:10px}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}h1 small,.h1 small{font-size:24px}h2 small,.h2 small{font-size:18px}h3 small,.h3 small,h4 small,.h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:1.428571429}dt{font-weight:bold}dd{margin-left:0}@media(min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{font-size:17.5px;font-weight:300;line-height:1.25}blockquote p:last-child{margin-bottom:0}blockquote small{display:block;line-height:1.428571429;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:1.428571429}code,pre{font-family:Monaco,Menlo,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;white-space:nowrap;background-color:#f9f2f4;border-radius:4px}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.428571429;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.row{margin-right:-15px;margin-left:-15px}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11{float:left}.col-xs-1{width:8.333333333333332%}.col-xs-2{width:16.666666666666664%}.col-xs-3{width:25%}.col-xs-4{width:33.33333333333333%}.col-xs-5{width:41.66666666666667%}.col-xs-6{width:50%}.col-xs-7{width:58.333333333333336%}.col-xs-8{width:66.66666666666666%}.col-xs-9{width:75%}.col-xs-10{width:83.33333333333334%}.col-xs-11{width:91.66666666666666%}.col-xs-12{width:100%}@media(min-width:768px){.container{max-width:750px}.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11{float:left}.col-sm-1{width:8.333333333333332%}.col-sm-2{width:16.666666666666664%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333333333%}.col-sm-5{width:41.66666666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.333333333333336%}.col-sm-8{width:66.66666666666666%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333333334%}.col-sm-11{width:91.66666666666666%}.col-sm-12{width:100%}.col-sm-push-1{left:8.333333333333332%}.col-sm-push-2{left:16.666666666666664%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333333333333%}.col-sm-push-5{left:41.66666666666667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.333333333333336%}.col-sm-push-8{left:66.66666666666666%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333333333334%}.col-sm-push-11{left:91.66666666666666%}.col-sm-pull-1{right:8.333333333333332%}.col-sm-pull-2{right:16.666666666666664%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333333333333%}.col-sm-pull-5{right:41.66666666666667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.333333333333336%}.col-sm-pull-8{right:66.66666666666666%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333333333334%}.col-sm-pull-11{right:91.66666666666666%}.col-sm-offset-1{margin-left:8.333333333333332%}.col-sm-offset-2{margin-left:16.666666666666664%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333333333%}.col-sm-offset-5{margin-left:41.66666666666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.333333333333336%}.col-sm-offset-8{margin-left:66.66666666666666%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333333334%}.col-sm-offset-11{margin-left:91.66666666666666%}}@media(min-width:992px){.container{max-width:970px}.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11{float:left}.col-md-1{width:8.333333333333332%}.col-md-2{width:16.666666666666664%}.col-md-3{width:25%}.col-md-4{width:33.33333333333333%}.col-md-5{width:41.66666666666667%}.col-md-6{width:50%}.col-md-7{width:58.333333333333336%}.col-md-8{width:66.66666666666666%}.col-md-9{width:75%}.col-md-10{width:83.33333333333334%}.col-md-11{width:91.66666666666666%}.col-md-12{width:100%}.col-md-push-0{left:auto}.col-md-push-1{left:8.333333333333332%}.col-md-push-2{left:16.666666666666664%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333333333333%}.col-md-push-5{left:41.66666666666667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.333333333333336%}.col-md-push-8{left:66.66666666666666%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333333333334%}.col-md-push-11{left:91.66666666666666%}.col-md-pull-0{right:auto}.col-md-pull-1{right:8.333333333333332%}.col-md-pull-2{right:16.666666666666664%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333333333333%}.col-md-pull-5{right:41.66666666666667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.333333333333336%}.col-md-pull-8{right:66.66666666666666%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333333333334%}.col-md-pull-11{right:91.66666666666666%}.col-md-offset-0{margin-left:0}.col-md-offset-1{margin-left:8.333333333333332%}.col-md-offset-2{margin-left:16.666666666666664%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333333333%}.col-md-offset-5{margin-left:41.66666666666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.333333333333336%}.col-md-offset-8{margin-left:66.66666666666666%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333333334%}.col-md-offset-11{margin-left:91.66666666666666%}}@media(min-width:1200px){.container{max-width:1170px}.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11{float:left}.col-lg-1{width:8.333333333333332%}.col-lg-2{width:16.666666666666664%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333333333%}.col-lg-5{width:41.66666666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.333333333333336%}.col-lg-8{width:66.66666666666666%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333333334%}.col-lg-11{width:91.66666666666666%}.col-lg-12{width:100%}.col-lg-push-0{left:auto}.col-lg-push-1{left:8.333333333333332%}.col-lg-push-2{left:16.666666666666664%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333333333333%}.col-lg-push-5{left:41.66666666666667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.333333333333336%}.col-lg-push-8{left:66.66666666666666%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333333333334%}.col-lg-push-11{left:91.66666666666666%}.col-lg-pull-0{right:auto}.col-lg-pull-1{right:8.333333333333332%}.col-lg-pull-2{right:16.666666666666664%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333333333333%}.col-lg-pull-5{right:41.66666666666667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.333333333333336%}.col-lg-pull-8{right:66.66666666666666%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333333333334%}.col-lg-pull-11{right:91.66666666666666%}.col-lg-offset-0{margin-left:0}.col-lg-offset-1{margin-left:8.333333333333332%}.col-lg-offset-2{margin-left:16.666666666666664%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333333333%}.col-lg-offset-5{margin-left:41.66666666666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.333333333333336%}.col-lg-offset-8{margin-left:66.66666666666666%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333333334%}.col-lg-offset-11{margin-left:91.66666666666666%}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table thead>tr>th,.table tbody>tr>th,.table tfoot>tr>th,.table thead>tr>td,.table tbody>tr>td,.table tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #ddd}.table thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table caption+thead tr:first-child th,.table colgroup+thead tr:first-child th,.table thead:first-child tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed thead>tr>th,.table-condensed tbody>tr>th,.table-condensed tfoot>tr>th,.table-condensed thead>tr>td,.table-condensed tbody>tr>td,.table-condensed tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*="col-"]{display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8;border-color:#d6e9c6}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td{background-color:#d0e9c6;border-color:#c9e2b3}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede;border-color:#eed3d7}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td{background-color:#ebcccc;border-color:#e6c1c7}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3;border-color:#fbeed5}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td{background-color:#faf2cc;border-color:#f8e5be}@media(max-width:768px){.table-responsive{width:100%;margin-bottom:15px;overflow-x:scroll;overflow-y:hidden;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0;background-color:#fff}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>thead>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>thead>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}select[multiple],select[size]{height:auto}select optgroup{font-family:inherit;font-size:inherit;font-style:inherit}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}input[type="number"]::-webkit-outer-spin-button,input[type="number"]::-webkit-inner-spin-button{height:auto}.form-control:-moz-placeholder{color:#999}.form-control::-moz-placeholder{color:#999}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.428571429;color:#555;vertical-align:middle;background-color:#fff;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee}textarea.form-control{height:auto}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;padding-left:20px;margin-top:10px;margin-bottom:10px;vertical-align:middle}.radio label,.checkbox label{display:inline;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:normal;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm{height:auto}.input-lg{height:45px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:45px;line-height:45px}textarea.input-lg{height:auto}.has-warning .help-block,.has-warning .control-label{color:#c09853}.has-warning .form-control{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-error .help-block,.has-error .control-label{color:#b94a48}.has-error .form-control{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-success .help-block,.has-success .control-label{color:#468847}.has-success .form-control{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.form-control-static{padding-top:7px;margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media(min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block}.form-inline .radio,.form-inline .checkbox{display:inline-block;padding-left:0;margin-top:0;margin-bottom:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:none;margin-left:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}@media(min-width:768px){.form-horizontal .control-label{text-align:right}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:normal;line-height:1.428571429;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;border:1px solid transparent;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-link{font-weight:normal;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-xs{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';-webkit-font-smoothing:antialiased;font-style:normal;font-weight:normal;line-height:1}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-print:before{content:"\e045"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-briefcase:before{content:"\1f4bc"}.glyphicon-calendar:before{content:"\1f4c5"}.glyphicon-pushpin:before{content:"\1f4cc"}.glyphicon-paperclip:before{content:"\1f4ce"}.glyphicon-camera:before{content:"\1f4f7"}.glyphicon-lock:before{content:"\1f512"}.glyphicon-bell:before{content:"\1f514"}.glyphicon-bookmark:before{content:"\1f516"}.glyphicon-fire:before{content:"\1f525"}.glyphicon-wrench:before{content:"\1f527"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid #000;border-right:4px solid transparent;border-bottom:0 dotted;border-left:4px solid transparent;content:""}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.428571429;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#fff;text-decoration:none;background-color:#428bca}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.428571429;color:#999}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0 dotted;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media(min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}}.btn-default .caret{border-top-color:#333}.btn-primary .caret,.btn-success .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret{border-top-color:#fff}.dropup .btn-default .caret{border-bottom-color:#333}.dropup .btn-primary .caret,.dropup .btn-success .caret,.dropup .btn-warning .caret,.dropup .btn-danger .caret,.dropup .btn-info .caret{border-bottom-color:#fff}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group{float:left}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group,.btn-toolbar>.btn-group+.btn-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group-xs>.btn{padding:5px 10px;padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-bottom-left-radius:4px;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child>.btn:last-child,.btn-group-vertical>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;border-collapse:separate;table-layout:fixed}.btn-group-justified .btn{display:table-cell;float:none;width:1%}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group.col{float:none;padding-right:0;padding-left:0}.input-group .form-control{width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:45px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:45px;line-height:45px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-4px}.input-group-btn>.btn:hover,.input-group-btn>.btn:active{z-index:2}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.428571429;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center}@media(min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}}.nav-tabs.nav-justified>li>a{margin-right:0;border-bottom:1px solid #ddd}.nav-tabs.nav-justified>.active>a{border-bottom-color:#fff}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:5px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center}@media(min-width:768px){.nav-justified>li{display:table-cell;width:1%}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-bottom:1px solid #ddd}.nav-tabs-justified>.active>a{border-bottom-color:#fff}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.nav .caret{border-top-color:#428bca;border-bottom-color:#428bca}.nav a:hover .caret{border-top-color:#2a6496;border-bottom-color:#2a6496}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;z-index:1000;min-height:50px;margin-bottom:20px;border:1px solid transparent}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}@media(min-width:768px){.navbar{border-radius:4px}}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}@media(min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media(min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-collapse .navbar-nav.navbar-left:first-child{margin-left:-15px}.navbar-collapse .navbar-nav.navbar-right:last-child{margin-right:-15px}.navbar-collapse .navbar-text:last-child{margin-right:0}}.container>.navbar-header,.container>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media(min-width:768px){.container>.navbar-header,.container>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{border-width:0 0 1px}@media(min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;border-width:0 0 1px}@media(min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;z-index:1030}.navbar-fixed-bottom{bottom:0;margin-bottom:0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media(min-width:768px){.navbar>.container .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;border:1px solid transparent;border-radius:4px}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media(min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media(max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media(min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}@media(min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}@media(min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;padding-left:0;margin-top:0;margin-bottom:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{float:none;margin-left:0}}@media(max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media(min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-nav.pull-right>li>.dropdown-menu,.navbar-nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-text{float:left;margin-top:15px;margin-bottom:15px}@media(min-width:768px){.navbar-text{margin-right:15px;margin-left:15px}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#ccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e6e6e6}.navbar-default .navbar-nav>.dropdown>a:hover .caret,.navbar-default .navbar-nav>.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.open>a .caret,.navbar-default .navbar-nav>.open>a:hover .caret,.navbar-default .navbar-nav>.open>a:focus .caret{border-top-color:#555;border-bottom-color:#555}.navbar-default .navbar-nav>.dropdown>a .caret{border-top-color:#777;border-bottom-color:#777}@media(max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-nav>.dropdown>a .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .navbar-nav>.open>a .caret,.navbar-inverse .navbar-nav>.open>a:hover .caret,.navbar-inverse .navbar-nav>.open>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}@media(max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.428571429;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{background-color:#eee}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#999;border-radius:10px}.badge:empty{display:none}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.btn .badge{position:relative;top:-1px}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;font-size:21px;font-weight:200;line-height:2.1428571435;color:inherit;background-color:#eee}.jumbotron h1{line-height:1;color:inherit}.jumbotron p{line-height:1.4}.container .jumbotron{border-radius:6px}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1{font-size:63px}}.thumbnail{display:inline-block;display:block;height:auto;max-width:100%;padding:4px;line-height:1.428571429;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img{display:block;height:auto;max-width:100%}a.thumbnail:hover,a.thumbnail:focus{border-color:#428bca}.thumbnail>img{margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0}.panel>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel>.list-group .list-group-item:last-child{border-bottom:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table{margin-bottom:0}.panel>.panel-body+.table{border-top:1px solid #ddd}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-title{margin-top:0;margin-bottom:0;font-size:16px}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-group .panel{margin-bottom:0;overflow:hidden;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-warning{border-color:#fbeed5}.panel-warning>.panel-heading{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#fbeed5}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#fbeed5}.panel-danger{border-color:#eed3d7}.panel-danger>.panel-heading{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#eed3d7}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#eed3d7}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}body.modal-open,.modal-open .navbar-fixed-top,.modal-open .navbar-fixed-bottom{margin-right:15px}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:auto;overflow-y:scroll}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{z-index:1050;width:auto;padding:10px;margin-right:auto;margin-left:auto}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1030;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{min-height:16.428571429px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.428571429}.modal-body{position:relative;padding:20px}.modal-footer{padding:19px 20px 20px;margin-top:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media screen and (min-width:768px){.modal-dialog{right:auto;left:50%;width:600px;padding-top:30px;padding-bottom:30px}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}}.tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-bottom-color:#000;border-width:0 5px 5px}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0;content:" "}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0;content:" "}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0;content:" "}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0;content:" "}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;height:auto;max-width:100%;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);opacity:.5;filter:alpha(opacity=50)}.carousel-control.left{background-image:-webkit-gradient(linear,0 top,100% top,from(rgba(0,0,0,0.5)),to(rgba(0,0,0,0.0001)));background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.5) 0),color-stop(rgba(0,0,0,0.0001) 100%));background-image:-moz-linear-gradient(left,rgba(0,0,0,0.5) 0,rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right,rgba(0,0,0,0.5) 0,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{right:0;left:auto;background-image:-webkit-gradient(linear,0 top,100% top,from(rgba(0,0,0,0.0001)),to(rgba(0,0,0,0.5)));background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.0001) 0),color-stop(rgba(0,0,0,0.5) 100%));background-image:-moz-linear-gradient(left,rgba(0,0,0,0.0001) 0,rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right,rgba(0,0,0,0.0001) 0,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;left:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.affix{position:fixed}@-ms-viewport{width:device-width}@media screen and (max-width:400px){@-ms-viewport{width:320px}}.hidden{display:none!important;visibility:hidden!important}.visible-xs{display:none!important}tr.visible-xs{display:none!important}th.visible-xs,td.visible-xs{display:none!important}@media(max-width:767px){.visible-xs{display:block!important}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-xs.visible-sm{display:block!important}tr.visible-xs.visible-sm{display:table-row!important}th.visible-xs.visible-sm,td.visible-xs.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-xs.visible-md{display:block!important}tr.visible-xs.visible-md{display:table-row!important}th.visible-xs.visible-md,td.visible-xs.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-xs.visible-lg{display:block!important}tr.visible-xs.visible-lg{display:table-row!important}th.visible-xs.visible-lg,td.visible-xs.visible-lg{display:table-cell!important}}.visible-sm{display:none!important}tr.visible-sm{display:none!important}th.visible-sm,td.visible-sm{display:none!important}@media(max-width:767px){.visible-sm.visible-xs{display:block!important}tr.visible-sm.visible-xs{display:table-row!important}th.visible-sm.visible-xs,td.visible-sm.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-sm{display:block!important}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-sm.visible-md{display:block!important}tr.visible-sm.visible-md{display:table-row!important}th.visible-sm.visible-md,td.visible-sm.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-sm.visible-lg{display:block!important}tr.visible-sm.visible-lg{display:table-row!important}th.visible-sm.visible-lg,td.visible-sm.visible-lg{display:table-cell!important}}.visible-md{display:none!important}tr.visible-md{display:none!important}th.visible-md,td.visible-md{display:none!important}@media(max-width:767px){.visible-md.visible-xs{display:block!important}tr.visible-md.visible-xs{display:table-row!important}th.visible-md.visible-xs,td.visible-md.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-md.visible-sm{display:block!important}tr.visible-md.visible-sm{display:table-row!important}th.visible-md.visible-sm,td.visible-md.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-md{display:block!important}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-md.visible-lg{display:block!important}tr.visible-md.visible-lg{display:table-row!important}th.visible-md.visible-lg,td.visible-md.visible-lg{display:table-cell!important}}.visible-lg{display:none!important}tr.visible-lg{display:none!important}th.visible-lg,td.visible-lg{display:none!important}@media(max-width:767px){.visible-lg.visible-xs{display:block!important}tr.visible-lg.visible-xs{display:table-row!important}th.visible-lg.visible-xs,td.visible-lg.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-lg.visible-sm{display:block!important}tr.visible-lg.visible-sm{display:table-row!important}th.visible-lg.visible-sm,td.visible-lg.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-lg.visible-md{display:block!important}tr.visible-lg.visible-md{display:table-row!important}th.visible-lg.visible-md,td.visible-lg.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-lg{display:block!important}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}.hidden-xs{display:block!important}tr.hidden-xs{display:table-row!important}th.hidden-xs,td.hidden-xs{display:table-cell!important}@media(max-width:767px){.hidden-xs{display:none!important}tr.hidden-xs{display:none!important}th.hidden-xs,td.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-xs.hidden-sm{display:none!important}tr.hidden-xs.hidden-sm{display:none!important}th.hidden-xs.hidden-sm,td.hidden-xs.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-xs.hidden-md{display:none!important}tr.hidden-xs.hidden-md{display:none!important}th.hidden-xs.hidden-md,td.hidden-xs.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-xs.hidden-lg{display:none!important}tr.hidden-xs.hidden-lg{display:none!important}th.hidden-xs.hidden-lg,td.hidden-xs.hidden-lg{display:none!important}}.hidden-sm{display:block!important}tr.hidden-sm{display:table-row!important}th.hidden-sm,td.hidden-sm{display:table-cell!important}@media(max-width:767px){.hidden-sm.hidden-xs{display:none!important}tr.hidden-sm.hidden-xs{display:none!important}th.hidden-sm.hidden-xs,td.hidden-sm.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}tr.hidden-sm{display:none!important}th.hidden-sm,td.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-sm.hidden-md{display:none!important}tr.hidden-sm.hidden-md{display:none!important}th.hidden-sm.hidden-md,td.hidden-sm.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-sm.hidden-lg{display:none!important}tr.hidden-sm.hidden-lg{display:none!important}th.hidden-sm.hidden-lg,td.hidden-sm.hidden-lg{display:none!important}}.hidden-md{display:block!important}tr.hidden-md{display:table-row!important}th.hidden-md,td.hidden-md{display:table-cell!important}@media(max-width:767px){.hidden-md.hidden-xs{display:none!important}tr.hidden-md.hidden-xs{display:none!important}th.hidden-md.hidden-xs,td.hidden-md.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-md.hidden-sm{display:none!important}tr.hidden-md.hidden-sm{display:none!important}th.hidden-md.hidden-sm,td.hidden-md.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}tr.hidden-md{display:none!important}th.hidden-md,td.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-md.hidden-lg{display:none!important}tr.hidden-md.hidden-lg{display:none!important}th.hidden-md.hidden-lg,td.hidden-md.hidden-lg{display:none!important}}.hidden-lg{display:block!important}tr.hidden-lg{display:table-row!important}th.hidden-lg,td.hidden-lg{display:table-cell!important}@media(max-width:767px){.hidden-lg.hidden-xs{display:none!important}tr.hidden-lg.hidden-xs{display:none!important}th.hidden-lg.hidden-xs,td.hidden-lg.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-lg.hidden-sm{display:none!important}tr.hidden-lg.hidden-sm{display:none!important}th.hidden-lg.hidden-sm,td.hidden-lg.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-lg.hidden-md{display:none!important}tr.hidden-lg.hidden-md{display:none!important}th.hidden-lg.hidden-md,td.hidden-lg.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-lg{display:none!important}tr.hidden-lg{display:none!important}th.hidden-lg,td.hidden-lg{display:none!important}}.visible-print{display:none!important}tr.visible-print{display:none!important}th.visible-print,td.visible-print{display:none!important}@media print{.visible-print{display:block!important}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}.hidden-print{display:none!important}tr.hidden-print{display:none!important}th.hidden-print,td.hidden-print{display:none!important}} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/favicon.ico b/Godeps/_workspace/src/github.com/gocql/gocql/website/favicon.ico new file mode 100644 index 000000000..513bf500c --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/favicon.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5931f1a5c7327e4b96bd27c8ea97e4f0744d34023b8e4c1332571047e84505d +size 13870 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.eot b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..aedd5fe8a --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.eot @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ff7c239555476e939e86d457bb78424b945b733b2c23791d9807c2357259d43 +size 14079 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.svg b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..5fee06854 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.ttf b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..645d8d9e9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb18b8d11f9698fc8cb3341cb5c882af1103ef8927d34e1602015b5f82eb234f +size 29512 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.woff b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..14b6f373f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.woff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71c12656535e99119c2a952c10554cd6f47c6923d2d96155a7833276e68992af +size 16448 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/gocql.png b/Godeps/_workspace/src/github.com/gocql/gocql/website/gocql.png new file mode 100644 index 000000000..fcd76bfe9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/gocql.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e8da095031af7282c9838fb548cadc7503926117879c2e3c771295f6a1a2df8 +size 2686 diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/index.html b/Godeps/_workspace/src/github.com/gocql/gocql/website/index.html new file mode 100644 index 000000000..7016b2a6d --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/index.html @@ -0,0 +1,133 @@ + + + + GoCQL + + + + + + + + + + + + + +
+ +
+
+ +

GoCQL tux21b.org/v1/gocql

+
+ +
+ +
+ Under Development: The GoCQL package is currently actively + developed and the API may change in the future. +
+ + +

Highlights

+ +
+
+

Cluster Management

+

GoCQL automatically discovers all data centers, racks and hosts + in your cluster, manages a pool of connections to them and distributes + queries in a reasonable and efficient way.

+
+ +
+

Type Conversation

+

Automatic and safe type conversation between Cassandra and Go without + any loss of precision. Basic types, collections and UUIDs are supported + by default and custom types can implement their own marshaling logic.

+
+ +
+

Synchronous and Concurrent

+

Synchronous API with an asynchronous and concurrent back-end. Each + connection can handle up to 128 concurrent queries and may receive + server side push events at any time.

+
+ +
+

Failover Management

+

TODO :(

+
+
+ +
+
+

Result Paging

+

Iterate over large results sets and let GoCQL fetch one page after + another. The next page is automatically pre-fetched in the background + once the iterator has passed a certain threshold.

+
+ +
+

Atomic Batches

+

Execute a batch of related updates in a single query. GoCQL supports + logged, unlogged and counter batches.

+
+ +
+

Query Tracing

+

Trace queries to obtain a detailed output of all events that + happened during the query execution from Cassandra. The output might + help to identify bugs and performance bottlenecks in your + application.

+
+ +
+

Frame Compression

+

Speed up and reduce the network traffic by compressing the frames + that are sent to Cassandra. + Snappy, a + compression algorithm that aims for very high speeds and reasonable + compression, is enabled by default.

+
+
+ +
+
+

Multiple Cassandra Versions

+

GoCQL supports multiple Cassandra version. Currently Cassandra 1.2 + and Cassandra 2.0 are fully supported.

+
+ +
+

Thoroughly Tested

+

TODO :(

+
+ +
+

BSD License

+

Completely open source. Browse the source on + GitHub and start + contributing today.

+
+
+ + + + diff --git a/Godeps/_workspace/src/github.com/gocql/gocql/website/js/bootstrap.js b/Godeps/_workspace/src/github.com/gocql/gocql/website/js/bootstrap.js new file mode 100644 index 000000000..2c6425714 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gocql/gocql/website/js/bootstrap.js @@ -0,0 +1,1999 @@ +/** +* bootstrap.js v3.0.0 by @fat and @mdo +* Copyright 2013 Twitter Inc. +* http://www.apache.org/licenses/LICENSE-2.0 +*/ +if (!jQuery) { throw new Error("Bootstrap requires jQuery") } + +/* ======================================================================== + * Bootstrap: transition.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#transitions + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * 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. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + 'WebkitTransition' : 'webkitTransitionEnd' + , 'MozTransition' : 'transitionend' + , 'OTransition' : 'oTransitionEnd otransitionend' + , 'transition' : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false, $el = this + $(this).one($.support.transition.end, function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + }) + +}(window.jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#alerts + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * 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. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.hasClass('alert') ? $this : $this.parent() + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + $parent.trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one($.support.transition.end, removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + var old = $.fn.alert + + $.fn.alert = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(window.jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#buttons + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * 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. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + } + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (!data.resetText) $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d); + }, 0) + } + + Button.prototype.toggle = function () { + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + .prop('checked', !this.$element.hasClass('active')) + .trigger('change') + if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active') + } + + this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + var old = $.fn.button + + $.fn.button = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + e.preventDefault() + }) + +}(window.jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#carousel + * ======================================================================== + * Copyright 2012 Twitter, Inc. + * + * 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. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter', $.proxy(this.pause, this)) + .on('mouseleave', $.proxy(this.cycle, this)) + } + + Carousel.DEFAULTS = { + interval: 5000 + , pause: 'hover' + , wrap: true + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getActiveIndex = function () { + this.$active = this.$element.find('.item.active') + this.$items = this.$active.parent().children() + + return this.$items.index(this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getActiveIndex() + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid', function () { that.to(pos) }) + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition.end) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + this.sliding = true + + isCycling && this.pause() + + var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction }) + + if ($next.hasClass('active')) return + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + this.$element.one('slid', function () { + var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) + $nextIndicator && $nextIndicator.addClass('active') + }) + } + + if ($.support.transition && this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one($.support.transition.end, function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { that.$element.trigger('slid') }, 0) + }) + .emulateTransitionEnd(600) + } else { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid') + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + var old = $.fn.carousel + + $.fn.carousel = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var $this = $(this), href + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + $target.carousel(options) + + if (slideIndex = $this.attr('data-slide-to')) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + $carousel.carousel($carousel.data()) + }) + }) + +}(window.jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#collapse + * ======================================================================== + * Copyright 2012 Twitter, Inc. + * + * 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. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + actives.collapse('hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing') + [dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('in') + [dimension]('auto') + this.transitioning = 0 + this.$element.trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + [dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element + [dimension](this.$element[dimension]()) + [0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + var old = $.fn.collapse + + $.fn.collapse = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) { + var $this = $(this), href + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + $target.collapse(option) + }) + +}(window.jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#dropdowns + * ======================================================================== + * Copyright 2012 Twitter, Inc. + * + * 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. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle=dropdown]' + var Dropdown = function (element) { + var $el = $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we we use a backdrop because click events don't delegate + $('