├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── code.google.com │ └── p │ │ └── snappy-go │ │ └── snappy │ │ ├── decode.go │ │ ├── encode.go │ │ ├── snappy.go │ │ └── snappy_test.go │ ├── github.com │ ├── gocql │ │ └── gocql │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── AUTHORS │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── cass1batch_test.go │ │ │ ├── cassandra_test.go │ │ │ ├── cluster.go │ │ │ ├── compressor.go │ │ │ ├── compressor_test.go │ │ │ ├── conn.go │ │ │ ├── conn_test.go │ │ │ ├── connectionpool.go │ │ │ ├── doc.go │ │ │ ├── errors.go │ │ │ ├── errors_test.go │ │ │ ├── frame.go │ │ │ ├── helpers.go │ │ │ ├── host_source.go │ │ │ ├── integration.sh │ │ │ ├── marshal.go │ │ │ ├── marshal_test.go │ │ │ ├── metadata.go │ │ │ ├── metadata_test.go │ │ │ ├── policies.go │ │ │ ├── session.go │ │ │ ├── session_test.go │ │ │ ├── testdata │ │ │ └── pki │ │ │ │ ├── .keystore │ │ │ │ ├── .truststore │ │ │ │ ├── ca.crt │ │ │ │ ├── ca.key │ │ │ │ ├── cassandra.crt │ │ │ │ ├── cassandra.key │ │ │ │ ├── gocql.crt │ │ │ │ └── gocql.key │ │ │ ├── token.go │ │ │ ├── token_test.go │ │ │ ├── topology.go │ │ │ ├── topology_test.go │ │ │ ├── tuple_test.go │ │ │ ├── uuid.go │ │ │ ├── uuid_test.go │ │ │ ├── website │ │ │ ├── css │ │ │ │ ├── bootstrap-theme.css │ │ │ │ ├── bootstrap-theme.min.css │ │ │ │ ├── bootstrap.css │ │ │ │ └── bootstrap.min.css │ │ │ ├── favicon.ico │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ ├── gocql.png │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── bootstrap.js │ │ │ │ └── bootstrap.min.js │ │ │ └── wiki_test.go │ └── golang │ │ └── groupcache │ │ └── lru │ │ ├── lru.go │ │ └── lru_test.go │ └── speter.net │ └── go │ └── exp │ └── math │ └── dec │ └── inf │ ├── LICENSE │ ├── benchmark_test.go │ ├── dec.go │ ├── dec_go1_2_test.go │ ├── dec_internal_test.go │ ├── dec_test.go │ ├── example_test.go │ ├── rounder.go │ ├── rounder_example_test.go │ └── rounder_test.go ├── LICENSE ├── README.md ├── examples ├── base │ └── base.go └── crud │ └── crud.go ├── keyspace.go ├── query.go ├── recipes └── crud.go ├── reflect ├── reflect.go └── reflect_test.go ├── table.go └── type.go /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/elvtechnology/gocqltable", 3 | "GoVersion": "go1.4.2", 4 | "Packages": [ 5 | "./..." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "code.google.com/p/snappy-go/snappy", 10 | "Comment": "null-20", 11 | "Rev": "8850bd446ad6f18a271a08aae26bdf85bdf87c7f" 12 | }, 13 | { 14 | "ImportPath": "github.com/gocql/gocql", 15 | "Comment": "1st_gen_framing-47-g30aa70a", 16 | "Rev": "30aa70a1dd108f2be24f297800630b1cc31e4d2a" 17 | }, 18 | { 19 | "ImportPath": "github.com/golang/groupcache/lru", 20 | "Rev": "604ed5785183e59ae2789449d89e73f3a2a77987" 21 | }, 22 | { 23 | "ImportPath": "speter.net/go/exp/math/dec/inf", 24 | "Rev": "42ca6cd68aa922bc3f32f1e056e61b65945d9ad7" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/decode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package snappy 6 | 7 | import ( 8 | "encoding/binary" 9 | "errors" 10 | "io" 11 | ) 12 | 13 | var ( 14 | // ErrCorrupt reports that the input is invalid. 15 | ErrCorrupt = errors.New("snappy: corrupt input") 16 | // ErrUnsupported reports that the input isn't supported. 17 | ErrUnsupported = errors.New("snappy: unsupported input") 18 | ) 19 | 20 | // DecodedLen returns the length of the decoded block. 21 | func DecodedLen(src []byte) (int, error) { 22 | v, _, err := decodedLen(src) 23 | return v, err 24 | } 25 | 26 | // decodedLen returns the length of the decoded block and the number of bytes 27 | // that the length header occupied. 28 | func decodedLen(src []byte) (blockLen, headerLen int, err error) { 29 | v, n := binary.Uvarint(src) 30 | if n == 0 { 31 | return 0, 0, ErrCorrupt 32 | } 33 | if uint64(int(v)) != v { 34 | return 0, 0, errors.New("snappy: decoded block is too large") 35 | } 36 | return int(v), n, nil 37 | } 38 | 39 | // Decode returns the decoded form of src. The returned slice may be a sub- 40 | // slice of dst if dst was large enough to hold the entire decoded block. 41 | // Otherwise, a newly allocated slice will be returned. 42 | // It is valid to pass a nil dst. 43 | func Decode(dst, src []byte) ([]byte, error) { 44 | dLen, s, err := decodedLen(src) 45 | if err != nil { 46 | return nil, err 47 | } 48 | if len(dst) < dLen { 49 | dst = make([]byte, dLen) 50 | } 51 | 52 | var d, offset, length int 53 | for s < len(src) { 54 | switch src[s] & 0x03 { 55 | case tagLiteral: 56 | x := uint(src[s] >> 2) 57 | switch { 58 | case x < 60: 59 | s += 1 60 | case x == 60: 61 | s += 2 62 | if s > len(src) { 63 | return nil, ErrCorrupt 64 | } 65 | x = uint(src[s-1]) 66 | case x == 61: 67 | s += 3 68 | if s > len(src) { 69 | return nil, ErrCorrupt 70 | } 71 | x = uint(src[s-2]) | uint(src[s-1])<<8 72 | case x == 62: 73 | s += 4 74 | if s > len(src) { 75 | return nil, ErrCorrupt 76 | } 77 | x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16 78 | case x == 63: 79 | s += 5 80 | if s > len(src) { 81 | return nil, ErrCorrupt 82 | } 83 | x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24 84 | } 85 | length = int(x + 1) 86 | if length <= 0 { 87 | return nil, errors.New("snappy: unsupported literal length") 88 | } 89 | if length > len(dst)-d || length > len(src)-s { 90 | return nil, ErrCorrupt 91 | } 92 | copy(dst[d:], src[s:s+length]) 93 | d += length 94 | s += length 95 | continue 96 | 97 | case tagCopy1: 98 | s += 2 99 | if s > len(src) { 100 | return nil, ErrCorrupt 101 | } 102 | length = 4 + int(src[s-2])>>2&0x7 103 | offset = int(src[s-2])&0xe0<<3 | int(src[s-1]) 104 | 105 | case tagCopy2: 106 | s += 3 107 | if s > len(src) { 108 | return nil, ErrCorrupt 109 | } 110 | length = 1 + int(src[s-3])>>2 111 | offset = int(src[s-2]) | int(src[s-1])<<8 112 | 113 | case tagCopy4: 114 | return nil, errors.New("snappy: unsupported COPY_4 tag") 115 | } 116 | 117 | end := d + length 118 | if offset > d || end > len(dst) { 119 | return nil, ErrCorrupt 120 | } 121 | for ; d < end; d++ { 122 | dst[d] = dst[d-offset] 123 | } 124 | } 125 | if d != dLen { 126 | return nil, ErrCorrupt 127 | } 128 | return dst[:d], nil 129 | } 130 | 131 | // NewReader returns a new Reader that decompresses from r, using the framing 132 | // format described at 133 | // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt 134 | func NewReader(r io.Reader) *Reader { 135 | return &Reader{ 136 | r: r, 137 | decoded: make([]byte, maxUncompressedChunkLen), 138 | buf: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)+checksumSize), 139 | } 140 | } 141 | 142 | // Reader is an io.Reader than can read Snappy-compressed bytes. 143 | type Reader struct { 144 | r io.Reader 145 | err error 146 | decoded []byte 147 | buf []byte 148 | // decoded[i:j] contains decoded bytes that have not yet been passed on. 149 | i, j int 150 | readHeader bool 151 | } 152 | 153 | // Reset discards any buffered data, resets all state, and switches the Snappy 154 | // reader to read from r. This permits reusing a Reader rather than allocating 155 | // a new one. 156 | func (r *Reader) Reset(reader io.Reader) { 157 | r.r = reader 158 | r.err = nil 159 | r.i = 0 160 | r.j = 0 161 | r.readHeader = false 162 | } 163 | 164 | func (r *Reader) readFull(p []byte) (ok bool) { 165 | if _, r.err = io.ReadFull(r.r, p); r.err != nil { 166 | if r.err == io.ErrUnexpectedEOF { 167 | r.err = ErrCorrupt 168 | } 169 | return false 170 | } 171 | return true 172 | } 173 | 174 | // Read satisfies the io.Reader interface. 175 | func (r *Reader) Read(p []byte) (int, error) { 176 | if r.err != nil { 177 | return 0, r.err 178 | } 179 | for { 180 | if r.i < r.j { 181 | n := copy(p, r.decoded[r.i:r.j]) 182 | r.i += n 183 | return n, nil 184 | } 185 | if !r.readFull(r.buf[:4]) { 186 | return 0, r.err 187 | } 188 | chunkType := r.buf[0] 189 | if !r.readHeader { 190 | if chunkType != chunkTypeStreamIdentifier { 191 | r.err = ErrCorrupt 192 | return 0, r.err 193 | } 194 | r.readHeader = true 195 | } 196 | chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 197 | if chunkLen > len(r.buf) { 198 | r.err = ErrUnsupported 199 | return 0, r.err 200 | } 201 | 202 | // The chunk types are specified at 203 | // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt 204 | switch chunkType { 205 | case chunkTypeCompressedData: 206 | // Section 4.2. Compressed data (chunk type 0x00). 207 | if chunkLen < checksumSize { 208 | r.err = ErrCorrupt 209 | return 0, r.err 210 | } 211 | buf := r.buf[:chunkLen] 212 | if !r.readFull(buf) { 213 | return 0, r.err 214 | } 215 | checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 216 | buf = buf[checksumSize:] 217 | 218 | n, err := DecodedLen(buf) 219 | if err != nil { 220 | r.err = err 221 | return 0, r.err 222 | } 223 | if n > len(r.decoded) { 224 | r.err = ErrCorrupt 225 | return 0, r.err 226 | } 227 | if _, err := Decode(r.decoded, buf); err != nil { 228 | r.err = err 229 | return 0, r.err 230 | } 231 | if crc(r.decoded[:n]) != checksum { 232 | r.err = ErrCorrupt 233 | return 0, r.err 234 | } 235 | r.i, r.j = 0, n 236 | continue 237 | 238 | case chunkTypeUncompressedData: 239 | // Section 4.3. Uncompressed data (chunk type 0x01). 240 | if chunkLen < checksumSize { 241 | r.err = ErrCorrupt 242 | return 0, r.err 243 | } 244 | buf := r.buf[:checksumSize] 245 | if !r.readFull(buf) { 246 | return 0, r.err 247 | } 248 | checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 249 | // Read directly into r.decoded instead of via r.buf. 250 | n := chunkLen - checksumSize 251 | if !r.readFull(r.decoded[:n]) { 252 | return 0, r.err 253 | } 254 | if crc(r.decoded[:n]) != checksum { 255 | r.err = ErrCorrupt 256 | return 0, r.err 257 | } 258 | r.i, r.j = 0, n 259 | continue 260 | 261 | case chunkTypeStreamIdentifier: 262 | // Section 4.1. Stream identifier (chunk type 0xff). 263 | if chunkLen != len(magicBody) { 264 | r.err = ErrCorrupt 265 | return 0, r.err 266 | } 267 | if !r.readFull(r.buf[:len(magicBody)]) { 268 | return 0, r.err 269 | } 270 | for i := 0; i < len(magicBody); i++ { 271 | if r.buf[i] != magicBody[i] { 272 | r.err = ErrCorrupt 273 | return 0, r.err 274 | } 275 | } 276 | continue 277 | } 278 | 279 | if chunkType <= 0x7f { 280 | // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). 281 | r.err = ErrUnsupported 282 | return 0, r.err 283 | 284 | } else { 285 | // Section 4.4 Padding (chunk type 0xfe). 286 | // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). 287 | if !r.readFull(r.buf[:chunkLen]) { 288 | return 0, r.err 289 | } 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/encode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package snappy 6 | 7 | import ( 8 | "encoding/binary" 9 | "io" 10 | ) 11 | 12 | // We limit how far copy back-references can go, the same as the C++ code. 13 | const maxOffset = 1 << 15 14 | 15 | // emitLiteral writes a literal chunk and returns the number of bytes written. 16 | func emitLiteral(dst, lit []byte) int { 17 | i, n := 0, uint(len(lit)-1) 18 | switch { 19 | case n < 60: 20 | dst[0] = uint8(n)<<2 | tagLiteral 21 | i = 1 22 | case n < 1<<8: 23 | dst[0] = 60<<2 | tagLiteral 24 | dst[1] = uint8(n) 25 | i = 2 26 | case n < 1<<16: 27 | dst[0] = 61<<2 | tagLiteral 28 | dst[1] = uint8(n) 29 | dst[2] = uint8(n >> 8) 30 | i = 3 31 | case n < 1<<24: 32 | dst[0] = 62<<2 | tagLiteral 33 | dst[1] = uint8(n) 34 | dst[2] = uint8(n >> 8) 35 | dst[3] = uint8(n >> 16) 36 | i = 4 37 | case int64(n) < 1<<32: 38 | dst[0] = 63<<2 | tagLiteral 39 | dst[1] = uint8(n) 40 | dst[2] = uint8(n >> 8) 41 | dst[3] = uint8(n >> 16) 42 | dst[4] = uint8(n >> 24) 43 | i = 5 44 | default: 45 | panic("snappy: source buffer is too long") 46 | } 47 | if copy(dst[i:], lit) != len(lit) { 48 | panic("snappy: destination buffer is too short") 49 | } 50 | return i + len(lit) 51 | } 52 | 53 | // emitCopy writes a copy chunk and returns the number of bytes written. 54 | func emitCopy(dst []byte, offset, length int) int { 55 | i := 0 56 | for length > 0 { 57 | x := length - 4 58 | if 0 <= x && x < 1<<3 && offset < 1<<11 { 59 | dst[i+0] = uint8(offset>>8)&0x07<<5 | uint8(x)<<2 | tagCopy1 60 | dst[i+1] = uint8(offset) 61 | i += 2 62 | break 63 | } 64 | 65 | x = length 66 | if x > 1<<6 { 67 | x = 1 << 6 68 | } 69 | dst[i+0] = uint8(x-1)<<2 | tagCopy2 70 | dst[i+1] = uint8(offset) 71 | dst[i+2] = uint8(offset >> 8) 72 | i += 3 73 | length -= x 74 | } 75 | return i 76 | } 77 | 78 | // Encode returns the encoded form of src. The returned slice may be a sub- 79 | // slice of dst if dst was large enough to hold the entire encoded block. 80 | // Otherwise, a newly allocated slice will be returned. 81 | // It is valid to pass a nil dst. 82 | func Encode(dst, src []byte) ([]byte, error) { 83 | if n := MaxEncodedLen(len(src)); len(dst) < n { 84 | dst = make([]byte, n) 85 | } 86 | 87 | // The block starts with the varint-encoded length of the decompressed bytes. 88 | d := binary.PutUvarint(dst, uint64(len(src))) 89 | 90 | // Return early if src is short. 91 | if len(src) <= 4 { 92 | if len(src) != 0 { 93 | d += emitLiteral(dst[d:], src) 94 | } 95 | return dst[:d], nil 96 | } 97 | 98 | // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. 99 | const maxTableSize = 1 << 14 100 | shift, tableSize := uint(32-8), 1<<8 101 | for tableSize < maxTableSize && tableSize < len(src) { 102 | shift-- 103 | tableSize *= 2 104 | } 105 | var table [maxTableSize]int 106 | 107 | // Iterate over the source bytes. 108 | var ( 109 | s int // The iterator position. 110 | t int // The last position with the same hash as s. 111 | lit int // The start position of any pending literal bytes. 112 | ) 113 | for s+3 < len(src) { 114 | // Update the hash table. 115 | b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3] 116 | h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24 117 | p := &table[(h*0x1e35a7bd)>>shift] 118 | // We need to to store values in [-1, inf) in table. To save 119 | // some initialization time, (re)use the table's zero value 120 | // and shift the values against this zero: add 1 on writes, 121 | // subtract 1 on reads. 122 | t, *p = *p-1, s+1 123 | // If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte. 124 | if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] { 125 | s++ 126 | continue 127 | } 128 | // Otherwise, we have a match. First, emit any pending literal bytes. 129 | if lit != s { 130 | d += emitLiteral(dst[d:], src[lit:s]) 131 | } 132 | // Extend the match to be as long as possible. 133 | s0 := s 134 | s, t = s+4, t+4 135 | for s < len(src) && src[s] == src[t] { 136 | s++ 137 | t++ 138 | } 139 | // Emit the copied bytes. 140 | d += emitCopy(dst[d:], s-t, s-s0) 141 | lit = s 142 | } 143 | 144 | // Emit any final pending literal bytes and return. 145 | if lit != len(src) { 146 | d += emitLiteral(dst[d:], src[lit:]) 147 | } 148 | return dst[:d], nil 149 | } 150 | 151 | // MaxEncodedLen returns the maximum length of a snappy block, given its 152 | // uncompressed length. 153 | func MaxEncodedLen(srcLen int) int { 154 | // Compressed data can be defined as: 155 | // compressed := item* literal* 156 | // item := literal* copy 157 | // 158 | // The trailing literal sequence has a space blowup of at most 62/60 159 | // since a literal of length 60 needs one tag byte + one extra byte 160 | // for length information. 161 | // 162 | // Item blowup is trickier to measure. Suppose the "copy" op copies 163 | // 4 bytes of data. Because of a special check in the encoding code, 164 | // we produce a 4-byte copy only if the offset is < 65536. Therefore 165 | // the copy op takes 3 bytes to encode, and this type of item leads 166 | // to at most the 62/60 blowup for representing literals. 167 | // 168 | // Suppose the "copy" op copies 5 bytes of data. If the offset is big 169 | // enough, it will take 5 bytes to encode the copy op. Therefore the 170 | // worst case here is a one-byte literal followed by a five-byte copy. 171 | // That is, 6 bytes of input turn into 7 bytes of "compressed" data. 172 | // 173 | // This last factor dominates the blowup, so the final estimate is: 174 | return 32 + srcLen + srcLen/6 175 | } 176 | 177 | // NewWriter returns a new Writer that compresses to w, using the framing 178 | // format described at 179 | // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt 180 | func NewWriter(w io.Writer) *Writer { 181 | return &Writer{ 182 | w: w, 183 | enc: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)), 184 | } 185 | } 186 | 187 | // Writer is an io.Writer than can write Snappy-compressed bytes. 188 | type Writer struct { 189 | w io.Writer 190 | err error 191 | enc []byte 192 | buf [checksumSize + chunkHeaderSize]byte 193 | wroteHeader bool 194 | } 195 | 196 | // Reset discards the writer's state and switches the Snappy writer to write to 197 | // w. This permits reusing a Writer rather than allocating a new one. 198 | func (w *Writer) Reset(writer io.Writer) { 199 | w.w = writer 200 | w.err = nil 201 | w.wroteHeader = false 202 | } 203 | 204 | // Write satisfies the io.Writer interface. 205 | func (w *Writer) Write(p []byte) (n int, errRet error) { 206 | if w.err != nil { 207 | return 0, w.err 208 | } 209 | if !w.wroteHeader { 210 | copy(w.enc, magicChunk) 211 | if _, err := w.w.Write(w.enc[:len(magicChunk)]); err != nil { 212 | w.err = err 213 | return n, err 214 | } 215 | w.wroteHeader = true 216 | } 217 | for len(p) > 0 { 218 | var uncompressed []byte 219 | if len(p) > maxUncompressedChunkLen { 220 | uncompressed, p = p[:maxUncompressedChunkLen], p[maxUncompressedChunkLen:] 221 | } else { 222 | uncompressed, p = p, nil 223 | } 224 | checksum := crc(uncompressed) 225 | 226 | // Compress the buffer, discarding the result if the improvement 227 | // isn't at least 12.5%. 228 | chunkType := uint8(chunkTypeCompressedData) 229 | chunkBody, err := Encode(w.enc, uncompressed) 230 | if err != nil { 231 | w.err = err 232 | return n, err 233 | } 234 | if len(chunkBody) >= len(uncompressed)-len(uncompressed)/8 { 235 | chunkType, chunkBody = chunkTypeUncompressedData, uncompressed 236 | } 237 | 238 | chunkLen := 4 + len(chunkBody) 239 | w.buf[0] = chunkType 240 | w.buf[1] = uint8(chunkLen >> 0) 241 | w.buf[2] = uint8(chunkLen >> 8) 242 | w.buf[3] = uint8(chunkLen >> 16) 243 | w.buf[4] = uint8(checksum >> 0) 244 | w.buf[5] = uint8(checksum >> 8) 245 | w.buf[6] = uint8(checksum >> 16) 246 | w.buf[7] = uint8(checksum >> 24) 247 | if _, err = w.w.Write(w.buf[:]); err != nil { 248 | w.err = err 249 | return n, err 250 | } 251 | if _, err = w.w.Write(chunkBody); err != nil { 252 | w.err = err 253 | return n, err 254 | } 255 | n += len(uncompressed) 256 | } 257 | return n, nil 258 | } 259 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/snappy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package snappy implements the snappy block-based compression format. 6 | // It aims for very high speeds and reasonable compression. 7 | // 8 | // The C++ snappy implementation is at http://code.google.com/p/snappy/ 9 | package snappy 10 | 11 | import ( 12 | "hash/crc32" 13 | ) 14 | 15 | /* 16 | Each encoded block begins with the varint-encoded length of the decoded data, 17 | followed by a sequence of chunks. Chunks begin and end on byte boundaries. The 18 | first byte of each chunk is broken into its 2 least and 6 most significant bits 19 | called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. 20 | Zero means a literal tag. All other values mean a copy tag. 21 | 22 | For literal tags: 23 | - If m < 60, the next 1 + m bytes are literal bytes. 24 | - Otherwise, let n be the little-endian unsigned integer denoted by the next 25 | m - 59 bytes. The next 1 + n bytes after that are literal bytes. 26 | 27 | For copy tags, length bytes are copied from offset bytes ago, in the style of 28 | Lempel-Ziv compression algorithms. In particular: 29 | - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). 30 | The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 31 | of the offset. The next byte is bits 0-7 of the offset. 32 | - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). 33 | The length is 1 + m. The offset is the little-endian unsigned integer 34 | denoted by the next 2 bytes. 35 | - For l == 3, this tag is a legacy format that is no longer supported. 36 | */ 37 | const ( 38 | tagLiteral = 0x00 39 | tagCopy1 = 0x01 40 | tagCopy2 = 0x02 41 | tagCopy4 = 0x03 42 | ) 43 | 44 | const ( 45 | checksumSize = 4 46 | chunkHeaderSize = 4 47 | magicChunk = "\xff\x06\x00\x00" + magicBody 48 | magicBody = "sNaPpY" 49 | // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt says 50 | // that "the uncompressed data in a chunk must be no longer than 65536 bytes". 51 | maxUncompressedChunkLen = 65536 52 | ) 53 | 54 | const ( 55 | chunkTypeCompressedData = 0x00 56 | chunkTypeUncompressedData = 0x01 57 | chunkTypePadding = 0xfe 58 | chunkTypeStreamIdentifier = 0xff 59 | ) 60 | 61 | var crcTable = crc32.MakeTable(crc32.Castagnoli) 62 | 63 | // crc implements the checksum specified in section 3 of 64 | // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt 65 | func crc(b []byte) uint32 { 66 | c := crc32.Update(0, crcTable, b) 67 | return uint32(c>>15|c<<17) + 0xa282ead8 68 | } 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/.gitignore -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | cache: 6 | directories: 7 | - $HOME/.ccm/repository 8 | 9 | matrix: 10 | fast_finish: true 11 | 12 | env: 13 | global: 14 | - GOMAXPROCS=2 15 | matrix: 16 | - CASS=1.2.19 AUTH=false 17 | - CASS=2.0.14 AUTH=false 18 | - CASS=2.1.4 AUTH=false 19 | - CASS=2.1.4 AUTH=true 20 | 21 | go: 22 | - 1.3 23 | - 1.4 24 | 25 | install: 26 | - pip install --user cql PyYAML six 27 | - go get golang.org/x/tools/cmd/vet 28 | - go get golang.org/x/tools/cmd/cover 29 | - git clone https://github.com/pcmanus/ccm.git 30 | - pushd ccm 31 | - ./setup.py install --user 32 | - popd 33 | - go get . 34 | 35 | script: 36 | - set -e 37 | - go test -v -tags unit 38 | - PATH=$PATH:$HOME/.local/bin bash -x integration.sh $CASS $AUTH 39 | - go vet . 40 | 41 | notifications: 42 | - email: false 43 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/AUTHORS: -------------------------------------------------------------------------------- 1 | # This source file refers to The gocql Authors for copyright purposes. 2 | 3 | Christoph Hack 4 | Jonathan Rudenberg 5 | Thorsten von Eicken 6 | Matt Robenolt 7 | Phillip Couto 8 | Niklas Korz 9 | Nimi Wariboko Jr 10 | Ghais Issa 11 | Sasha Klizhentas 12 | Konstantin Cherkasov 13 | Ben Hood <0x6e6562@gmail.com> 14 | Pete Hopkins 15 | Chris Bannister 16 | Maxim Bublis 17 | Alex Zorin 18 | Kasper Middelboe Petersen 19 | Harpreet Sawhney 20 | Charlie Andrews 21 | Stanislavs Koikovs 22 | Dan Forest 23 | Miguel Serrano 24 | Stefan Radomski 25 | Josh Wright 26 | Jacob Rhoden 27 | Ben Frye 28 | Fred McCann 29 | Dan Simmons 30 | Muir Manders 31 | Sankar P 32 | Julien Da Silva 33 | Dan Kennedy 34 | Nick Dhupia 35 | Yasuharu Goto 36 | Jeremy Schlatter 37 | Matthias Kadenbach 38 | Dean Elbaz 39 | Mike Berman 40 | Dmitriy Fedorenko 41 | Zach Marcantel 42 | James Maloney 43 | Ashwin Purohit 44 | Dan Kinder 45 | Oliver Beattie 46 | Justin Corpron 47 | Miles Delahunty 48 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to gocql 2 | 3 | **TL;DR** - this manifesto sets out the bare mimimum requirements for submitting a patch to gocql. 4 | 5 | This guide outlines the process of landing patches in gocql and the general approach to maintaining the code base. 6 | 7 | ## Background 8 | 9 | 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. 10 | 11 | ## Minimum Requirement Checklist 12 | 13 | The following is a check list of requirements that need to be satisfied in order for us to merge your patch: 14 | 15 | * You should raise a pull request to gocql/gocql on Github 16 | * The pull request has a title that clearly summarizes the purpose of the patch 17 | * The motivation behind the patch is clearly defined in the pull request summary 18 | * Your name and email have been added to the `AUTHORS` file (for copyright purposes) 19 | * The patch will merge cleanly 20 | * The test coverage does not fall below the critical threshold (currently 64%) 21 | * The merge commit passes the regression test suite on Travis 22 | * `go fmt` has been applied to the submitted code 23 | * 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) 24 | 25 | 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. 26 | 27 | ## Beyond The Checklist 28 | 29 | 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. 30 | 31 | ### General QA Approach 32 | 33 | 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. 34 | 35 | Your patch will get merged quicker if you have decent test cases that provide test coverage for the new behavior you wish to introduce. 36 | 37 | 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. 38 | 39 | 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. 40 | 41 | ### When It's Too Difficult To Automate Testing 42 | 43 | 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. 44 | 45 | Examples of pull requests that have been accepted without tests include: 46 | 47 | * 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 48 | * https://github.com/gocql/gocql/pull/179 - this bug can only be reproduced under heavy load in certain circumstances 49 | 50 | ### Sign Off Procedure 51 | 52 | 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. 53 | 54 | ### Supported Features 55 | 56 | 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. 57 | 58 | ### Longer Term Evolution 59 | 60 | 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. 61 | 62 | ## Officially Supported Server Versions 63 | 64 | Currently, the officiallly supported versions of the Cassandra server include: 65 | 66 | * 1.2.18 67 | * 2.0.9 68 | 69 | 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. 70 | 71 | ## The Core Dev Team 72 | 73 | The core development team includes: 74 | 75 | * tux21b 76 | * phillipCouto 77 | * Zariel 78 | * 0x6e6562 79 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The gocql Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/README.md: -------------------------------------------------------------------------------- 1 | gocql 2 | ===== 3 | 4 | [![Build Status](https://travis-ci.org/gocql/gocql.png?branch=master)](https://travis-ci.org/gocql/gocql) 5 | [![GoDoc](http://godoc.org/github.com/gocql/gocql?status.png)](http://godoc.org/github.com/gocql/gocql) 6 | 7 | Package gocql implements a fast and robust Cassandra client for the 8 | Go programming language. 9 | 10 | Project Website: http://gocql.github.io/
11 | API documentation: http://godoc.org/github.com/gocql/gocql
12 | Discussions: https://groups.google.com/forum/#!forum/gocql 13 | 14 | Production Stability 15 | --------- 16 | The underlying framing code was rewritten as part of [#339](https://github.com/gocql/gocql/pull/339) and as such may have 17 | 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. 18 | 19 | Supported Versions 20 | ------------------ 21 | 22 | The following matrix shows the versions of Go and Cassandra that are tested with the integration test suite as part of the CI build: 23 | 24 | Go/Cassandra | 1.2.19 | 2.0.14 | 2.1.4 25 | -------------| -------| ------| --------- 26 | 1.3 | yes | yes | yes 27 | 1.4 | yes | yes | yes 28 | 29 | 30 | Sunsetting Model 31 | ---------------- 32 | 33 | 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. 34 | 35 | Installation 36 | ------------ 37 | 38 | go get github.com/gocql/gocql 39 | 40 | 41 | Features 42 | -------- 43 | 44 | * Modern Cassandra client using the native transport 45 | * Automatic type conversations between Cassandra and Go 46 | * Support for all common types including sets, lists and maps 47 | * Custom types can implement a `Marshaler` and `Unmarshaler` interface 48 | * Strict type conversations without any loss of precision 49 | * Built-In support for UUIDs (version 1 and 4) 50 | * Support for logged, unlogged and counter batches 51 | * Cluster management 52 | * Automatic reconnect on connection failures with exponential falloff 53 | * Round robin distribution of queries to different hosts 54 | * Round robin distribution of queries to different connections on a host 55 | * Each connection can execute up to n concurrent queries (whereby n is the limit set by the protocol version the client chooses to use) 56 | * Optional automatic discovery of nodes 57 | * Optional support for periodic node discovery via system.peers 58 | * Support for password authentication 59 | * Iteration over paged results with configurable page size 60 | * Support for TLS/SSL 61 | * Optional frame compression (using snappy) 62 | * Automatic query preparation 63 | * Support for query tracing 64 | * Experimental support for CQL protocol version 3 65 | * Support for up to 32768 streams 66 | * Support for tuple types 67 | * An API to access the schema metadata of a given keyspace 68 | 69 | Please visit the [Roadmap](https://github.com/gocql/gocql/wiki/Roadmap) page to see what is on the horizion. 70 | 71 | Important Default Keyspace Changes 72 | ---------------------------------- 73 | gocql no longer supports executing "use " statements to simplfy the library. The user still has the 74 | ability to define the default keyspace for connections but now the keyspace can only be defined before a 75 | session is created. Queries can still access keyspaces by indicating the keyspace in the query: 76 | ```sql 77 | SELECT * FROM example2.table; 78 | ``` 79 | 80 | Example of correct usage: 81 | ```go 82 | cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") 83 | cluster.Keyspace = "example" 84 | ... 85 | session, err := cluster.CreateSession() 86 | 87 | ``` 88 | Example of incorrect usage: 89 | ```go 90 | cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") 91 | cluster.Keyspace = "example" 92 | ... 93 | session, err := cluster.CreateSession() 94 | 95 | if err = session.Query("use example2").Exec(); err != nil { 96 | log.Fatal(err) 97 | } 98 | ``` 99 | This will result in an err being returned from the session.Query line as the user is trying to execute a "use" 100 | statement. 101 | 102 | Example 103 | ------- 104 | 105 | ```go 106 | /* Before you execute the program, Launch `cqlsh` and execute: 107 | create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; 108 | create table example.tweet(timeline text, id UUID, text text, PRIMARY KEY(id)); 109 | create index on example.tweet(timeline); 110 | */ 111 | package main 112 | 113 | import ( 114 | "fmt" 115 | "log" 116 | 117 | "github.com/gocql/gocql" 118 | ) 119 | 120 | func main() { 121 | // connect to the cluster 122 | cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") 123 | cluster.Keyspace = "example" 124 | cluster.Consistency = gocql.Quorum 125 | session, _ := cluster.CreateSession() 126 | defer session.Close() 127 | 128 | // insert a tweet 129 | if err := session.Query(`INSERT INTO tweet (timeline, id, text) VALUES (?, ?, ?)`, 130 | "me", gocql.TimeUUID(), "hello world").Exec(); err != nil { 131 | log.Fatal(err) 132 | } 133 | 134 | var id gocql.UUID 135 | var text string 136 | 137 | /* Search for a specific set of records whose 'timeline' column matches 138 | * the value 'me'. The secondary index that we created earlier will be 139 | * used for optimizing the search */ 140 | if err := session.Query(`SELECT id, text FROM tweet WHERE timeline = ? LIMIT 1`, 141 | "me").Consistency(gocql.One).Scan(&id, &text); err != nil { 142 | log.Fatal(err) 143 | } 144 | fmt.Println("Tweet:", id, text) 145 | 146 | // list all tweets 147 | iter := session.Query(`SELECT id, text FROM tweet WHERE timeline = ?`, "me").Iter() 148 | for iter.Scan(&id, &text) { 149 | fmt.Println("Tweet:", id, text) 150 | } 151 | if err := iter.Close(); err != nil { 152 | log.Fatal(err) 153 | } 154 | } 155 | ``` 156 | 157 | Data Binding 158 | ------------ 159 | 160 | There are various ways to bind application level data structures to CQL statements: 161 | 162 | * 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. 163 | * 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. 164 | * As a refinement on the `SliceMap()` API you can also call `MapScan()` which returns `map[string]interface{}` instances in a row by row fashion. 165 | * 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. 166 | * 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. 167 | * 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. 168 | * [gocassa](https://github.com/hailocab/gocassa) is an external project that layers on top of gocql to provide convenient query building and data binding. 169 | 170 | Ecosphere 171 | --------- 172 | 173 | The following community maintained tools are known to integrate with gocql: 174 | 175 | * [migrate](https://github.com/mattes/migrate) is a migration handling tool written in Go with Cassandra support. 176 | * [negronicql](https://github.com/mikebthun/negronicql) is gocql middleware for Negroni. 177 | * [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. 178 | * [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. 179 | * [gocassa](https://github.com/hailocab/gocassa) provides query building, adds data binding, and provides easy-to-use "recipe" tables for common query use-cases. 180 | 181 | Other Projects 182 | -------------- 183 | 184 | * [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. 185 | 186 | SEO 187 | --- 188 | 189 | 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. 190 | 191 | License 192 | ------- 193 | 194 | > Copyright (c) 2012-2015 The gocql Authors. All rights reserved. 195 | > Use of this source code is governed by a BSD-style 196 | > license that can be found in the LICENSE file. 197 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/cass1batch_test.go: -------------------------------------------------------------------------------- 1 | // +build all integration 2 | 3 | package gocql 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestProto1BatchInsert(t *testing.T) { 11 | session := createSession(t) 12 | if err := session.Query("CREATE TABLE large (id int primary key)").Exec(); err != nil { 13 | t.Fatal("create table:", err) 14 | } 15 | 16 | begin := "BEGIN BATCH" 17 | end := "APPLY BATCH" 18 | query := "INSERT INTO large (id) VALUES (?)" 19 | fullQuery := strings.Join([]string{begin, query, end}, "\n") 20 | args := []interface{}{5} 21 | if err := session.Query(fullQuery, args...).Consistency(Quorum).Exec(); err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | } 26 | 27 | func TestShouldPrepareFunction(t *testing.T) { 28 | var shouldPrepareTests = []struct { 29 | Stmt string 30 | Result bool 31 | }{ 32 | {` 33 | BEGIN BATCH 34 | INSERT INTO users (userID, password) 35 | VALUES ('smith', 'secret') 36 | APPLY BATCH 37 | ; 38 | `, true}, 39 | {`INSERT INTO users (userID, password, name) VALUES ('user2', 'ch@ngem3b', 'second user')`, true}, 40 | {`BEGIN COUNTER BATCH UPDATE stats SET views = views + 1 WHERE pageid = 1 APPLY BATCH`, true}, 41 | {`delete name from users where userID = 'smith';`, true}, 42 | {` UPDATE users SET password = 'secret' WHERE userID = 'smith' `, true}, 43 | {`CREATE TABLE users ( 44 | user_name varchar PRIMARY KEY, 45 | password varchar, 46 | gender varchar, 47 | session_token varchar, 48 | state varchar, 49 | birth_year bigint 50 | );`, false}, 51 | } 52 | 53 | for _, test := range shouldPrepareTests { 54 | q := &Query{stmt: test.Stmt} 55 | if got := q.shouldPrepare(); got != test.Result { 56 | t.Fatalf("%q: got %v, expected %v\n", test.Stmt, got, test.Result) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/cluster.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocql 6 | 7 | import ( 8 | "errors" 9 | "sync" 10 | "time" 11 | 12 | "github.com/golang/groupcache/lru" 13 | ) 14 | 15 | const defaultMaxPreparedStmts = 1000 16 | 17 | //Package global reference to Prepared Statements LRU 18 | var stmtsLRU preparedLRU 19 | 20 | //preparedLRU is the prepared statement cache 21 | type preparedLRU struct { 22 | sync.Mutex 23 | lru *lru.Cache 24 | } 25 | 26 | //Max adjusts the maximum size of the cache and cleans up the oldest records if 27 | //the new max is lower than the previous value. Not concurrency safe. 28 | func (p *preparedLRU) Max(max int) { 29 | for p.lru.Len() > max { 30 | p.lru.RemoveOldest() 31 | } 32 | p.lru.MaxEntries = max 33 | } 34 | 35 | func initStmtsLRU(max int) { 36 | if stmtsLRU.lru != nil { 37 | stmtsLRU.Max(max) 38 | } else { 39 | stmtsLRU.lru = lru.New(max) 40 | } 41 | } 42 | 43 | // To enable periodic node discovery enable DiscoverHosts in ClusterConfig 44 | type DiscoveryConfig struct { 45 | // If not empty will filter all discoverred hosts to a single Data Centre (default: "") 46 | DcFilter string 47 | // If not empty will filter all discoverred hosts to a single Rack (default: "") 48 | RackFilter string 49 | // The interval to check for new hosts (default: 30s) 50 | Sleep time.Duration 51 | } 52 | 53 | // ClusterConfig is a struct to configure the default cluster implementation 54 | // of gocoql. It has a varity of attributes that can be used to modify the 55 | // behavior to fit the most common use cases. Applications that requre a 56 | // different setup must implement their own cluster. 57 | type ClusterConfig struct { 58 | Hosts []string // addresses for the initial connections 59 | CQLVersion string // CQL version (default: 3.0.0) 60 | ProtoVersion int // version of the native protocol (default: 2) 61 | Timeout time.Duration // connection timeout (default: 600ms) 62 | Port int // port (default: 9042) 63 | Keyspace string // initial keyspace (optional) 64 | NumConns int // number of connections per host (default: 2) 65 | NumStreams int // number of streams per connection (default: max per protocol, either 128 or 32768) 66 | Consistency Consistency // default consistency level (default: Quorum) 67 | Compressor Compressor // compression algorithm (default: nil) 68 | Authenticator Authenticator // authenticator (default: nil) 69 | RetryPolicy RetryPolicy // Default retry policy to use for queries (default: 0) 70 | SocketKeepalive time.Duration // The keepalive period to use, enabled if > 0 (default: 0) 71 | ConnPoolType NewPoolFunc // The function used to create the connection pool for the session (default: NewSimplePool) 72 | DiscoverHosts bool // If set, gocql will attempt to automatically discover other members of the Cassandra cluster (default: false) 73 | MaxPreparedStmts int // Sets the maximum cache size for prepared statements globally for gocql (default: 1000) 74 | MaxRoutingKeyInfo int // Sets the maximum cache size for query info about statements for each session (default: 1000) 75 | PageSize int // Default page size to use for created sessions (default: 0) 76 | Discovery DiscoveryConfig 77 | SslOpts *SslOptions 78 | } 79 | 80 | // NewCluster generates a new config for the default cluster implementation. 81 | func NewCluster(hosts ...string) *ClusterConfig { 82 | cfg := &ClusterConfig{ 83 | Hosts: hosts, 84 | CQLVersion: "3.0.0", 85 | ProtoVersion: 2, 86 | Timeout: 600 * time.Millisecond, 87 | Port: 9042, 88 | NumConns: 2, 89 | Consistency: Quorum, 90 | ConnPoolType: NewSimplePool, 91 | DiscoverHosts: false, 92 | MaxPreparedStmts: defaultMaxPreparedStmts, 93 | MaxRoutingKeyInfo: 1000, 94 | } 95 | return cfg 96 | } 97 | 98 | // CreateSession initializes the cluster based on this config and returns a 99 | // session object that can be used to interact with the database. 100 | func (cfg *ClusterConfig) CreateSession() (*Session, error) { 101 | 102 | //Check that hosts in the ClusterConfig is not empty 103 | if len(cfg.Hosts) < 1 { 104 | return nil, ErrNoHosts 105 | } 106 | 107 | maxStreams := 128 108 | if cfg.ProtoVersion > protoVersion2 { 109 | maxStreams = 32768 110 | } 111 | 112 | if cfg.NumStreams <= 0 || cfg.NumStreams > maxStreams { 113 | cfg.NumStreams = maxStreams 114 | } 115 | 116 | pool, err := cfg.ConnPoolType(cfg) 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | //Adjust the size of the prepared statements cache to match the latest configuration 122 | stmtsLRU.Lock() 123 | initStmtsLRU(cfg.MaxPreparedStmts) 124 | stmtsLRU.Unlock() 125 | 126 | //See if there are any connections in the pool 127 | if pool.Size() > 0 { 128 | s := NewSession(pool, *cfg) 129 | s.SetConsistency(cfg.Consistency) 130 | s.SetPageSize(cfg.PageSize) 131 | 132 | if cfg.DiscoverHosts { 133 | hostSource := &ringDescriber{ 134 | session: s, 135 | dcFilter: cfg.Discovery.DcFilter, 136 | rackFilter: cfg.Discovery.RackFilter, 137 | } 138 | 139 | go hostSource.run(cfg.Discovery.Sleep) 140 | } 141 | 142 | return s, nil 143 | } 144 | 145 | pool.Close() 146 | return nil, ErrNoConnectionsStarted 147 | } 148 | 149 | var ( 150 | ErrNoHosts = errors.New("no hosts provided") 151 | ErrNoConnectionsStarted = errors.New("no connections were made when creating the session") 152 | ErrHostQueryFailed = errors.New("unable to populate Hosts") 153 | ) 154 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/compressor.go: -------------------------------------------------------------------------------- 1 | package gocql 2 | 3 | import ( 4 | "code.google.com/p/snappy-go/snappy" 5 | ) 6 | 7 | type Compressor interface { 8 | Name() string 9 | Encode(data []byte) ([]byte, error) 10 | Decode(data []byte) ([]byte, error) 11 | } 12 | 13 | // SnappyCompressor implements the Compressor interface and can be used to 14 | // compress incoming and outgoing frames. The snappy compression algorithm 15 | // aims for very high speeds and reasonable compression. 16 | type SnappyCompressor struct{} 17 | 18 | func (s SnappyCompressor) Name() string { 19 | return "snappy" 20 | } 21 | 22 | func (s SnappyCompressor) Encode(data []byte) ([]byte, error) { 23 | return snappy.Encode(nil, data) 24 | } 25 | 26 | func (s SnappyCompressor) Decode(data []byte) ([]byte, error) { 27 | return snappy.Decode(nil, data) 28 | } 29 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/compressor_test.go: -------------------------------------------------------------------------------- 1 | // +build all unit 2 | 3 | package gocql 4 | 5 | import ( 6 | "bytes" 7 | "code.google.com/p/snappy-go/snappy" 8 | "testing" 9 | ) 10 | 11 | func TestSnappyCompressor(t *testing.T) { 12 | c := SnappyCompressor{} 13 | if c.Name() != "snappy" { 14 | t.Fatalf("expected name to be 'snappy', got %v", c.Name()) 15 | } 16 | 17 | str := "My Test String" 18 | //Test Encoding 19 | if expected, err := snappy.Encode(nil, []byte(str)); err != nil { 20 | t.Fatalf("failed to encode '%v' with error %v", str, err) 21 | } else if res, err := c.Encode([]byte(str)); err != nil { 22 | t.Fatalf("failed to encode '%v' with error %v", str, err) 23 | } else if bytes.Compare(expected, res) != 0 { 24 | t.Fatal("failed to match the expected encoded value with the result encoded value.") 25 | } 26 | 27 | val, err := c.Encode([]byte(str)) 28 | if err != nil { 29 | t.Fatalf("failed to encode '%v' with error '%v'", str, err) 30 | } 31 | 32 | //Test Decoding 33 | if expected, err := snappy.Decode(nil, val); err != nil { 34 | t.Fatalf("failed to decode '%v' with error %v", val, err) 35 | } else if res, err := c.Decode(val); err != nil { 36 | t.Fatalf("failed to decode '%v' with error %v", val, err) 37 | } else if bytes.Compare(expected, res) != 0 { 38 | t.Fatal("failed to match the expected decoded value with the result decoded value.") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2015 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package gocql implements a fast and robust Cassandra driver for the 6 | // Go programming language. 7 | package gocql 8 | 9 | // TODO(tux21b): write more docs. 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/errors.go: -------------------------------------------------------------------------------- 1 | package gocql 2 | 3 | import "fmt" 4 | 5 | const ( 6 | errServer = 0x0000 7 | errProtocol = 0x000A 8 | errCredentials = 0x0100 9 | errUnavailable = 0x1000 10 | errOverloaded = 0x1001 11 | errBootstrapping = 0x1002 12 | errTruncate = 0x1003 13 | errWriteTimeout = 0x1100 14 | errReadTimeout = 0x1200 15 | errSyntax = 0x2000 16 | errUnauthorized = 0x2100 17 | errInvalid = 0x2200 18 | errConfig = 0x2300 19 | errAlreadyExists = 0x2400 20 | errUnprepared = 0x2500 21 | ) 22 | 23 | type RequestError interface { 24 | Code() int 25 | Message() string 26 | Error() string 27 | } 28 | 29 | type errorFrame struct { 30 | frameHeader 31 | 32 | code int 33 | message string 34 | } 35 | 36 | func (e errorFrame) Code() int { 37 | return e.code 38 | } 39 | 40 | func (e errorFrame) Message() string { 41 | return e.message 42 | } 43 | 44 | func (e errorFrame) Error() string { 45 | return e.Message() 46 | } 47 | 48 | func (e errorFrame) String() string { 49 | return fmt.Sprintf("[error code=%x message=%q]", e.code, e.message) 50 | } 51 | 52 | type RequestErrUnavailable struct { 53 | errorFrame 54 | Consistency Consistency 55 | Required int 56 | Alive int 57 | } 58 | 59 | func (e *RequestErrUnavailable) String() string { 60 | return fmt.Sprintf("[request_error_unavailable consistency=%s required=%d alive=%d]", e.Consistency, e.Required, e.Alive) 61 | } 62 | 63 | type RequestErrWriteTimeout struct { 64 | errorFrame 65 | Consistency Consistency 66 | Received int 67 | BlockFor int 68 | WriteType string 69 | } 70 | 71 | type RequestErrReadTimeout struct { 72 | errorFrame 73 | Consistency Consistency 74 | Received int 75 | BlockFor int 76 | DataPresent byte 77 | } 78 | 79 | type RequestErrAlreadyExists struct { 80 | errorFrame 81 | Keyspace string 82 | Table string 83 | } 84 | 85 | type RequestErrUnprepared struct { 86 | errorFrame 87 | StatementId []byte 88 | } 89 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/errors_test.go: -------------------------------------------------------------------------------- 1 | // +build all integration 2 | 3 | package gocql 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestErrorsParse(t *testing.T) { 10 | session := createSession(t) 11 | defer session.Close() 12 | 13 | if err := createTable(session, `CREATE TABLE errors_parse (id int primary key)`); err != nil { 14 | t.Fatal("create:", err) 15 | } 16 | 17 | if err := createTable(session, `CREATE TABLE errors_parse (id int primary key)`); err == nil { 18 | t.Fatal("Should have gotten already exists error from cassandra server.") 19 | } else { 20 | switch e := err.(type) { 21 | case *RequestErrAlreadyExists: 22 | if e.Table != "errors_parse" { 23 | t.Fatal("Failed to parse error response from cassandra for ErrAlreadyExists.") 24 | } 25 | default: 26 | t.Fatal("Failed to parse error response from cassandra for ErrAlreadyExists.") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocql 6 | 7 | import ( 8 | "math/big" 9 | "reflect" 10 | "strings" 11 | "time" 12 | 13 | "speter.net/go/exp/math/dec/inf" 14 | ) 15 | 16 | type RowData struct { 17 | Columns []string 18 | Values []interface{} 19 | } 20 | 21 | func goType(t TypeInfo) reflect.Type { 22 | switch t.Type() { 23 | case TypeVarchar, TypeAscii, TypeInet: 24 | return reflect.TypeOf(*new(string)) 25 | case TypeBigInt, TypeCounter: 26 | return reflect.TypeOf(*new(int64)) 27 | case TypeTimestamp: 28 | return reflect.TypeOf(*new(time.Time)) 29 | case TypeBlob: 30 | return reflect.TypeOf(*new([]byte)) 31 | case TypeBoolean: 32 | return reflect.TypeOf(*new(bool)) 33 | case TypeFloat: 34 | return reflect.TypeOf(*new(float32)) 35 | case TypeDouble: 36 | return reflect.TypeOf(*new(float64)) 37 | case TypeInt: 38 | return reflect.TypeOf(*new(int)) 39 | case TypeDecimal: 40 | return reflect.TypeOf(*new(*inf.Dec)) 41 | case TypeUUID, TypeTimeUUID: 42 | return reflect.TypeOf(*new(UUID)) 43 | case TypeList, TypeSet: 44 | return reflect.SliceOf(goType(t.(CollectionType).Elem)) 45 | case TypeMap: 46 | return reflect.MapOf(goType(t.(CollectionType).Key), goType(t.(CollectionType).Elem)) 47 | case TypeVarint: 48 | return reflect.TypeOf(*new(*big.Int)) 49 | case TypeTuple: 50 | // what can we do here? all there is to do is to make a list of interface{} 51 | tuple := t.(TupleTypeInfo) 52 | return reflect.TypeOf(make([]interface{}, len(tuple.Elems))) 53 | default: 54 | return nil 55 | } 56 | } 57 | 58 | func dereference(i interface{}) interface{} { 59 | return reflect.Indirect(reflect.ValueOf(i)).Interface() 60 | } 61 | 62 | func getApacheCassandraType(class string) Type { 63 | switch strings.TrimPrefix(class, apacheCassandraTypePrefix) { 64 | case "AsciiType": 65 | return TypeAscii 66 | case "LongType": 67 | return TypeBigInt 68 | case "BytesType": 69 | return TypeBlob 70 | case "BooleanType": 71 | return TypeBoolean 72 | case "CounterColumnType": 73 | return TypeCounter 74 | case "DecimalType": 75 | return TypeDecimal 76 | case "DoubleType": 77 | return TypeDouble 78 | case "FloatType": 79 | return TypeFloat 80 | case "Int32Type": 81 | return TypeInt 82 | case "DateType", "TimestampType": 83 | return TypeTimestamp 84 | case "UUIDType": 85 | return TypeUUID 86 | case "UTF8Type": 87 | return TypeVarchar 88 | case "IntegerType": 89 | return TypeVarint 90 | case "TimeUUIDType": 91 | return TypeTimeUUID 92 | case "InetAddressType": 93 | return TypeInet 94 | case "MapType": 95 | return TypeMap 96 | case "ListType": 97 | return TypeList 98 | case "SetType": 99 | return TypeSet 100 | case "TupleType": 101 | return TypeTuple 102 | default: 103 | return TypeCustom 104 | } 105 | } 106 | 107 | func (r *RowData) rowMap(m map[string]interface{}) { 108 | for i, column := range r.Columns { 109 | val := dereference(r.Values[i]) 110 | if valVal := reflect.ValueOf(val); valVal.Kind() == reflect.Slice { 111 | valCopy := reflect.MakeSlice(valVal.Type(), valVal.Len(), valVal.Cap()) 112 | reflect.Copy(valCopy, valVal) 113 | m[column] = valCopy.Interface() 114 | } else { 115 | m[column] = val 116 | } 117 | } 118 | } 119 | 120 | func (iter *Iter) RowData() (RowData, error) { 121 | if iter.err != nil { 122 | return RowData{}, iter.err 123 | } 124 | columns := make([]string, 0) 125 | values := make([]interface{}, 0) 126 | for _, column := range iter.Columns() { 127 | val := column.TypeInfo.New() 128 | columns = append(columns, column.Name) 129 | values = append(values, val) 130 | } 131 | rowData := RowData{ 132 | Columns: columns, 133 | Values: values, 134 | } 135 | return rowData, nil 136 | } 137 | 138 | // SliceMap is a helper function to make the API easier to use 139 | // returns the data from the query in the form of []map[string]interface{} 140 | func (iter *Iter) SliceMap() ([]map[string]interface{}, error) { 141 | if iter.err != nil { 142 | return nil, iter.err 143 | } 144 | 145 | // Not checking for the error because we just did 146 | rowData, _ := iter.RowData() 147 | dataToReturn := make([]map[string]interface{}, 0) 148 | for iter.Scan(rowData.Values...) { 149 | m := make(map[string]interface{}) 150 | rowData.rowMap(m) 151 | dataToReturn = append(dataToReturn, m) 152 | } 153 | if iter.err != nil { 154 | return nil, iter.err 155 | } 156 | return dataToReturn, nil 157 | } 158 | 159 | // MapScan takes a map[string]interface{} and populates it with a row 160 | // That is returned from cassandra. 161 | func (iter *Iter) MapScan(m map[string]interface{}) bool { 162 | if iter.err != nil { 163 | return false 164 | } 165 | 166 | // Not checking for the error because we just did 167 | rowData, _ := iter.RowData() 168 | 169 | for i, col := range rowData.Columns { 170 | if dest, ok := m[col]; ok { 171 | rowData.Values[i] = dest 172 | } 173 | } 174 | 175 | if iter.Scan(rowData.Values...) { 176 | rowData.rowMap(m) 177 | return true 178 | } 179 | return false 180 | } 181 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/host_source.go: -------------------------------------------------------------------------------- 1 | package gocql 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "time" 7 | ) 8 | 9 | type HostInfo struct { 10 | Peer string 11 | DataCenter string 12 | Rack string 13 | HostId string 14 | Tokens []string 15 | } 16 | 17 | // Polls system.peers at a specific interval to find new hosts 18 | type ringDescriber struct { 19 | dcFilter string 20 | rackFilter string 21 | previous []HostInfo 22 | session *Session 23 | } 24 | 25 | func (r *ringDescriber) GetHosts() ([]HostInfo, error) { 26 | // we need conn to be the same because we need to query system.peers and system.local 27 | // on the same node to get the whole cluster 28 | conn := r.session.Pool.Pick(nil) 29 | if conn == nil { 30 | return r.previous, nil 31 | } 32 | 33 | query := r.session.Query("SELECT data_center, rack, host_id, tokens FROM system.local") 34 | iter := conn.executeQuery(query) 35 | 36 | host := &HostInfo{} 37 | iter.Scan(&host.DataCenter, &host.Rack, &host.HostId, &host.Tokens) 38 | 39 | if err := iter.Close(); err != nil { 40 | return nil, err 41 | } 42 | 43 | addr, _, err := net.SplitHostPort(conn.Address()) 44 | if err != nil { 45 | // this should not happen, ever, as this is the address that was dialed by conn, here 46 | // a panic makes sense, please report a bug if it occurs. 47 | panic(err) 48 | } 49 | 50 | host.Peer = addr 51 | 52 | hosts := []HostInfo{*host} 53 | 54 | query = r.session.Query("SELECT peer, data_center, rack, host_id, tokens FROM system.peers") 55 | iter = conn.executeQuery(query) 56 | 57 | for iter.Scan(&host.Peer, &host.DataCenter, &host.Rack, &host.HostId, &host.Tokens) { 58 | if r.matchFilter(host) { 59 | hosts = append(hosts, *host) 60 | } 61 | } 62 | 63 | if err := iter.Close(); err != nil { 64 | return nil, err 65 | } 66 | 67 | r.previous = hosts 68 | 69 | return hosts, nil 70 | } 71 | 72 | func (r *ringDescriber) matchFilter(host *HostInfo) bool { 73 | 74 | if r.dcFilter != "" && r.dcFilter != host.DataCenter { 75 | return false 76 | } 77 | 78 | if r.rackFilter != "" && r.rackFilter != host.Rack { 79 | return false 80 | } 81 | 82 | return true 83 | } 84 | 85 | func (h *ringDescriber) run(sleep time.Duration) { 86 | if sleep == 0 { 87 | sleep = 30 * time.Second 88 | } 89 | 90 | for { 91 | // if we have 0 hosts this will return the previous list of hosts to 92 | // attempt to reconnect to the cluster otherwise we would never find 93 | // downed hosts again, could possibly have an optimisation to only 94 | // try to add new hosts if GetHosts didnt error and the hosts didnt change. 95 | hosts, err := h.GetHosts() 96 | if err != nil { 97 | log.Println("RingDescriber: unable to get ring topology:", err) 98 | } else { 99 | h.session.Pool.SetHosts(hosts) 100 | } 101 | 102 | time.Sleep(sleep) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/integration.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | function run_tests() { 6 | local clusterSize=3 7 | local version=$1 8 | local auth=$2 9 | 10 | if [ "$auth" = true ]; then 11 | clusterSize=1 12 | fi 13 | 14 | local keypath="$(pwd)/testdata/pki" 15 | 16 | local conf=( 17 | "client_encryption_options.enabled: true" 18 | "client_encryption_options.keystore: $keypath/.keystore" 19 | "client_encryption_options.keystore_password: cassandra" 20 | "client_encryption_options.require_client_auth: true" 21 | "client_encryption_options.truststore: $keypath/.truststore" 22 | "client_encryption_options.truststore_password: cassandra" 23 | "concurrent_reads: 2" 24 | "concurrent_writes: 2" 25 | "rpc_server_type: sync" 26 | "rpc_min_threads: 2" 27 | "rpc_max_threads: 2" 28 | "write_request_timeout_in_ms: 5000" 29 | "read_request_timeout_in_ms: 5000" 30 | ) 31 | 32 | ccm remove test || true 33 | 34 | ccm create test -v binary:$version -n $clusterSize -d --vnodes --jvm_arg="-Xmx256m -XX:NewSize=100m" 35 | ccm updateconf "${conf[@]}" 36 | 37 | if [ "$auth" = true ] 38 | then 39 | ccm updateconf 'authenticator: PasswordAuthenticator' 'authorizer: CassandraAuthorizer' 40 | rm -rf $HOME/.ccm/test/node1/data/system_auth 41 | fi 42 | 43 | ccm start -v 44 | ccm status 45 | ccm node1 nodetool status 46 | 47 | local proto=2 48 | if [[ $version == 1.2.* ]]; then 49 | proto=1 50 | elif [[ $version == 2.1.* ]]; then 51 | proto=3 52 | fi 53 | 54 | if [ "$auth" = true ] 55 | then 56 | sleep 30s 57 | go test -v . -timeout 15s -run=TestAuthentication -tags integration -runssl -runauth -proto=$proto -cluster=$(ccm liveset) -clusterSize=$clusterSize -autowait=1000ms 58 | else 59 | 60 | 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 61 | 62 | if [ ${PIPESTATUS[0]} -ne 0 ]; then 63 | echo "--- FAIL: ccm status follows:" 64 | ccm status 65 | ccm node1 nodetool status 66 | ccm node1 showlog > status.log 67 | cat status.log 68 | echo "--- FAIL: Received a non-zero exit code from the go test execution, please investigate this" 69 | exit 1 70 | fi 71 | 72 | cover=`cat results.txt | grep coverage: | grep -o "[0-9]\{1,3\}" | head -n 1` 73 | 74 | if [[ $cover -lt "55" ]]; then 75 | echo "--- FAIL: expected coverage of at least 60 %, but coverage was $cover %" 76 | exit 1 77 | fi 78 | fi 79 | 80 | ccm remove 81 | } 82 | 83 | run_tests $1 $2 84 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/policies.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | //This file will be the future home for more policies 5 | package gocql 6 | 7 | //RetryableQuery is an interface that represents a query or batch statement that 8 | //exposes the correct functions for the retry policy logic to evaluate correctly. 9 | type RetryableQuery interface { 10 | Attempts() int 11 | GetConsistency() Consistency 12 | } 13 | 14 | // RetryPolicy interace is used by gocql to determine if a query can be attempted 15 | // again after a retryable error has been received. The interface allows gocql 16 | // users to implement their own logic to determine if a query can be attempted 17 | // again. 18 | // 19 | // See SimpleRetryPolicy as an example of implementing and using a RetryPolicy 20 | // interface. 21 | type RetryPolicy interface { 22 | Attempt(RetryableQuery) bool 23 | } 24 | 25 | /* 26 | SimpleRetryPolicy has simple logic for attempting a query a fixed number of times. 27 | 28 | See below for examples of usage: 29 | 30 | //Assign to the cluster 31 | cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: 3} 32 | 33 | //Assign to a query 34 | query.RetryPolicy(&gocql.SimpleRetryPolicy{NumRetries: 1}) 35 | */ 36 | type SimpleRetryPolicy struct { 37 | NumRetries int //Number of times to retry a query 38 | } 39 | 40 | // Attempt tells gocql to attempt the query again based on query.Attempts being less 41 | // than the NumRetries defined in the policy. 42 | func (s *SimpleRetryPolicy) Attempt(q RetryableQuery) bool { 43 | return q.Attempts() <= s.NumRetries 44 | } 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/session_test.go: -------------------------------------------------------------------------------- 1 | // +build all integration 2 | 3 | package gocql 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestSessionAPI(t *testing.T) { 10 | 11 | cfg := ClusterConfig{} 12 | pool, err := NewSimplePool(&cfg) 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | 17 | s := NewSession(pool, cfg) 18 | 19 | s.SetConsistency(All) 20 | if s.cons != All { 21 | t.Fatalf("expected consistency 'All', got '%v'", s.cons) 22 | } 23 | 24 | s.SetPageSize(100) 25 | if s.pageSize != 100 { 26 | t.Fatalf("expected pageSize 100, got %v", s.pageSize) 27 | } 28 | 29 | s.SetPrefetch(0.75) 30 | if s.prefetch != 0.75 { 31 | t.Fatalf("expceted prefetch 0.75, got %v", s.prefetch) 32 | } 33 | 34 | trace := &traceWriter{} 35 | 36 | s.SetTrace(trace) 37 | if s.trace != trace { 38 | t.Fatalf("expected traceWriter '%v',got '%v'", trace, s.trace) 39 | } 40 | 41 | qry := s.Query("test", 1) 42 | if v, ok := qry.values[0].(int); !ok { 43 | t.Fatalf("expected qry.values[0] to be an int, got %v", qry.values[0]) 44 | } else if v != 1 { 45 | t.Fatalf("expceted qry.values[0] to be 1, got %v", v) 46 | } else if qry.stmt != "test" { 47 | t.Fatalf("expected qry.stmt to be 'test', got '%v'", qry.stmt) 48 | } 49 | 50 | boundQry := s.Bind("test", func(q *QueryInfo) ([]interface{}, error) { 51 | return nil, nil 52 | }) 53 | if boundQry.binding == nil { 54 | t.Fatal("expected qry.binding to be defined, got nil") 55 | } else if boundQry.stmt != "test" { 56 | t.Fatalf("expected qry.stmt to be 'test', got '%v'", boundQry.stmt) 57 | } 58 | 59 | itr := s.executeQuery(qry) 60 | if itr.err != ErrNoConnections { 61 | t.Fatalf("expected itr.err to be '%v', got '%v'", ErrNoConnections, itr.err) 62 | } 63 | 64 | testBatch := s.NewBatch(LoggedBatch) 65 | testBatch.Query("test") 66 | err = s.ExecuteBatch(testBatch) 67 | 68 | if err != ErrNoConnections { 69 | t.Fatalf("expected session.ExecuteBatch to return '%v', got '%v'", ErrNoConnections, err) 70 | } 71 | 72 | s.Close() 73 | if !s.Closed() { 74 | t.Fatal("expected s.Closed() to be true, got false") 75 | } 76 | //Should just return cleanly 77 | s.Close() 78 | 79 | err = s.ExecuteBatch(testBatch) 80 | if err != ErrSessionClosed { 81 | t.Fatalf("expected session.ExecuteBatch to return '%v', got '%v'", ErrSessionClosed, err) 82 | } 83 | } 84 | 85 | func TestQueryBasicAPI(t *testing.T) { 86 | qry := &Query{} 87 | 88 | if qry.Latency() != 0 { 89 | t.Fatalf("expected Query.Latency() to return 0, got %v", qry.Latency()) 90 | } 91 | 92 | qry.attempts = 2 93 | qry.totalLatency = 4 94 | if qry.Attempts() != 2 { 95 | t.Fatalf("expected Query.Attempts() to return 2, got %v", qry.Attempts()) 96 | } 97 | if qry.Latency() != 2 { 98 | t.Fatalf("expected Query.Latency() to return 2, got %v", qry.Latency()) 99 | } 100 | 101 | qry.Consistency(All) 102 | if qry.GetConsistency() != All { 103 | t.Fatalf("expected Query.GetConsistency to return 'All', got '%s'", qry.GetConsistency()) 104 | } 105 | 106 | trace := &traceWriter{} 107 | qry.Trace(trace) 108 | if qry.trace != trace { 109 | t.Fatalf("expected Query.Trace to be '%v', got '%v'", trace, qry.trace) 110 | } 111 | 112 | qry.PageSize(10) 113 | if qry.pageSize != 10 { 114 | t.Fatalf("expected Query.PageSize to be 10, got %v", qry.pageSize) 115 | } 116 | 117 | qry.Prefetch(0.75) 118 | if qry.prefetch != 0.75 { 119 | t.Fatalf("expected Query.Prefetch to be 0.75, got %v", qry.prefetch) 120 | } 121 | 122 | rt := &SimpleRetryPolicy{NumRetries: 3} 123 | if qry.RetryPolicy(rt); qry.rt != rt { 124 | t.Fatalf("expected Query.RetryPolicy to be '%v', got '%v'", rt, qry.rt) 125 | } 126 | 127 | qry.Bind(qry) 128 | if qry.values[0] != qry { 129 | t.Fatalf("expected Query.Values[0] to be '%v', got '%v'", qry, qry.values[0]) 130 | } 131 | } 132 | 133 | func TestQueryShouldPrepare(t *testing.T) { 134 | toPrepare := []string{"select * ", "INSERT INTO", "update table", "delete from", "begin batch"} 135 | cantPrepare := []string{"create table", "USE table", "LIST keyspaces", "alter table", "drop table", "grant user", "revoke user"} 136 | q := &Query{} 137 | 138 | for i := 0; i < len(toPrepare); i++ { 139 | q.stmt = toPrepare[i] 140 | if !q.shouldPrepare() { 141 | t.Fatalf("expected Query.shouldPrepare to return true, got false for statement '%v'", toPrepare[i]) 142 | } 143 | } 144 | 145 | for i := 0; i < len(cantPrepare); i++ { 146 | q.stmt = cantPrepare[i] 147 | if q.shouldPrepare() { 148 | t.Fatalf("expected Query.shouldPrepare to return false, got true for statement '%v'", cantPrepare[i]) 149 | } 150 | } 151 | } 152 | 153 | func TestBatchBasicAPI(t *testing.T) { 154 | 155 | cfg := ClusterConfig{} 156 | cfg.RetryPolicy = &SimpleRetryPolicy{NumRetries: 2} 157 | pool, err := NewSimplePool(&cfg) 158 | if err != nil { 159 | t.Fatal(err) 160 | } 161 | 162 | s := NewSession(pool, cfg) 163 | b := s.NewBatch(UnloggedBatch) 164 | if b.Type != UnloggedBatch { 165 | t.Fatalf("expceted batch.Type to be '%v', got '%v'", UnloggedBatch, b.Type) 166 | } else if b.rt != cfg.RetryPolicy { 167 | t.Fatalf("expceted batch.RetryPolicy to be '%v', got '%v'", cfg.RetryPolicy, b.rt) 168 | } 169 | 170 | b = NewBatch(LoggedBatch) 171 | if b.Type != LoggedBatch { 172 | t.Fatalf("expected batch.Type to be '%v', got '%v'", LoggedBatch, b.Type) 173 | } 174 | 175 | b.attempts = 1 176 | if b.Attempts() != 1 { 177 | t.Fatalf("expceted batch.Attempts() to return %v, got %v", 1, b.Attempts()) 178 | } 179 | 180 | if b.Latency() != 0 { 181 | t.Fatalf("expected batch.Latency() to be 0, got %v", b.Latency()) 182 | } 183 | 184 | b.totalLatency = 4 185 | if b.Latency() != 4 { 186 | t.Fatalf("expected batch.Latency() to return %v, got %v", 4, b.Latency()) 187 | } 188 | 189 | b.Cons = One 190 | if b.GetConsistency() != One { 191 | t.Fatalf("expected batch.GetConsistency() to return 'One', got '%s'", b.GetConsistency()) 192 | } 193 | 194 | b.Query("test", 1) 195 | if b.Entries[0].Stmt != "test" { 196 | t.Fatalf("expected batch.Entries[0].Stmt to be 'test', got '%v'", b.Entries[0].Stmt) 197 | } else if b.Entries[0].Args[0].(int) != 1 { 198 | t.Fatalf("expected batch.Entries[0].Args[0] to be 1, got %v", b.Entries[0].Args[0]) 199 | } 200 | 201 | b.Bind("test2", func(q *QueryInfo) ([]interface{}, error) { 202 | return nil, nil 203 | }) 204 | 205 | if b.Entries[1].Stmt != "test2" { 206 | t.Fatalf("expected batch.Entries[1].Stmt to be 'test2', got '%v'", b.Entries[1].Stmt) 207 | } else if b.Entries[1].binding == nil { 208 | t.Fatal("expected batch.Entries[1].binding to be defined, got nil") 209 | } 210 | r := &SimpleRetryPolicy{NumRetries: 4} 211 | 212 | b.RetryPolicy(r) 213 | if b.rt != r { 214 | t.Fatalf("expected batch.RetryPolicy to be '%v', got '%v'", r, b.rt) 215 | } 216 | 217 | if b.Size() != 2 { 218 | t.Fatalf("expected batch.Size() to return 2, got %v", b.Size()) 219 | } 220 | 221 | } 222 | 223 | func TestConsistencyNames(t *testing.T) { 224 | names := map[Consistency]string{ 225 | Any: "ANY", 226 | One: "ONE", 227 | Two: "TWO", 228 | Three: "THREE", 229 | Quorum: "QUORUM", 230 | All: "ALL", 231 | LocalQuorum: "LOCAL_QUORUM", 232 | EachQuorum: "EACH_QUORUM", 233 | Serial: "SERIAL", 234 | LocalSerial: "LOCAL_SERIAL", 235 | LocalOne: "LOCAL_ONE", 236 | } 237 | 238 | for k, v := range names { 239 | if k.String() != v { 240 | t.Fatalf("expected '%v', got '%v'", v, k.String()) 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.keystore -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.truststore -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLzCCAhegAwIBAgIJAIKbAXgemwsjMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV 3 | BAMTCWNhc3NhbmRyYTAeFw0xNDA5MTkyMTE4MTNaFw0yNDA5MTYyMTE4MTNaMBQx 4 | EjAQBgNVBAMTCWNhc3NhbmRyYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 5 | ggEBAL5fX0l1WDNa+mO1krxw7k8lfUQn+Ec4L3Mqv6IstGoNdCPq4YRA+SXRD5YC 6 | k/UXrFBWh9Hbs849GiuTYMPdj9HDLYz40RaQjM9GbieS23iy3UStQ0tKhxaaG6FN 7 | 6XBypXFKCTsanu0TkEoDGhAkSzAMcCAC3gkFBzMrZ5qt4HEzjY9rasZ2gthN+xop 8 | nq3t4dDkE8HGaiFJcFvqTor7xmrnAaPjrPzUpvOF/ObIC09omwg/KXdPRx4DKPon 9 | gCMKEE3ckebKnJvbsRX3WO8H5nTHBYZ6v1JxLZz5pqmV+P0NGxldCARM0gCQUBz5 10 | wjMJkD/3e1ETC+q6uwfnAG0hlD8CAwEAAaOBgzCBgDAdBgNVHQ4EFgQUjHzn0nYF 11 | iXEaI1vUWbRR4lwKXOgwRAYDVR0jBD0wO4AUjHzn0nYFiXEaI1vUWbRR4lwKXOih 12 | GKQWMBQxEjAQBgNVBAMTCWNhc3NhbmRyYYIJAIKbAXgemwsjMAwGA1UdEwQFMAMB 13 | Af8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBCYDdIhtf/Y12Et947 14 | am1B8TzSX+/iQ1V1J3JtvgD5F4fvNjfArat/I3D277WREUTAc76o16BCp2OBGqzO 15 | zf9MvZPkjkAUoyU0TtPUEHyqxq4gZxbWKugIZGYkmQ1hCvSIgA5UnjRL3dylMmZb 16 | Y33JJA2QY63FZwnhmWsM8FYZwh+8MzVCQx3mgXC/k/jS6OuYyIT/KjxQHHjyr5ZS 17 | zAAQln1IcZycLfh1w5MtCFahCIethFcVDnWUWYPcPGDGgMJW7WBpNZdHbLxYY8cI 18 | eCc3Hcrbdc/CG5CaLJeqUidBayjnlUIO/NNgglkJ1KhQzkM6bd+37e0AX1hLIqx7 19 | gIZR 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,54C8072C0FF3B3A3 4 | 5 | 27eijmHdgB+s3beNPmU0+iz+muxMD0BVvWkDzyec/uawMv/Cn4c3mYXOcsFxS3BL 6 | +qLT9MEttOmjqhHSaVrDYOPKoJIMpn+bVeKiR08V89icO36shEPy1feGqanagKtw 7 | ecgzFDBTA8ZbqjAhftXlhTwxADebvNms/2aDh5Aw04vIcbo8nQ/8z1Wz8O7Firsn 8 | kaseSTMTC6lxc+pa2V1X6mN0/2UpDi55bZbx1Z/mQ3+1CsdHOx0p7m/KY2m3ysov 9 | XluaC0sqmzHkcwNgDhUs3Jh+apE33vXzLGU+W4BDOwrYJiL6KpspZW/mJj3OEx8B 10 | 8xdAZU3a/ei8NUA/lDStGmcYX+dOysExwJ6GMrCBm9iufZiefDQCQ8yRqWnr6Zop 11 | lsFd+CqHNWYxfWDI1pSUBw3bsgIjevI0f0B7PxkFEF0DmIhCgB324/uqToRzGsOF 12 | 4MSVg6cSK7Sjo/u3r8r75A3aUAcY8NbR3peiZfAPMsTiUcfp4DoU+MJTqkX5PyQq 13 | FNxHOJoARZqjjQ2IhZiUQWfIINHvZ8F9G2K7VaES8A0EATyUghqaRyeLbyI3IYdW 14 | pGZBzrpGtdFlk9AVetHDDlY+gQiurtYhxOsxvlxJJuTj8FV+A5NWSElfPele0OiR 15 | iprE3xkFSk3whHu5L1vnzamvdSlnBWOAE7pQD7kQA6NmcEw/tqnXK0dVdAw8RIFh 16 | 4BKgv0sNrXzBgnzE8+bKLUf1a2Byc/YKuBrI7EpSZ9/VHYvOcgmOxNxMmRS6NYd1 17 | Ly+agQn0AyvsDmSlBZBp8GCzVp6JYBMDKSXyPVN8+wjK9OQM0PZdEdXouMwPCOVN 18 | oNSjhmMtfjOsnG2SZ9tRas3p0qFdfh/N/E6Q7QHG3WD3cUIEweFV9ji1FTSRUrIa 19 | shuKug8MUfNjvDJNMsdGyf6Hi/7Iik++42Rq3ZdTy0ZVkj5snv5yBN77pr2M/J4b 20 | M+dsXjyXPO4SDW3kP/e3RnLRlWmUv1PNdOmNDdjBBUTKgVZ3ur+4HmSY1iDvhlUF 21 | /hz2tz3/XUKQwYuv3KJVlBhLrniXeES36GK+JQadIszrjwb5N4q4p6xrIdIR7XgR 22 | TJCSL1NGPLeQyjK6byWLNPRcCGrvnxWs0k0ev6trMRJL1EjsIFDCJam9szhcXkZP 23 | iYl1d7ZMKPS3cAqCjdaFRSe65cZ+qI/cqxiv122orq/jkDY7ZSA9rWywY4YnYQ7A 24 | BqvcPzC/6K0bteXqmMQkIy/84aSnEts6ecb/4s5e5xXLhHe0dchG0HkasC/Gb+v/ 25 | m9NOqACTerWvSD+Ecv9OvnBjP+GTlA1g7xTiRANLXsTJuiJomtxewXcV6kGZEMmZ 26 | QWerGtPJGGUx36WRWrMiPeBfWZoIbjYGPmOO5mYNXMTjABGGWcFnKAqWUKsFihi9 27 | pC0OpZ7A0dtc9uSm0ZmsHUc3XENMHTeeEN+qgWxVKcMzRKEcnapu/0OcHrOUHDZf 28 | qPoG4EkNnG9kPMq3HzvFPx3qbQ017yl87vAkWy/Edo+ojfHoNghRBVGCw1zt/BMN 29 | eJbFFHop+rQ87omz8WIL4K+zVf91rJ0REVAJssQVDo16O5wrMo+f+c8v2GANQks5 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 2 (0x2) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: CN=cassandra 7 | Validity 8 | Not Before: Sep 19 21:18:48 2014 GMT 9 | Not After : Sep 16 21:18:48 2024 GMT 10 | Subject: CN=cassandra 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (2048 bit) 14 | Modulus (2048 bit): 15 | 00:e5:9c:20:9e:de:98:73:44:41:0d:37:4c:62:c3: 16 | 9f:87:5f:9b:4f:aa:cf:f6:90:6e:a5:e0:89:88:7a: 17 | 00:c6:bb:d7:80:87:69:2e:fa:f0:35:59:80:6e:82: 18 | 25:c8:b3:6c:f6:a4:97:97:93:93:ea:f0:70:70:a4: 19 | e1:b7:aa:da:c1:99:66:9b:93:04:3a:ce:0b:83:07: 20 | 06:22:3d:a6:db:7f:68:0f:49:80:bd:86:a8:bb:54: 21 | 6d:38:5f:0f:b0:fa:1b:97:24:ae:cc:9d:37:98:7e: 22 | 76:cc:e3:1b:45:1b:21:25:17:02:c0:1a:c5:fb:76: 23 | c3:8b:93:d7:c5:85:14:0a:5c:a4:12:e7:18:69:98: 24 | f5:76:cd:78:cd:99:5a:29:65:f1:68:20:97:d3:be: 25 | 09:b3:68:1b:f2:a3:a2:9a:73:58:53:7e:ed:86:32: 26 | a3:5a:d5:46:03:f9:b3:b4:ec:63:71:ba:bb:fb:6f: 27 | f9:82:63:e4:55:47:7a:7a:e4:7b:17:6b:d7:e6:cf: 28 | 3b:c9:ab:0c:30:15:c9:ed:c7:d6:fc:b6:72:b2:14: 29 | 7d:c7:f3:7f:8a:f4:63:70:64:8e:0f:db:e8:3a:45: 30 | 47:cd:b9:7b:ae:c8:31:c1:52:d1:3e:34:12:b7:73: 31 | e7:ba:89:86:9a:36:ed:a0:5a:69:d0:d4:e3:b6:16: 32 | 85:af 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Basic Constraints: 36 | CA:FALSE 37 | X509v3 Subject Key Identifier: 38 | 4A:D3:EC:63:07:E0:8F:1A:4E:F5:09:43:90:9F:7A:C5:31:D1:8F:D8 39 | X509v3 Authority Key Identifier: 40 | keyid:8C:7C:E7:D2:76:05:89:71:1A:23:5B:D4:59:B4:51:E2:5C:0A:5C:E8 41 | DirName:/CN=cassandra 42 | serial:82:9B:01:78:1E:9B:0B:23 43 | 44 | X509v3 Extended Key Usage: 45 | TLS Web Server Authentication 46 | X509v3 Key Usage: 47 | Digital Signature, Key Encipherment 48 | Signature Algorithm: sha256WithRSAEncryption 49 | ac:bc:80:82:2d:6d:f1:a0:46:eb:00:05:d2:25:9a:83:66:57: 50 | 40:51:6e:ff:db:e3:28:04:7b:16:63:74:ec:55:a0:c0:5b:47: 51 | 13:e1:5a:a5:6d:22:d0:e5:fe:c1:51:e8:f6:c6:9c:f9:be:b7: 52 | be:82:14:e4:a0:b2:0b:9f:ee:68:bc:ac:17:0d:13:50:c6:9e: 53 | 52:91:8c:a0:98:db:4e:2d:f6:3d:6e:85:0a:bb:b9:dd:01:bf: 54 | ad:52:dd:6e:e4:41:01:a5:93:58:dd:3f:cf:bf:15:e6:25:aa: 55 | a0:4f:98:0d:75:8a:3f:5b:ba:67:37:f6:b1:0b:3f:21:34:97: 56 | 50:9a:85:97:2b:b6:05:41:9a:f3:cf:c4:92:23:06:ab:3e:87: 57 | 98:30:eb:cb:d3:83:ab:04:7d:5c:b9:f0:12:d1:43:b3:c5:7d: 58 | 33:9a:2e:2b:80:3a:66:be:f1:8c:08:37:7a:93:9c:9b:60:60: 59 | 53:71:16:70:86:df:ca:5f:a9:0b:e2:8b:3d:af:02:62:3b:61: 60 | 30:da:53:89:e3:d8:0b:88:04:9a:93:6a:f6:28:f8:dd:0d:8f: 61 | 0c:82:5b:c0:e5:f8:0d:ad:06:76:a7:3b:4b:ae:54:37:25:15: 62 | f5:0c:67:0f:77:c5:c4:97:68:09:c3:02:a7:a0:46:10:1c:d1: 63 | 95:3a:4c:94 64 | -----BEGIN CERTIFICATE----- 65 | MIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwljYXNz 66 | YW5kcmEwHhcNMTQwOTE5MjExODQ4WhcNMjQwOTE2MjExODQ4WjAUMRIwEAYDVQQD 67 | EwljYXNzYW5kcmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlnCCe 68 | 3phzREENN0xiw5+HX5tPqs/2kG6l4ImIegDGu9eAh2ku+vA1WYBugiXIs2z2pJeX 69 | k5Pq8HBwpOG3qtrBmWabkwQ6zguDBwYiPabbf2gPSYC9hqi7VG04Xw+w+huXJK7M 70 | nTeYfnbM4xtFGyElFwLAGsX7dsOLk9fFhRQKXKQS5xhpmPV2zXjNmVopZfFoIJfT 71 | vgmzaBvyo6Kac1hTfu2GMqNa1UYD+bO07GNxurv7b/mCY+RVR3p65HsXa9fmzzvJ 72 | qwwwFcntx9b8tnKyFH3H83+K9GNwZI4P2+g6RUfNuXuuyDHBUtE+NBK3c+e6iYaa 73 | Nu2gWmnQ1OO2FoWvAgMBAAGjgZUwgZIwCQYDVR0TBAIwADAdBgNVHQ4EFgQUStPs 74 | YwfgjxpO9QlDkJ96xTHRj9gwRAYDVR0jBD0wO4AUjHzn0nYFiXEaI1vUWbRR4lwK 75 | XOihGKQWMBQxEjAQBgNVBAMTCWNhc3NhbmRyYYIJAIKbAXgemwsjMBMGA1UdJQQM 76 | MAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQsFAAOCAQEArLyA 77 | gi1t8aBG6wAF0iWag2ZXQFFu/9vjKAR7FmN07FWgwFtHE+FapW0i0OX+wVHo9sac 78 | +b63voIU5KCyC5/uaLysFw0TUMaeUpGMoJjbTi32PW6FCru53QG/rVLdbuRBAaWT 79 | WN0/z78V5iWqoE+YDXWKP1u6Zzf2sQs/ITSXUJqFlyu2BUGa88/EkiMGqz6HmDDr 80 | y9ODqwR9XLnwEtFDs8V9M5ouK4A6Zr7xjAg3epOcm2BgU3EWcIbfyl+pC+KLPa8C 81 | YjthMNpTiePYC4gEmpNq9ij43Q2PDIJbwOX4Da0Gdqc7S65UNyUV9QxnD3fFxJdo 82 | CcMCp6BGEBzRlTpMlA== 83 | -----END CERTIFICATE----- 84 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/cassandra.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA5Zwgnt6Yc0RBDTdMYsOfh1+bT6rP9pBupeCJiHoAxrvXgIdp 3 | LvrwNVmAboIlyLNs9qSXl5OT6vBwcKTht6rawZlmm5MEOs4LgwcGIj2m239oD0mA 4 | vYaou1RtOF8PsPoblySuzJ03mH52zOMbRRshJRcCwBrF+3bDi5PXxYUUClykEucY 5 | aZj1ds14zZlaKWXxaCCX074Js2gb8qOimnNYU37thjKjWtVGA/mztOxjcbq7+2/5 6 | gmPkVUd6euR7F2vX5s87yasMMBXJ7cfW/LZyshR9x/N/ivRjcGSOD9voOkVHzbl7 7 | rsgxwVLRPjQSt3PnuomGmjbtoFpp0NTjthaFrwIDAQABAoIBAQChjdjl73kUoVGk 8 | GuSEGWCFv59nzqfEtJsl23bpr+4b5s8agCxiAe5Bm1fiaXBsZtKkN+rxm8TX6ZUz 9 | rM+ki3KgBW9Mx4SSW6d96dNHBFoC1wJAv1b2A2l1ZVHz9+7ydwgysHzNO1GC2nh8 10 | cM8fMJeBoU8uG6hx5n5wFvYa5CfVoUQh8+Oq0b+mVxEFKHmRPnWp9/jPzL5eBIdr 11 | ulbDt9S3dKJtouHgHBUNdkq/7Ex3QeHrUOahX6Y4eX1rzLnfLYY+0J4EA2PCKvgQ 12 | bfKCxVnnzL6ywviH8eS3ql6OvTfnbK9kCRw7WxX9CC50qKj3EmwC/51MPhWohWlq 13 | jw3qf38BAoGBAPPNyb3vUiyUqoErZxxIPFc2ob3vCjj06cvi7uKpOgrkdgC3iBhz 14 | aCFQ28r7LrxLAHaKvNvwp71Lc7WYo8WWkLI1DVn0dx+GiQYW3DbNcwZOS40ZQz5L 15 | zsjEcG4+cnZmuqGZBMNvQ+xUjkuucxvxPWKpEKM18GfDjgEkKbmDr+uNAoGBAPEY 16 | kVSfSZGtP0MoXIfRkrxBlhvCj9m+p60P37pyHrJBrlrwvxB7x3Oz8S70D6kV8s2g 17 | vVHgOS3VPj17VaQG8a3jBLKjzp5JLe34G8D1Ny8GqDc2wzOBtZySpJbifXuSUSPk 18 | cqF7yiu1cD/wRPlwyWxBX9ZbaxvxnIUwLLd3ygkrAoGBAKQaw42uVkCdvPr/DQOT 19 | d9I4erxO9zGJYQmU8bjtsZz9VJR89QWIQPIT7C3/zuB9F42zKxZcMXwQGo2EddAc 20 | 3b6mSRtgmwJEW10W7BmTRrZa4y3RcFqxSjoHR6pdLEyYL01woy0taqnb7H/yp5aK 21 | VghfxkwllXEyxxXrko5FnpdNAoGBANeJLBunz2BxrnW+doJhZDnytFya4nk6TbKU 22 | 12FaNoEL4PCh+12kGtogSwS74eg6m/citT2mI9gKpHrYcOaT4qmeo4uEj+nH6Eyv 23 | Gzi0wCHFZMr/pSC92/teyc+uKZo4Y1ugFq6w+Tt8GB7BERiisR+bji8XSTkRFemn 24 | +MIIUFFDAoGAM8Va2Q5aTUkfg2mYlNLqT2tUAXVEhbmzjPA6laSo25PQEYWmX7vj 25 | hiU0DPCDJQ/PlPI23xYtDDLNk83Zbx+Oj29GO5pawJY9NvFI8n60EFXfLbP1nEdG 26 | j077QZNZOKfcgJirWi3+RrHSAK4tFftCe7rkV8ZmlMRBY3SDxzKOGcc= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 1 (0x1) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: CN=cassandra 7 | Validity 8 | Not Before: Sep 19 21:18:33 2014 GMT 9 | Not After : Sep 16 21:18:33 2024 GMT 10 | Subject: CN=gocql 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (2048 bit) 14 | Modulus (2048 bit): 15 | 00:ae:e9:fa:9e:fd:e2:69:85:1d:08:0f:35:68:bc: 16 | 63:7b:92:50:7f:73:50:fc:42:43:35:06:b3:5c:9e: 17 | 27:1e:16:05:69:ec:88:d5:9c:4f:ef:e8:13:69:7a: 18 | b5:b3:7f:66:6d:14:00:2e:d6:af:5b:ff:2c:90:91: 19 | a6:11:07:72:5e:b0:37:c0:6d:ff:7b:76:2b:fe:de: 20 | 4c:d2:8d:ce:43:3b:1a:c4:1d:de:b6:d8:26:08:25: 21 | 89:59:a1:4b:94:a3:57:9e:19:46:28:6e:97:11:7c: 22 | e6:b7:41:96:8f:42:dd:66:da:86:d2:53:dd:d8:f5: 23 | 20:cd:24:8b:0f:ab:df:c4:10:b2:64:20:1d:e0:0f: 24 | f4:2d:f6:ca:94:be:83:ac:3e:a8:4a:77:b6:08:97: 25 | 3a:7e:7b:e0:3e:ab:68:cf:ee:f6:a1:8e:bf:ec:be: 26 | 06:d1:ad:6c:ed:4f:35:d1:04:97:08:33:b1:65:5b: 27 | 61:32:8d:4b:f0:30:35:4b:8b:6b:06:f2:1a:72:8c: 28 | 69:bd:f3:b2:c4:a4:a4:70:45:e3:67:a2:7a:9f:2e: 29 | cb:28:2d:9f:68:03:f1:c7:d9:4f:83:c9:3d:8c:34: 30 | 04:0a:3b:13:87:92:e1:f7:e3:79:7e:ab:c0:25:b1: 31 | e5:38:09:44:3e:31:df:12:d4:dc:7b:0e:35:bf:ee: 32 | 25:5f 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Basic Constraints: 36 | CA:FALSE 37 | X509v3 Subject Key Identifier: 38 | 9F:F1:B2:C4:82:34:D0:2F:FF:E9:7F:19:F1:3B:51:57:BF:E8:95:BB 39 | X509v3 Authority Key Identifier: 40 | keyid:8C:7C:E7:D2:76:05:89:71:1A:23:5B:D4:59:B4:51:E2:5C:0A:5C:E8 41 | DirName:/CN=cassandra 42 | serial:82:9B:01:78:1E:9B:0B:23 43 | 44 | X509v3 Extended Key Usage: 45 | TLS Web Client Authentication 46 | X509v3 Key Usage: 47 | Digital Signature 48 | Signature Algorithm: sha256WithRSAEncryption 49 | 12:aa:1b:a6:58:27:52:32:c9:46:19:32:d3:69:ae:95:ad:23: 50 | 55:ad:12:65:da:2c:4c:72:f3:29:bd:2b:5a:97:3b:b7:68:8b: 51 | 68:80:77:55:e6:32:81:f1:f5:20:54:ba:0e:2b:86:90:d8:44: 52 | cf:f2:9f:ec:4d:39:67:4e:36:6c:9b:49:4a:80:e6:c1:ed:a4: 53 | 41:39:19:16:d2:88:df:17:0c:46:5a:b9:88:53:f5:67:19:f0: 54 | 1f:9a:51:40:1b:40:12:bc:57:db:de:dd:d3:f5:a8:93:68:30: 55 | ac:ba:4e:ee:6b:af:f8:13:3d:11:1a:fa:90:93:d0:68:ce:77: 56 | 5f:85:8b:a4:95:2a:4c:25:7b:53:9c:44:43:b1:d9:fe:0c:83: 57 | b8:19:2a:88:cc:d8:d1:d9:b3:04:eb:45:9b:30:5e:cb:61:e0: 58 | e1:88:23:9c:b0:34:79:62:82:0d:f8:10:ed:96:bb:a0:fd:0d: 59 | 02:cb:c5:d3:47:1f:35:a7:e3:39:31:56:d5:b3:eb:2f:93:8f: 60 | 18:b4:b7:3c:00:03:a7:b4:1c:17:72:91:7e:b6:f6:36:17:3d: 61 | f6:54:3b:87:84:d1:9b:43:d1:88:42:64:20:7a:e3:cc:f7:05: 62 | 98:0e:1c:51:da:20:b7:9b:49:88:e8:c6:e1:de:0d:f5:56:4f: 63 | 79:41:d0:7f 64 | -----BEGIN CERTIFICATE----- 65 | MIIDNTCCAh2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwljYXNz 66 | YW5kcmEwHhcNMTQwOTE5MjExODMzWhcNMjQwOTE2MjExODMzWjAQMQ4wDAYDVQQD 67 | EwVnb2NxbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7p+p794mmF 68 | HQgPNWi8Y3uSUH9zUPxCQzUGs1yeJx4WBWnsiNWcT+/oE2l6tbN/Zm0UAC7Wr1v/ 69 | LJCRphEHcl6wN8Bt/3t2K/7eTNKNzkM7GsQd3rbYJggliVmhS5SjV54ZRihulxF8 70 | 5rdBlo9C3WbahtJT3dj1IM0kiw+r38QQsmQgHeAP9C32ypS+g6w+qEp3tgiXOn57 71 | 4D6raM/u9qGOv+y+BtGtbO1PNdEElwgzsWVbYTKNS/AwNUuLawbyGnKMab3zssSk 72 | pHBF42eiep8uyygtn2gD8cfZT4PJPYw0BAo7E4eS4ffjeX6rwCWx5TgJRD4x3xLU 73 | 3HsONb/uJV8CAwEAAaOBlTCBkjAJBgNVHRMEAjAAMB0GA1UdDgQWBBSf8bLEgjTQ 74 | L//pfxnxO1FXv+iVuzBEBgNVHSMEPTA7gBSMfOfSdgWJcRojW9RZtFHiXApc6KEY 75 | pBYwFDESMBAGA1UEAxMJY2Fzc2FuZHJhggkAgpsBeB6bCyMwEwYDVR0lBAwwCgYI 76 | KwYBBQUHAwIwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQASqhumWCdS 77 | MslGGTLTaa6VrSNVrRJl2ixMcvMpvStalzu3aItogHdV5jKB8fUgVLoOK4aQ2ETP 78 | 8p/sTTlnTjZsm0lKgObB7aRBORkW0ojfFwxGWrmIU/VnGfAfmlFAG0ASvFfb3t3T 79 | 9aiTaDCsuk7ua6/4Ez0RGvqQk9BozndfhYuklSpMJXtTnERDsdn+DIO4GSqIzNjR 80 | 2bME60WbMF7LYeDhiCOcsDR5YoIN+BDtlrug/Q0Cy8XTRx81p+M5MVbVs+svk48Y 81 | tLc8AAOntBwXcpF+tvY2Fz32VDuHhNGbQ9GIQmQgeuPM9wWYDhxR2iC3m0mI6Mbh 82 | 3g31Vk95QdB/ 83 | -----END CERTIFICATE----- 84 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/gocql.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEArun6nv3iaYUdCA81aLxje5JQf3NQ/EJDNQazXJ4nHhYFaeyI 3 | 1ZxP7+gTaXq1s39mbRQALtavW/8skJGmEQdyXrA3wG3/e3Yr/t5M0o3OQzsaxB3e 4 | ttgmCCWJWaFLlKNXnhlGKG6XEXzmt0GWj0LdZtqG0lPd2PUgzSSLD6vfxBCyZCAd 5 | 4A/0LfbKlL6DrD6oSne2CJc6fnvgPqtoz+72oY6/7L4G0a1s7U810QSXCDOxZVth 6 | Mo1L8DA1S4trBvIacoxpvfOyxKSkcEXjZ6J6ny7LKC2faAPxx9lPg8k9jDQECjsT 7 | h5Lh9+N5fqvAJbHlOAlEPjHfEtTcew41v+4lXwIDAQABAoIBAQCCP9XSwzfwX6Fo 8 | uPqKjY5/HEs5PQPXdPha6ixyEYsLilZptCuI9adI/MZHy4q2qW36V+Ry/IcEuJXU 9 | 6cCB+cue2xYJA2A17Z+BYMRQHiy0P7UEyUFpYrefZWRMDCIeAyxhnGxz+zYfXaTo 10 | Xbzh3WbFCoFO6gjPYGoWmNm8x74PXyunNaMa/gWFECX5MMBXoOk5xSFGbHzI2Cds 11 | iT7sdCQJVbBs7yidYwNqPWQuOwrskFinPIFSc7bZ0Sx9wO3XTIrQFCE94v/AN6yR 12 | 9Q37ida54g5tgtoeg/5EGsUM++i4wqJVoT3tWUHv1jBozO4Lm65uWR/1HcrusVnr 13 | x0TM9SaBAoGBAOMeaZdUrCJXnIiSoqCGDvZmylTAeOo6n2RAiviOYxVB4GP/SSjh 14 | 8VeddFhYT1GCmZ+YjIXnRWK+dSqVukzCuf5xW5mWY7PDNGZe2P6O78lXnY4cb8Nc 15 | Uo9/S2aPnNmNHL2TYVBYUiZj+t2azIQEFvRth4Vu/AHRUG41/USxpwm/AoGBAMUo 16 | GX0xgSFAVpHnTLdzWrHNRrzHgYN8ywPKFgNOASvdgW0BFoqXEvVGc1Ak6uW82m1/ 17 | L9ChOzWjCY7CoT+LPmdUVyGT9/UAPtWeLfo8Owl4tG91jQjePmJFvLoXErryCFRt 18 | SOOvCsTTTq2gN3PREHxY3dj2kJqaCBLCEzx3cYxhAoGBAIUxdrc6/t/9BV3KsPj2 19 | 5Zt3WL0vSzoCOyut9lIiHtV+lrvOIPeK2eCKBIsy7wFcV/+SlQaKRNTN4SSiPml5 20 | 4V3o2NFPsxTfK8HFafiPluw7J7kJ0Dl/0SM6gduZ6WBkMzCyV+WohjTheWOwvrPF 21 | OjkKaunD1qKyQDsCCo/Yp589AoGAdKgnfNZf68bf8nEECcBtt6sY4fbCgYTDszhO 22 | EiKDuurT/CWaquJ9SzgmXxOZEdrO+9838aCVIkWYECrFso23nPhgnfOp0gQVKdzw 23 | o5Ij9JTBXvoVO1wVWZyd8RZZ9Nflad9IM8CNBK1rbnzQkuzvbkQ+8HPkWDYv9Ll1 24 | HGAohcECgYBQeirIumumj1B17WD/KmNe0U0qCHHp+oSW4W2r7pjlEVZzeQmggX4O 25 | anbEngyQaZKeUiUOj9snBDmzLv7S+j5p7Us4d1fbp70sCKuK6tcAnROU8gK8IGiI 26 | I01ypD8Z1Mb556qek56eRWlr71sy6wI1lbQa856cUBvePajUOKsKsw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/token.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocql 6 | 7 | import ( 8 | "bytes" 9 | "crypto/md5" 10 | "fmt" 11 | "math/big" 12 | "sort" 13 | "strconv" 14 | "strings" 15 | "unsafe" 16 | ) 17 | 18 | // a token partitioner 19 | type partitioner interface { 20 | Hash([]byte) token 21 | ParseString(string) token 22 | } 23 | 24 | // a token 25 | type token interface { 26 | fmt.Stringer 27 | Less(token) bool 28 | } 29 | 30 | // murmur3 partitioner and token 31 | type murmur3Partitioner struct{} 32 | type murmur3Token int64 33 | 34 | func (p murmur3Partitioner) Hash(partitionKey []byte) token { 35 | h1 := murmur3H1(partitionKey) 36 | return murmur3Token(int64(h1)) 37 | } 38 | 39 | // murmur3 little-endian, 128-bit hash, but returns only h1 40 | func murmur3H1(data []byte) uint64 { 41 | length := len(data) 42 | 43 | var h1, h2, k1, k2 uint64 44 | 45 | const ( 46 | c1 = 0x87c37b91114253d5 47 | c2 = 0x4cf5ad432745937f 48 | ) 49 | 50 | // body 51 | nBlocks := length / 16 52 | for i := 0; i < nBlocks; i++ { 53 | block := (*[2]uint64)(unsafe.Pointer(&data[i*16])) 54 | 55 | k1 = block[0] 56 | k2 = block[1] 57 | 58 | k1 *= c1 59 | k1 = (k1 << 31) | (k1 >> 33) // ROTL64(k1, 31) 60 | k1 *= c2 61 | h1 ^= k1 62 | 63 | h1 = (h1 << 27) | (h1 >> 37) // ROTL64(h1, 27) 64 | h1 += h2 65 | h1 = h1*5 + 0x52dce729 66 | 67 | k2 *= c2 68 | k2 = (k2 << 33) | (k2 >> 31) // ROTL64(k2, 33) 69 | k2 *= c1 70 | h2 ^= k2 71 | 72 | h2 = (h2 << 31) | (h2 >> 33) // ROTL64(h2, 31) 73 | h2 += h1 74 | h2 = h2*5 + 0x38495ab5 75 | } 76 | 77 | // tail 78 | tail := data[nBlocks*16:] 79 | k1 = 0 80 | k2 = 0 81 | switch length & 15 { 82 | case 15: 83 | k2 ^= uint64(tail[14]) << 48 84 | fallthrough 85 | case 14: 86 | k2 ^= uint64(tail[13]) << 40 87 | fallthrough 88 | case 13: 89 | k2 ^= uint64(tail[12]) << 32 90 | fallthrough 91 | case 12: 92 | k2 ^= uint64(tail[11]) << 24 93 | fallthrough 94 | case 11: 95 | k2 ^= uint64(tail[10]) << 16 96 | fallthrough 97 | case 10: 98 | k2 ^= uint64(tail[9]) << 8 99 | fallthrough 100 | case 9: 101 | k2 ^= uint64(tail[8]) 102 | 103 | k2 *= c2 104 | k2 = (k2 << 33) | (k2 >> 31) // ROTL64(k2, 33) 105 | k2 *= c1 106 | h2 ^= k2 107 | 108 | fallthrough 109 | case 8: 110 | k1 ^= uint64(tail[7]) << 56 111 | fallthrough 112 | case 7: 113 | k1 ^= uint64(tail[6]) << 48 114 | fallthrough 115 | case 6: 116 | k1 ^= uint64(tail[5]) << 40 117 | fallthrough 118 | case 5: 119 | k1 ^= uint64(tail[4]) << 32 120 | fallthrough 121 | case 4: 122 | k1 ^= uint64(tail[3]) << 24 123 | fallthrough 124 | case 3: 125 | k1 ^= uint64(tail[2]) << 16 126 | fallthrough 127 | case 2: 128 | k1 ^= uint64(tail[1]) << 8 129 | fallthrough 130 | case 1: 131 | k1 ^= uint64(tail[0]) 132 | 133 | k1 *= c1 134 | k1 = (k1 << 31) | (k1 >> 33) // ROTL64(k1, 31) 135 | k1 *= c2 136 | h1 ^= k1 137 | } 138 | 139 | h1 ^= uint64(length) 140 | h2 ^= uint64(length) 141 | 142 | h1 += h2 143 | h2 += h1 144 | 145 | // finalizer 146 | const ( 147 | fmix1 = 0xff51afd7ed558ccd 148 | fmix2 = 0xc4ceb9fe1a85ec53 149 | ) 150 | 151 | // fmix64(h1) 152 | h1 ^= h1 >> 33 153 | h1 *= fmix1 154 | h1 ^= h1 >> 33 155 | h1 *= fmix2 156 | h1 ^= h1 >> 33 157 | 158 | // fmix64(h2) 159 | h2 ^= h2 >> 33 160 | h2 *= fmix1 161 | h2 ^= h2 >> 33 162 | h2 *= fmix2 163 | h2 ^= h2 >> 33 164 | 165 | h1 += h2 166 | // the following is extraneous since h2 is discarded 167 | // h2 += h1 168 | 169 | return h1 170 | } 171 | 172 | func (p murmur3Partitioner) ParseString(str string) token { 173 | val, _ := strconv.ParseInt(str, 10, 64) 174 | return murmur3Token(val) 175 | } 176 | 177 | func (m murmur3Token) String() string { 178 | return strconv.FormatInt(int64(m), 10) 179 | } 180 | 181 | func (m murmur3Token) Less(token token) bool { 182 | return m < token.(murmur3Token) 183 | } 184 | 185 | // order preserving partitioner and token 186 | type orderPreservingPartitioner struct{} 187 | type orderPreservingToken []byte 188 | 189 | func (p orderPreservingPartitioner) Hash(partitionKey []byte) token { 190 | // the partition key is the token 191 | return orderPreservingToken(partitionKey) 192 | } 193 | 194 | func (p orderPreservingPartitioner) ParseString(str string) token { 195 | return orderPreservingToken([]byte(str)) 196 | } 197 | 198 | func (o orderPreservingToken) String() string { 199 | return string([]byte(o)) 200 | } 201 | 202 | func (o orderPreservingToken) Less(token token) bool { 203 | return -1 == bytes.Compare(o, token.(orderPreservingToken)) 204 | } 205 | 206 | // random partitioner and token 207 | type randomPartitioner struct{} 208 | type randomToken big.Int 209 | 210 | func (p randomPartitioner) Hash(partitionKey []byte) token { 211 | hash := md5.New() 212 | sum := hash.Sum(partitionKey) 213 | 214 | val := new(big.Int) 215 | val = val.SetBytes(sum) 216 | val = val.Abs(val) 217 | 218 | return (*randomToken)(val) 219 | } 220 | 221 | func (p randomPartitioner) ParseString(str string) token { 222 | val := new(big.Int) 223 | val.SetString(str, 10) 224 | return (*randomToken)(val) 225 | } 226 | 227 | func (r *randomToken) String() string { 228 | return (*big.Int)(r).String() 229 | } 230 | 231 | func (r *randomToken) Less(token token) bool { 232 | return -1 == (*big.Int)(r).Cmp((*big.Int)(token.(*randomToken))) 233 | } 234 | 235 | // a data structure for organizing the relationship between tokens and hosts 236 | type tokenRing struct { 237 | partitioner partitioner 238 | tokens []token 239 | hosts []*HostInfo 240 | } 241 | 242 | func newTokenRing(partitioner string, hosts []*HostInfo) (*tokenRing, error) { 243 | tokenRing := &tokenRing{ 244 | tokens: []token{}, 245 | hosts: []*HostInfo{}, 246 | } 247 | 248 | if strings.HasSuffix(partitioner, "Murmur3Partitioner") { 249 | tokenRing.partitioner = murmur3Partitioner{} 250 | } else if strings.HasSuffix(partitioner, "OrderedPartitioner") { 251 | tokenRing.partitioner = orderPreservingPartitioner{} 252 | } else if strings.HasSuffix(partitioner, "RandomPartitioner") { 253 | tokenRing.partitioner = randomPartitioner{} 254 | } else { 255 | return nil, fmt.Errorf("Unsupported partitioner '%s'", partitioner) 256 | } 257 | 258 | for _, host := range hosts { 259 | for _, strToken := range host.Tokens { 260 | token := tokenRing.partitioner.ParseString(strToken) 261 | tokenRing.tokens = append(tokenRing.tokens, token) 262 | tokenRing.hosts = append(tokenRing.hosts, host) 263 | } 264 | } 265 | 266 | sort.Sort(tokenRing) 267 | 268 | return tokenRing, nil 269 | } 270 | 271 | func (t *tokenRing) Len() int { 272 | return len(t.tokens) 273 | } 274 | 275 | func (t *tokenRing) Less(i, j int) bool { 276 | return t.tokens[i].Less(t.tokens[j]) 277 | } 278 | 279 | func (t *tokenRing) Swap(i, j int) { 280 | t.tokens[i], t.hosts[i], t.tokens[j], t.hosts[j] = 281 | t.tokens[j], t.hosts[j], t.tokens[i], t.hosts[i] 282 | } 283 | 284 | func (t *tokenRing) String() string { 285 | buf := &bytes.Buffer{} 286 | buf.WriteString("TokenRing={") 287 | sep := "" 288 | for i := range t.tokens { 289 | buf.WriteString(sep) 290 | sep = "," 291 | buf.WriteString("\n\t[") 292 | buf.WriteString(strconv.Itoa(i)) 293 | buf.WriteString("]") 294 | buf.WriteString(t.tokens[i].String()) 295 | buf.WriteString(":") 296 | buf.WriteString(t.hosts[i].Peer) 297 | } 298 | buf.WriteString("\n}") 299 | return string(buf.Bytes()) 300 | } 301 | 302 | func (t *tokenRing) GetHostForPartitionKey(partitionKey []byte) *HostInfo { 303 | token := t.partitioner.Hash(partitionKey) 304 | return t.GetHostForToken(token) 305 | } 306 | 307 | func (t *tokenRing) GetHostForToken(token token) *HostInfo { 308 | // find the primary repica 309 | ringIndex := sort.Search( 310 | len(t.tokens), 311 | func(i int) bool { 312 | return !t.tokens[i].Less(token) 313 | }, 314 | ) 315 | if ringIndex == len(t.tokens) { 316 | // wrap around to the first in the ring 317 | ringIndex = 0 318 | } 319 | host := t.hosts[ringIndex] 320 | return host 321 | } 322 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/token_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocql 6 | 7 | import ( 8 | "math/big" 9 | "strconv" 10 | "testing" 11 | ) 12 | 13 | func TestMurmur3H1(t *testing.T) { 14 | assertMurmur3H1(t, []byte{}, 0x000000000000000) 15 | assertMurmur3H1(t, []byte{0}, 0x4610abe56eff5cb5) 16 | assertMurmur3H1(t, []byte{0, 1}, 0x7cb3f5c58dab264c) 17 | assertMurmur3H1(t, []byte{0, 1, 2}, 0xb872a12fef53e6be) 18 | assertMurmur3H1(t, []byte{0, 1, 2, 3}, 0xe1c594ae0ddfaf10) 19 | assertMurmur3H1(t, []byte("hello"), 0xcbd8a7b341bd9b02) 20 | assertMurmur3H1(t, []byte("hello, world"), 0x342fac623a5ebc8e) 21 | assertMurmur3H1(t, []byte("19 Jan 2038 at 3:14:07 AM"), 0xb89e5988b737affc) 22 | assertMurmur3H1(t, []byte("The quick brown fox jumps over the lazy dog."), 0xcd99481f9ee902c9) 23 | } 24 | 25 | func assertMurmur3H1(t *testing.T, data []byte, expected uint64) { 26 | actual := murmur3H1(data) 27 | if actual != expected { 28 | t.Errorf("Expected h1 = %x for data = %x, but was %x", expected, data, actual) 29 | } 30 | } 31 | 32 | func BenchmarkMurmur3H1(b *testing.B) { 33 | var h1 uint64 34 | var data [1024]byte 35 | for i := 0; i < 1024; i++ { 36 | data[i] = byte(i) 37 | } 38 | for i := 0; i < b.N; i++ { 39 | b.ResetTimer() 40 | h1 = murmur3H1(data[:]) 41 | _ = murmur3Token(int64(h1)) 42 | } 43 | } 44 | 45 | func TestMurmur3Partitioner(t *testing.T) { 46 | token := murmur3Partitioner{}.ParseString("-1053604476080545076") 47 | 48 | if "-1053604476080545076" != token.String() { 49 | t.Errorf("Expected '-1053604476080545076' but was '%s'", token) 50 | } 51 | 52 | // at least verify that the partitioner 53 | // doesn't return nil 54 | pk, _ := marshalInt(nil, 1) 55 | token = murmur3Partitioner{}.Hash(pk) 56 | if token == nil { 57 | t.Fatal("token was nil") 58 | } 59 | } 60 | 61 | func TestMurmur3Token(t *testing.T) { 62 | if murmur3Token(42).Less(murmur3Token(42)) { 63 | t.Errorf("Expected Less to return false, but was true") 64 | } 65 | if !murmur3Token(-42).Less(murmur3Token(42)) { 66 | t.Errorf("Expected Less to return true, but was false") 67 | } 68 | if murmur3Token(42).Less(murmur3Token(-42)) { 69 | t.Errorf("Expected Less to return false, but was true") 70 | } 71 | } 72 | 73 | func TestOrderPreservingPartitioner(t *testing.T) { 74 | // at least verify that the partitioner 75 | // doesn't return nil 76 | pk, _ := marshalInt(nil, 1) 77 | token := orderPreservingPartitioner{}.Hash(pk) 78 | if token == nil { 79 | t.Fatal("token was nil") 80 | } 81 | } 82 | 83 | func TestOrderPreservingToken(t *testing.T) { 84 | if orderPreservingToken([]byte{0, 0, 4, 2}).Less(orderPreservingToken([]byte{0, 0, 4, 2})) { 85 | t.Errorf("Expected Less to return false, but was true") 86 | } 87 | if !orderPreservingToken([]byte{0, 0, 3}).Less(orderPreservingToken([]byte{0, 0, 4, 2})) { 88 | t.Errorf("Expected Less to return true, but was false") 89 | } 90 | if orderPreservingToken([]byte{0, 0, 4, 2}).Less(orderPreservingToken([]byte{0, 0, 3})) { 91 | t.Errorf("Expected Less to return false, but was true") 92 | } 93 | } 94 | 95 | func TestRandomPartitioner(t *testing.T) { 96 | // at least verify that the partitioner 97 | // doesn't return nil 98 | pk, _ := marshalInt(nil, 1) 99 | token := randomPartitioner{}.Hash(pk) 100 | if token == nil { 101 | t.Fatal("token was nil") 102 | } 103 | } 104 | 105 | func TestRandomToken(t *testing.T) { 106 | if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(42))) { 107 | t.Errorf("Expected Less to return false, but was true") 108 | } 109 | if !((*randomToken)(big.NewInt(41))).Less((*randomToken)(big.NewInt(42))) { 110 | t.Errorf("Expected Less to return true, but was false") 111 | } 112 | if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(41))) { 113 | t.Errorf("Expected Less to return false, but was true") 114 | } 115 | } 116 | 117 | type intToken int 118 | 119 | func (i intToken) String() string { 120 | return strconv.Itoa(int(i)) 121 | } 122 | 123 | func (i intToken) Less(token token) bool { 124 | return i < token.(intToken) 125 | } 126 | 127 | func TestIntTokenRing(t *testing.T) { 128 | // test based on example at the start of this page of documentation: 129 | // http://www.datastax.com/docs/0.8/cluster_architecture/partitioning 130 | host0 := &HostInfo{} 131 | host25 := &HostInfo{} 132 | host50 := &HostInfo{} 133 | host75 := &HostInfo{} 134 | tokenRing := &tokenRing{ 135 | partitioner: nil, 136 | tokens: []token{ 137 | intToken(0), 138 | intToken(25), 139 | intToken(50), 140 | intToken(75), 141 | }, 142 | hosts: []*HostInfo{ 143 | host0, 144 | host25, 145 | host50, 146 | host75, 147 | }, 148 | } 149 | 150 | if tokenRing.GetHostForToken(intToken(0)) != host0 { 151 | t.Error("Expected host 0 for token 0") 152 | } 153 | if tokenRing.GetHostForToken(intToken(1)) != host25 { 154 | t.Error("Expected host 25 for token 1") 155 | } 156 | if tokenRing.GetHostForToken(intToken(24)) != host25 { 157 | t.Error("Expected host 25 for token 24") 158 | } 159 | if tokenRing.GetHostForToken(intToken(25)) != host25 { 160 | t.Error("Expected host 25 for token 25") 161 | } 162 | if tokenRing.GetHostForToken(intToken(26)) != host50 { 163 | t.Error("Expected host 50 for token 26") 164 | } 165 | if tokenRing.GetHostForToken(intToken(49)) != host50 { 166 | t.Error("Expected host 50 for token 49") 167 | } 168 | if tokenRing.GetHostForToken(intToken(50)) != host50 { 169 | t.Error("Expected host 50 for token 50") 170 | } 171 | if tokenRing.GetHostForToken(intToken(51)) != host75 { 172 | t.Error("Expected host 75 for token 51") 173 | } 174 | if tokenRing.GetHostForToken(intToken(74)) != host75 { 175 | t.Error("Expected host 75 for token 74") 176 | } 177 | if tokenRing.GetHostForToken(intToken(75)) != host75 { 178 | t.Error("Expected host 75 for token 75") 179 | } 180 | if tokenRing.GetHostForToken(intToken(76)) != host0 { 181 | t.Error("Expected host 0 for token 76") 182 | } 183 | if tokenRing.GetHostForToken(intToken(99)) != host0 { 184 | t.Error("Expected host 0 for token 99") 185 | } 186 | if tokenRing.GetHostForToken(intToken(100)) != host0 { 187 | t.Error("Expected host 0 for token 100") 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/topology.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocql 6 | 7 | import ( 8 | "sync" 9 | "sync/atomic" 10 | ) 11 | 12 | type Node interface { 13 | Pick(qry *Query) *Conn 14 | Close() 15 | } 16 | 17 | type RoundRobin struct { 18 | pool []Node 19 | pos uint32 20 | mu sync.RWMutex 21 | } 22 | 23 | func NewRoundRobin() *RoundRobin { 24 | return &RoundRobin{} 25 | } 26 | 27 | func (r *RoundRobin) AddNode(node Node) { 28 | r.mu.Lock() 29 | r.pool = append(r.pool, node) 30 | r.mu.Unlock() 31 | } 32 | 33 | func (r *RoundRobin) RemoveNode(node Node) { 34 | r.mu.Lock() 35 | n := len(r.pool) 36 | for i := 0; i < n; i++ { 37 | if r.pool[i] == node { 38 | r.pool[i], r.pool[n-1] = r.pool[n-1], r.pool[i] 39 | r.pool = r.pool[:n-1] 40 | break 41 | } 42 | } 43 | r.mu.Unlock() 44 | } 45 | 46 | func (r *RoundRobin) Size() int { 47 | r.mu.RLock() 48 | n := len(r.pool) 49 | r.mu.RUnlock() 50 | return n 51 | } 52 | 53 | func (r *RoundRobin) Pick(qry *Query) *Conn { 54 | pos := atomic.AddUint32(&r.pos, 1) 55 | var node Node 56 | r.mu.RLock() 57 | if len(r.pool) > 0 { 58 | node = r.pool[pos%uint32(len(r.pool))] 59 | } 60 | r.mu.RUnlock() 61 | if node == nil { 62 | return nil 63 | } 64 | return node.Pick(qry) 65 | } 66 | 67 | func (r *RoundRobin) Close() { 68 | r.mu.Lock() 69 | for i := 0; i < len(r.pool); i++ { 70 | r.pool[i].Close() 71 | } 72 | r.pool = nil 73 | r.mu.Unlock() 74 | } 75 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/topology_test.go: -------------------------------------------------------------------------------- 1 | // +build all unit 2 | 3 | package gocql 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | // fakeNode is used as a simple structure to test the RoundRobin API 10 | type fakeNode struct { 11 | conn *Conn 12 | closed bool 13 | } 14 | 15 | // Pick is needed to satisfy the Node interface 16 | func (n *fakeNode) Pick(qry *Query) *Conn { 17 | if n.conn == nil { 18 | n.conn = &Conn{} 19 | } 20 | return n.conn 21 | } 22 | 23 | //Close is needed to satisfy the Node interface 24 | func (n *fakeNode) Close() { 25 | n.closed = true 26 | } 27 | 28 | //TestRoundRobinAPI tests the exported methods of the RoundRobin struct 29 | //to make sure the API behaves accordingly. 30 | func TestRoundRobinAPI(t *testing.T) { 31 | node := &fakeNode{} 32 | rr := NewRoundRobin() 33 | rr.AddNode(node) 34 | 35 | if rr.Size() != 1 { 36 | t.Fatalf("expected size to be 1, got %v", rr.Size()) 37 | } 38 | 39 | if c := rr.Pick(nil); c != node.conn { 40 | t.Fatalf("expected conn %v, got %v", node.conn, c) 41 | } 42 | 43 | rr.Close() 44 | if rr.pool != nil { 45 | t.Fatalf("expected rr.pool to be nil, got %v", rr.pool) 46 | } 47 | 48 | if !node.closed { 49 | t.Fatal("expected node.closed to be true, got false") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/tuple_test.go: -------------------------------------------------------------------------------- 1 | // +build all integration 2 | 3 | package gocql 4 | 5 | import "testing" 6 | 7 | func TestTupleSimple(t *testing.T) { 8 | if *flagProto < protoVersion3 { 9 | t.Skip("tuple types are only available of proto>=3") 10 | } 11 | 12 | session := createSession(t) 13 | defer session.Close() 14 | 15 | err := createTable(session, `CREATE TABLE tuple_test( 16 | id int, 17 | coord frozen>, 18 | 19 | primary key(id))`) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | err = session.Query("INSERT INTO tuple_test(id, coord) VALUES(?, (?, ?))", 1, 100, -100).Exec() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | var ( 30 | id int 31 | coord struct { 32 | x int 33 | y int 34 | } 35 | ) 36 | 37 | iter := session.Query("SELECT id, coord FROM tuple_test WHERE id=?", 1) 38 | if err := iter.Scan(&id, &coord.x, &coord.y); err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | if id != 1 { 43 | t.Errorf("expected to get id=1 got: %v", id) 44 | } 45 | if coord.x != 100 { 46 | t.Errorf("expected to get coord.x=100 got: %v", coord.x) 47 | } 48 | if coord.y != -100 { 49 | t.Errorf("expected to get coord.y=-100 got: %v", coord.y) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/uuid.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The gocql Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // The uuid package can be used to generate and parse universally unique 6 | // identifiers, a standardized format in the form of a 128 bit number. 7 | // 8 | // http://tools.ietf.org/html/rfc4122 9 | package gocql 10 | 11 | import ( 12 | "crypto/rand" 13 | "errors" 14 | "fmt" 15 | "io" 16 | "net" 17 | "strings" 18 | "sync/atomic" 19 | "time" 20 | ) 21 | 22 | type UUID [16]byte 23 | 24 | var hardwareAddr []byte 25 | var clockSeq uint32 26 | 27 | const ( 28 | VariantNCSCompat = 0 29 | VariantIETF = 2 30 | VariantMicrosoft = 6 31 | VariantFuture = 7 32 | ) 33 | 34 | func init() { 35 | if interfaces, err := net.Interfaces(); err == nil { 36 | for _, i := range interfaces { 37 | if i.Flags&net.FlagLoopback == 0 && len(i.HardwareAddr) > 0 { 38 | hardwareAddr = i.HardwareAddr 39 | break 40 | } 41 | } 42 | } 43 | if hardwareAddr == nil { 44 | // If we failed to obtain the MAC address of the current computer, 45 | // we will use a randomly generated 6 byte sequence instead and set 46 | // the multicast bit as recommended in RFC 4122. 47 | hardwareAddr = make([]byte, 6) 48 | _, err := io.ReadFull(rand.Reader, hardwareAddr) 49 | if err != nil { 50 | panic(err) 51 | } 52 | hardwareAddr[0] = hardwareAddr[0] | 0x01 53 | } 54 | 55 | // initialize the clock sequence with a random number 56 | var clockSeqRand [2]byte 57 | io.ReadFull(rand.Reader, clockSeqRand[:]) 58 | clockSeq = uint32(clockSeqRand[1])<<8 | uint32(clockSeqRand[0]) 59 | } 60 | 61 | // ParseUUID parses a 32 digit hexadecimal number (that might contain hypens) 62 | // representing an UUID. 63 | func ParseUUID(input string) (UUID, error) { 64 | var u UUID 65 | j := 0 66 | for _, r := range input { 67 | switch { 68 | case r == '-' && j&1 == 0: 69 | continue 70 | case r >= '0' && r <= '9' && j < 32: 71 | u[j/2] |= byte(r-'0') << uint(4-j&1*4) 72 | case r >= 'a' && r <= 'f' && j < 32: 73 | u[j/2] |= byte(r-'a'+10) << uint(4-j&1*4) 74 | case r >= 'A' && r <= 'F' && j < 32: 75 | u[j/2] |= byte(r-'A'+10) << uint(4-j&1*4) 76 | default: 77 | return UUID{}, fmt.Errorf("invalid UUID %q", input) 78 | } 79 | j += 1 80 | } 81 | if j != 32 { 82 | return UUID{}, fmt.Errorf("invalid UUID %q", input) 83 | } 84 | return u, nil 85 | } 86 | 87 | // UUIDFromBytes converts a raw byte slice to an UUID. 88 | func UUIDFromBytes(input []byte) (UUID, error) { 89 | var u UUID 90 | if len(input) != 16 { 91 | return u, errors.New("UUIDs must be exactly 16 bytes long") 92 | } 93 | 94 | copy(u[:], input) 95 | return u, nil 96 | } 97 | 98 | // RandomUUID generates a totally random UUID (version 4) as described in 99 | // RFC 4122. 100 | func RandomUUID() (UUID, error) { 101 | var u UUID 102 | _, err := io.ReadFull(rand.Reader, u[:]) 103 | if err != nil { 104 | return u, err 105 | } 106 | u[6] &= 0x0F // clear version 107 | u[6] |= 0x40 // set version to 4 (random uuid) 108 | u[8] &= 0x3F // clear variant 109 | u[8] |= 0x80 // set to IETF variant 110 | return u, nil 111 | } 112 | 113 | var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix() 114 | 115 | // TimeUUID generates a new time based UUID (version 1) using the current 116 | // time as the timestamp. 117 | func TimeUUID() UUID { 118 | return UUIDFromTime(time.Now()) 119 | } 120 | 121 | // UUIDFromTime generates a new time based UUID (version 1) as described in 122 | // RFC 4122. This UUID contains the MAC address of the node that generated 123 | // the UUID, the given timestamp and a sequence number. 124 | func UUIDFromTime(aTime time.Time) UUID { 125 | var u UUID 126 | 127 | utcTime := aTime.In(time.UTC) 128 | t := uint64(utcTime.Unix()-timeBase)*10000000 + uint64(utcTime.Nanosecond()/100) 129 | u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t) 130 | u[4], u[5] = byte(t>>40), byte(t>>32) 131 | u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48) 132 | 133 | clock := atomic.AddUint32(&clockSeq, 1) 134 | u[8] = byte(clock >> 8) 135 | u[9] = byte(clock) 136 | 137 | copy(u[10:], hardwareAddr) 138 | 139 | u[6] |= 0x10 // set version to 1 (time based uuid) 140 | u[8] &= 0x3F // clear variant 141 | u[8] |= 0x80 // set to IETF variant 142 | 143 | return u 144 | } 145 | 146 | // String returns the UUID in it's canonical form, a 32 digit hexadecimal 147 | // number in the form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. 148 | func (u UUID) String() string { 149 | var offsets = [...]int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} 150 | const hexString = "0123456789abcdef" 151 | r := make([]byte, 36) 152 | for i, b := range u { 153 | r[offsets[i]] = hexString[b>>4] 154 | r[offsets[i]+1] = hexString[b&0xF] 155 | } 156 | r[8] = '-' 157 | r[13] = '-' 158 | r[18] = '-' 159 | r[23] = '-' 160 | return string(r) 161 | 162 | } 163 | 164 | // Bytes returns the raw byte slice for this UUID. A UUID is always 128 bits 165 | // (16 bytes) long. 166 | func (u UUID) Bytes() []byte { 167 | return u[:] 168 | } 169 | 170 | // Variant returns the variant of this UUID. This package will only generate 171 | // UUIDs in the IETF variant. 172 | func (u UUID) Variant() int { 173 | x := u[8] 174 | if x&0x80 == 0 { 175 | return VariantNCSCompat 176 | } 177 | if x&0x40 == 0 { 178 | return VariantIETF 179 | } 180 | if x&0x20 == 0 { 181 | return VariantMicrosoft 182 | } 183 | return VariantFuture 184 | } 185 | 186 | // Version extracts the version of this UUID variant. The RFC 4122 describes 187 | // five kinds of UUIDs. 188 | func (u UUID) Version() int { 189 | return int(u[6] & 0xF0 >> 4) 190 | } 191 | 192 | // Node extracts the MAC address of the node who generated this UUID. It will 193 | // return nil if the UUID is not a time based UUID (version 1). 194 | func (u UUID) Node() []byte { 195 | if u.Version() != 1 { 196 | return nil 197 | } 198 | return u[10:] 199 | } 200 | 201 | // Timestamp extracts the timestamp information from a time based UUID 202 | // (version 1). 203 | func (u UUID) Timestamp() int64 { 204 | if u.Version() != 1 { 205 | return 0 206 | } 207 | return int64(uint64(u[0])<<24|uint64(u[1])<<16| 208 | uint64(u[2])<<8|uint64(u[3])) + 209 | int64(uint64(u[4])<<40|uint64(u[5])<<32) + 210 | int64(uint64(u[6]&0x0F)<<56|uint64(u[7])<<48) 211 | } 212 | 213 | // Time is like Timestamp, except that it returns a time.Time. 214 | func (u UUID) Time() time.Time { 215 | if u.Version() != 1 { 216 | return time.Time{} 217 | } 218 | t := u.Timestamp() 219 | sec := t / 1e7 220 | nsec := (t % 1e7) * 100 221 | return time.Unix(sec+timeBase, nsec).UTC() 222 | } 223 | 224 | // Marshaling for JSON 225 | func (u UUID) MarshalJSON() ([]byte, error) { 226 | return []byte(`"` + u.String() + `"`), nil 227 | } 228 | 229 | // Unmarshaling for JSON 230 | func (u *UUID) UnmarshalJSON(data []byte) error { 231 | str := strings.Trim(string(data), `"`) 232 | if len(str) > 36 { 233 | return fmt.Errorf("invalid JSON UUID %s", str) 234 | } 235 | 236 | parsed, err := ParseUUID(str) 237 | if err == nil { 238 | copy(u[:], parsed[:]) 239 | } 240 | 241 | return err 242 | } 243 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/uuid_test.go: -------------------------------------------------------------------------------- 1 | // +build all unit 2 | 3 | package gocql 4 | 5 | import ( 6 | "bytes" 7 | "strings" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestUUIDNil(t *testing.T) { 13 | var uuid UUID 14 | want, got := "00000000-0000-0000-0000-000000000000", uuid.String() 15 | if want != got { 16 | t.Fatalf("TestNil: expected %q got %q", want, got) 17 | } 18 | } 19 | 20 | var testsUUID = []struct { 21 | input string 22 | variant int 23 | version int 24 | }{ 25 | {"b4f00409-cef8-4822-802c-deb20704c365", VariantIETF, 4}, 26 | {"B4F00409-CEF8-4822-802C-DEB20704C365", VariantIETF, 4}, //Use capital letters 27 | {"f81d4fae-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1}, 28 | {"00000000-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1}, 29 | {"3051a8d7-aea7-1801-e0bf-bc539dd60cf3", VariantFuture, 1}, 30 | {"3051a8d7-aea7-2801-e0bf-bc539dd60cf3", VariantFuture, 2}, 31 | {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 3}, 32 | {"3051a8d7-aea7-4801-e0bf-bc539dd60cf3", VariantFuture, 4}, 33 | {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 5}, 34 | {"d0e817e1-e4b1-1801-3fe6-b4b60ccecf9d", VariantNCSCompat, 0}, 35 | {"d0e817e1-e4b1-1801-bfe6-b4b60ccecf9d", VariantIETF, 1}, 36 | {"d0e817e1-e4b1-1801-dfe6-b4b60ccecf9d", VariantMicrosoft, 0}, 37 | {"d0e817e1-e4b1-1801-ffe6-b4b60ccecf9d", VariantFuture, 0}, 38 | } 39 | 40 | func TestPredefinedUUID(t *testing.T) { 41 | for i := range testsUUID { 42 | uuid, err := ParseUUID(testsUUID[i].input) 43 | if err != nil { 44 | t.Errorf("ParseUUID #%d: %v", i, err) 45 | continue 46 | } 47 | 48 | if str := uuid.String(); str != strings.ToLower(testsUUID[i].input) { 49 | t.Errorf("String #%d: expected %q got %q", i, testsUUID[i].input, str) 50 | continue 51 | } 52 | 53 | if variant := uuid.Variant(); variant != testsUUID[i].variant { 54 | t.Errorf("Variant #%d: expected %d got %d", i, testsUUID[i].variant, variant) 55 | } 56 | 57 | if testsUUID[i].variant == VariantIETF { 58 | if version := uuid.Version(); version != testsUUID[i].version { 59 | t.Errorf("Version #%d: expected %d got %d", i, testsUUID[i].version, version) 60 | } 61 | } 62 | 63 | json, err := uuid.MarshalJSON() 64 | if err != nil { 65 | t.Errorf("MarshalJSON #%d: %v", i, err) 66 | } 67 | expectedJson := `"` + strings.ToLower(testsUUID[i].input) + `"` 68 | if string(json) != expectedJson { 69 | t.Errorf("MarshalJSON #%d: expected %v got %v", i, expectedJson, string(json)) 70 | } 71 | 72 | var unmarshaled UUID 73 | err = unmarshaled.UnmarshalJSON(json) 74 | if err != nil { 75 | t.Errorf("UnmarshalJSON #%d: %v", i, err) 76 | } 77 | if unmarshaled != uuid { 78 | t.Errorf("UnmarshalJSON #%d: expected %v got %v", i, uuid, unmarshaled) 79 | } 80 | } 81 | } 82 | 83 | func TestInvalidUUIDCharacter(t *testing.T) { 84 | _, err := ParseUUID("z4f00409-cef8-4822-802c-deb20704c365") 85 | if err == nil || !strings.Contains(err.Error(), "invalid UUID") { 86 | t.Fatalf("expected invalid UUID error, got '%v' ", err) 87 | } 88 | } 89 | 90 | func TestInvalidUUIDLength(t *testing.T) { 91 | _, err := ParseUUID("4f00") 92 | if err == nil || !strings.Contains(err.Error(), "invalid UUID") { 93 | t.Fatalf("expected invalid UUID error, got '%v' ", err) 94 | } 95 | 96 | _, err = UUIDFromBytes(TimeUUID().Bytes()[:15]) 97 | if err == nil || err.Error() != "UUIDs must be exactly 16 bytes long" { 98 | t.Fatalf("expected error '%v', got '%v'", "UUIDs must be exactly 16 bytes long", err) 99 | } 100 | } 101 | 102 | func TestRandomUUID(t *testing.T) { 103 | for i := 0; i < 20; i++ { 104 | uuid, err := RandomUUID() 105 | if err != nil { 106 | t.Errorf("RandomUUID: %v", err) 107 | } 108 | if variant := uuid.Variant(); variant != VariantIETF { 109 | t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant) 110 | } 111 | if version := uuid.Version(); version != 4 { 112 | t.Errorf("wrong version. expected %d got %d", 4, version) 113 | } 114 | } 115 | } 116 | 117 | func TestRandomUUIDInvalidAPICalls(t *testing.T) { 118 | uuid, err := RandomUUID() 119 | if err != nil { 120 | t.Fatalf("unexpected error %v", err) 121 | } 122 | 123 | if node := uuid.Node(); node != nil { 124 | t.Fatalf("expected nil, got %v", node) 125 | } 126 | 127 | if stamp := uuid.Timestamp(); stamp != 0 { 128 | t.Fatalf("expceted 0, got %v", stamp) 129 | } 130 | zeroT := time.Time{} 131 | if to := uuid.Time(); to != zeroT { 132 | t.Fatalf("expected %v, got %v", zeroT, to) 133 | } 134 | } 135 | 136 | func TestUUIDFromTime(t *testing.T) { 137 | date := time.Date(1982, 5, 5, 12, 34, 56, 400, time.UTC) 138 | uuid := UUIDFromTime(date) 139 | 140 | if uuid.Time() != date { 141 | t.Errorf("embedded time incorrect. Expected %v got %v", date, uuid.Time()) 142 | } 143 | } 144 | 145 | func TestParseUUID(t *testing.T) { 146 | uuid, _ := ParseUUID("486f3a88-775b-11e3-ae07-d231feb1dc81") 147 | if uuid.Time() != time.Date(2014, 1, 7, 5, 19, 29, 222516000, time.UTC) { 148 | t.Errorf("Expected date of 1/7/2014 at 5:19:29.222516, got %v", uuid.Time()) 149 | } 150 | } 151 | 152 | func TestTimeUUID(t *testing.T) { 153 | var node []byte 154 | timestamp := int64(0) 155 | for i := 0; i < 20; i++ { 156 | uuid := TimeUUID() 157 | 158 | if variant := uuid.Variant(); variant != VariantIETF { 159 | t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant) 160 | } 161 | if version := uuid.Version(); version != 1 { 162 | t.Errorf("wrong version. expected %d got %d", 1, version) 163 | } 164 | 165 | if n := uuid.Node(); !bytes.Equal(n, node) && i > 0 { 166 | t.Errorf("wrong node. expected %x, got %x", node, n) 167 | } else if i == 0 { 168 | node = n 169 | } 170 | 171 | ts := uuid.Timestamp() 172 | if ts < timestamp { 173 | t.Errorf("timestamps must grow") 174 | } 175 | timestamp = ts 176 | } 177 | } 178 | 179 | func TestUnmarshalJSON(t *testing.T) { 180 | var withHyphens, withoutHypens, tooLong UUID 181 | 182 | withHyphens.UnmarshalJSON([]byte(`"486f3a88-775b-11e3-ae07-d231feb1dc81"`)) 183 | if withHyphens.Time().Truncate(time.Second) != time.Date(2014, 1, 7, 5, 19, 29, 0, time.UTC) { 184 | t.Errorf("Expected date of 1/7/2014 at 5:19:29, got %v", withHyphens.Time()) 185 | } 186 | 187 | withoutHypens.UnmarshalJSON([]byte(`"486f3a88775b11e3ae07d231feb1dc81"`)) 188 | if withoutHypens.Time().Truncate(time.Second) != time.Date(2014, 1, 7, 5, 19, 29, 0, time.UTC) { 189 | t.Errorf("Expected date of 1/7/2014 at 5:19:29, got %v", withoutHypens.Time()) 190 | } 191 | 192 | err := tooLong.UnmarshalJSON([]byte(`"486f3a88-775b-11e3-ae07-d231feb1dc81486f3a88"`)) 193 | if err == nil { 194 | t.Errorf("no error for invalid JSON UUID") 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/website/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/website/favicon.ico -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/website/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/website/gocql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoiv/gocqltable/50cb774da6766cc1eddc16b1edf0f131bde46bff/Godeps/_workspace/src/github.com/gocql/gocql/website/gocql.png -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GoCQL 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | 24 |
25 | 26 |
27 |
28 | 29 |

GoCQL tux21b.org/v1/gocql

30 |
31 |
32 | 38 |
39 |
40 | 41 |
42 | Under Development: The GoCQL package is currently actively 43 | developed and the API may change in the future. 44 |
45 | 46 | 47 |

Highlights

48 | 49 |
50 |
51 |

Cluster Management

52 |

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

55 |
56 | 57 |
58 |

Type Conversation

59 |

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

62 |
63 | 64 |
65 |

Synchronous and Concurrent

66 |

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

69 |
70 | 71 |
72 |

Failover Management

73 |

TODO :(

74 |
75 |
76 | 77 |
78 |
79 |

Result Paging

80 |

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

83 |
84 | 85 |
86 |

Atomic Batches

87 |

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

89 |
90 | 91 |
92 |

Query Tracing

93 |

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

97 |
98 | 99 |
100 |

Frame Compression

101 |

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

106 |
107 |
108 | 109 |
110 |
111 |

Multiple Cassandra Versions

112 |

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

114 |
115 | 116 |
117 |

Thoroughly Tested

118 |

TODO :(

119 |
120 | 121 |
122 |

BSD License

123 |

Completely open source. Browse the source on 124 | GitHub and start 125 | contributing today.

126 |
127 |
128 | 129 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gocql/gocql/wiki_test.go: -------------------------------------------------------------------------------- 1 | // +build all integration 2 | 3 | package gocql 4 | 5 | import ( 6 | "fmt" 7 | "reflect" 8 | "sort" 9 | "speter.net/go/exp/math/dec/inf" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | type WikiPage struct { 15 | Title string 16 | RevId UUID 17 | Body string 18 | Views int64 19 | Protected bool 20 | Modified time.Time 21 | Rating *inf.Dec 22 | Tags []string 23 | Attachments map[string]WikiAttachment 24 | } 25 | 26 | type WikiAttachment []byte 27 | 28 | var wikiTestData = []*WikiPage{ 29 | &WikiPage{ 30 | Title: "Frontpage", 31 | RevId: TimeUUID(), 32 | Body: "Welcome to this wiki page!", 33 | Rating: inf.NewDec(131, 3), 34 | Modified: time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC), 35 | Tags: []string{"start", "important", "test"}, 36 | Attachments: map[string]WikiAttachment{ 37 | "logo": WikiAttachment("\x00company logo\x00"), 38 | "favicon": WikiAttachment("favicon.ico"), 39 | }, 40 | }, 41 | &WikiPage{ 42 | Title: "Foobar", 43 | RevId: TimeUUID(), 44 | Body: "foo::Foo f = new foo::Foo(foo::Foo::INIT);", 45 | Modified: time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC), 46 | }, 47 | } 48 | 49 | type WikiTest struct { 50 | session *Session 51 | tb testing.TB 52 | } 53 | 54 | func (w *WikiTest) CreateSchema() { 55 | 56 | if err := w.session.Query(`DROP TABLE wiki_page`).Exec(); err != nil && err.Error() != "unconfigured columnfamily wiki_page" { 57 | w.tb.Fatal("CreateSchema:", err) 58 | } 59 | err := createTable(w.session, `CREATE TABLE wiki_page ( 60 | title varchar, 61 | revid timeuuid, 62 | body varchar, 63 | views bigint, 64 | protected boolean, 65 | modified timestamp, 66 | rating decimal, 67 | tags set, 68 | attachments map, 69 | PRIMARY KEY (title, revid) 70 | )`) 71 | if *clusterSize > 1 { 72 | // wait for table definition to propogate 73 | time.Sleep(250 * time.Millisecond) 74 | } 75 | if err != nil { 76 | w.tb.Fatal("CreateSchema:", err) 77 | } 78 | } 79 | 80 | func (w *WikiTest) CreatePages(n int) { 81 | var page WikiPage 82 | t0 := time.Now() 83 | for i := 0; i < n; i++ { 84 | page.Title = fmt.Sprintf("generated_%d", (i&16)+1) 85 | page.Modified = t0.Add(time.Duration(i-n) * time.Minute) 86 | page.RevId = UUIDFromTime(page.Modified) 87 | page.Body = fmt.Sprintf("text %d", i) 88 | if err := w.InsertPage(&page); err != nil { 89 | w.tb.Error("CreatePages:", err) 90 | } 91 | } 92 | } 93 | 94 | func (w *WikiTest) InsertPage(page *WikiPage) error { 95 | return w.session.Query(`INSERT INTO wiki_page 96 | (title, revid, body, views, protected, modified, rating, tags, attachments) 97 | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, 98 | page.Title, page.RevId, page.Body, page.Views, page.Protected, 99 | page.Modified, page.Rating, page.Tags, page.Attachments).Exec() 100 | } 101 | 102 | func (w *WikiTest) SelectPage(page *WikiPage, title string, revid UUID) error { 103 | return w.session.Query(`SELECT title, revid, body, views, protected, 104 | modified,tags, attachments, rating 105 | FROM wiki_page WHERE title = ? AND revid = ? LIMIT 1`, 106 | title, revid).Scan(&page.Title, &page.RevId, 107 | &page.Body, &page.Views, &page.Protected, &page.Modified, &page.Tags, 108 | &page.Attachments, &page.Rating) 109 | } 110 | 111 | func (w *WikiTest) GetPageCount() int { 112 | var count int 113 | if err := w.session.Query(`SELECT COUNT(*) FROM wiki_page`).Scan(&count); err != nil { 114 | w.tb.Error("GetPageCount", err) 115 | } 116 | return count 117 | } 118 | 119 | func TestWikiCreateSchema(t *testing.T) { 120 | session := createSession(t) 121 | defer session.Close() 122 | 123 | w := WikiTest{session, t} 124 | w.CreateSchema() 125 | } 126 | 127 | func BenchmarkWikiCreateSchema(b *testing.B) { 128 | b.StopTimer() 129 | session := createSession(b) 130 | defer func() { 131 | b.StopTimer() 132 | session.Close() 133 | }() 134 | w := WikiTest{session, b} 135 | b.StartTimer() 136 | 137 | for i := 0; i < b.N; i++ { 138 | w.CreateSchema() 139 | } 140 | } 141 | 142 | func TestWikiCreatePages(t *testing.T) { 143 | session := createSession(t) 144 | defer session.Close() 145 | 146 | w := WikiTest{session, t} 147 | w.CreateSchema() 148 | numPages := 5 149 | w.CreatePages(numPages) 150 | if count := w.GetPageCount(); count != numPages { 151 | t.Errorf("expected %d pages, got %d pages.", numPages, count) 152 | } 153 | } 154 | 155 | func BenchmarkWikiCreatePages(b *testing.B) { 156 | b.StopTimer() 157 | session := createSession(b) 158 | defer func() { 159 | b.StopTimer() 160 | session.Close() 161 | }() 162 | w := WikiTest{session, b} 163 | w.CreateSchema() 164 | b.StartTimer() 165 | 166 | w.CreatePages(b.N) 167 | } 168 | 169 | func BenchmarkWikiSelectAllPages(b *testing.B) { 170 | b.StopTimer() 171 | session := createSession(b) 172 | defer func() { 173 | b.StopTimer() 174 | session.Close() 175 | }() 176 | w := WikiTest{session, b} 177 | w.CreateSchema() 178 | w.CreatePages(100) 179 | b.StartTimer() 180 | 181 | var page WikiPage 182 | for i := 0; i < b.N; i++ { 183 | iter := session.Query(`SELECT title, revid, body, views, protected, 184 | modified, tags, attachments, rating 185 | FROM wiki_page`).Iter() 186 | for iter.Scan(&page.Title, &page.RevId, &page.Body, &page.Views, 187 | &page.Protected, &page.Modified, &page.Tags, &page.Attachments, 188 | &page.Rating) { 189 | // pass 190 | } 191 | if err := iter.Close(); err != nil { 192 | b.Error(err) 193 | } 194 | } 195 | } 196 | 197 | func BenchmarkWikiSelectSinglePage(b *testing.B) { 198 | b.StopTimer() 199 | session := createSession(b) 200 | defer func() { 201 | b.StopTimer() 202 | session.Close() 203 | }() 204 | w := WikiTest{session, b} 205 | w.CreateSchema() 206 | pages := make([]WikiPage, 100) 207 | w.CreatePages(len(pages)) 208 | iter := session.Query(`SELECT title, revid FROM wiki_page`).Iter() 209 | for i := 0; i < len(pages); i++ { 210 | if !iter.Scan(&pages[i].Title, &pages[i].RevId) { 211 | pages = pages[:i] 212 | break 213 | } 214 | } 215 | if err := iter.Close(); err != nil { 216 | b.Error(err) 217 | } 218 | b.StartTimer() 219 | 220 | var page WikiPage 221 | for i := 0; i < b.N; i++ { 222 | p := &pages[i%len(pages)] 223 | if err := w.SelectPage(&page, p.Title, p.RevId); err != nil { 224 | b.Error(err) 225 | } 226 | } 227 | } 228 | 229 | func BenchmarkWikiSelectPageCount(b *testing.B) { 230 | b.StopTimer() 231 | session := createSession(b) 232 | defer func() { 233 | b.StopTimer() 234 | session.Close() 235 | }() 236 | w := WikiTest{session, b} 237 | w.CreateSchema() 238 | numPages := 10 239 | w.CreatePages(numPages) 240 | b.StartTimer() 241 | for i := 0; i < b.N; i++ { 242 | if count := w.GetPageCount(); count != numPages { 243 | b.Errorf("expected %d pages, got %d pages.", numPages, count) 244 | } 245 | } 246 | } 247 | 248 | func TestWikiTypicalCRUD(t *testing.T) { 249 | session := createSession(t) 250 | defer session.Close() 251 | 252 | w := WikiTest{session, t} 253 | w.CreateSchema() 254 | for _, page := range wikiTestData { 255 | if err := w.InsertPage(page); err != nil { 256 | t.Error("InsertPage:", err) 257 | } 258 | } 259 | if count := w.GetPageCount(); count != len(wikiTestData) { 260 | t.Errorf("count: expected %d, got %d\n", len(wikiTestData), count) 261 | } 262 | for _, original := range wikiTestData { 263 | page := new(WikiPage) 264 | if err := w.SelectPage(page, original.Title, original.RevId); err != nil { 265 | t.Error("SelectPage:", err) 266 | continue 267 | } 268 | sort.Sort(sort.StringSlice(page.Tags)) 269 | sort.Sort(sort.StringSlice(original.Tags)) 270 | if !reflect.DeepEqual(page, original) { 271 | t.Errorf("page: expected %#v, got %#v\n", original, page) 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/golang/groupcache/lru/lru.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package lru implements an LRU cache. 18 | package lru 19 | 20 | import "container/list" 21 | 22 | // Cache is an LRU cache. It is not safe for concurrent access. 23 | type Cache struct { 24 | // MaxEntries is the maximum number of cache entries before 25 | // an item is evicted. Zero means no limit. 26 | MaxEntries int 27 | 28 | // OnEvicted optionally specificies a callback function to be 29 | // executed when an entry is purged from the cache. 30 | OnEvicted func(key Key, value interface{}) 31 | 32 | ll *list.List 33 | cache map[interface{}]*list.Element 34 | } 35 | 36 | // A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators 37 | type Key interface{} 38 | 39 | type entry struct { 40 | key Key 41 | value interface{} 42 | } 43 | 44 | // New creates a new Cache. 45 | // If maxEntries is zero, the cache has no limit and it's assumed 46 | // that eviction is done by the caller. 47 | func New(maxEntries int) *Cache { 48 | return &Cache{ 49 | MaxEntries: maxEntries, 50 | ll: list.New(), 51 | cache: make(map[interface{}]*list.Element), 52 | } 53 | } 54 | 55 | // Add adds a value to the cache. 56 | func (c *Cache) Add(key Key, value interface{}) { 57 | if c.cache == nil { 58 | c.cache = make(map[interface{}]*list.Element) 59 | c.ll = list.New() 60 | } 61 | if ee, ok := c.cache[key]; ok { 62 | c.ll.MoveToFront(ee) 63 | ee.Value.(*entry).value = value 64 | return 65 | } 66 | ele := c.ll.PushFront(&entry{key, value}) 67 | c.cache[key] = ele 68 | if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { 69 | c.RemoveOldest() 70 | } 71 | } 72 | 73 | // Get looks up a key's value from the cache. 74 | func (c *Cache) Get(key Key) (value interface{}, ok bool) { 75 | if c.cache == nil { 76 | return 77 | } 78 | if ele, hit := c.cache[key]; hit { 79 | c.ll.MoveToFront(ele) 80 | return ele.Value.(*entry).value, true 81 | } 82 | return 83 | } 84 | 85 | // Remove removes the provided key from the cache. 86 | func (c *Cache) Remove(key Key) { 87 | if c.cache == nil { 88 | return 89 | } 90 | if ele, hit := c.cache[key]; hit { 91 | c.removeElement(ele) 92 | } 93 | } 94 | 95 | // RemoveOldest removes the oldest item from the cache. 96 | func (c *Cache) RemoveOldest() { 97 | if c.cache == nil { 98 | return 99 | } 100 | ele := c.ll.Back() 101 | if ele != nil { 102 | c.removeElement(ele) 103 | } 104 | } 105 | 106 | func (c *Cache) removeElement(e *list.Element) { 107 | c.ll.Remove(e) 108 | kv := e.Value.(*entry) 109 | delete(c.cache, kv.key) 110 | if c.OnEvicted != nil { 111 | c.OnEvicted(kv.key, kv.value) 112 | } 113 | } 114 | 115 | // Len returns the number of items in the cache. 116 | func (c *Cache) Len() int { 117 | if c.cache == nil { 118 | return 0 119 | } 120 | return c.ll.Len() 121 | } 122 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package lru 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | type simpleStruct struct { 24 | int 25 | string 26 | } 27 | 28 | type complexStruct struct { 29 | int 30 | simpleStruct 31 | } 32 | 33 | var getTests = []struct { 34 | name string 35 | keyToAdd interface{} 36 | keyToGet interface{} 37 | expectedOk bool 38 | }{ 39 | {"string_hit", "myKey", "myKey", true}, 40 | {"string_miss", "myKey", "nonsense", false}, 41 | {"simple_struct_hit", simpleStruct{1, "two"}, simpleStruct{1, "two"}, true}, 42 | {"simeple_struct_miss", simpleStruct{1, "two"}, simpleStruct{0, "noway"}, false}, 43 | {"complex_struct_hit", complexStruct{1, simpleStruct{2, "three"}}, 44 | complexStruct{1, simpleStruct{2, "three"}}, true}, 45 | } 46 | 47 | func TestGet(t *testing.T) { 48 | for _, tt := range getTests { 49 | lru := New(0) 50 | lru.Add(tt.keyToAdd, 1234) 51 | val, ok := lru.Get(tt.keyToGet) 52 | if ok != tt.expectedOk { 53 | t.Fatalf("%s: cache hit = %v; want %v", tt.name, ok, !ok) 54 | } else if ok && val != 1234 { 55 | t.Fatalf("%s expected get to return 1234 but got %v", tt.name, val) 56 | } 57 | } 58 | } 59 | 60 | func TestRemove(t *testing.T) { 61 | lru := New(0) 62 | lru.Add("myKey", 1234) 63 | if val, ok := lru.Get("myKey"); !ok { 64 | t.Fatal("TestRemove returned no match") 65 | } else if val != 1234 { 66 | t.Fatalf("TestRemove failed. Expected %d, got %v", 1234, val) 67 | } 68 | 69 | lru.Remove("myKey") 70 | if _, ok := lru.Get("myKey"); ok { 71 | t.Fatal("TestRemove returned a removed entry") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Péter Surányi. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | ---------------------------------------------------------------------- 27 | Portions of inf.Dec's source code have been derived from Go and are 28 | covered by the following license: 29 | ---------------------------------------------------------------------- 30 | 31 | Copyright (c) 2009 The Go Authors. All rights reserved. 32 | 33 | Redistribution and use in source and binary forms, with or without 34 | modification, are permitted provided that the following conditions are 35 | met: 36 | 37 | * Redistributions of source code must retain the above copyright 38 | notice, this list of conditions and the following disclaimer. 39 | * Redistributions in binary form must reproduce the above 40 | copyright notice, this list of conditions and the following disclaimer 41 | in the documentation and/or other materials provided with the 42 | distribution. 43 | * Neither the name of Google Inc. nor the names of its 44 | contributors may be used to endorse or promote products derived from 45 | this software without specific prior written permission. 46 | 47 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 48 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 49 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 50 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 51 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 52 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 53 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 54 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 55 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 56 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 57 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/benchmark_test.go: -------------------------------------------------------------------------------- 1 | package inf 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "math/rand" 7 | "sync" 8 | "testing" 9 | ) 10 | 11 | const maxcap = 1024 * 1024 12 | const bits = 256 13 | const maxscale = 32 14 | 15 | var once sync.Once 16 | 17 | var decInput [][2]Dec 18 | var intInput [][2]big.Int 19 | 20 | var initBench = func() { 21 | decInput = make([][2]Dec, maxcap) 22 | intInput = make([][2]big.Int, maxcap) 23 | max := new(big.Int).Lsh(big.NewInt(1), bits) 24 | r := rand.New(rand.NewSource(0)) 25 | for i := 0; i < cap(decInput); i++ { 26 | decInput[i][0].SetUnscaledBig(new(big.Int).Rand(r, max)). 27 | SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale))) 28 | decInput[i][1].SetUnscaledBig(new(big.Int).Rand(r, max)). 29 | SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale))) 30 | } 31 | for i := 0; i < cap(intInput); i++ { 32 | intInput[i][0].Rand(r, max) 33 | intInput[i][1].Rand(r, max) 34 | } 35 | } 36 | 37 | func doBenchmarkDec1(b *testing.B, f func(z *Dec)) { 38 | once.Do(initBench) 39 | b.ResetTimer() 40 | b.StartTimer() 41 | for i := 0; i < b.N; i++ { 42 | f(&decInput[i%maxcap][0]) 43 | } 44 | } 45 | 46 | func doBenchmarkDec2(b *testing.B, f func(x, y *Dec)) { 47 | once.Do(initBench) 48 | b.ResetTimer() 49 | b.StartTimer() 50 | for i := 0; i < b.N; i++ { 51 | f(&decInput[i%maxcap][0], &decInput[i%maxcap][1]) 52 | } 53 | } 54 | 55 | func doBenchmarkInt1(b *testing.B, f func(z *big.Int)) { 56 | once.Do(initBench) 57 | b.ResetTimer() 58 | b.StartTimer() 59 | for i := 0; i < b.N; i++ { 60 | f(&intInput[i%maxcap][0]) 61 | } 62 | } 63 | 64 | func doBenchmarkInt2(b *testing.B, f func(x, y *big.Int)) { 65 | once.Do(initBench) 66 | b.ResetTimer() 67 | b.StartTimer() 68 | for i := 0; i < b.N; i++ { 69 | f(&intInput[i%maxcap][0], &intInput[i%maxcap][1]) 70 | } 71 | } 72 | 73 | func Benchmark_Dec_String(b *testing.B) { 74 | doBenchmarkDec1(b, func(x *Dec) { 75 | x.String() 76 | }) 77 | } 78 | 79 | func Benchmark_Dec_StringScan(b *testing.B) { 80 | doBenchmarkDec1(b, func(x *Dec) { 81 | s := x.String() 82 | d := new(Dec) 83 | fmt.Sscan(s, d) 84 | }) 85 | } 86 | 87 | func Benchmark_Dec_GobEncode(b *testing.B) { 88 | doBenchmarkDec1(b, func(x *Dec) { 89 | x.GobEncode() 90 | }) 91 | } 92 | 93 | func Benchmark_Dec_GobEnDecode(b *testing.B) { 94 | doBenchmarkDec1(b, func(x *Dec) { 95 | g, _ := x.GobEncode() 96 | new(Dec).GobDecode(g) 97 | }) 98 | } 99 | 100 | func Benchmark_Dec_Add(b *testing.B) { 101 | doBenchmarkDec2(b, func(x, y *Dec) { 102 | ys := y.Scale() 103 | y.SetScale(x.Scale()) 104 | _ = new(Dec).Add(x, y) 105 | y.SetScale(ys) 106 | }) 107 | } 108 | 109 | func Benchmark_Dec_AddMixed(b *testing.B) { 110 | doBenchmarkDec2(b, func(x, y *Dec) { 111 | _ = new(Dec).Add(x, y) 112 | }) 113 | } 114 | 115 | func Benchmark_Dec_Sub(b *testing.B) { 116 | doBenchmarkDec2(b, func(x, y *Dec) { 117 | ys := y.Scale() 118 | y.SetScale(x.Scale()) 119 | _ = new(Dec).Sub(x, y) 120 | y.SetScale(ys) 121 | }) 122 | } 123 | 124 | func Benchmark_Dec_SubMixed(b *testing.B) { 125 | doBenchmarkDec2(b, func(x, y *Dec) { 126 | _ = new(Dec).Sub(x, y) 127 | }) 128 | } 129 | 130 | func Benchmark_Dec_Mul(b *testing.B) { 131 | doBenchmarkDec2(b, func(x, y *Dec) { 132 | _ = new(Dec).Mul(x, y) 133 | }) 134 | } 135 | 136 | func Benchmark_Dec_Mul_QuoExact(b *testing.B) { 137 | doBenchmarkDec2(b, func(x, y *Dec) { 138 | v := new(Dec).Mul(x, y) 139 | _ = new(Dec).QuoExact(v, y) 140 | }) 141 | } 142 | 143 | func Benchmark_Dec_QuoRound_Fixed_Down(b *testing.B) { 144 | doBenchmarkDec2(b, func(x, y *Dec) { 145 | _ = new(Dec).QuoRound(x, y, 0, RoundDown) 146 | }) 147 | } 148 | 149 | func Benchmark_Dec_QuoRound_Fixed_HalfUp(b *testing.B) { 150 | doBenchmarkDec2(b, func(x, y *Dec) { 151 | _ = new(Dec).QuoRound(x, y, 0, RoundHalfUp) 152 | }) 153 | } 154 | 155 | func Benchmark_Int_String(b *testing.B) { 156 | doBenchmarkInt1(b, func(x *big.Int) { 157 | x.String() 158 | }) 159 | } 160 | 161 | func Benchmark_Int_StringScan(b *testing.B) { 162 | doBenchmarkInt1(b, func(x *big.Int) { 163 | s := x.String() 164 | d := new(big.Int) 165 | fmt.Sscan(s, d) 166 | }) 167 | } 168 | 169 | func Benchmark_Int_GobEncode(b *testing.B) { 170 | doBenchmarkInt1(b, func(x *big.Int) { 171 | x.GobEncode() 172 | }) 173 | } 174 | 175 | func Benchmark_Int_GobEnDecode(b *testing.B) { 176 | doBenchmarkInt1(b, func(x *big.Int) { 177 | g, _ := x.GobEncode() 178 | new(big.Int).GobDecode(g) 179 | }) 180 | } 181 | 182 | func Benchmark_Int_Add(b *testing.B) { 183 | doBenchmarkInt2(b, func(x, y *big.Int) { 184 | _ = new(big.Int).Add(x, y) 185 | }) 186 | } 187 | 188 | func Benchmark_Int_Sub(b *testing.B) { 189 | doBenchmarkInt2(b, func(x, y *big.Int) { 190 | _ = new(big.Int).Sub(x, y) 191 | }) 192 | } 193 | 194 | func Benchmark_Int_Mul(b *testing.B) { 195 | doBenchmarkInt2(b, func(x, y *big.Int) { 196 | _ = new(big.Int).Mul(x, y) 197 | }) 198 | } 199 | 200 | func Benchmark_Int_Quo(b *testing.B) { 201 | doBenchmarkInt2(b, func(x, y *big.Int) { 202 | _ = new(big.Int).Quo(x, y) 203 | }) 204 | } 205 | 206 | func Benchmark_Int_QuoRem(b *testing.B) { 207 | doBenchmarkInt2(b, func(x, y *big.Int) { 208 | _, _ = new(big.Int).QuoRem(x, y, new(big.Int)) 209 | }) 210 | } 211 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_go1_2_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.2 2 | 3 | package inf 4 | 5 | import ( 6 | "encoding" 7 | "encoding/json" 8 | "testing" 9 | ) 10 | 11 | var _ encoding.TextMarshaler = new(Dec) 12 | var _ encoding.TextUnmarshaler = new(Dec) 13 | 14 | type Obj struct { 15 | Val *Dec 16 | } 17 | 18 | func TestDecJsonMarshalUnmarshal(t *testing.T) { 19 | o := Obj{Val: NewDec(123, 2)} 20 | js, err := json.Marshal(o) 21 | if err != nil { 22 | t.Fatalf("json.Marshal(%v): got %v, want ok", o, err) 23 | } 24 | o2 := &Obj{} 25 | err = json.Unmarshal(js, o2) 26 | if err != nil { 27 | t.Fatalf("json.Unmarshal(%#q): got %v, want ok", js, err) 28 | } 29 | if o.Val.Scale() != o2.Val.Scale() || 30 | o.Val.UnscaledBig().Cmp(o2.Val.UnscaledBig()) != 0 { 31 | t.Fatalf("json.Unmarshal(json.Marshal(%v)): want %v, got %v", o, o, o2) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_internal_test.go: -------------------------------------------------------------------------------- 1 | package inf 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | ) 7 | 8 | var decQuoRemZZZ = []struct { 9 | z, x, y *Dec 10 | r *big.Rat 11 | srA, srB int 12 | }{ 13 | // basic examples 14 | {NewDec(1, 0), NewDec(2, 0), NewDec(2, 0), big.NewRat(0, 1), 0, 1}, 15 | {NewDec(15, 1), NewDec(3, 0), NewDec(2, 0), big.NewRat(0, 1), 0, 1}, 16 | {NewDec(1, 1), NewDec(1, 0), NewDec(10, 0), big.NewRat(0, 1), 0, 1}, 17 | {NewDec(0, 0), NewDec(2, 0), NewDec(3, 0), big.NewRat(2, 3), 1, 1}, 18 | {NewDec(0, 0), NewDec(2, 0), NewDec(6, 0), big.NewRat(1, 3), 1, 1}, 19 | {NewDec(1, 1), NewDec(2, 0), NewDec(12, 0), big.NewRat(2, 3), 1, 1}, 20 | 21 | // examples from the Go Language Specification 22 | {NewDec(1, 0), NewDec(5, 0), NewDec(3, 0), big.NewRat(2, 3), 1, 1}, 23 | {NewDec(-1, 0), NewDec(-5, 0), NewDec(3, 0), big.NewRat(-2, 3), -1, 1}, 24 | {NewDec(-1, 0), NewDec(5, 0), NewDec(-3, 0), big.NewRat(-2, 3), 1, -1}, 25 | {NewDec(1, 0), NewDec(-5, 0), NewDec(-3, 0), big.NewRat(2, 3), -1, -1}, 26 | } 27 | 28 | func TestDecQuoRem(t *testing.T) { 29 | for i, a := range decQuoRemZZZ { 30 | z, rA, rB := new(Dec), new(big.Int), new(big.Int) 31 | s := scaleQuoExact{}.Scale(a.x, a.y) 32 | z.quoRem(a.x, a.y, s, true, rA, rB) 33 | if a.z.Cmp(z) != 0 || a.r.Cmp(new(big.Rat).SetFrac(rA, rB)) != 0 { 34 | t.Errorf("#%d QuoRemZZZ got %v, %v, %v; expected %v, %v", i, z, rA, rB, a.z, a.r) 35 | } 36 | if a.srA != rA.Sign() || a.srB != rB.Sign() { 37 | t.Errorf("#%d QuoRemZZZ wrong signs, got %v, %v; expected %v, %v", i, rA.Sign(), rB.Sign(), a.srA, a.srB) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/example_test.go: -------------------------------------------------------------------------------- 1 | package inf_test 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | import "speter.net/go/exp/math/dec/inf" 9 | 10 | func ExampleDec_SetString() { 11 | d := new(inf.Dec) 12 | d.SetString("012345.67890") // decimal; leading 0 ignored; trailing 0 kept 13 | fmt.Println(d) 14 | // Output: 12345.67890 15 | } 16 | 17 | func ExampleDec_Scan() { 18 | // The Scan function is rarely used directly; 19 | // the fmt package recognizes it as an implementation of fmt.Scanner. 20 | d := new(inf.Dec) 21 | _, err := fmt.Sscan("184467440.73709551617", d) 22 | if err != nil { 23 | log.Println("error scanning value:", err) 24 | } else { 25 | fmt.Println(d) 26 | } 27 | // Output: 184467440.73709551617 28 | } 29 | 30 | func ExampleDec_QuoRound_scale2RoundDown() { 31 | // 10 / 3 is an infinite decimal; it has no exact Dec representation 32 | x, y := inf.NewDec(10, 0), inf.NewDec(3, 0) 33 | // use 2 digits beyond the decimal point, round towards 0 34 | z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundDown) 35 | fmt.Println(z) 36 | // Output: 3.33 37 | } 38 | 39 | func ExampleDec_QuoRound_scale2RoundCeil() { 40 | // -42 / 400 is an finite decimal with 3 digits beyond the decimal point 41 | x, y := inf.NewDec(-42, 0), inf.NewDec(400, 0) 42 | // use 2 digits beyond decimal point, round towards positive infinity 43 | z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundCeil) 44 | fmt.Println(z) 45 | // Output: -0.10 46 | } 47 | 48 | func ExampleDec_QuoExact_ok() { 49 | // 1 / 25 is a finite decimal; it has exact Dec representation 50 | x, y := inf.NewDec(1, 0), inf.NewDec(25, 0) 51 | z := new(inf.Dec).QuoExact(x, y) 52 | fmt.Println(z) 53 | // Output: 0.04 54 | } 55 | 56 | func ExampleDec_QuoExact_fail() { 57 | // 1 / 3 is an infinite decimal; it has no exact Dec representation 58 | x, y := inf.NewDec(1, 0), inf.NewDec(3, 0) 59 | z := new(inf.Dec).QuoExact(x, y) 60 | fmt.Println(z) 61 | // Output: 62 | } 63 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder.go: -------------------------------------------------------------------------------- 1 | package inf 2 | 3 | import ( 4 | "math/big" 5 | ) 6 | 7 | // Rounder represents a method for rounding the (possibly infinite decimal) 8 | // result of a division to a finite Dec. It is used by Dec.Round() and 9 | // Dec.Quo(). 10 | // 11 | // See the Example for results of using each Rounder with some sample values. 12 | // 13 | type Rounder rounder 14 | 15 | // See http://speleotrove.com/decimal/damodel.html#refround for more detailed 16 | // definitions of these rounding modes. 17 | var ( 18 | RoundDown Rounder // towards 0 19 | RoundUp Rounder // away from 0 20 | RoundFloor Rounder // towards -infinity 21 | RoundCeil Rounder // towards +infinity 22 | RoundHalfDown Rounder // to nearest; towards 0 if same distance 23 | RoundHalfUp Rounder // to nearest; away from 0 if same distance 24 | RoundHalfEven Rounder // to nearest; even last digit if same distance 25 | ) 26 | 27 | // RoundExact is to be used in the case when rounding is not necessary. 28 | // When used with Quo or Round, it returns the result verbatim when it can be 29 | // expressed exactly with the given precision, and it returns nil otherwise. 30 | // QuoExact is a shorthand for using Quo with RoundExact. 31 | var RoundExact Rounder 32 | 33 | type rounder interface { 34 | 35 | // When UseRemainder() returns true, the Round() method is passed the 36 | // remainder of the division, expressed as the numerator and denominator of 37 | // a rational. 38 | UseRemainder() bool 39 | 40 | // Round sets the rounded value of a quotient to z, and returns z. 41 | // quo is rounded down (truncated towards zero) to the scale obtained from 42 | // the Scaler in Quo(). 43 | // 44 | // When the remainder is not used, remNum and remDen are nil. 45 | // When used, the remainder is normalized between -1 and 1; that is: 46 | // 47 | // -|remDen| < remNum < |remDen| 48 | // 49 | // remDen has the same sign as y, and remNum is zero or has the same sign 50 | // as x. 51 | Round(z, quo *Dec, remNum, remDen *big.Int) *Dec 52 | } 53 | 54 | type rndr struct { 55 | useRem bool 56 | round func(z, quo *Dec, remNum, remDen *big.Int) *Dec 57 | } 58 | 59 | func (r rndr) UseRemainder() bool { 60 | return r.useRem 61 | } 62 | 63 | func (r rndr) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec { 64 | return r.round(z, quo, remNum, remDen) 65 | } 66 | 67 | var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)} 68 | 69 | func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *big.Int) *Dec { 70 | return func(z, q *Dec, rA, rB *big.Int) *Dec { 71 | z.Set(q) 72 | brA, brB := rA.BitLen(), rB.BitLen() 73 | if brA < brB-1 { 74 | // brA < brB-1 => |rA| < |rB/2| 75 | return z 76 | } 77 | roundUp := false 78 | srA, srB := rA.Sign(), rB.Sign() 79 | s := srA * srB 80 | if brA == brB-1 { 81 | rA2 := new(big.Int).Lsh(rA, 1) 82 | if s < 0 { 83 | rA2.Neg(rA2) 84 | } 85 | roundUp = f(rA2.Cmp(rB)*srB, z.UnscaledBig().Bit(0)) 86 | } else { 87 | // brA > brB-1 => |rA| > |rB/2| 88 | roundUp = true 89 | } 90 | if roundUp { 91 | z.UnscaledBig().Add(z.UnscaledBig(), intSign[s+1]) 92 | } 93 | return z 94 | } 95 | } 96 | 97 | func init() { 98 | RoundExact = rndr{true, 99 | func(z, q *Dec, rA, rB *big.Int) *Dec { 100 | if rA.Sign() != 0 { 101 | return nil 102 | } 103 | return z.Set(q) 104 | }} 105 | RoundDown = rndr{false, 106 | func(z, q *Dec, rA, rB *big.Int) *Dec { 107 | return z.Set(q) 108 | }} 109 | RoundUp = rndr{true, 110 | func(z, q *Dec, rA, rB *big.Int) *Dec { 111 | z.Set(q) 112 | if rA.Sign() != 0 { 113 | z.UnscaledBig().Add(z.UnscaledBig(), intSign[rA.Sign()*rB.Sign()+1]) 114 | } 115 | return z 116 | }} 117 | RoundFloor = rndr{true, 118 | func(z, q *Dec, rA, rB *big.Int) *Dec { 119 | z.Set(q) 120 | if rA.Sign()*rB.Sign() < 0 { 121 | z.UnscaledBig().Add(z.UnscaledBig(), intSign[0]) 122 | } 123 | return z 124 | }} 125 | RoundCeil = rndr{true, 126 | func(z, q *Dec, rA, rB *big.Int) *Dec { 127 | z.Set(q) 128 | if rA.Sign()*rB.Sign() > 0 { 129 | z.UnscaledBig().Add(z.UnscaledBig(), intSign[2]) 130 | } 131 | return z 132 | }} 133 | RoundHalfDown = rndr{true, roundHalf( 134 | func(c int, odd uint) bool { 135 | return c > 0 136 | })} 137 | RoundHalfUp = rndr{true, roundHalf( 138 | func(c int, odd uint) bool { 139 | return c >= 0 140 | })} 141 | RoundHalfEven = rndr{true, roundHalf( 142 | func(c int, odd uint) bool { 143 | return c > 0 || c == 0 && odd == 1 144 | })} 145 | } 146 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder_example_test.go: -------------------------------------------------------------------------------- 1 | package inf_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "text/tabwriter" 7 | 8 | "speter.net/go/exp/math/dec/inf" 9 | ) 10 | 11 | // This example displays the results of Dec.Round with each of the Rounders. 12 | // 13 | func ExampleRounder() { 14 | var vals = []struct { 15 | x string 16 | s inf.Scale 17 | }{ 18 | {"-0.18", 1}, {"-0.15", 1}, {"-0.12", 1}, {"-0.10", 1}, 19 | {"-0.08", 1}, {"-0.05", 1}, {"-0.02", 1}, {"0.00", 1}, 20 | {"0.02", 1}, {"0.05", 1}, {"0.08", 1}, {"0.10", 1}, 21 | {"0.12", 1}, {"0.15", 1}, {"0.18", 1}, 22 | } 23 | 24 | var rounders = []struct { 25 | name string 26 | rounder inf.Rounder 27 | }{ 28 | {"RoundDown", inf.RoundDown}, {"RoundUp", inf.RoundUp}, 29 | {"RoundCeil", inf.RoundCeil}, {"RoundFloor", inf.RoundFloor}, 30 | {"RoundHalfDown", inf.RoundHalfDown}, {"RoundHalfUp", inf.RoundHalfUp}, 31 | {"RoundHalfEven", inf.RoundHalfEven}, {"RoundExact", inf.RoundExact}, 32 | } 33 | 34 | fmt.Println("The results of new(inf.Dec).Round(x, s, inf.RoundXXX):\n") 35 | w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight) 36 | fmt.Fprint(w, "x\ts\t|\t") 37 | for _, r := range rounders { 38 | fmt.Fprintf(w, "%s\t", r.name[5:]) 39 | } 40 | fmt.Fprintln(w) 41 | for _, v := range vals { 42 | fmt.Fprintf(w, "%s\t%d\t|\t", v.x, v.s) 43 | for _, r := range rounders { 44 | x, _ := new(inf.Dec).SetString(v.x) 45 | z := new(inf.Dec).Round(x, v.s, r.rounder) 46 | fmt.Fprintf(w, "%d\t", z) 47 | } 48 | fmt.Fprintln(w) 49 | } 50 | w.Flush() 51 | 52 | // Output: 53 | // The results of new(inf.Dec).Round(x, s, inf.RoundXXX): 54 | // 55 | // x s | Down Up Ceil Floor HalfDown HalfUp HalfEven Exact 56 | // -0.18 1 | -0.1 -0.2 -0.1 -0.2 -0.2 -0.2 -0.2 57 | // -0.15 1 | -0.1 -0.2 -0.1 -0.2 -0.1 -0.2 -0.2 58 | // -0.12 1 | -0.1 -0.2 -0.1 -0.2 -0.1 -0.1 -0.1 59 | // -0.10 1 | -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 60 | // -0.08 1 | 0.0 -0.1 0.0 -0.1 -0.1 -0.1 -0.1 61 | // -0.05 1 | 0.0 -0.1 0.0 -0.1 0.0 -0.1 0.0 62 | // -0.02 1 | 0.0 -0.1 0.0 -0.1 0.0 0.0 0.0 63 | // 0.00 1 | 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 64 | // 0.02 1 | 0.0 0.1 0.1 0.0 0.0 0.0 0.0 65 | // 0.05 1 | 0.0 0.1 0.1 0.0 0.0 0.1 0.0 66 | // 0.08 1 | 0.0 0.1 0.1 0.0 0.1 0.1 0.1 67 | // 0.10 1 | 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 68 | // 0.12 1 | 0.1 0.2 0.2 0.1 0.1 0.1 0.1 69 | // 0.15 1 | 0.1 0.2 0.2 0.1 0.1 0.2 0.2 70 | // 0.18 1 | 0.1 0.2 0.2 0.1 0.2 0.2 0.2 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder_test.go: -------------------------------------------------------------------------------- 1 | package inf_test 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "speter.net/go/exp/math/dec/inf" 8 | ) 9 | 10 | var decRounderInputs = [...]struct { 11 | quo *inf.Dec 12 | rA, rB *big.Int 13 | }{ 14 | // examples from go language spec 15 | {inf.NewDec(1, 0), big.NewInt(2), big.NewInt(3)}, // 5 / 3 16 | {inf.NewDec(-1, 0), big.NewInt(-2), big.NewInt(3)}, // -5 / 3 17 | {inf.NewDec(-1, 0), big.NewInt(2), big.NewInt(-3)}, // 5 / -3 18 | {inf.NewDec(1, 0), big.NewInt(-2), big.NewInt(-3)}, // -5 / -3 19 | // examples from godoc 20 | {inf.NewDec(-1, 1), big.NewInt(-8), big.NewInt(10)}, 21 | {inf.NewDec(-1, 1), big.NewInt(-5), big.NewInt(10)}, 22 | {inf.NewDec(-1, 1), big.NewInt(-2), big.NewInt(10)}, 23 | {inf.NewDec(0, 1), big.NewInt(-8), big.NewInt(10)}, 24 | {inf.NewDec(0, 1), big.NewInt(-5), big.NewInt(10)}, 25 | {inf.NewDec(0, 1), big.NewInt(-2), big.NewInt(10)}, 26 | {inf.NewDec(0, 1), big.NewInt(0), big.NewInt(1)}, 27 | {inf.NewDec(0, 1), big.NewInt(2), big.NewInt(10)}, 28 | {inf.NewDec(0, 1), big.NewInt(5), big.NewInt(10)}, 29 | {inf.NewDec(0, 1), big.NewInt(8), big.NewInt(10)}, 30 | {inf.NewDec(1, 1), big.NewInt(2), big.NewInt(10)}, 31 | {inf.NewDec(1, 1), big.NewInt(5), big.NewInt(10)}, 32 | {inf.NewDec(1, 1), big.NewInt(8), big.NewInt(10)}, 33 | } 34 | 35 | var decRounderResults = [...]struct { 36 | rounder inf.Rounder 37 | results [len(decRounderInputs)]*inf.Dec 38 | }{ 39 | {inf.RoundExact, [...]*inf.Dec{nil, nil, nil, nil, 40 | nil, nil, nil, nil, nil, nil, 41 | inf.NewDec(0, 1), nil, nil, nil, nil, nil, nil}}, 42 | {inf.RoundDown, [...]*inf.Dec{ 43 | inf.NewDec(1, 0), inf.NewDec(-1, 0), inf.NewDec(-1, 0), inf.NewDec(1, 0), 44 | inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1), 45 | inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1), 46 | inf.NewDec(0, 1), 47 | inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1), 48 | inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1)}}, 49 | {inf.RoundUp, [...]*inf.Dec{ 50 | inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0), 51 | inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-2, 1), 52 | inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1), 53 | inf.NewDec(0, 1), 54 | inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1), 55 | inf.NewDec(2, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}}, 56 | {inf.RoundHalfDown, [...]*inf.Dec{ 57 | inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0), 58 | inf.NewDec(-2, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1), 59 | inf.NewDec(-1, 1), inf.NewDec(0, 1), inf.NewDec(0, 1), 60 | inf.NewDec(0, 1), 61 | inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(1, 1), 62 | inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(2, 1)}}, 63 | {inf.RoundHalfUp, [...]*inf.Dec{ 64 | inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0), 65 | inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-1, 1), 66 | inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(0, 1), 67 | inf.NewDec(0, 1), 68 | inf.NewDec(0, 1), inf.NewDec(1, 1), inf.NewDec(1, 1), 69 | inf.NewDec(1, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}}, 70 | {inf.RoundHalfEven, [...]*inf.Dec{ 71 | inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0), 72 | inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-1, 1), 73 | inf.NewDec(-1, 1), inf.NewDec(0, 1), inf.NewDec(0, 1), 74 | inf.NewDec(0, 1), 75 | inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(1, 1), 76 | inf.NewDec(1, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}}, 77 | {inf.RoundFloor, [...]*inf.Dec{ 78 | inf.NewDec(1, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(1, 0), 79 | inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-2, 1), 80 | inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1), 81 | inf.NewDec(0, 1), 82 | inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1), 83 | inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1)}}, 84 | {inf.RoundCeil, [...]*inf.Dec{ 85 | inf.NewDec(2, 0), inf.NewDec(-1, 0), inf.NewDec(-1, 0), inf.NewDec(2, 0), 86 | inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1), 87 | inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1), 88 | inf.NewDec(0, 1), 89 | inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1), 90 | inf.NewDec(2, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}}, 91 | } 92 | 93 | func TestDecRounders(t *testing.T) { 94 | for i, a := range decRounderResults { 95 | for j, input := range decRounderInputs { 96 | q := new(inf.Dec).Set(input.quo) 97 | rA, rB := new(big.Int).Set(input.rA), new(big.Int).Set(input.rB) 98 | res := a.rounder.Round(new(inf.Dec), q, rA, rB) 99 | if a.results[j] == nil && res == nil { 100 | continue 101 | } 102 | if (a.results[j] == nil && res != nil) || 103 | (a.results[j] != nil && res == nil) || 104 | a.results[j].Cmp(res) != 0 { 105 | t.Errorf("#%d,%d Rounder got %v; expected %v", i, j, res, a.results[j]) 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Elv Technology AS (https://elv.technology/) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of the FreeBSD Project. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #GoCqlTable 2 | 3 | GoCqlTable is a wrapper around the GoCql-driver that seeks to simplify working with the Cassandra database in Golang projects. 4 | 5 | The project consists of two packages you need to know about: 6 | 7 | 1. gocqltable (the base) – Contains wrapper objects for working with Keyspaces and Tables. Simplifies creating, dropping and querying. Returns rowset's as ```[]interface{}```, that should be type asserted to the row model struct. 8 | 1. recipes – Contains code that extends the table implementation by adding more functionality. The recipes. CRUD type implements a simple object relational mapper (ORM) that makes it simple to Insert/Update/Get/Delete, and for compound clustering theres even List/Range methods that allow you to filter your results on range columns. 9 | 10 | _Note: The project is very much in development, and may not be stable enough for production use. The API may change without notice._ 11 | 12 | #### [Documentation](https://godoc.org/github.com/elvtechnology/gocqltable) 13 | 14 | ## Examples of use 15 | 16 | For extensive examples see the examples folder. The base-example shows you the base API. The CRUD example takes you through most of the ORM functionality. 17 | 18 | ### For a quick taste (using recipes.CRUD): 19 | 20 | ``` go 21 | 22 | // Generic initialization of gocql 23 | c := gocql.NewCluster("127.0.0.1") 24 | s, err := c.CreateSession() 25 | if err != nil { 26 | log.Fatalln("Unable to open up a session with the Cassandra database (err=" + err.Error() + ")") 27 | } 28 | 29 | // Tell gocqltable to use this session object as the default for new objects 30 | gocqltable.SetDefaultSession(s) 31 | 32 | 33 | // Now we're ready to create our first keyspace. We start by getting a keyspace object 34 | keyspace := gocqltable.NewKeyspace("gocqltable_test") 35 | 36 | // Now lets create that in the database using the simple strategy and durable writes (true) 37 | err = keyspace.Create(map[string]interface{}{ 38 | "class": "SimpleStrategy", 39 | "replication_factor": 1, 40 | }, true) 41 | if err != nil { // If something went wrong we print the error and quit. 42 | log.Fatalln(err) 43 | } 44 | 45 | 46 | // Now that we have a very own keyspace to play with, lets create our first table. 47 | 48 | // First we need a Row-object to base the table on. It will later be passed to the table wrapper 49 | // to be used for returning row-objects as the answer to fetch requests. 50 | type User struct{ 51 | Email string // Our primary key 52 | Password string `password` // Use Tags to rename fields 53 | Active bool `cql:"active"` // If there are multiple tags, use `cql:""` to specify what the table column will be 54 | Created time.Time 55 | } 56 | 57 | // Let's define and instantiate a table object for our user table 58 | userTable := struct{ 59 | recipes.CRUD // If you looked at the base example first, notice we replaced this line with the recipe 60 | }{ 61 | recipes.CRUD{ // Here we didn't replace, but rather wrapped the table object in our recipe, effectively adding more methods to the end API 62 | keyspace.NewTable( 63 | "users", // The table name 64 | []string{"email"}, // Row keys 65 | nil, // Range keys 66 | User{}, // We pass an instance of the user struct that will be used as a type template during fetches. 67 | ), 68 | }, 69 | } 70 | 71 | // Lets create this table in our cassandra database 72 | err = userTable.Create() 73 | if err != nil { 74 | log.Fatalln(err) 75 | } 76 | 77 | 78 | // Now that we have a keyspace with a table in it: lets make a few rows! In the base example we had to write out the CQL manually, this time 79 | // around, however, we can insert entire User objects. 80 | 81 | // Lets instantiate a user object, set its values and insert it 82 | user1 := User{ 83 | Email: "1@example.com", 84 | Password: "123456", 85 | Active: true, 86 | Created: time.Now().UTC(), 87 | } 88 | err = userTable.Insert(user1) 89 | if err != nil { 90 | log.Fatalln(err) 91 | } 92 | 93 | 94 | // With our database filled up with users, lets query it and print out the results (containing all users in the database). 95 | rowset, err := userTable.List() // Our rowset variable is a "interface{}", and here we type assert it to a slice of pointers to "User" 96 | for _, user := range rowset.([]*User) { 97 | fmt.Println(user) 98 | } 99 | if err != nil { 100 | log.Fatalln(err) 101 | } 102 | 103 | 104 | // You can also fetch a single row, obviously 105 | row, err := userTable.Get("1@example.com") 106 | if err != nil { 107 | log.Fatalln(err) 108 | } 109 | user := row.(*User) 110 | 111 | 112 | // Lets update this user by changing his password 113 | user.Password = "654321" 114 | err = userTable.Update(user) 115 | if err != nil { 116 | log.Fatalln(err) 117 | } 118 | 119 | 120 | // Lets delete user 1@example.com 121 | err = userTable.Delete(user) 122 | if err != nil { 123 | log.Fatalln(err) 124 | } 125 | 126 | // Lets clean up after ourselves by dropping the keyspace. 127 | keyspace.Drop() 128 | 129 | ``` 130 | -------------------------------------------------------------------------------- /examples/base/base.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/kristoiv/gocqltable" 9 | "github.com/gocql/gocql" 10 | ) 11 | 12 | func main() { 13 | 14 | // Generic initialization of gocql 15 | c := gocql.NewCluster("127.0.0.1") 16 | s, err := c.CreateSession() 17 | if err != nil { 18 | log.Fatalln("Unable to open up a session with the Cassandra database (err=" + err.Error() + ")") 19 | } 20 | 21 | // Tell gocqltable to use this session object as the default for new objects 22 | gocqltable.SetDefaultSession(s) 23 | fmt.Println("Gocql session setup complete") 24 | 25 | // Now we're ready to create our first keyspace. We start by getting a keyspace object 26 | keyspace := gocqltable.NewKeyspace("gocqltable_test") 27 | 28 | // Now lets create that in the database using the simple strategy and durable writes (true) 29 | err = keyspace.Create(map[string]interface{}{ 30 | "class": "SimpleStrategy", 31 | "replication_factor": 1, 32 | }, true) 33 | if err != nil { // If something went wrong we print the error and quit. 34 | log.Fatalln(err) 35 | } 36 | fmt.Println("Keyspace created") 37 | 38 | // Now that we have a very own keyspace to play with, lets create our first table. 39 | 40 | // First we need a Row-object to base the table on. It will later be passed to the table wrapper 41 | // to be used for returning row-objects as the answer to fetch requests. 42 | type User struct { 43 | Email string // Our primary key 44 | Password string 45 | Active bool 46 | Created time.Time 47 | } 48 | 49 | // Let's define and instantiate a table object for our user table 50 | userTable := struct { 51 | gocqltable.Table 52 | }{ 53 | keyspace.NewTable( 54 | "users", // The table name 55 | []string{"email"}, // Row keys 56 | nil, // Range keys 57 | User{}, // We pass an instance of the user struct that will be used as a type template during fetches. 58 | ), 59 | } 60 | 61 | // Lets create this table in our cassandra database 62 | err = userTable.Create() 63 | if err != nil { 64 | log.Fatalln(err) 65 | } 66 | fmt.Println("") 67 | fmt.Println("Table created: users") 68 | 69 | // Now that we have a keyspace with a table in it: lets make a few rows! Notice that this is the base example, it uses CQL (not ORM) 70 | // for database interactions such as INSERT/SELECT/UPDATE/DELETE. 71 | err = userTable.Query("INSERT INTO gocqltable_test.users (email, password, active, created) VALUES (?, ?, ?, ?)", "1@example.com", "123456", true, time.Now().UTC()).Exec() 72 | if err != nil { 73 | log.Fatalln(err) 74 | } 75 | fmt.Println("User inserted: 1@example.com") 76 | 77 | err = userTable.Query("INSERT INTO gocqltable_test.users (email, password, active, created) VALUES (?, ?, ?, ?)", "2@example.com", "123456", true, time.Now().UTC()).Exec() 78 | if err != nil { 79 | log.Fatalln(err) 80 | } 81 | fmt.Println("User inserted: 2@example.com") 82 | 83 | err = userTable.Query("INSERT INTO gocqltable_test.users (email, password, active, created) VALUES (?, ?, ?, ?)", "3@example.com", "123456", true, time.Now().UTC()).Exec() 84 | if err != nil { 85 | log.Fatalln(err) 86 | } 87 | fmt.Println("User inserted: 3@example.com") 88 | 89 | // With our database filled up with users, lets query it and print out the results. 90 | iter := userTable.Query("SELECT * FROM gocqltable_test.users").Fetch() 91 | fmt.Println("") 92 | fmt.Println("Fetched all from users:") 93 | for row := range iter.Range() { 94 | user := row.(*User) // Our row variable is a pointer to "interface{}", and here we type assert it to a pointer to "User" 95 | fmt.Println("User:", user) // Let's just print that 96 | } 97 | if err := iter.Close(); err != nil { 98 | log.Fatalln(err) 99 | } 100 | 101 | // You can also fetch a single row, obviously 102 | row, err := userTable.Query(`SELECT * FROM gocqltable_test.users WHERE email = ? LIMIT 1`, "2@example.com").FetchRow() 103 | if err != nil { 104 | log.Fatalln(err) 105 | } 106 | user := row.(*User) 107 | fmt.Println("") 108 | fmt.Println("Fetched single row by email: ", user) 109 | 110 | // Lets clean up after ourselves by dropping the keyspace. 111 | keyspace.Drop() 112 | fmt.Println("") 113 | fmt.Println("Keyspace dropped") 114 | 115 | } 116 | -------------------------------------------------------------------------------- /examples/crud/crud.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/kristoiv/gocqltable" 9 | "github.com/kristoiv/gocqltable/recipes" 10 | "github.com/gocql/gocql" 11 | ) 12 | 13 | func main() { 14 | 15 | // Generic initialization of gocql 16 | c := gocql.NewCluster("127.0.0.1") 17 | s, err := c.CreateSession() 18 | if err != nil { 19 | log.Fatalln("Unable to open up a session with the Cassandra database (err=" + err.Error() + ")") 20 | } 21 | 22 | // Tell gocqltable to use this session object as the default for new objects 23 | gocqltable.SetDefaultSession(s) 24 | fmt.Println("Gocql session setup complete") 25 | 26 | // Now we're ready to create our first keyspace. We start by getting a keyspace object 27 | keyspace := gocqltable.NewKeyspace("gocqltable_test") 28 | 29 | // Now lets create that in the database using the simple strategy and durable writes (true) 30 | err = keyspace.Create(map[string]interface{}{ 31 | "class": "SimpleStrategy", 32 | "replication_factor": 1, 33 | }, true) 34 | if err != nil { // If something went wrong we print the error and quit. 35 | log.Fatalln(err) 36 | } 37 | fmt.Println("Keyspace created") 38 | 39 | // Now that we have a very own keyspace to play with, lets create our first table. 40 | 41 | // First we need a Row-object to base the table on. It will later be passed to the table wrapper 42 | // to be used for returning row-objects as the answer to fetch requests. 43 | type User struct { 44 | Email string // Our primary key 45 | Password string `password` // Use Tags to rename fields 46 | Active bool `cql:"active"` // If there are multiple tags, use `cql:""` to specify what the table column will be 47 | Created time.Time 48 | } 49 | 50 | // Let's define and instantiate a table object for our user table 51 | userTable := struct { 52 | recipes.CRUD // If you looked at the base example first, notice we replaced this line with the recipe 53 | }{ 54 | recipes.CRUD{ // Here we didn't replace, but rather wrapped the table object in our recipe, effectively adding more methods to the end API 55 | keyspace.NewTable( 56 | "users", // The table name 57 | []string{"email"}, // Row keys 58 | nil, // Range keys 59 | User{}, // We pass an instance of the user struct that will be used as a type template during fetches. 60 | ), 61 | }, 62 | } 63 | 64 | // Lets create this table in our cassandra database 65 | err = userTable.Create() 66 | if err != nil { 67 | log.Fatalln(err) 68 | } 69 | fmt.Println("") 70 | fmt.Println("Table created: users") 71 | 72 | // Now that we have a keyspace with a table in it: lets make a few rows! In the base example we had to write out the CQL manually, this time 73 | // around, however, we can insert entire User objects. 74 | 75 | // Lets instantiate a user object, set its values and insert it 76 | user1 := User{ 77 | Email: "1@example.com", 78 | Password: "123456", 79 | Active: true, 80 | Created: time.Now().UTC(), 81 | } 82 | err = userTable.Insert(user1) 83 | if err != nil { 84 | log.Fatalln(err) 85 | } 86 | fmt.Println("User inserted:", user1) 87 | 88 | // And again for our next user. 89 | user2 := User{ 90 | Email: "2@example.com", 91 | Password: "123456", 92 | Active: true, 93 | Created: time.Now().UTC(), 94 | } 95 | err = userTable.Insert(user2) 96 | if err != nil { 97 | log.Fatalln(err) 98 | } 99 | fmt.Println("User inserted:", user2) 100 | 101 | // And finally again for the third user. 102 | user3 := User{ 103 | Email: "3@example.com", 104 | Password: "123456", 105 | Active: true, 106 | Created: time.Now().UTC(), 107 | } 108 | err = userTable.Insert(user3) 109 | if err != nil { 110 | log.Fatalln(err) 111 | } 112 | fmt.Println("User inserted:", user3) 113 | 114 | // With our database filled up with users, lets query it and print out the results (containing all users in the database). 115 | rowset, err := userTable.List() 116 | fmt.Println("") 117 | fmt.Println("Fetched all from users:") 118 | for _, user := range rowset.([]*User) { 119 | // user := row.(*User) // Our row variable is a pointer to "interface{}", and here we type assert it to a pointer to "User" 120 | fmt.Println("User: ", user) // Let's just print that 121 | } 122 | if err != nil { 123 | log.Fatalln(err) 124 | } 125 | 126 | // You can also fetch a single row, obviously 127 | row, err := userTable.Get("2@example.com") 128 | if err != nil { 129 | log.Fatalln(err) 130 | } 131 | user := row.(*User) 132 | fmt.Println("") 133 | fmt.Println("Fetched single row by email: ", user) 134 | 135 | // Lets update this user by changing his password 136 | user.Password = "654321" 137 | err = userTable.Update(user) 138 | if err != nil { 139 | log.Fatalln(err) 140 | } 141 | fmt.Println("") 142 | fmt.Println("Updated user to:", user) 143 | 144 | // Lets delete user 3@example.com 145 | err = userTable.Delete(user3) 146 | if err != nil { 147 | log.Fatalln(err) 148 | } 149 | fmt.Println("Deleted user:", user3) 150 | 151 | // Lets print the final list of users 152 | rowset, err = userTable.List() 153 | fmt.Println("") 154 | fmt.Println("Final list of users:") 155 | for _, user := range rowset.([]*User) { 156 | // user := row.(*User) // Our row variable is a pointer to "interface{}", and here we type assert it to a pointer to "User" 157 | fmt.Println("User: ", user) // Let's just print that 158 | } 159 | if err != nil { 160 | log.Fatalln(err) 161 | } 162 | 163 | // Now lets get a little more advanced in our data modelling. Lets do a range-model with log items belonging to one of our users 164 | // and do range scans on them using the high-level API in gocqltable 165 | 166 | // First we need a structure to represent these log items 167 | type UserLog struct { 168 | Email string // Row key part of our primary key 169 | Id gocql.UUID // Range key part of our primary key. Will use UUID version 1 (timeuuid). 170 | Data int // The data we will log (in this case a integer value) 171 | } 172 | 173 | // Then we need a new table to hold these log items 174 | userLogTable := struct { 175 | recipes.CRUD 176 | }{ 177 | recipes.CRUD{ 178 | keyspace.NewTable( 179 | "user_logs", 180 | []string{"email"}, 181 | []string{"id"}, 182 | UserLog{}, 183 | ), 184 | }, 185 | } 186 | 187 | // Lets create this table in the database 188 | err = userLogTable.Create() 189 | if err != nil { 190 | log.Fatalln(err) 191 | } 192 | fmt.Println("") 193 | fmt.Println("Table created: user_logs") 194 | 195 | // Then we populate it with example log data 196 | log1 := UserLog{ 197 | Email: "1@example.com", 198 | Id: gocql.TimeUUID(), 199 | Data: 1, 200 | } 201 | err = userLogTable.Insert(log1) 202 | if err != nil { 203 | log.Fatalln(err) 204 | } 205 | fmt.Println("Log inserted:", log1) 206 | 207 | log2 := UserLog{ 208 | Email: "2@example.com", 209 | Id: gocql.TimeUUID(), 210 | Data: 2, 211 | } 212 | err = userLogTable.Insert(log2) 213 | if err != nil { 214 | log.Fatalln(err) 215 | } 216 | fmt.Println("Log inserted:", log2) 217 | 218 | log3 := UserLog{ 219 | Email: "2@example.com", 220 | Id: gocql.TimeUUID(), 221 | Data: 3, 222 | } 223 | err = userLogTable.Insert(log3) 224 | if err != nil { 225 | log.Fatalln(err) 226 | } 227 | fmt.Println("Log inserted:", log3) 228 | 229 | log4 := UserLog{ 230 | Email: "2@example.com", 231 | Id: gocql.TimeUUID(), 232 | Data: 4, 233 | } 234 | err = userLogTable.Insert(log4) 235 | if err != nil { 236 | log.Fatalln(err) 237 | } 238 | fmt.Println("Log inserted:", log4) 239 | 240 | // Now finally lets do some range queries on our new table. 241 | rowset, err = userLogTable.Range().Fetch() // If we don't specify any ids in Range(ids...), then it will do a full Scan of the table 242 | if err != nil { 243 | log.Fatalln(err) 244 | } 245 | fmt.Println("") 246 | fmt.Println("Fetching all user logs:") 247 | for _, userLog := range rowset.([]*UserLog) { 248 | //userLog := row.(*UserLog) 249 | fmt.Println("UserLog: ", userLog) 250 | } 251 | 252 | rowset, err = userLogTable.Range("2@example.com").Fetch() // In this case we filter the result by row key and scan over all log items 253 | if err != nil { 254 | log.Fatalln(err) 255 | } 256 | fmt.Println("") 257 | fmt.Println("Fetching all user logs by user 2@example.com:") 258 | for _, userLog := range rowset.([]*UserLog) { 259 | // userLog := row.(*UserLog) 260 | fmt.Println("UserLog: ", userLog) 261 | } 262 | 263 | rowset, err = userLogTable.Range("2@example.com").MoreThanOrEqual("id", log3.Id).OrderBy("id DESC").Fetch() 264 | if err != nil { 265 | log.Fatalln(err) 266 | } 267 | fmt.Println("") 268 | fmt.Println("Fetching all user logs by user 2@example.com, where id >= log3.Id:") 269 | for _, userLog := range rowset.([]*UserLog) { 270 | //userLog := row.(*UserLog) 271 | fmt.Println("UserLog: ", userLog) 272 | } 273 | 274 | rowset, err = userLogTable.Range("2@example.com").MoreThan("id", log3.Id).Fetch() 275 | if err != nil { 276 | log.Fatalln(err) 277 | } 278 | fmt.Println("") 279 | fmt.Println("Fetching all user logs by user 2@example.com, where id > log3.Id:") 280 | for _, userLog := range rowset.([]*UserLog) { 281 | //userLog := row.(*UserLog) 282 | fmt.Println("UserLog: ", userLog) 283 | } 284 | 285 | rowset, err = userLogTable.Range("2@example.com").LessThan("id", log3.Id).Limit(10).Fetch() 286 | if err != nil { 287 | log.Fatalln(err) 288 | } 289 | fmt.Println("") 290 | fmt.Println("Fetching all user logs by user 2@example.com, where id < log3.Id:") 291 | for _, userLog := range rowset.([]*UserLog) { 292 | // userLog := row.(*UserLog) 293 | fmt.Println("UserLog: ", userLog) 294 | } 295 | 296 | // Lets clean up after ourselves by dropping the keyspace. 297 | keyspace.Drop() 298 | fmt.Println("") 299 | fmt.Println("Keyspace dropped") 300 | 301 | } 302 | -------------------------------------------------------------------------------- /keyspace.go: -------------------------------------------------------------------------------- 1 | package gocqltable 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/gocql/gocql" 9 | ) 10 | 11 | var ( 12 | defaultSession *gocql.Session 13 | ) 14 | 15 | func SetDefaultSession(s *gocql.Session) { 16 | defaultSession = s 17 | } 18 | 19 | type KeyspaceInterface interface { 20 | Name() string 21 | Session() *gocql.Session 22 | } 23 | 24 | type Keyspace struct { 25 | name string 26 | session *gocql.Session 27 | } 28 | 29 | func NewKeyspace(name string) Keyspace { 30 | return Keyspace{ 31 | name: name, 32 | session: defaultSession, 33 | } 34 | } 35 | 36 | func (ks Keyspace) Create(replication map[string]interface{}, durableWrites bool) error { 37 | 38 | if ks.session == nil { 39 | ks.session = defaultSession 40 | } 41 | 42 | replicationBytes, err := json.Marshal(replication) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | replicationMap := strings.Replace(string(replicationBytes), `"`, `'`, -1) 48 | 49 | durableWritesString := "false" 50 | if durableWrites { 51 | durableWritesString = "true" 52 | } 53 | 54 | return ks.session.Query(fmt.Sprintf(`CREATE KEYSPACE %q WITH REPLICATION = %s AND DURABLE_WRITES = %s`, ks.Name(), replicationMap, durableWritesString)).Exec() 55 | 56 | } 57 | 58 | func (ks Keyspace) Drop() error { 59 | if ks.session == nil { 60 | ks.session = defaultSession 61 | } 62 | return ks.session.Query(fmt.Sprintf(`DROP KEYSPACE %q`, ks.Name())).Exec() 63 | } 64 | 65 | func (ks Keyspace) Tables() ([]string, error) { 66 | if ks.session == nil { 67 | ks.session = defaultSession 68 | } 69 | var name string 70 | var resultSet []string 71 | iterator := ks.session.Query(`SELECT columnfamily_name FROM system.schema_columnfamilies WHERE keyspace_name = ?;`, ks.Name()).Iter() 72 | for iterator.Scan(&name) { 73 | resultSet = append(resultSet, name) 74 | } 75 | if err := iterator.Close(); err != nil { 76 | return nil, err 77 | } 78 | return resultSet, nil 79 | } 80 | 81 | func (ks Keyspace) NewTable(name string, rowKeys, rangeKeys []string, row interface{}) Table { 82 | if ks.session == nil { 83 | ks.session = defaultSession 84 | } 85 | return Table{ 86 | name: name, 87 | rowKeys: rowKeys, 88 | rangeKeys: rangeKeys, 89 | row: row, 90 | 91 | keyspace: ks, 92 | session: ks.session, 93 | } 94 | } 95 | 96 | func (ks Keyspace) Name() string { 97 | return ks.name 98 | } 99 | 100 | func (ks Keyspace) Session() *gocql.Session { 101 | if ks.session == nil { 102 | ks.session = defaultSession 103 | } 104 | return ks.session 105 | } 106 | 107 | func (ks *Keyspace) SetSession(session *gocql.Session) { 108 | ks.session = session 109 | } 110 | -------------------------------------------------------------------------------- /query.go: -------------------------------------------------------------------------------- 1 | package gocqltable 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | 7 | "github.com/gocql/gocql" 8 | 9 | r "github.com/kristoiv/gocqltable/reflect" 10 | ) 11 | 12 | type Query struct { 13 | Statement string 14 | Values []interface{} 15 | 16 | Table Table 17 | Session *gocql.Session 18 | } 19 | 20 | func (q Query) FetchRow() (interface{}, error) { 21 | iter := q.Fetch() 22 | row := iter.Next() 23 | if err := iter.Close(); err != nil { 24 | return nil, err 25 | } 26 | if row == nil { 27 | return nil, gocql.ErrNotFound 28 | } 29 | return row, nil 30 | } 31 | 32 | func (q Query) Fetch() *Iterator { 33 | iter := q.Session.Query(q.Statement, q.Values...).Iter() 34 | return &Iterator{ 35 | iter: iter, 36 | row: q.Table.Row(), 37 | } 38 | } 39 | 40 | func (q Query) Exec() error { 41 | return q.Session.Query(q.Statement, q.Values...).Exec() 42 | } 43 | 44 | type Iterator struct { 45 | iter *gocql.Iter 46 | row interface{} 47 | 48 | done chan bool 49 | } 50 | 51 | func (i *Iterator) Next() interface{} { 52 | m := make(map[string]interface{}) 53 | if i.iter.MapScan(m) == false { 54 | return nil 55 | } 56 | t := reflect.TypeOf(i.row) 57 | v := reflect.New(t) 58 | r.MapToStruct(m, v.Interface()) 59 | r.MapToStruct(ucfirstKeys(m), v.Interface()) 60 | return v.Interface() 61 | } 62 | 63 | func (i *Iterator) Range() <-chan interface{} { 64 | rangeChan := make(chan interface{}) 65 | done := make(chan bool) 66 | i.done = done 67 | go func() { 68 | for { 69 | next := i.Next() 70 | if next == nil { // We clean up when we're done 71 | close(rangeChan) 72 | return 73 | } 74 | select { 75 | case <-done: // We clean up when we're done 76 | close(rangeChan) 77 | return 78 | case rangeChan <- next: 79 | } 80 | } 81 | }() 82 | return rangeChan 83 | } 84 | 85 | func (i *Iterator) Close() error { 86 | if done := i.done; done != nil { 87 | close(i.done) 88 | i.done = nil 89 | } 90 | return i.iter.Close() 91 | } 92 | 93 | func ucfirst(s string) string { 94 | if len(s) < 2 { 95 | return strings.ToUpper(s) 96 | } 97 | return strings.ToUpper(s[0:1]) + s[1:] 98 | } 99 | 100 | func ucfirstKeys(s map[string]interface{}) map[string]interface{} { 101 | result := make(map[string]interface{}) 102 | for key, value := range s { 103 | result[ucfirst(key)] = value 104 | } 105 | return result 106 | } 107 | -------------------------------------------------------------------------------- /recipes/crud.go: -------------------------------------------------------------------------------- 1 | package recipes 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "strconv" 8 | "strings" 9 | "reflect" 10 | "time" 11 | 12 | "github.com/kristoiv/gocqltable" 13 | 14 | r "github.com/kristoiv/gocqltable/reflect" 15 | ) 16 | 17 | type RangeInterface interface { 18 | LessThan(rangeKey string, value interface{}) RangeInterface 19 | LessThanOrEqual(rangeKey string, value interface{}) RangeInterface 20 | MoreThan(rangeKey string, value interface{}) RangeInterface 21 | MoreThanOrEqual(rangeKey string, value interface{}) RangeInterface 22 | EqualTo(rangeKey string, value interface{}) RangeInterface 23 | OrderBy(fieldAndDirection string) RangeInterface 24 | Limit(l int) RangeInterface 25 | Select(s []string) RangeInterface 26 | WhereIn(m map[string][]string) RangeInterface 27 | Fetch() (interface{}, error) 28 | } 29 | 30 | type CRUD struct { 31 | gocqltable.TableInterface 32 | } 33 | 34 | func (t CRUD) Insert(row interface{}) error { 35 | return t.insert(row, nil) 36 | } 37 | 38 | func (t CRUD) InsertWithTTL(row interface{}, ttl *time.Time) error { 39 | return t.insert(row, ttl) 40 | } 41 | 42 | func (t CRUD) insert(row interface{}, ttl *time.Time) error { 43 | 44 | rowKeys := t.RowKeys() 45 | rangeKeys := t.RangeKeys() 46 | 47 | // TODO: Test and remove 48 | // where := []string{} 49 | // for _, key := range append(rowKeys, rangeKeys...) { 50 | // where = append(where, key+" = ?") 51 | // } 52 | 53 | m, ok := r.StructToMap(row) 54 | if !ok { 55 | panic("Unable to get map from struct during update") 56 | } 57 | 58 | fields := []string{} 59 | placeholders := []string{} 60 | vals := []interface{}{} 61 | for key, value := range m { 62 | // Check for empty row- or range keys 63 | for _, rowKey := range append(rowKeys, rangeKeys...) { 64 | if strings.ToLower(key) == strings.ToLower(rowKey) { 65 | if value == nil { 66 | return errors.New(fmt.Sprintf("Inserting row failed due to missing key value (for key %q)", rowKey)) 67 | } 68 | break 69 | } 70 | } 71 | // Append to insertion slices 72 | fields = append(fields, strings.ToLower(fmt.Sprintf("%q", key))) 73 | placeholders = append(placeholders, "?") 74 | vals = append(vals, value) 75 | } 76 | 77 | options := "" 78 | if ttl != nil { 79 | options = "USING TTL ?" 80 | vals = append(vals, int(ttl.Sub(time.Now().UTC()).Seconds()+.5)) 81 | } 82 | 83 | err := t.Query(fmt.Sprintf(`INSERT INTO %q.%q (%s) VALUES (%s) %s`, t.Keyspace().Name(), t.Name(), strings.Join(fields, ", "), strings.Join(placeholders, ", "), options), vals...).Exec() 84 | if err != nil { 85 | for _, v := range vals { 86 | log.Printf("%T %v", v, v) 87 | } 88 | return err 89 | } 90 | 91 | return nil 92 | 93 | } 94 | 95 | func (t CRUD) Get(ids ...interface{}) (interface{}, error) { 96 | 97 | rowKeys := t.RowKeys() 98 | rangeKeys := t.RangeKeys() 99 | 100 | if len(ids) < len(rowKeys)+len(rangeKeys) { 101 | return nil, errors.New(fmt.Sprintf("To few key-values to query for row (%d of the required %d)", len(ids), len(rowKeys)+len(rangeKeys))) 102 | } 103 | 104 | where := []string{} 105 | for _, key := range append(rowKeys, rangeKeys...) { 106 | where = append(where, strings.ToLower(fmt.Sprintf("%q", key))+" = ?") 107 | } 108 | 109 | row, err := t.Query(fmt.Sprintf(`SELECT * FROM %q.%q WHERE %s LIMIT 1`, t.Keyspace().Name(), t.Name(), strings.Join(where, " AND ")), ids...).FetchRow() 110 | if err != nil { 111 | return nil, err 112 | } 113 | 114 | return row, nil 115 | 116 | } 117 | 118 | func (t CRUD) List(ids ...interface{}) (interface{}, error) { 119 | return t.Range(ids...).Fetch() 120 | } 121 | 122 | func (t CRUD) Update(row interface{}) error { 123 | 124 | rowKeys := t.RowKeys() 125 | rangeKeys := t.RangeKeys() 126 | 127 | where := []string{} 128 | for _, key := range append(rowKeys, rangeKeys...) { 129 | where = append(where, strings.ToLower(fmt.Sprintf("%q", key))+" = ?") 130 | } 131 | 132 | m, ok := r.StructToMap(row) 133 | if !ok { 134 | panic("Unable to get map from struct during update") 135 | } 136 | 137 | ids := []interface{}{} 138 | set := []string{} 139 | vals := []interface{}{} 140 | 141 | for _, rowKey := range append(rowKeys, rangeKeys...) { 142 | for key, value := range m { 143 | if strings.ToLower(key) == strings.ToLower(rowKey) { 144 | ids = append(ids, value) 145 | break 146 | } 147 | } 148 | } 149 | 150 | for key, value := range m { 151 | isAKey := false 152 | for _, rowKey := range append(rowKeys, rangeKeys...) { 153 | if strings.ToLower(key) == strings.ToLower(rowKey) { 154 | isAKey = true 155 | break 156 | } 157 | } 158 | if !isAKey { 159 | set = append(set, strings.ToLower(fmt.Sprintf("%q", key))+" = ?") 160 | vals = append(vals, value) 161 | } 162 | } 163 | 164 | if len(ids) < len(rowKeys)+len(rangeKeys) { 165 | return errors.New(fmt.Sprintf("To few key-values to update row (%d of the required minimum of %d)", len(ids), len(rowKeys)+len(rangeKeys))) 166 | } 167 | 168 | err := t.Query(fmt.Sprintf(`UPDATE %q.%q SET %s WHERE %s`, t.Keyspace().Name(), t.Name(), strings.Join(set, ", "), strings.Join(where, " AND ")), append(vals, ids...)...).Exec() 169 | if err != nil { 170 | return err 171 | } 172 | return nil 173 | 174 | } 175 | 176 | func (t CRUD) Delete(row interface{}) error { 177 | 178 | rowKeys := t.RowKeys() 179 | rangeKeys := t.RangeKeys() 180 | 181 | where := []string{} 182 | for _, key := range append(rowKeys, rangeKeys...) { 183 | where = append(where, strings.ToLower(fmt.Sprintf("%q", key))+" = ?") 184 | } 185 | 186 | m, ok := r.StructToMap(row) 187 | if !ok { 188 | panic("Unable to get map from struct during update") 189 | } 190 | 191 | ids := []interface{}{} 192 | for _, rowKey := range append(rowKeys, rangeKeys...) { 193 | for key, value := range m { 194 | if strings.ToLower(key) == strings.ToLower(rowKey) { 195 | ids = append(ids, value) 196 | break 197 | } 198 | } 199 | } 200 | 201 | if len(ids) < len(rowKeys)+len(rangeKeys) { 202 | return errors.New(fmt.Sprintf("To few key-values to delete row (%d of the required %d)", len(ids), len(rowKeys)+len(rangeKeys))) 203 | } 204 | 205 | err := t.Query(fmt.Sprintf(`DELETE FROM %q.%q WHERE %s`, t.Keyspace().Name(), t.Name(), strings.Join(where, " AND ")), ids...).Exec() 206 | if err != nil { 207 | return err 208 | } 209 | return nil 210 | 211 | } 212 | 213 | func (t CRUD) Range(ids ...interface{}) RangeInterface { 214 | rowKeys := t.RowKeys() 215 | rangeKeys := t.RangeKeys() 216 | 217 | rangeObj := Range{ 218 | table: t, 219 | } 220 | 221 | numAppended := 0 222 | for idx, key := range append(rowKeys, rangeKeys...) { 223 | if len(ids) == numAppended { 224 | break 225 | } 226 | rangeObj = rangeObj.EqualTo(key, ids[idx]).(Range) 227 | numAppended++ 228 | } 229 | 230 | return rangeObj 231 | } 232 | 233 | type Range struct { 234 | table gocqltable.TableInterface 235 | 236 | selectCols []string 237 | whereIn map[string][]string 238 | where []string 239 | whereVals []interface{} 240 | order string 241 | limit *int 242 | filtering bool 243 | } 244 | 245 | func (r Range) LessThan(rangeKey string, value interface{}) RangeInterface { 246 | r.where = append(r.where, fmt.Sprintf("%q", strings.ToLower(rangeKey))+" < ?") 247 | r.whereVals = append(r.whereVals, value) 248 | r.filtering = true 249 | return r 250 | } 251 | 252 | func (r Range) LessThanOrEqual(rangeKey string, value interface{}) RangeInterface { 253 | r.where = append(r.where, fmt.Sprintf("%q", strings.ToLower(rangeKey))+" <= ?") 254 | r.whereVals = append(r.whereVals, value) 255 | r.filtering = true 256 | return r 257 | } 258 | 259 | func (r Range) MoreThan(rangeKey string, value interface{}) RangeInterface { 260 | r.where = append(r.where, fmt.Sprintf("%q", strings.ToLower(rangeKey))+" > ?") 261 | r.whereVals = append(r.whereVals, value) 262 | r.filtering = true 263 | return r 264 | } 265 | 266 | func (r Range) MoreThanOrEqual(rangeKey string, value interface{}) RangeInterface { 267 | r.where = append(r.where, fmt.Sprintf("%q", strings.ToLower(rangeKey))+" >= ?") 268 | r.whereVals = append(r.whereVals, value) 269 | r.filtering = true 270 | return r 271 | } 272 | 273 | func (r Range) EqualTo(rangeKey string, value interface{}) RangeInterface { 274 | r.where = append(r.where, fmt.Sprintf("%q", strings.ToLower(rangeKey))+" = ?") 275 | r.whereVals = append(r.whereVals, value) 276 | return r 277 | } 278 | 279 | func (r Range) OrderBy(fieldAndDirection string) RangeInterface { 280 | r.order = fieldAndDirection 281 | return r 282 | } 283 | 284 | func (r Range) Limit(l int) RangeInterface { 285 | r.limit = &l 286 | return r 287 | } 288 | 289 | func (r Range) Select(s []string) RangeInterface { 290 | r.selectCols = s 291 | return r 292 | } 293 | 294 | func (r Range) WhereIn(m map[string][]string) RangeInterface { 295 | r.whereIn = m 296 | return r 297 | } 298 | 299 | func (r Range) Fetch() (interface{}, error) { 300 | where := r.where 301 | whereVals := r.whereVals 302 | order := r.order 303 | limit := r.limit 304 | filtering := r.filtering 305 | selectCols := r.selectCols 306 | whereIn := r.whereIn 307 | 308 | whereString := "" 309 | if len(whereIn) > 0 { 310 | numberOfInClauses := 0 311 | for col, in := range whereIn { 312 | if len(in) == 0 { 313 | continue 314 | } 315 | if numberOfInClauses > 0 { 316 | whereString = whereString + " AND" 317 | } 318 | whereString = fmt.Sprintf("%s %q IN (%v)", whereString, col, strings.Join(in, ", ")) 319 | numberOfInClauses++ 320 | } 321 | } 322 | if len(where) > 0 { 323 | if len(whereString) > 0 { 324 | whereString = whereString + " AND " 325 | } 326 | whereString = whereString + strings.Join(where, " AND ") 327 | } 328 | if len(whereString) > 0 { 329 | whereString = "WHERE " + whereString 330 | } 331 | 332 | orderString := "" 333 | if order != "" { 334 | orderString = fmt.Sprintf("ORDER BY %v", order) 335 | } 336 | 337 | limitString := "" 338 | if limit != nil { 339 | limitString = "LIMIT " + strconv.Itoa(*limit) 340 | } 341 | 342 | filteringString := "" 343 | if filtering { 344 | filteringString = "ALLOW FILTERING" 345 | } 346 | 347 | selectString := "*" 348 | if len(selectCols) > 0 { 349 | selectString = strings.Join(selectCols, ", ") 350 | } 351 | query := fmt.Sprintf(`SELECT %s FROM %q.%q %s %s %s %s`, selectString, r.table.Keyspace().Name(), r.table.Name(), whereString, orderString, limitString, filteringString) 352 | iter := r.table.Query(query, whereVals...).Fetch() 353 | 354 | result := reflect.Zero(reflect.SliceOf(reflect.PtrTo(reflect.TypeOf(r.table.Row())))) // Create a zero-value slice of pointers to our model type 355 | for row := range iter.Range() { 356 | result = reflect.Append(result, reflect.ValueOf(row)) // Append the rows to our slice 357 | } 358 | 359 | if err := iter.Close(); err != nil { 360 | return nil, err 361 | } 362 | 363 | return result.Interface(), nil 364 | 365 | } 366 | -------------------------------------------------------------------------------- /reflect/reflect.go: -------------------------------------------------------------------------------- 1 | // This package provides some punk-rock reflection which is not in the stdlib. 2 | package reflect 3 | 4 | import ( 5 | "fmt" 6 | r "reflect" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | // StructToMap converts a struct to map. The object's default key string 12 | // is the struct field name but can be specified in the struct field's 13 | // tag value. The "cql" key in the struct field's tag value is the key 14 | // name. Examples: 15 | // 16 | // // Field appears in the resulting map as key "myName". 17 | // Field int `cql:"myName"` 18 | // 19 | // // Field appears in the resulting as key "Field" 20 | // Field int 21 | // 22 | // // Field appears in the resulting map as key "myName" 23 | // Field int "myName" 24 | func StructToMap(val interface{}) (map[string]interface{}, bool) { 25 | // indirect so function works with both structs and pointers to them 26 | structVal := r.Indirect(r.ValueOf(val)) 27 | kind := structVal.Kind() 28 | if kind != r.Struct { 29 | return nil, false 30 | } 31 | sinfo := getStructInfo(structVal) 32 | mapVal := make(map[string]interface{}, len(sinfo.FieldsList)) 33 | for _, field := range sinfo.FieldsList { 34 | if structVal.Field(field.Num).CanInterface() { 35 | mapVal[field.Key] = structVal.Field(field.Num).Interface() 36 | } 37 | } 38 | return mapVal, true 39 | } 40 | 41 | // MapToStruct converts a map to a struct. It is the inverse of the StructToMap 42 | // function. For details see StructToMap. 43 | func MapToStruct(m map[string]interface{}, struc interface{}) error { 44 | val := r.Indirect(r.ValueOf(struc)) 45 | sinfo := getStructInfo(val) 46 | for k, v := range m { 47 | if info, ok := sinfo.FieldsMap[k]; ok { 48 | structField := val.Field(info.Num) 49 | if structField.Type().Name() == r.TypeOf(v).Name() { 50 | structField.Set(r.ValueOf(v)) 51 | } 52 | } 53 | } 54 | return nil 55 | } 56 | 57 | // FieldsAndValues returns a list field names and a corresponing list of values 58 | // for the given struct. For details on how the field names are determined please 59 | // see StructToMap. 60 | func FieldsAndValues(val interface{}) ([]string, []interface{}, bool) { 61 | // indirect so function works with both structs and pointers to them 62 | structVal := r.Indirect(r.ValueOf(val)) 63 | kind := structVal.Kind() 64 | if kind != r.Struct { 65 | return nil, nil, false 66 | } 67 | sinfo := getStructInfo(structVal) 68 | fields := make([]string, len(sinfo.FieldsList)) 69 | values := make([]interface{}, len(sinfo.FieldsList)) 70 | for i, info := range sinfo.FieldsList { 71 | field := structVal.Field(info.Num) 72 | fields[i] = info.Key 73 | values[i] = field.Interface() 74 | } 75 | return fields, values, true 76 | } 77 | 78 | var structMapMutex sync.RWMutex 79 | var structMap = make(map[r.Type]*structInfo) 80 | 81 | type fieldInfo struct { 82 | Key string 83 | Num int 84 | } 85 | 86 | type structInfo struct { 87 | // FieldsMap is used to access fields by their key 88 | FieldsMap map[string]fieldInfo 89 | // FieldsList allows iteration over the fields in their struct order. 90 | FieldsList []fieldInfo 91 | } 92 | 93 | func getStructInfo(v r.Value) *structInfo { 94 | st := r.Indirect(v).Type() 95 | structMapMutex.RLock() 96 | sinfo, found := structMap[st] 97 | structMapMutex.RUnlock() 98 | if found { 99 | return sinfo 100 | } 101 | 102 | n := st.NumField() 103 | fieldsMap := make(map[string]fieldInfo, n) 104 | fieldsList := make([]fieldInfo, 0, n) 105 | for i := 0; i != n; i++ { 106 | field := st.Field(i) 107 | info := fieldInfo{Num: i} 108 | tag := field.Tag.Get("cql") 109 | // If there is no cql specific tag and there are no other tags 110 | // set the cql tag to the whole field tag 111 | if tag == "" && strings.Index(string(field.Tag), ":") < 0 { 112 | tag = string(field.Tag) 113 | } 114 | if tag != "" { 115 | info.Key = tag 116 | } else { 117 | info.Key = field.Name 118 | } 119 | 120 | if _, found = fieldsMap[info.Key]; found { 121 | msg := fmt.Sprintf("Duplicated key '%s' in struct %s", info.Key, st.String()) 122 | panic(msg) 123 | } 124 | 125 | fieldsList = append(fieldsList, info) 126 | fieldsMap[info.Key] = info 127 | } 128 | sinfo = &structInfo{ 129 | fieldsMap, 130 | fieldsList, 131 | } 132 | structMapMutex.Lock() 133 | structMap[st] = sinfo 134 | structMapMutex.Unlock() 135 | return sinfo 136 | } 137 | -------------------------------------------------------------------------------- /reflect/reflect_test.go: -------------------------------------------------------------------------------- 1 | package reflect 2 | 3 | import ( 4 | "github.com/gocql/gocql" 5 | 6 | "testing" 7 | ) 8 | 9 | type Tweet struct { 10 | Timeline string 11 | ID gocql.UUID `cql:"id"` 12 | Text string `teXt` 13 | OriginalTweet *gocql.UUID `json:"origin"` 14 | } 15 | 16 | func TestStructToMap(t *testing.T) { 17 | //Test that if the value is not a struct we return nil, false 18 | m, ok := StructToMap("str") 19 | if m != nil { 20 | t.Error("map is not nil when val is a string") 21 | } 22 | if ok { 23 | t.Error("ok result from StructToMap when the val is a string") 24 | 25 | } 26 | 27 | tweet := Tweet{ 28 | "t", 29 | gocql.TimeUUID(), 30 | "hello gocassa", 31 | nil, 32 | } 33 | 34 | m, ok = StructToMap(tweet) 35 | if !ok { 36 | t.Error("ok is false for a tweet") 37 | } 38 | 39 | if m["Timeline"] != tweet.Timeline { 40 | t.Errorf("Expected %s but got %s", tweet.Timeline, m["Timeline"]) 41 | } 42 | 43 | if m["id"] != tweet.ID { 44 | t.Errorf("Expected %s but got %s", tweet.ID, m["id"]) 45 | } 46 | if m["teXt"] != tweet.Text { 47 | t.Errorf("Expected %s but got %s", tweet.Text, m["teXt"]) 48 | } 49 | if m["OriginalTweet"] != tweet.OriginalTweet { 50 | t.Errorf("Expected %v but got %s", tweet.OriginalTweet, m["OriginalTweet"]) 51 | } 52 | 53 | id := gocql.TimeUUID() 54 | tweet.OriginalTweet = &id 55 | m, _ = StructToMap(tweet) 56 | if m["OriginalTweet"] != tweet.OriginalTweet { 57 | t.Errorf("Expected nil but got %s", m["OriginalTweet"]) 58 | } 59 | } 60 | 61 | func TestMapToStruct(t *testing.T) { 62 | 63 | m := make(map[string]interface{}) 64 | assert := func() { 65 | tweet := Tweet{} 66 | if err := MapToStruct(m, &tweet); err != nil { 67 | t.Fatal(err.Error()) 68 | } 69 | timeline, ok := m["Timeline"] 70 | if ok { 71 | if timeline != tweet.Timeline { 72 | t.Errorf("Expected timeline to be %s but got %s", timeline, tweet.Timeline) 73 | } 74 | } else { 75 | if "" != tweet.Timeline { 76 | t.Errorf("Expected timeline to be empty but got %s", tweet.Timeline) 77 | } 78 | } 79 | id, ok := m["id"] 80 | if ok { 81 | if id != tweet.ID { 82 | t.Errorf("Expected id to be %s but got %s", id, tweet.ID) 83 | } 84 | } else { 85 | var emptyID gocql.UUID 86 | if emptyID != tweet.ID { 87 | t.Errorf("Expected id to be empty but got %s", tweet.ID) 88 | } 89 | } 90 | text, ok := m["teXt"] 91 | if ok { 92 | if text != tweet.Text { 93 | t.Errorf("Expected text to be %s but got %s", text, tweet.Text) 94 | } 95 | } else { 96 | if "" != tweet.Text { 97 | t.Errorf("Expected text to be empty but got %s", tweet.Text) 98 | } 99 | } 100 | 101 | originalTweet, ok := m["OriginalTweet"] 102 | if ok { 103 | if originalTweet != tweet.OriginalTweet { 104 | t.Errorf("Expected original tweet to be %s but got %s", 105 | originalTweet, tweet.OriginalTweet) 106 | } 107 | } else { 108 | if nil != tweet.OriginalTweet { 109 | t.Errorf("Expected original tweet to be empty but got %s", 110 | tweet.OriginalTweet) 111 | } 112 | } 113 | } 114 | 115 | assert() 116 | m["Timeline"] = "timeline" 117 | assert() 118 | m["id"] = gocql.TimeUUID() 119 | assert() 120 | m["text"] = "Hello gocassa" 121 | assert() 122 | id := gocql.TimeUUID() 123 | m["OriginalTweet"] = &id 124 | assert() 125 | } 126 | 127 | func TestFieldsAndValues(t *testing.T) { 128 | var emptyUUID gocql.UUID 129 | id := gocql.TimeUUID() 130 | var nilID *gocql.UUID 131 | var tests = []struct { 132 | tweet Tweet 133 | fields []string 134 | values []interface{} 135 | }{ 136 | { 137 | Tweet{}, 138 | []string{"Timeline", "id", "teXt", "OriginalTweet"}, 139 | []interface{}{"", emptyUUID, "", nilID}, 140 | }, 141 | { 142 | Tweet{"timeline1", id, "hello gocassa", &id}, 143 | []string{"Timeline", "id", "teXt", "OriginalTweet"}, 144 | []interface{}{"timeline1", id, "hello gocassa", &id}, 145 | }, 146 | } 147 | for _, test := range tests { 148 | fields, values, _ := FieldsAndValues(test.tweet) 149 | assertFieldsEqual(t, test.fields, fields) 150 | assertValuesEqual(t, test.values, values) 151 | } 152 | } 153 | 154 | func assertFieldsEqual(t *testing.T, a, b []string) { 155 | if len(a) != len(b) { 156 | t.Errorf("expected fields %v but got %v", a, b) 157 | return 158 | } 159 | 160 | for i := range a { 161 | if a[i] != b[i] { 162 | t.Errorf("expected fields %v but got %v", a, b) 163 | } 164 | } 165 | } 166 | 167 | func assertValuesEqual(t *testing.T, a, b []interface{}) { 168 | if len(a) != len(b) { 169 | t.Errorf("expected values %v but got %v different length", a, b) 170 | return 171 | } 172 | 173 | for i := range a { 174 | if a[i] != b[i] { 175 | t.Errorf("expected values %v but got %v a[i] = %v and b[i] = %v", a, b, a[i], b[i]) 176 | return 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /table.go: -------------------------------------------------------------------------------- 1 | package gocqltable 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/gocql/gocql" 8 | 9 | r "github.com/kristoiv/gocqltable/reflect" 10 | ) 11 | 12 | type TableInterface interface { 13 | Create() error 14 | Drop() error 15 | Query(statement string, params ...interface{}) Query 16 | Name() string 17 | Keyspace() Keyspace 18 | RowKeys() []string 19 | RangeKeys() []string 20 | Row() interface{} 21 | } 22 | 23 | type Table struct { 24 | name string 25 | rowKeys []string 26 | rangeKeys []string 27 | row interface{} 28 | 29 | keyspace Keyspace 30 | session *gocql.Session 31 | } 32 | 33 | func (t Table) Create() error { 34 | return t.create() 35 | } 36 | 37 | func (t Table) CreateWithProperties(props ...string) error { 38 | return t.create(props...) 39 | } 40 | 41 | func (t Table) create(props ...string) error { 42 | 43 | if t.session == nil { 44 | t.session = defaultSession 45 | } 46 | 47 | rowKeys := t.RowKeys() 48 | rangeKeys := t.RangeKeys() 49 | 50 | pkString := "PRIMARY KEY ((" + strings.Join(rowKeys, ", ") + ")" 51 | if len(rangeKeys) > 0 { 52 | pkString = pkString + ", " + strings.Join(rangeKeys, ", ") 53 | } 54 | pkString = pkString + ")" 55 | 56 | fields := []string{} 57 | 58 | m, ok := r.StructToMap(t.Row()) 59 | if !ok { 60 | panic("Unable to get map from struct during create table") 61 | } 62 | 63 | for key, value := range m { 64 | key = strings.ToLower(key) 65 | typ, err := stringTypeOf(value) 66 | if err != nil { 67 | return err 68 | } 69 | fields = append(fields, fmt.Sprintf(`%q %v`, key, typ)) 70 | } 71 | 72 | // Add primary key value to fields list 73 | fields = append(fields, pkString) 74 | 75 | propertiesString := "" 76 | if len(props) > 0 { 77 | propertiesString = "WITH " + strings.Join(props, " AND ") 78 | } 79 | 80 | return t.session.Query(fmt.Sprintf(`CREATE TABLE %q.%q (%s) %s`, t.Keyspace().Name(), t.Name(), strings.Join(fields, ", "), propertiesString)).Exec() 81 | 82 | } 83 | 84 | func (t Table) Drop() error { 85 | if t.session == nil { 86 | t.session = defaultSession 87 | } 88 | return t.session.Query(fmt.Sprintf(`DROP TABLE %q.%q`, t.Keyspace().Name(), t.Name())).Exec() 89 | } 90 | 91 | func (t Table) Query(statement string, values ...interface{}) Query { 92 | if t.session == nil { 93 | t.session = defaultSession 94 | } 95 | return Query{ 96 | Statement: statement, 97 | Values: values, 98 | 99 | Table: t, 100 | Session: t.session, 101 | } 102 | } 103 | 104 | func (t Table) Name() string { 105 | return t.name 106 | } 107 | 108 | func (t Table) Keyspace() Keyspace { 109 | return t.keyspace 110 | } 111 | 112 | func (t Table) RowKeys() []string { 113 | return t.rowKeys 114 | } 115 | 116 | func (t Table) RangeKeys() []string { 117 | return t.rangeKeys 118 | } 119 | 120 | func (t Table) Row() interface{} { 121 | return t.row 122 | } 123 | -------------------------------------------------------------------------------- /type.go: -------------------------------------------------------------------------------- 1 | package gocqltable 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | "time" 8 | 9 | "github.com/gocql/gocql" 10 | ) 11 | 12 | type Counter int 13 | 14 | func stringTypeOf(i interface{}) (string, error) { 15 | _, isByteSlice := i.([]byte) 16 | if !isByteSlice { 17 | // Check if we found a higher kinded type 18 | switch reflect.ValueOf(i).Kind() { 19 | case reflect.Slice: 20 | elemVal := reflect.Indirect(reflect.New(reflect.TypeOf(i).Elem())).Interface() 21 | ct := cassaType(elemVal) 22 | if ct == gocql.TypeCustom { 23 | return "", fmt.Errorf("Unsupported type %T", i) 24 | } 25 | return fmt.Sprintf("list<%v>", ct), nil 26 | case reflect.Map: 27 | keyVal := reflect.Indirect(reflect.New(reflect.TypeOf(i).Key())).Interface() 28 | elemVal := reflect.Indirect(reflect.New(reflect.TypeOf(i).Elem())).Interface() 29 | keyCt := cassaType(keyVal) 30 | elemCt := cassaType(elemVal) 31 | if keyCt == gocql.TypeCustom || elemCt == gocql.TypeCustom { 32 | return "", fmt.Errorf("Unsupported map key or value type %T", i) 33 | } 34 | return fmt.Sprintf("map<%v, %v>", keyCt, elemCt), nil 35 | } 36 | } 37 | ct := cassaType(i) 38 | if ct == gocql.TypeCustom { 39 | return "", fmt.Errorf("Unsupported type %T", i) 40 | } 41 | return cassaTypeToString(ct) 42 | } 43 | 44 | func cassaType(i interface{}) gocql.Type { 45 | switch i.(type) { 46 | case int, int32: 47 | return gocql.TypeInt 48 | case int64: 49 | return gocql.TypeBigInt 50 | case string: 51 | return gocql.TypeVarchar 52 | case float32: 53 | return gocql.TypeFloat 54 | case float64: 55 | return gocql.TypeDouble 56 | case bool: 57 | return gocql.TypeBoolean 58 | case time.Time: 59 | return gocql.TypeTimestamp 60 | case gocql.UUID: 61 | return gocql.TypeUUID 62 | case []byte: 63 | return gocql.TypeBlob 64 | case Counter: 65 | return gocql.TypeCounter 66 | } 67 | return gocql.TypeCustom 68 | } 69 | 70 | func cassaTypeToString(t gocql.Type) (string, error) { 71 | switch t { 72 | case gocql.TypeInt: 73 | return "int", nil 74 | case gocql.TypeBigInt: 75 | return "bigint", nil 76 | case gocql.TypeVarchar: 77 | return "varchar", nil 78 | case gocql.TypeFloat: 79 | return "float", nil 80 | case gocql.TypeDouble: 81 | return "double", nil 82 | case gocql.TypeBoolean: 83 | return "boolean", nil 84 | case gocql.TypeTimestamp: 85 | return "timestamp", nil 86 | case gocql.TypeUUID: 87 | return "uuid", nil 88 | case gocql.TypeBlob: 89 | return "blob", nil 90 | case gocql.TypeCounter: 91 | return "counter", nil 92 | default: 93 | return "", errors.New("unkown cassandra type") 94 | } 95 | } 96 | --------------------------------------------------------------------------------