├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── src ├── client.rs ├── def.rs ├── lib.rs ├── reader.rs └── serialize.rs └── tests ├── test.rs └── test_cql └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | lib/ 3 | *sublime-* 4 | .idea/ 5 | *.bin 6 | target/ 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "cql" 4 | version = "0.0.2" 5 | authors = ["Ignacio Martín "] 6 | 7 | [dependencies.uuid] 8 | 9 | git = "https://github.com/rust-lang/uuid.git" 10 | 11 | [dependencies.num] 12 | 13 | git = "https://github.com/rust-lang/num" 14 | 15 | [dependencies] 16 | byteorder = "*" 17 | 18 | [dependencies] 19 | enum_primitive = "*" 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ignacio Martín, Álvaro Berruezo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAIN = cql.rs 2 | CRATES = src/lib.rs 3 | 4 | TO_LIB = $(addprefix lib/, $(shell rustc --print-file-name $(1))) 5 | 6 | LIBS = $(foreach crate, $(CRATES), $(call TO_LIB, $(crate))) 7 | 8 | all: exe 9 | 10 | exe: $(MAIN) $(LIBS) 11 | rustc -g --out-dir bin -L lib $(MAIN) 12 | 13 | 14 | define COMPILE_CRATE 15 | $(call TO_LIB, $(1)): $(1) 16 | rustc -g --crate-type=lib --out-dir lib $(1) 17 | endef 18 | 19 | $(foreach crate, $(CRATES), $(eval $(call COMPILE_CRATE, $(crate)))) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rust-cql 2 | ======== 3 | 4 | This project is based on [yjh0502/rust-cql](https://github.com/yjh0502/rust-cql) 5 | 6 | Cassandra Query Language version 3 (cql3) binary protocol implementation with rust-lang. It should work for versions [v1](https://git-wip-us.apache.org/repos/asf?p=cassandra.git;a=blob_plain;f=doc/native_protocol.spec;hb=refs/heads/cassandra-1.2), [v2](https://git-wip-us.apache.org/repos/asf?p=cassandra.git;a=blob_plain;f=doc/native_protocol_v2.spec) and [v3](https://git-wip-us.apache.org/repos/asf?p=cassandra.git;a=blob_plain;f=doc/native_protocol_v3.spec) of the protocol. It compiles with rustc 1.5. 7 | 8 | It uses Cargo as the build system and it includes a VERY simple integration test. 9 | 10 | This is a low level driver that does not implement fancy features like node auto discovery or load balancing. 11 | 12 | Please, take into account that the (very small) example included has only been tested on Cassandra 2.1 running on Ubuntu 14.04 x64. It has not been tested on Windows or OS/X, or other versions or Cassandra. 13 | 14 | Native protocol is disabled in some versions of Cassandra 1.2. [Please enable the native protocol before start](http://www.datastax.com/dev/blog/binary-protocol). 15 | 16 | What works: 17 | - Execute queries 18 | - Create prepared queries 19 | - Execute prepared queries 20 | - Execute batch queries 21 | 22 | What doesn't work: 23 | - Decimal and Varint types 24 | - Authentication 25 | - SSL 26 | - Pagination 27 | - ... 28 | 29 | **Disclaimer**: this software is in alpha state, so expect bugs and rust anti-patterns (this is my first code in rust). 30 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | extern crate num; 3 | extern crate uuid; 4 | 5 | use std::collections::BTreeMap; 6 | use std::borrow::Cow; 7 | use std::path::Path; 8 | use std::error::Error; 9 | 10 | use super::def::*; 11 | use super::def::OpcodeRequest::*; 12 | use super::def::CqlRequestBody::*; 13 | use super::def::RCErrorType::*; 14 | use super::def::CqlResponseBody::*; 15 | use super::serialize::CqlSerializable; 16 | use super::reader::*; 17 | 18 | pub static CQL_VERSION_STRINGS: [&'static str; 3] = ["3.0.0", "3.0.0", "3.0.0"]; 19 | pub static CQL_MAX_SUPPORTED_VERSION:u8 = 0x03; 20 | 21 | type PrepsStore = BTreeMap>; 22 | 23 | pub struct Client { 24 | socket: std::net::TcpStream, 25 | pub version: u8, 26 | prepared: PrepsStore 27 | } 28 | 29 | impl Client { 30 | 31 | fn new(socket: std::net::TcpStream, version: u8) -> Client { 32 | Client {socket: socket, version: version, prepared: BTreeMap::new()} 33 | } 34 | 35 | fn build_options(&self) -> CqlRequest { 36 | return CqlRequest { 37 | version: self.version, 38 | flags: 0x00, 39 | stream: 0x01, 40 | opcode: OpcodeOptions, 41 | body: RequestOptions, 42 | }; 43 | } 44 | 45 | pub fn get_prepared_statement(&mut self, ps_id: &str) -> RCResult<&CqlPreparedStat> { 46 | match self.prepared.get(ps_id) { 47 | Some(ps) => Ok(&**ps), 48 | None => return Err(RCError::new(format!("Unknown prepared statement <{}>", ps_id), GenericError)) 49 | } 50 | } 51 | 52 | pub fn exec_query(&mut self, query_str: &str, con: Consistency) -> RCResult { 53 | let q = CqlRequest { 54 | version: self.version, 55 | flags: 0x00, 56 | stream: 0x01, 57 | opcode: OpcodeQuery, 58 | body: RequestQuery(query_str, con, 0)}; 59 | 60 | let mut socket = try_io!(self.socket.try_clone(), "Cannot clone tcp handle"); 61 | try_rc!(q.serialize(&mut socket, self.version), "Error serializing query"); 62 | Ok(try_rc!(socket.read_cql_response(self.version), "Error reading query")) 63 | } 64 | 65 | pub fn exec_prepared(&mut self, preps: &Vec, params: &[CqlValue], con: Consistency) -> RCResult { 66 | 67 | let q = CqlRequest { 68 | version: self.version, 69 | flags: 0x00, 70 | stream: 0x01, 71 | opcode: OpcodeExecute, 72 | body: RequestExec(preps.clone(), params, con, 0x01), 73 | }; 74 | 75 | let mut socket = try_io!(self.socket.try_clone(), "Cannot clone tcp handle"); 76 | try_rc!(q.serialize(&mut socket, self.version), "Error serializing prepared statement execution"); 77 | 78 | // Code to debug prepared statements. Write to file the serialization of the request 79 | let path = Path::new("prepared_data.bin"); 80 | let display = path.display(); 81 | let mut file = match std::fs::File::create(&path) { 82 | Err(err) => panic!("couldn't create {}: {}", display, err.description()), 83 | Ok(file) => file, 84 | }; 85 | try_rc!(q.serialize(&mut file, self.version), "Error serializing query to file"); 86 | 87 | Ok(try_rc!(socket.read_cql_response(self.version), "Error reading prepared statement execution result")) 88 | } 89 | 90 | pub fn exec_batch(&mut self, q_type: BatchType, q_vec: Vec, con: Consistency) -> RCResult { 91 | let q = CqlRequest { 92 | version: self.version, 93 | flags: 0x00, 94 | stream: 0x01, 95 | opcode: OpcodeBatch, 96 | body: RequestBatch(q_vec, q_type, con, 0)}; 97 | 98 | /* Code to debug batch statements. Write to file the serialization of the request 99 | 100 | let path = Path::new("batch_data.bin"); 101 | let display = path.display(); 102 | let mut file = match std::old_io::File::create(&path) { 103 | Err(why) => panic!("couldn't create {}: {}", display, why.desc), 104 | Ok(file) => file, 105 | }; 106 | 107 | serialize_and_check_io_error!(serialize_with_client, &mut file, q, self, "Error serializing to file"); 108 | */ 109 | 110 | let mut socket = try_io!(self.socket.try_clone(), "Cannot clone tcp handle"); 111 | try_rc!(q.serialize(&mut socket, self.version), "Error serializing BATCH request"); 112 | let res = try_rc!(socket.read_cql_response(self.version), "Error reading query"); 113 | Ok(res) 114 | } 115 | 116 | 117 | pub fn prepared_statement(&mut self, query_str: &str) -> RCResult { 118 | let q = CqlRequest { 119 | version: self.version, 120 | flags: 0x00, 121 | stream: 0x01, 122 | opcode: OpcodePrepare, 123 | body: RequestPrepare(query_str), 124 | }; 125 | 126 | let mut socket = try_io!(self.socket.try_clone(), "Cannot clone tcp handle"); 127 | try_rc!(q.serialize(&mut socket, self.version), "Error serializing prepared statement"); 128 | 129 | let res = try_rc!(socket.read_cql_response(self.version), "Error reading query"); 130 | match res.body { 131 | ResultPrepared(preps) => { 132 | Ok(preps) 133 | }, 134 | _ => Err(RCError::new("Response does not contain prepared statement", ReadError)) 135 | } 136 | } 137 | } 138 | 139 | fn approve_authenticator(authenticator: &CowStr) -> bool { 140 | authenticator == "org.apache.cassandra.auth.PasswordAuthenticator" 141 | } 142 | 143 | /// 144 | /// Makes an authentication response token that is compatible with PasswordAuthenticator. 145 | /// 146 | fn make_token(creds: &Vec) -> Vec { 147 | let mut token : Vec = Vec::new(); 148 | for cred in creds { 149 | token.push(0); 150 | token.extend(cred.as_bytes()); 151 | } 152 | return token; 153 | } 154 | 155 | fn send_startup(socket: &mut std::net::TcpStream, version: u8, creds: Option<&Vec>) -> RCResult<()> { 156 | let body = CqlStringMap { 157 | pairs:vec![CqlPair{key: "CQL_VERSION", value: CQL_VERSION_STRINGS[(version-1) as usize]}], 158 | }; 159 | let msg_startup = CqlRequest { 160 | version: version, 161 | flags: 0x00, 162 | stream: 0x01, 163 | opcode: OpcodeStartup, 164 | body: RequestStartup(body), 165 | }; 166 | 167 | try_rc!(msg_startup.serialize(socket, version), "Error serializing startup message"); 168 | 169 | let response = try_rc!(socket.read_cql_response(version), "Error reading response"); 170 | match response.body { 171 | ResponseReady => Ok(()), 172 | ResponseAuthenticate(authenticator) => { 173 | if approve_authenticator(&authenticator) { 174 | match creds { 175 | Some(cred) => { 176 | if version >= 2 { 177 | let msg_auth = CqlRequest { 178 | version: version, 179 | flags: 0x00, 180 | stream: 0x01, 181 | opcode: OpcodeAuthResponse, 182 | body: RequestAuthResponse(make_token(cred)), 183 | }; 184 | try_rc!(msg_auth.serialize(socket, version), "Error serializing request (auth)"); 185 | let response = try_rc!(socket.read_cql_response(version), "Error reading authentication response"); 186 | match response.body { 187 | ResponseAuthSuccess(_) => Ok(()), 188 | ResponseError(_, ref msg) => Err(RCError::new(format!("Error in authentication: {}", msg), ReadError)), 189 | _ => Err(RCError::new("Server returned unknown message", ReadError)) 190 | } 191 | } else { 192 | Err(RCError::new("Authentication is not supported for v1 protocol", ReadError)) 193 | } 194 | }, 195 | None => Err(RCError::new("Credential should be provided for authentication", ReadError)) 196 | } 197 | } else { 198 | Err(RCError::new(format!("Unexpected authenticator: {}", authenticator), ReadError)) 199 | } 200 | }, 201 | ResponseError(_, ref msg) => Err(RCError::new(format!("Error connecting: {}", msg), ReadError)), 202 | _ => Err(RCError::new("Wrong response to startup", ReadError)) 203 | } 204 | } 205 | 206 | pub fn connect(ip: &'static str, port: u16, creds:Option<&Vec>) -> RCResult { 207 | 208 | let mut version = CQL_MAX_SUPPORTED_VERSION; 209 | 210 | while version >= 0x01 { 211 | let res = std::net::TcpStream::connect((ip, port)); 212 | if res.is_err() { 213 | return Err(RCError::new(format!("Failed to connect to server at {}:{}", ip, port), ConnectionError)); 214 | } 215 | 216 | let mut socket = res.unwrap(); 217 | 218 | match send_startup(& mut socket, version, creds) { 219 | Ok(_) => return Ok(Client::new(socket, version)), 220 | Err(e) => println!("Error connecting with protocol version v{}: {}", version, e.desc) 221 | } 222 | version -= 1; 223 | } 224 | Err(RCError::new("Unable to find suitable protocol version (v1, v2, v3)", ReadError)) 225 | } 226 | -------------------------------------------------------------------------------- /src/def.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | extern crate num; 3 | extern crate uuid; 4 | 5 | use std::net::Ipv4Addr; 6 | use std::net::Ipv6Addr; 7 | use self::uuid::Uuid; 8 | use std::borrow::Cow; 9 | use std::ops::Deref; 10 | use std::error::Error; 11 | 12 | pub type CowStr = Cow<'static, str>; 13 | 14 | 15 | #[derive(Clone, Copy)] 16 | pub enum OpcodeRequest { 17 | OpcodeStartup = 0x01, 18 | OpcodeOptions = 0x05, 19 | OpcodeQuery = 0x07, 20 | OpcodePrepare = 0x09, 21 | OpcodeExecute = 0x0A, 22 | OpcodeRegister = 0x0B, 23 | OpcodeBatch = 0x0D, 24 | OpcodeAuthResponse = 0x0F, 25 | } 26 | 27 | #[derive(Debug)] 28 | pub enum OpcodeResponse { 29 | OpcodeError = 0x00, 30 | OpcodeReady = 0x02, 31 | OpcodeAuthenticate = 0x03, 32 | OpcodeSupported = 0x06, 33 | OpcodeResult = 0x08, 34 | OpcodeEvent = 0x0C, 35 | OpcodeAuthChallenge = 0x0E, 36 | OpcodeAuthSuccess = 0x10, 37 | 38 | OpcodeUnknown 39 | } 40 | 41 | #[derive(Debug)] 42 | pub struct CqlFrameHeader { 43 | pub version: u8, 44 | pub flags: u8, 45 | pub stream: i16, 46 | pub opcode: u8, 47 | } 48 | 49 | enum_from_primitive! { 50 | #[derive(Debug, PartialEq)] 51 | pub enum KindResult { 52 | KindVoid = 0x0001, 53 | KindRows = 0x0002, 54 | KindSetKeyspace = 0x0003, 55 | KindPrepared = 0x0004, 56 | KindSchemaChange = 0x0005 57 | } 58 | } 59 | 60 | pub fn opcode_response(val: u8) -> OpcodeResponse { 61 | match val { 62 | 0x00 => OpcodeResponse::OpcodeError, 63 | 0x02 => OpcodeResponse::OpcodeReady, 64 | 0x03 => OpcodeResponse::OpcodeAuthenticate, 65 | 0x06 => OpcodeResponse::OpcodeSupported, 66 | 0x08 => OpcodeResponse::OpcodeResult, 67 | 0x0C => OpcodeResponse::OpcodeEvent, 68 | 0x0E => OpcodeResponse::OpcodeAuthChallenge, 69 | 0x10 => OpcodeResponse::OpcodeAuthSuccess, 70 | 71 | _ => OpcodeResponse::OpcodeUnknown 72 | } 73 | } 74 | 75 | 76 | #[derive(Clone, Copy)] 77 | pub enum Consistency { 78 | Any = 0x0000, 79 | One = 0x0001, 80 | Two = 0x0002, 81 | Three = 0x0003, 82 | Quorum = 0x0004, 83 | All = 0x0005, 84 | LocalQuorum = 0x0006, 85 | EachQuorum = 0x0007, 86 | Unknown, 87 | } 88 | 89 | #[derive(Clone, Copy)] 90 | pub enum BatchType { 91 | Logged = 0x00, 92 | Unlogged = 0x01, 93 | Counter = 0x02 94 | } 95 | 96 | #[derive(Debug)] 97 | pub enum CqlValueType { 98 | ColumnCustom = 0x0000, 99 | ColumnASCII = 0x0001, 100 | ColumnBigInt = 0x0002, 101 | ColumnBlob = 0x0003, 102 | ColumnBoolean = 0x0004, 103 | ColumnCounter = 0x0005, 104 | ColumnDecimal = 0x0006, 105 | ColumnDouble = 0x0007, 106 | ColumnFloat = 0x0008, 107 | ColumnInt = 0x0009, 108 | ColumnText = 0x000A, 109 | ColumnTimestamp = 0x000B, 110 | ColumnUuid = 0x000C, 111 | ColumnVarChar = 0x000D, 112 | ColumnVarint = 0x000E, 113 | ColumnTimeUuid = 0x000F, 114 | ColumnInet = 0x0010, 115 | ColumnList = 0x0020, 116 | ColumnMap = 0x0021, 117 | ColumnSet = 0x0022, 118 | ColumnUnknown, 119 | } 120 | 121 | pub fn cql_column_type(val: u16) -> CqlValueType { 122 | match val { 123 | 0x0000 => CqlValueType::ColumnCustom, 124 | 0x0001 => CqlValueType::ColumnASCII, 125 | 0x0002 => CqlValueType::ColumnBigInt, 126 | 0x0003 => CqlValueType::ColumnBlob, 127 | 0x0004 => CqlValueType::ColumnBoolean, 128 | 0x0005 => CqlValueType::ColumnCounter, 129 | 0x0006 => CqlValueType::ColumnDecimal, 130 | 0x0007 => CqlValueType::ColumnDouble, 131 | 0x0008 => CqlValueType::ColumnFloat, 132 | 0x0009 => CqlValueType::ColumnInt, 133 | 0x000A => CqlValueType::ColumnText, 134 | 0x000B => CqlValueType::ColumnTimestamp, 135 | 0x000C => CqlValueType::ColumnUuid, 136 | 0x000D => CqlValueType::ColumnVarChar, 137 | 0x000E => CqlValueType::ColumnVarint, 138 | 0x000F => CqlValueType::ColumnTimeUuid, 139 | 0x0010 => CqlValueType::ColumnInet, 140 | 0x0020 => CqlValueType::ColumnList, 141 | 0x0021 => CqlValueType::ColumnMap, 142 | 0x0022 => CqlValueType::ColumnSet, 143 | _ => CqlValueType::ColumnUnknown 144 | } 145 | } 146 | 147 | 148 | #[derive(Debug)] 149 | pub enum RCErrorType { 150 | ReadError, 151 | WriteError, 152 | SerializeError, 153 | ConnectionError, 154 | NoDataError, 155 | GenericError, 156 | IOError 157 | } 158 | 159 | #[derive(Debug)] 160 | pub struct RCError { 161 | pub kind: RCErrorType, 162 | pub desc: CowStr, 163 | } 164 | 165 | 166 | impl RCError { 167 | pub fn new>(msg: S, kind: RCErrorType) -> RCError { 168 | RCError { 169 | kind: kind, 170 | desc: msg.into() 171 | } 172 | } 173 | 174 | pub fn description(&self) -> &str { 175 | return self.desc.deref(); 176 | } 177 | 178 | } 179 | 180 | 181 | impl std::error::Error for RCError { 182 | fn description(&self) -> &str { 183 | return self.desc.deref(); 184 | } 185 | 186 | fn cause(&self) -> Option<&std::error::Error> { 187 | return None; 188 | } 189 | } 190 | 191 | impl std::fmt::Display for RCError { 192 | fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { 193 | write!(f, "Error: {}", self.desc) 194 | } 195 | } 196 | 197 | pub type RCResult = Result; 198 | 199 | #[derive(Debug)] 200 | pub enum IpAddr { 201 | Ipv4(Ipv4Addr), 202 | Ipv6(Ipv6Addr) 203 | } 204 | 205 | pub struct CqlStringMap { 206 | pub pairs: Vec, 207 | } 208 | 209 | pub struct CqlPair { 210 | pub key: &'static str, 211 | pub value: &'static str, 212 | } 213 | 214 | #[derive(Debug, Clone, Copy)] 215 | pub enum CqlBytesSize { 216 | Cqli32, 217 | Cqli16 218 | } 219 | 220 | pub struct CqlTableDesc { 221 | pub keyspace: String, 222 | pub tablename: String 223 | } 224 | 225 | #[derive(Debug)] 226 | pub struct CqlColMetadata { 227 | pub keyspace: CowStr, 228 | pub table: CowStr, 229 | pub col_name: CowStr, 230 | pub col_type: CqlValueType, 231 | pub col_type_aux1: CqlValueType, 232 | pub col_type_aux2: CqlValueType 233 | } 234 | 235 | #[derive(Debug)] 236 | pub struct CqlMetadata { 237 | pub flags: u32, 238 | pub column_count: u32, 239 | pub keyspace: CowStr, 240 | pub table: CowStr, 241 | pub row_metadata: Vec, 242 | } 243 | 244 | #[derive(Debug)] 245 | pub struct Pair { 246 | pub key: T, 247 | pub value: V 248 | } 249 | 250 | pub type CQLList = Vec; 251 | pub type CQLMap = Vec>; 252 | pub type CQLSet = Vec; 253 | 254 | #[derive(Debug)] 255 | pub enum CqlValue { 256 | CqlASCII(Option), 257 | CqlBigInt(Option), 258 | CqlBlob(Option>), 259 | CqlBoolean(Option), 260 | CqlCounter(Option), 261 | CqlDecimal(Option), 262 | CqlDouble(Option), 263 | CqlFloat(Option), 264 | CqlInet(Option), 265 | CqlInt(Option), 266 | CqlList(Option), 267 | CqlMap(Option), 268 | CqlSet(Option), 269 | CqlText(Option), 270 | CqlTimestamp(Option), 271 | CqlUuid(Option), 272 | CqlTimeUuid(Option), 273 | CqlVarchar(Option), 274 | CqlVarint(Option), 275 | CqlUnknown, 276 | } 277 | 278 | #[derive(Debug)] 279 | pub struct CqlRow { 280 | pub cols: Vec, 281 | } 282 | 283 | #[derive(Debug)] 284 | pub struct CqlRows { 285 | pub metadata: CqlMetadata, 286 | pub rows: Vec, 287 | } 288 | 289 | pub struct CqlRequest<'a> { 290 | pub version: u8, 291 | pub flags: u8, 292 | pub stream: i16, 293 | pub opcode: OpcodeRequest, 294 | pub body: CqlRequestBody<'a>, 295 | } 296 | 297 | pub enum CqlRequestBody<'a> { 298 | RequestStartup(CqlStringMap), 299 | RequestQuery(&'a str, Consistency, u8), 300 | RequestPrepare(&'a str), 301 | RequestExec(Vec, &'a [CqlValue], Consistency, u8), 302 | RequestBatch(Vec, BatchType, Consistency, u8), 303 | RequestOptions, 304 | RequestAuthResponse(Vec), 305 | } 306 | 307 | #[derive(Debug)] 308 | pub struct CqlResponse { 309 | pub version: u8, 310 | pub flags: u8, 311 | pub stream: i16, 312 | pub opcode: OpcodeResponse, 313 | pub body: CqlResponseBody, 314 | } 315 | 316 | #[derive(Debug)] 317 | pub enum CqlResponseBody { 318 | ResponseError(u32, CowStr), 319 | ResponseReady, 320 | ResponseAuthenticate(CowStr), 321 | ResponseAuthChallenge(Vec), 322 | ResponseAuthSuccess(Vec), 323 | 324 | ResultVoid, 325 | ResultRows(CqlRows), 326 | ResultKeyspace(CowStr), 327 | ResultPrepared(CqlPreparedStat), 328 | ResultSchemaChange(CowStr, CowStr, CowStr), 329 | ResultUnknown, 330 | 331 | ResponseEmpty, 332 | } 333 | 334 | #[derive(Debug)] 335 | pub struct CqlPreparedStat { 336 | pub id: Vec, 337 | pub meta: CqlMetadata, 338 | pub meta_result: Option 339 | } 340 | 341 | 342 | pub enum Query { 343 | QueryStr(CowStr), 344 | QueryPrepared(Vec, Vec), 345 | QueryBatch(Vec) 346 | } 347 | 348 | 349 | 350 | 351 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "cql"] 2 | #![crate_type = "rlib"] 3 | #![crate_type = "dylib"] 4 | 5 | #[macro_use] extern crate enum_primitive as ep; 6 | 7 | pub use client::connect; 8 | pub use def::Consistency; 9 | pub use def::BatchType; 10 | pub use def::CqlValue; 11 | pub use def::CqlValue::CqlFloat; 12 | pub use def::CqlValue::CqlVarchar; 13 | pub use def::CQLList; 14 | pub use def::CQLMap; 15 | pub use def::CQLSet; 16 | pub use def::Query::QueryStr; 17 | pub use def::Query::QueryPrepared; 18 | pub use def::OpcodeResponse; 19 | pub use def::CqlResponseBody; 20 | pub use def::RCResult; 21 | pub use def::RCError; 22 | 23 | #[macro_export] 24 | macro_rules! try_bo( 25 | ($call: expr, $msg: expr) => { 26 | match $call { 27 | Ok(val) => val, 28 | Err(self::byteorder::Error::UnexpectedEOF) => return Err($crate::def::RCError::new(format!("{} -> {}", $msg, "Unexpected EOF"), $crate::def::RCErrorType::IOError)), 29 | Err(self::byteorder::Error::Io(ref err)) => { 30 | use std::error::Error; 31 | return Err($crate::def::RCError::new(format!("{} -> {}", $msg, err.description()), $crate::def::RCErrorType::IOError)) 32 | } 33 | }; 34 | } 35 | ); 36 | 37 | #[macro_export] 38 | macro_rules! try_io( 39 | ($call: expr, $msg: expr) => { 40 | match $call { 41 | Ok(val) => val, 42 | Err(ref err) => { 43 | use std::error::Error; 44 | return Err(RCError::new(format!("{} -> {}", $msg, err.description()), RCErrorType::IOError)) 45 | } 46 | }; 47 | } 48 | ); 49 | 50 | 51 | #[macro_export] 52 | macro_rules! try_rc( 53 | ($call: expr, $msg: expr) => { 54 | match $call { 55 | Ok(val) => val, 56 | Err(ref err) => return Err($crate::def::RCError::new(format!("{} -> {}", $msg, err.description()), $crate::def::RCErrorType::IOError)) 57 | }; 58 | } 59 | ); 60 | 61 | macro_rules! try_rc_length( 62 | ($call: expr, $msg: expr) => { 63 | match $call { 64 | Ok(-1) => return Ok(None), 65 | Ok(val) => val, 66 | Err(ref err) => return Err($crate::def::RCError::new(format!("{} -> {}", $msg, err.description()), $crate::def::RCErrorType::IOError)) 67 | }; 68 | } 69 | ); 70 | 71 | macro_rules! try_rc_noption( 72 | ($call: expr, $msg: expr) => { 73 | match $call { 74 | Ok(option) => match option { 75 | None => return Err($crate::def::RCError::new(format!("{} -> {}", $msg, "No data found (length == -1)"), $crate::def::RCErrorType::IOError)), 76 | Some(val) => val 77 | }, 78 | Err(ref err) => return Err($crate::def::RCError::new(format!("{} -> {}", $msg, err.description()), $crate::def::RCErrorType::IOError)) 79 | }; 80 | } 81 | ); 82 | 83 | 84 | macro_rules! CowStr_tuple_void( 85 | () => { 86 | (Cow::Borrowed(""), Cow::Borrowed("")) 87 | } 88 | ); 89 | 90 | mod def; 91 | mod reader; 92 | mod serialize; 93 | pub mod client; 94 | -------------------------------------------------------------------------------- /src/reader.rs: -------------------------------------------------------------------------------- 1 | extern crate uuid; 2 | extern crate std; 3 | extern crate byteorder; 4 | extern crate num; 5 | 6 | use super::def::*; 7 | use super::def::CqlValue::*; 8 | use super::def::CqlValueType::*; 9 | use super::def::CqlResponseBody::*; 10 | use super::def::RCErrorType::*; 11 | use super::def::CqlBytesSize::*; 12 | use super::def::KindResult::*; 13 | use super::def::OpcodeResponse::*; 14 | 15 | use self::uuid::Uuid; 16 | use std::net::{Ipv4Addr, Ipv6Addr}; 17 | use std::borrow::{Cow, ToOwned}; 18 | use std::io::{Read, Write, Cursor}; 19 | use self::byteorder::{ReadBytesExt, BigEndian, LittleEndian}; 20 | use std::mem::size_of; 21 | use std::path::Path; 22 | use std::error::Error; 23 | use ep::FromPrimitive; 24 | 25 | pub trait CqlReader { 26 | fn read_cql_bytes(&mut self, val_type: CqlBytesSize) -> RCResult>; 27 | fn read_cql_bytes_length(&mut self, val_type: CqlBytesSize) -> RCResult; 28 | fn read_cql_bytes_length_fixed(&mut self, val_type: CqlBytesSize, length: i32) -> RCResult; 29 | 30 | fn read_cql_str(&mut self, val_type: CqlBytesSize) -> RCResult>; 31 | fn read_cql_f32(&mut self, val_type: CqlBytesSize) -> RCResult>; 32 | fn read_cql_f64(&mut self, val_type: CqlBytesSize) -> RCResult>; 33 | fn read_cql_i32(&mut self, val_type: CqlBytesSize) -> RCResult>; 34 | fn read_cql_i64(&mut self, val_type: CqlBytesSize) -> RCResult>; 35 | fn read_cql_u64(&mut self, val_type: CqlBytesSize) -> RCResult>; 36 | fn read_cql_blob(&mut self, val_type: CqlBytesSize) -> RCResult>>; 37 | fn read_cql_boolean(&mut self, val_type: CqlBytesSize) -> RCResult>; 38 | fn read_cql_uuid(&mut self, val_type: CqlBytesSize) -> RCResult>; 39 | fn read_cql_inet(&mut self, val_type: CqlBytesSize) -> RCResult>; 40 | 41 | fn read_cql_list(&mut self, col_meta: &CqlColMetadata, value_size: CqlBytesSize) -> RCResult>; 42 | fn read_cql_set(&mut self, col_meta: &CqlColMetadata, value_size: CqlBytesSize) -> RCResult>; 43 | fn read_cql_map(&mut self, col_meta: &CqlColMetadata, value_size: CqlBytesSize) -> RCResult>; 44 | 45 | fn read_cql_metadata(&mut self) -> RCResult; 46 | fn read_cql_frame_header(&mut self, version: u8) -> RCResult; 47 | fn read_cql_response(&mut self, version: u8) -> RCResult; 48 | fn read_cql_rows(&mut self, collection_size: CqlBytesSize) -> RCResult; 49 | 50 | fn read_cql_skip(&mut self, val_type: CqlBytesSize) -> RCResult<()>; 51 | 52 | fn read_cql_value(&mut self, col_meta: &CqlColMetadata, collection_size: CqlBytesSize) -> RCResult; 53 | fn read_cql_value_single(&mut self, col_type: &CqlValueType, value_size: CqlBytesSize) -> RCResult; 54 | } 55 | 56 | 57 | impl CqlReader for T { 58 | fn read_cql_bytes(&mut self, val_type: CqlBytesSize) -> RCResult> { 59 | let len:i32 = match val_type { 60 | CqlBytesSize::Cqli32 => try_bo!(self.read_i32::(), "Error reading bytes length"), 61 | CqlBytesSize::Cqli16 => try_bo!(self.read_i16::(), "Error reading collection bytes length") as i32 62 | }; 63 | 64 | if len < 0 { 65 | Ok(vec![]) 66 | } else { 67 | let mut buf = Vec::with_capacity(len as usize); 68 | try_io!(std::io::copy(&mut self.take(len as u64), &mut buf), "Error at read_exact"); 69 | Ok(buf) 70 | } 71 | } 72 | 73 | fn read_cql_bytes_length(&mut self, val_type: CqlBytesSize) -> RCResult { 74 | match val_type { 75 | CqlBytesSize::Cqli32 => Ok(try_bo!(self.read_i32::(), "Error reading bytes length")), 76 | CqlBytesSize::Cqli16 => Ok(try_bo!(self.read_i16::(), "Error reading collection bytes length") as i32) 77 | } 78 | } 79 | 80 | fn read_cql_bytes_length_fixed(&mut self, val_type: CqlBytesSize, length: i32) -> RCResult { 81 | let len = match val_type { 82 | CqlBytesSize::Cqli32 => try_bo!(self.read_i32::(), "Error reading bytes length") as i32, 83 | CqlBytesSize::Cqli16 => try_bo!(self.read_i16::(), "Error reading collection bytes length") as i32 84 | }; 85 | if len != -1 && len != length { 86 | Err(RCError::new(format!("Error reading bytes, length ({}) different than expected ({})", len, length), RCErrorType::ReadError)) 87 | } else { 88 | Ok(len) 89 | } 90 | } 91 | 92 | fn read_cql_str(&mut self, val_type: CqlBytesSize) -> RCResult> { 93 | let vec_u8 = try_rc!(self.read_cql_bytes(val_type), "Error reading string data"); 94 | match std::str::from_utf8(&vec_u8) { 95 | Ok(s) => Ok(Some(Cow::Owned(s.to_owned()))), 96 | Err(_) => Err(RCError::new("Error reading string, invalid utf8 sequence", RCErrorType::ReadError)) 97 | } 98 | } 99 | 100 | fn read_cql_f32(&mut self, val_type: CqlBytesSize) -> RCResult> { 101 | try_rc_length!(self.read_cql_bytes_length_fixed(val_type, size_of::() as i32), "Error reading bytes (float) length"); 102 | Ok(Some(try_bo!(self.read_f32::(), "Error reading float (float)"))) 103 | } 104 | 105 | fn read_cql_f64(&mut self, val_type: CqlBytesSize) -> RCResult> { 106 | try_rc_length!(self.read_cql_bytes_length_fixed(val_type, size_of::() as i32), "Error reading bytes (double) length"); 107 | Ok(Some(try_bo!(self.read_f64::(), "Error reading double (double)"))) 108 | } 109 | 110 | fn read_cql_i32(&mut self, val_type: CqlBytesSize) -> RCResult> { 111 | try_rc_length!(self.read_cql_bytes_length(val_type), "Error reading bytes (int) length"); 112 | Ok(Some(try_bo!(self.read_i32::(), "Error reading int (i32)"))) 113 | } 114 | 115 | fn read_cql_i64(&mut self, val_type: CqlBytesSize) -> RCResult> { 116 | try_rc_length!(self.read_cql_bytes_length(val_type), "Error reading bytes (long) length"); 117 | Ok(Some(try_bo!(self.read_i64::(), "Error reading long (i64)"))) 118 | } 119 | 120 | fn read_cql_u64(&mut self, val_type: CqlBytesSize) -> RCResult> { 121 | try_rc!(self.read_cql_bytes_length(val_type), "Error reading bytes (long) length"); 122 | Ok(Some(try_bo!(self.read_u64::(), "Error reading long (u64)"))) 123 | } 124 | 125 | fn read_cql_blob(&mut self, val_type: CqlBytesSize) -> RCResult>> { 126 | let len = try_rc_length!(self.read_cql_bytes_length(val_type), "Error reading blob length"); 127 | Ok(Some(try_rc!(self.read_cql_bytes(val_type), "Error reading string data"))) 128 | } 129 | 130 | fn read_cql_boolean(&mut self, val_type: CqlBytesSize) -> RCResult> { 131 | try_rc_length!(self.read_cql_bytes_length(val_type), "Error reading boolean length"); 132 | match try_bo!(self.read_u8(), "Error reading boolean data") { 133 | 0 => Ok(Some(false)), 134 | _ => Ok(Some(true)) 135 | } 136 | } 137 | 138 | fn read_cql_uuid(&mut self, val_type: CqlBytesSize) -> RCResult> { 139 | let len = try_rc_length!(self.read_cql_bytes_length(val_type), "Error reading uuid length"); 140 | if len != 16 { 141 | return Err(RCError::new("Invalid uuid length", RCErrorType::ReadError)) 142 | } 143 | let vec_u8 = try_rc!(self.read_cql_bytes(val_type), "Error reading uuid data"); 144 | match Uuid::from_bytes(&vec_u8) { 145 | Some(u) => Ok(Some(u)), 146 | None => Err(RCError::new("Invalid uuid", RCErrorType::ReadError)) 147 | } 148 | } 149 | 150 | fn read_cql_inet(&mut self, val_type: CqlBytesSize) -> RCResult> { 151 | let vec = try_rc!(self.read_cql_bytes(val_type), "Error reading value data"); 152 | if vec.len() == 0 { 153 | Ok(None) 154 | } else if vec.len() == 4 { 155 | Ok(Some(IpAddr::Ipv4(Ipv4Addr::new(vec[0], vec[1], vec[2], vec[3])))) 156 | } else { 157 | Ok(Some(IpAddr::Ipv6(Ipv6Addr::new(vec[1] as u16 + ((vec[0] as u16) << 8), 158 | vec[3] as u16 + ((vec[2] as u16) << 8), 159 | vec[5] as u16 + ((vec[4] as u16) << 8), 160 | vec[7] as u16 + ((vec[6] as u16) << 8), 161 | vec[9] as u16 + ((vec[8] as u16) << 8), 162 | vec[11] as u16 + ((vec[10] as u16) << 8), 163 | vec[13] as u16 + ((vec[12] as u16) << 8), 164 | vec[15] as u16 + ((vec[14] as u16) << 8))))) 165 | } 166 | } 167 | 168 | fn read_cql_list(&mut self, col_meta: &CqlColMetadata, value_size: CqlBytesSize) -> RCResult> { 169 | try_bo!(self.read_i32::(), "Error reading list size"); 170 | let len = match value_size { 171 | CqlBytesSize::Cqli32 => try_bo!(self.read_i32::(), "Error reading list length"), 172 | CqlBytesSize::Cqli16 => try_bo!(self.read_i16::(), "Error reading list length") as i32 173 | }; 174 | 175 | let mut list: CQLList = vec![]; 176 | for _ in 0 .. len { 177 | let col = try_rc!(self.read_cql_value_single(&col_meta.col_type_aux1, value_size), "Error reading list value"); 178 | list.push(col); 179 | } 180 | Ok(Some(list)) 181 | } 182 | 183 | fn read_cql_set(&mut self, col_meta: &CqlColMetadata, value_size: CqlBytesSize) -> RCResult> { 184 | try_bo!(self.read_i32::(), "Error reading set size"); 185 | let len = match value_size { 186 | CqlBytesSize::Cqli32 => try_bo!(self.read_i32::(), "Error reading list length"), 187 | CqlBytesSize::Cqli16 => try_bo!(self.read_i16::(), "Error reading list length") as i32 188 | }; 189 | 190 | let mut set: CQLSet = vec![]; 191 | for _ in 0 .. len { 192 | let col = try_rc!(self.read_cql_value_single(&col_meta.col_type_aux1, value_size), "Error reading set value"); 193 | set.push(col); 194 | } 195 | Ok(Some(set)) 196 | } 197 | 198 | fn read_cql_map(&mut self, col_meta: &CqlColMetadata, value_size: CqlBytesSize) -> RCResult> { 199 | try_bo!(self.read_i32::(), "Error reading map size"); 200 | let len = match value_size { 201 | CqlBytesSize::Cqli32 => try_bo!(self.read_i32::(), "Error reading list length"), 202 | CqlBytesSize::Cqli16 => try_bo!(self.read_i16::(), "Error reading list length") as i32 203 | }; 204 | 205 | let mut map: CQLMap = vec![]; 206 | for _ in 0 .. len { 207 | let key = try_rc!(self.read_cql_value_single(&col_meta.col_type_aux1, value_size), "Error reading map key"); 208 | let value = try_rc!(self.read_cql_value_single(&col_meta.col_type_aux2, value_size), "Error reading map value"); 209 | map.push(Pair { key: key, value: value}); 210 | } 211 | Ok(Some(map)) 212 | } 213 | 214 | fn read_cql_skip(&mut self, val_type: CqlBytesSize) -> RCResult<()> { 215 | let len = try_rc!(self.read_cql_bytes_length(val_type), "Error reading value length"); 216 | try_rc!(self.read_cql_bytes(val_type), "Error reading value data"); 217 | Ok(()) 218 | } 219 | 220 | fn read_cql_metadata(&mut self) -> RCResult { 221 | let flags = try_bo!(self.read_u32::(), "Error reading flags"); 222 | let column_count = try_bo!(self.read_u32::(), "Error reading column count"); 223 | 224 | let (ks, tb) = 225 | if flags & 0x0001 != 0 { 226 | let keyspace_str = try_rc_noption!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading keyspace name"); 227 | let table_str = try_rc_noption!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading table name"); 228 | (keyspace_str, table_str) 229 | } else { 230 | CowStr_tuple_void!() 231 | }; 232 | 233 | let mut row_metadata:Vec = vec![]; 234 | for _ in 0u32 .. column_count { 235 | let (keyspace, table) = 236 | if flags & 0x0001 != 0 { 237 | (ks.clone(), tb.clone()) 238 | } else { 239 | let keyspace_str = try_rc_noption!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading keyspace name"); 240 | let table_str = try_rc_noption!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading table name"); 241 | (keyspace_str, table_str) 242 | }; 243 | let col_name = try_rc_noption!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading column name"); 244 | let type_key = try_bo!(self.read_u16::(), "Error reading type key"); 245 | let type_aux1 = 246 | if type_key >= 0x20 { 247 | let ctype = try_bo!(self.read_u16::(), "Error reading list/set/map type"); 248 | cql_column_type(ctype) 249 | } else { 250 | CqlValueType::ColumnUnknown 251 | }; 252 | let type_aux2 = 253 | if type_key == 0x21 { 254 | let ctype = try_bo!(self.read_u16::(), "Error reading map type value"); 255 | cql_column_type(ctype) 256 | } else { 257 | CqlValueType::ColumnUnknown 258 | }; 259 | 260 | row_metadata.push(CqlColMetadata { 261 | keyspace: keyspace, 262 | table: table, 263 | col_name: col_name, 264 | col_type: cql_column_type(type_key), 265 | col_type_aux1: type_aux1, 266 | col_type_aux2: type_aux2 267 | }); 268 | } 269 | 270 | Ok(CqlMetadata { 271 | flags: flags, 272 | column_count: column_count, 273 | keyspace: ks, 274 | table: tb, 275 | row_metadata: row_metadata, 276 | }) 277 | } 278 | 279 | fn read_cql_value(&mut self, col_meta: &CqlColMetadata, collection_size: CqlBytesSize) -> RCResult { 280 | match col_meta.col_type { 281 | ColumnList => { Ok(CqlList(try_rc!(self.read_cql_list(col_meta, collection_size), "Error reading column value (list)"))) }, 282 | ColumnMap => { Ok(CqlMap(try_rc!(self.read_cql_map(col_meta, collection_size), "Error reading column value (map)"))) }, 283 | ColumnSet => { Ok(CqlSet(try_rc!(self.read_cql_set(col_meta, collection_size), "Error reading column value (set)"))) }, 284 | _ => self.read_cql_value_single(&col_meta.col_type, CqlBytesSize::Cqli32) 285 | } 286 | } 287 | 288 | fn read_cql_value_single(&mut self, col_type: &CqlValueType, val_type: CqlBytesSize) -> RCResult { 289 | match *col_type { 290 | ColumnASCII => Ok(CqlASCII(try_rc!(self.read_cql_str(val_type), "Error reading column value (ASCII)"))), 291 | ColumnVarChar => Ok(CqlVarchar(try_rc!(self.read_cql_str(val_type), "Error reading column value (VarChar)"))), 292 | ColumnText => Ok(CqlText(try_rc!(self.read_cql_str(val_type), "Error reading column value (Text)"))), 293 | 294 | ColumnInt => Ok(CqlInt(try_rc!(self.read_cql_i32(val_type), "Error reading column value (Int)"))), 295 | ColumnBigInt => Ok(CqlBigInt(try_rc!(self.read_cql_i64(val_type), "Error reading column value (BigInt)"))), 296 | ColumnFloat => Ok(CqlFloat(try_rc!(self.read_cql_f32(val_type), "Error reading column value (Float)"))), 297 | ColumnDouble => Ok(CqlDouble(try_rc!(self.read_cql_f64(val_type), "Error reading column value (Double)"))), 298 | 299 | ColumnCustom => { 300 | try_rc!(self.read_cql_skip(val_type), "Error reading column value (custom)"); 301 | println!("Custom parse not implemented"); 302 | Ok(CqlUnknown) 303 | }, 304 | ColumnBlob => Ok(CqlBlob(try_rc!(self.read_cql_blob(val_type), "Error reading column value (blob)"))), 305 | ColumnBoolean => Ok(CqlBoolean(try_rc!(self.read_cql_boolean(val_type), "Error reading column vaue (boolean)"))), 306 | ColumnCounter => Ok(CqlCounter(try_rc!(self.read_cql_i64(val_type), "Error reading column value (counter"))), 307 | ColumnDecimal => { 308 | try_rc!(self.read_cql_skip(val_type), "Error reading column value (decimal)"); 309 | println!("Decimal parse not implemented"); 310 | Ok(CqlUnknown) 311 | }, 312 | ColumnTimestamp => Ok(CqlTimestamp(try_rc!(self.read_cql_u64(val_type), "Error reading column value (timestamp)"))), 313 | ColumnUuid => Ok(CqlUuid(try_rc!(self.read_cql_uuid(val_type), "Error reading column value (uuid)"))), 314 | ColumnVarint => { 315 | try_rc!(self.read_cql_skip(val_type), "Error reading column value (varint)"); 316 | println!("Varint parse not implemented"); 317 | Ok(CqlUnknown) 318 | }, 319 | ColumnTimeUuid => Ok(CqlTimeUuid(try_rc!(self.read_cql_uuid(val_type), "Error reading column value (timeuuid)"))), 320 | ColumnInet => Ok(CqlInet(try_rc!(self.read_cql_inet(val_type), "Error reading column value (inet)"))), 321 | CqlValueType::ColumnUnknown => panic!("Unknown column type !"), 322 | _ => Err(RCError::new("Trying to read a non-single value type", ReadError)) 323 | } 324 | } 325 | 326 | // fn read_cql_collection_value(&mut self, col_type: &CqlValueType) -> RCResult { 327 | // match *col_type { 328 | // ColumnASCII => Ok(CqlASCII(try_rc!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading collection value (ASCII)"))), 329 | // ColumnVarChar => Ok(CqlVarchar(try_rc!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading collection value (VarChar)"))), 330 | // ColumnText => Ok(CqlText(try_rc!(self.read_cql_str(CqlBytesSize::Cqli16), "Error reading collection value (Text)"))), 331 | // ColumnInt => Ok(CqlInt(try_rc!(self.read_cql_i32(CqlBytesSize::Cqli16), "Error reading collection value (Int)"))), 332 | // ColumnBigInt => Ok(CqlBigInt(try_rc!(self.read_cql_i64(CqlBytesSize::Cqli16), "Error reading collection value (BigInt)"))), 333 | // ColumnFloat => Ok(CqlFloat(try_rc!(self.read_cql_f32(CqlBytesSize::Cqli16), "Error reading collection value (Float)"))), 334 | // ColumnDouble => Ok(CqlDouble(try_rc!(self.read_cql_f64(CqlBytesSize::Cqli16), "Error reading collection value (Double)"))), 335 | // ColumnCustom => { 336 | // try_rc!(self.read_cql_skip(CqlBytesSize::Cqli16), "Error reading collection value (custom)"); 337 | // println!("Custom parse not implemented"); 338 | // Ok(CqlUnknown) 339 | // }, 340 | // ColumnBlob => Ok(CqlBlob(try_rc!(self.read_cql_blob(CqlBytesSize::Cqli16), "Error reading collection value (blob)"))), 341 | // ColumnBoolean => Ok(CqlBoolean(try_rc!(self.read_cql_boolean(CqlBytesSize::Cqli16), "Error reading collection vaue (boolean)"))), 342 | // ColumnCounter => Ok(CqlCounter(try_rc!(self.read_cql_i64(CqlBytesSize::Cqli16), "Error reading collection value (counter"))), 343 | // ColumnDecimal => { 344 | // try_rc!(self.read_cql_skip(CqlBytesSize::Cqli16), "Error reading collection value (decimal)"); 345 | // println!("Decimal parse not implemented"); 346 | // Ok(CqlUnknown) 347 | // }, 348 | // ColumnTimestamp => Ok(CqlTimestamp(try_rc!(self.read_cql_u64(CqlBytesSize::Cqli16), "Error reading collection value (timestamp)"))), 349 | // ColumnUuid => Ok(CqlUuid(try_rc!(self.read_cql_uuid(CqlBytesSize::Cqli16), "Error reading collection value (uuid)"))), 350 | // ColumnVarint => { 351 | // try_rc!(self.read_cql_skip(CqlBytesSize::Cqli16), "Error reading collection value (varint)"); 352 | // println!("Varint parse not implemented"); 353 | // Ok(CqlUnknown) 354 | // }, 355 | // ColumnTimeUuid => Ok(CqlTimeUuid(try_rc!(self.read_cql_uuid(CqlBytesSize::Cqli16), "Error reading collection value (timeuuid)"))), 356 | // ColumnInet => Ok(CqlInet(try_rc!(self.read_cql_inet(CqlBytesSize::Cqli16), "Error reading collection value (inet)"))), 357 | // _ => panic!("Unknown column type !") 358 | // } 359 | // } 360 | 361 | 362 | fn read_cql_rows(&mut self, collection_size: CqlBytesSize) -> RCResult { 363 | let metadata = try_rc!(self.read_cql_metadata(), "Error reading metadata"); 364 | let rows_count = try_bo!(self.read_u32::(), "Error reading metadata"); 365 | 366 | let mut rows:Vec = vec![]; 367 | for _ in 0u32..rows_count { 368 | let mut row = CqlRow{ cols: vec![] }; 369 | for meta in metadata.row_metadata.iter() { 370 | let col = try_rc!(self.read_cql_value(meta, collection_size), "Error reading column value"); 371 | row.cols.push(col); 372 | } 373 | rows.push(row); 374 | } 375 | 376 | Ok(CqlRows { 377 | metadata: metadata, 378 | rows: rows, 379 | }) 380 | } 381 | 382 | fn read_cql_frame_header(&mut self, version: u8) -> RCResult { 383 | if version >= 3 { 384 | let mut header_data = [0; 5]; 385 | try_rc!(self.take(5).read(&mut header_data), "Error reading response header"); 386 | 387 | let version_header = header_data[0]; 388 | let flags = header_data[1]; 389 | let stream = (header_data[2] as u16 + ((header_data[3] as u16) << 8)) as i16; 390 | let opcode = header_data[4]; 391 | Ok(CqlFrameHeader{ 392 | version: version_header, 393 | flags: flags, 394 | stream: stream, 395 | opcode: opcode, 396 | }) 397 | } else { 398 | let mut header_data = [0; 4]; 399 | try_rc!(self.take(4).read(&mut header_data), "Error reading response header"); 400 | 401 | let version_header = header_data[0]; 402 | let flags = header_data[1]; 403 | let stream = header_data[2] as i16; 404 | let opcode = header_data[3]; 405 | Ok(CqlFrameHeader{ 406 | version: version_header, 407 | flags: flags, 408 | stream: stream, 409 | opcode: opcode, 410 | }) 411 | } 412 | } 413 | 414 | fn read_cql_response(&mut self, version: u8) -> RCResult { 415 | let header = try_rc!(self.read_cql_frame_header(version), "Error reading CQL frame header"); 416 | 417 | let body_data = try_rc!(self.read_cql_bytes(CqlBytesSize::Cqli32), "Error reading body response"); 418 | // Code to debug response result. It writes the response's body to a file for inspecting. 419 | let path = Path::new("body_data.bin"); 420 | let display = path.display(); 421 | 422 | // Open a file in write-only mode, returns `IoResult` 423 | let mut file = match std::fs::File::create(&path) { 424 | Err(why) => panic!("couldn't create {}: {}", display, why.description()), 425 | Ok(file) => file, 426 | }; 427 | 428 | match file.write(&body_data) { 429 | Err(why) => { 430 | panic!("couldn't write to {}: {}", display, why.description()) 431 | }, 432 | Ok(_) => println!("successfully wrote to {}", display), 433 | } 434 | 435 | // 436 | 437 | let mut reader = std::io::BufReader::new(Cursor::new(body_data)); 438 | 439 | let opcode = opcode_response(header.opcode); 440 | 441 | let body = match opcode { 442 | OpcodeReady => ResponseReady, 443 | OpcodeAuthenticate => { 444 | ResponseAuthenticate(try_rc_noption!(reader.read_cql_str(CqlBytesSize::Cqli16), "Error reading ResponseAuthenticate")) 445 | } 446 | OpcodeError => { 447 | let code = try_bo!(reader.read_u32::(), "Error reading error code"); 448 | let msg = try_rc_noption!(reader.read_cql_str(CqlBytesSize::Cqli16), "Error reading error message"); 449 | ResponseError(code, msg) 450 | }, 451 | OpcodeResult => { 452 | let kind = KindResult::from_u32(try_bo!(reader.read_u32::(), "Error reading result kind")); 453 | match kind { 454 | Some(KindVoid) => { 455 | ResultVoid 456 | }, 457 | Some(KindRows) => { 458 | let collection_size = if version >= 3 { CqlBytesSize::Cqli32 } else { CqlBytesSize::Cqli16 }; 459 | ResultRows(try_rc!(reader.read_cql_rows(collection_size), "Error reading result Rows")) 460 | }, 461 | Some(KindSetKeyspace) => { 462 | let msg = try_rc_noption!(reader.read_cql_str(CqlBytesSize::Cqli16), "Error reading result Keyspace"); 463 | ResultKeyspace(msg) 464 | }, 465 | Some(KindSchemaChange) => { 466 | let change = try_rc_noption!(reader.read_cql_str(CqlBytesSize::Cqli16), "Error reading result SchemaChange (change)"); 467 | let keyspace = try_rc_noption!(reader.read_cql_str(CqlBytesSize::Cqli16), "Error reading result SchemaChange (keyspace)"); 468 | let table = try_rc_noption!(reader.read_cql_str(CqlBytesSize::Cqli16), "Error reading result SchemaChange (table)"); 469 | ResultSchemaChange(change, keyspace, table) 470 | }, 471 | Some(KindPrepared) => { 472 | let id = try_rc!(reader.read_cql_bytes(CqlBytesSize::Cqli16), "Error reading result Prepared (id)"); 473 | let metadata = try_rc!(reader.read_cql_metadata(), "Error reading result Prepared (metadata)"); 474 | let meta_result = if version >= 0x02 { 475 | Some(try_rc!(reader.read_cql_metadata(), "Error reading result Prepared (metadata result)")) 476 | } else { 477 | None 478 | }; 479 | ResultPrepared(CqlPreparedStat { id: id, meta: metadata, meta_result: meta_result}) 480 | } 481 | None => return Err(RCError::new("Error reading response body (unknow result kind)", ReadError)) 482 | } 483 | } 484 | OpcodeAuthChallenge => { 485 | ResponseAuthChallenge(try_rc!(reader.read_cql_bytes(CqlBytesSize::Cqli16), "Error reading ResponseAuthChallenge")) 486 | } 487 | OpcodeAuthSuccess => { 488 | ResponseAuthSuccess(try_rc!(reader.read_cql_bytes(CqlBytesSize::Cqli16), "Error reading ResponseAuthSuccess")) 489 | } 490 | _ => { 491 | ResultUnknown 492 | },//ResponseEmpty, 493 | }; 494 | 495 | Ok(CqlResponse { 496 | version: header.version, 497 | flags: header.flags, 498 | stream: header.stream, 499 | opcode: opcode, 500 | body: body, 501 | }) 502 | } 503 | } 504 | 505 | 506 | 507 | 508 | 509 | 510 | -------------------------------------------------------------------------------- /src/serialize.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | extern crate byteorder; 3 | 4 | use std::net::Ipv4Addr; 5 | use std::net::Ipv6Addr; 6 | use std::borrow::Cow; 7 | use self::byteorder::{ReadBytesExt, WriteBytesExt, BigEndian, LittleEndian, Error}; 8 | use std::mem; 9 | 10 | use super::def::*; 11 | use super::def::CqlBytesSize::*; 12 | use super::def::CqlRequestBody::*; 13 | use super::def::RCErrorType::*; 14 | use super::def::Query::*; 15 | use super::def::CqlValue::*; 16 | use super::client::Client; 17 | 18 | pub trait CqlSerializable<'a> { 19 | fn len(&'a self, version: u8) -> usize; 20 | fn serialize_size(&'a self, buf: &mut T, bytes_size: CqlBytesSize, version: u8) -> RCResult<()>; 21 | fn serialize(&'a self, buf: &mut T, version: u8) -> RCResult<()>; 22 | } 23 | 24 | macro_rules! write_size( 25 | ($buf: ident, $size: expr, $bytes_size: ident) => { 26 | match $bytes_size { 27 | Cqli16 => try_bo!($buf.write_i16::($size as i16), "Error serializing CqlValue (length of [short bytes])"), 28 | Cqli32 => try_bo!($buf.write_i32::($size as i32), "Error serializing CqlValue (length of [bytes])"), 29 | } 30 | } 31 | ); 32 | 33 | impl<'a> CqlSerializable<'a> for CqlPair { 34 | fn serialize_size(&'a self, buf: &mut T, bytes_size: CqlBytesSize, version: u8) -> RCResult<()> { 35 | try_bo!(buf.write_u16::(self.key.len() as u16), "Error serializing CqlPair (key length)"); 36 | try_io!(buf.write(self.key.as_bytes()), "Error serializing CqlPair (key)"); 37 | try_bo!(buf.write_u16::(self.value.len() as u16), "Error serializing CqlPair (value length)"); 38 | try_io!(buf.write(self.value.as_bytes()), "Error serializing CqlPair (value)"); 39 | Ok(()) 40 | } 41 | fn serialize(&'a self, buf: &mut T, version: u8) -> RCResult<()> { 42 | self.serialize_size(buf, Cqli32, version) 43 | } 44 | 45 | 46 | fn len(&'a self, version: u8) -> usize { 47 | return 4 + self.key.len() + self.value.len(); 48 | } 49 | } 50 | 51 | 52 | impl<'a> CqlSerializable<'a> for CqlStringMap { 53 | fn serialize_size(&'a self, buf: &mut T, bytes_size: CqlBytesSize, version: u8) -> RCResult<()> { 54 | try_bo!(buf.write_u16::(self.pairs.len() as u16), "Error serializing CqlStringMap (length)"); 55 | for pair in self.pairs.iter() { 56 | pair.serialize_size(buf, Cqli16, version); 57 | } 58 | Ok(()) 59 | } 60 | fn serialize(&'a self, buf: &mut T, version: u8) -> RCResult<()> { 61 | self.serialize_size(buf, Cqli16, version) 62 | } 63 | 64 | 65 | fn len(&'a self, version: u8) -> usize { 66 | let mut len = 2usize; 67 | for pair in self.pairs.iter() { 68 | len += pair.len(version); 69 | } 70 | len 71 | } 72 | } 73 | 74 | fn serialize_header(buf: &mut T, version: &u8, flags: &u8, stream: &i16, opcode: &u8, len: &u32) -> RCResult<()> { 75 | try_bo!(buf.write_u8(*version), "Error serializing CqlRequest (version)"); 76 | try_bo!(buf.write_u8(*flags), "Error serializing CqlRequest (flags)"); 77 | if *version >= 3 { 78 | try_bo!(buf.write_i16::(*stream), "Error serializing CqlRequest (stream)"); 79 | } else { 80 | try_bo!(buf.write_i8(*stream as i8), "Error serializing CqlRequest (stream)"); 81 | } 82 | try_bo!(buf.write_u8(*opcode), "Error serializing CqlRequest (opcode)"); 83 | try_bo!(buf.write_u32::(*len), "Error serializing CqlRequest (length)"); 84 | Ok(()) 85 | } 86 | 87 | impl<'a> CqlSerializable<'a> for CqlRequest<'a> { 88 | fn serialize_size(&'a self, buf: &mut T, bytes_size: CqlBytesSize, version: u8) -> RCResult<()> { 89 | Err(RCError::new("Cannot serialize Request without Client context", WriteError)) 90 | } 91 | 92 | fn serialize(&'a self, buf: &mut T, version: u8) -> RCResult<()> { 93 | let len = (self.len(version)-8) as u32; 94 | let ocode = self.opcode as u8; 95 | serialize_header(buf, &version, &self.flags, &self.stream, &ocode, &len); 96 | 97 | match self.body { 98 | RequestExec(ref preps, ref params, ref cons, flags) => { 99 | try_bo!(buf.write_i16::(preps.len() as i16), "Error serializing EXEC request (id length)"); 100 | try_io!(buf.write(&preps), "Error serializing EXEC request (id)"); 101 | if version >= 2 { 102 | try_bo!(buf.write_u16::(*cons as u16), "Error serializing CqlRequest (query consistency)"); 103 | try_bo!(buf.write_u8(flags), "Error serializing CqlRequest (query flags)"); 104 | 105 | try_bo!(buf.write_i16::(params.len() as i16), "Error serializing EXEC request (params length)"); 106 | for v in params.iter() { 107 | v.serialize_size(buf, Cqli32, version); 108 | } 109 | } else { 110 | try_bo!(buf.write_i16::(params.len() as i16), "Error serializing EXEC request (params length)"); 111 | for v in params.iter() { 112 | v.serialize_size(buf, Cqli32, version); 113 | } 114 | try_bo!(buf.write_u16::(*cons as u16), "Error serializing CqlRequest (query consistency)"); 115 | } 116 | Ok(()) 117 | }, 118 | RequestBatch(ref q_vec, ref r_type, ref con, flags) => { 119 | try_bo!(buf.write_u8(*r_type as u8), "Error serializing BATCH request (request type)"); 120 | try_bo!(buf.write_u16::(q_vec.len() as u16), "Error serializing BATCH request (number of requests)"); 121 | q_vec.iter().all(|r| { r.serialize(buf, version); true }); 122 | try_bo!(buf.write_u16::(*con as u16), "Error serializing BATCH request (consistency)"); 123 | if version >= 3 { 124 | try_bo!(buf.write_u8(0 as u8), "Error serializing BATCH request (flags)"); 125 | } 126 | Ok(()) 127 | }, 128 | RequestStartup(ref map) => { 129 | map.serialize(buf, version) 130 | }, 131 | RequestQuery(ref query_str, ref consistency, flags) => { 132 | let len_str = query_str.len() as u32; 133 | try_bo!(buf.write_u32::(len_str), "Error serializing CqlRequest (query length)"); 134 | try_io!(buf.write(query_str.as_bytes()), "Error serializing CqlRequest (query)"); 135 | try_bo!(buf.write_u16::(*consistency as u16), "Error serializing CqlRequest (query consistency)"); 136 | if version >= 2 { 137 | try_bo!(buf.write_u8(flags), "Error serializing CqlRequest (query flags)"); 138 | } 139 | Ok(()) 140 | }, 141 | RequestPrepare(ref query_str) => { 142 | let len_str = query_str.len() as u32; 143 | try_bo!(buf.write_u32::(len_str), "Error serializing CqlRequest (query length)"); 144 | try_io!(buf.write(query_str.as_bytes()), "Error serializing CqlRequest (query)"); 145 | Ok(()) 146 | }, 147 | RequestAuthResponse(ref token) => { 148 | let len_str = token.len() as u32; 149 | try_bo!(buf.write_u32::(len_str), "Error serializing CqlRequest (token length)"); 150 | try_io!(buf.write(token), "Error serializing CqlRequest (token)"); 151 | Ok(()) 152 | }, 153 | _ => Ok(()) 154 | } 155 | } 156 | 157 | fn len(&'a self, version: u8) -> usize { 158 | 8 + match self.body { 159 | RequestStartup(ref map) => map.len(version), 160 | RequestQuery(ref query_str, _, _) => { 161 | let final_bytes = if version >= 2 { 3 } else { 2 }; 162 | 4 + query_str.len() + final_bytes 163 | }, 164 | RequestPrepare(ref query_str) => 4 + query_str.len(), 165 | RequestExec(ref preps, ref values, _, _) => { 166 | let final_bytes = if version >= 2 { 3 } else { 2 }; 167 | let values_size:usize = values.iter().fold(0, |a, ref b| a + 4 + b.len(version)); 168 | 2 + preps.len() as usize + 2 + values_size + final_bytes as usize 169 | }, 170 | RequestBatch(ref q_vec, ref r_type, ref con, flags) => { 171 | let q_vec_size:usize = q_vec.iter().fold(0, |a, ref b| a + b.len(version)); 172 | if version >= 3 { 173 | 3 + q_vec_size + 3 174 | } else { 175 | 3 + q_vec_size + 2 176 | } 177 | }, 178 | RequestAuthResponse(ref token) => { 179 | 4 + token.len() 180 | } 181 | _ => 0 182 | } 183 | } 184 | } 185 | 186 | impl<'a> CqlSerializable<'a> for Query { 187 | fn serialize_size(&'a self, buf: &mut T, bytes_size: CqlBytesSize, version: u8) -> RCResult<()> { 188 | self.serialize(buf, version) 189 | } 190 | 191 | fn serialize(&'a self, buf: &mut T, version: u8) -> RCResult<()> { 192 | match *self { 193 | QueryStr(ref q_str) => { 194 | try_bo!(buf.write_u8(0u8), "Error serializing BATCH query (type)"); 195 | write_size!(buf, q_str.len(), Cqli32); 196 | try_io!(buf.write(q_str.as_bytes()), "Error serializing BATCH query (query string)"); 197 | try_bo!(buf.write_u16::(0u16), "Error serializing BATCH query (values length)"); 198 | Ok(()) 199 | }, 200 | QueryPrepared(ref preps, ref values) => { 201 | try_bo!(buf.write_u8(1u8), "Error serializing BATCH prepared query (type)"); 202 | write_size!(buf, preps.len(), Cqli16); 203 | try_io!(buf.write(&preps), "Error serializing BATCH prepared query (id)"); 204 | try_bo!(buf.write_u16::(values.len() as u16), "Error serializing BATCH prepared query (values length)"); 205 | values.iter().all(|v| { v.serialize(buf, version); true}); 206 | Ok(()) 207 | }, 208 | _ => Err(RCError::new(" ad serialize query in BATH request", WriteError)) 209 | } 210 | } 211 | 212 | fn len(&'a self, version: u8) -> usize { 213 | match *self { 214 | QueryStr(ref q_str) => { 215 | 7 + q_str.len() 216 | }, 217 | QueryPrepared(ref preps, ref values) => { 218 | let values_size:usize = values.iter().fold(0, |a, ref b| a + 4 + b.len(version)); 219 | 5 + preps.len() + values_size 220 | }, 221 | _ => 0 222 | } 223 | } 224 | } 225 | 226 | impl<'a> CqlSerializable<'a> for CqlValue { 227 | fn serialize_size(&'a self, buf: &mut T, bytes_size: CqlBytesSize, version: u8) -> RCResult<()> { 228 | match *self { 229 | CqlASCII(ref o) => match *o { 230 | Some(ref s) => { 231 | write_size!(buf, s.len(), bytes_size); 232 | try_io!(buf.write(s.as_bytes()), "Error serializing CqlValue (ascci)"); 233 | Ok(()) 234 | } 235 | None => Ok(()) 236 | }, 237 | CqlBigInt(ref o) => match *o { 238 | Some(ref i) => { 239 | write_size!(buf, 8, bytes_size); 240 | try_bo!(buf.write_i64::(*i), "Error serializing CqlValue (Bigint)"); 241 | Ok(()) 242 | } 243 | None => Ok(()) 244 | }, 245 | CqlBlob(ref o) => match *o { 246 | Some(ref b) => { 247 | write_size!(buf, b.len(), bytes_size); 248 | try_io!(buf.write(&b), "Error serializing CqlValue (Blob)"); 249 | Ok(()) 250 | } 251 | None => Ok(()) 252 | }, 253 | CqlBoolean(ref o) => match *o { 254 | Some(ref b) => { 255 | write_size!(buf, 1, bytes_size); 256 | try_bo!(buf.write_u8(*b as u8), "Error serializing CqlValue (Boolean)"); 257 | Ok(()) 258 | } 259 | None => Ok(()) 260 | }, 261 | CqlCounter(ref o) => match *o { 262 | Some(ref c) => { 263 | write_size!(buf, 8, bytes_size); 264 | try_bo!(buf.write_i64::(*c), "Error serializing CqlValue (Counter)"); 265 | Ok(()) 266 | } 267 | None => Ok(()) 268 | }, 269 | CqlDecimal(_) => Err(RCError::new("Decimal seralization not implemented", SerializeError)), 270 | CqlDouble(ref o) => match *o { 271 | Some(ref d) => { 272 | write_size!(buf, 8, bytes_size); 273 | try_bo!(buf.write_f64::(*d), "Error serializing CqlValue (Double)"); 274 | Ok(()) 275 | } 276 | None => Ok(()) 277 | }, 278 | CqlFloat(ref o) => match *o { 279 | Some(ref f) => { 280 | write_size!(buf, 4, bytes_size); 281 | try_bo!(buf.write_f32::(*f), "Error serializing CqlValue (Float)"); 282 | Ok(()) 283 | } 284 | None => Ok(()) 285 | }, 286 | CqlInet(ref o) => match *o { 287 | Some(ref ip) => match *ip { 288 | IpAddr::Ipv4(ref ipv4) => { 289 | write_size!(buf, 5, bytes_size); 290 | try_bo!(buf.write_u8(4), "Error serializing CqlValue (Ipv4Addr size)"); 291 | try_io!(buf.write(&ipv4.octets()), "Error serializing CqlValue (Ipv4Addr)"); 292 | Ok(()) 293 | }, 294 | IpAddr::Ipv6(ref ipv6) => { 295 | write_size!(buf, 17, bytes_size); 296 | try_bo!(buf.write_u8(16u8), "Error serializing CqlValue (Ipv6Addr size)"); 297 | for n in ipv6.segments().iter() { 298 | try_io!(buf.write_u16::(*n), "Error serializing CqlValue (Ipv6Addr)"); 299 | } 300 | Ok(()) 301 | }, 302 | }, 303 | None => Ok(()) 304 | }, 305 | CqlInt(ref o) => match *o { 306 | Some(ref i) => { 307 | write_size!(buf, std::mem::size_of::(), bytes_size); 308 | try_bo!(buf.write_i32::(*i), "Error serializing CqlValue (Int)"); 309 | Ok(()) 310 | } 311 | None => Ok(()) 312 | }, 313 | CqlList(ref o) => match *o { 314 | Some(ref v) => { 315 | let len = v.len(); 316 | if version >= 3 { 317 | try_bo!(buf.write_i32::(len as i32), "Error serializing CqlValue (List length)") 318 | } else { 319 | try_bo!(buf.write_i16::(len as i16), "Error serializing CqlValue (List length)") 320 | } 321 | v.iter().map(|e| e.serialize_size(buf, Cqli16, version)); 322 | Ok(()) 323 | }, 324 | None => Ok(()) 325 | }, 326 | CqlMap(ref o) => match *o { 327 | Some(ref v) => { 328 | let len = v.len(); 329 | if version >= 3 { 330 | try_bo!(buf.write_i32::(len as i32), "Error serializing CqlValue (List length)") 331 | } else { 332 | try_bo!(buf.write_i16::(len as i16), "Error serializing CqlValue (List length)") 333 | } 334 | v.iter().map(|e| e.serialize_size(buf, Cqli16, version)); 335 | Ok(()) 336 | }, 337 | None => Ok(()) 338 | }, 339 | CqlSet(ref o) => match *o { 340 | Some(ref v) => { 341 | let len = v.len(); 342 | if version >= 3 { 343 | try_bo!(buf.write_i32::(len as i32), "Error serializing CqlValue (List length)") 344 | } else { 345 | try_bo!(buf.write_i16::(len as i16), "Error serializing CqlValue (List length)") 346 | } 347 | v.iter().map(|e| e.serialize_size(buf, Cqli16, version)); 348 | Ok(()) 349 | }, 350 | None => Ok(()) 351 | }, 352 | CqlText(ref o) => match *o { 353 | Some(ref s) => { 354 | write_size!(buf, s.len(), bytes_size); 355 | try_io!(buf.write(s.as_bytes()), "Error serializing CqlValue (Text)"); 356 | Ok(()) 357 | } 358 | None => Ok(()) 359 | }, 360 | CqlTimestamp(ref o) => match *o { 361 | Some(ref i) => { 362 | write_size!(buf, 8, bytes_size); 363 | try_bo!(buf.write_u64::(*i), "Error serializing CqlValue (Counter)"); 364 | Ok(()) 365 | } 366 | None => Ok(()) 367 | }, 368 | CqlUuid(ref o) => match *o { 369 | Some(ref u) => { 370 | write_size!(buf, u.as_bytes().len(), bytes_size); 371 | try_io!(buf.write(u.as_bytes()), "Error serializing CqlValue (Uuid)"); 372 | Ok(()) 373 | } 374 | None => Ok(()) 375 | }, 376 | CqlTimeUuid(ref o) => match *o { 377 | Some(ref u) => { 378 | write_size!(buf, u.as_bytes().len(), bytes_size); 379 | try_io!(buf.write(u.as_bytes()), "Error serializing CqlValue (TimeUuid)"); 380 | Ok(()) 381 | } 382 | None => Ok(()) 383 | }, 384 | CqlVarchar(ref o) => match *o { 385 | Some(ref s) => { 386 | write_size!(buf, s.len(), bytes_size); 387 | try_io!(buf.write(s.as_bytes()), "Error serializing CqlValue (Varchar)"); 388 | Ok(()) 389 | } 390 | None => Ok(()) 391 | }, 392 | CqlVarint(_) => Err(RCError::new("Varint seralization not implemented", SerializeError)), 393 | _ => Err(RCError::new("Error serializing CqlValue (no", SerializeError)) 394 | } 395 | 396 | } 397 | 398 | fn serialize(&'a self, buf: &mut T, version: u8) -> RCResult<()> { 399 | self.serialize_size(buf, Cqli32, version) 400 | } 401 | 402 | fn len(&'a self, version: u8) -> usize { 403 | match self { 404 | &CqlASCII(ref o) => match *o { 405 | Some(ref s) => s.len() as usize, 406 | None => 0 407 | }, 408 | &CqlBigInt(ref o) => match *o { 409 | Some(_) => std::mem::size_of::(), 410 | None => 0 411 | }, 412 | &CqlBlob(ref o) => match *o { 413 | Some(ref b) => b.len() as usize, 414 | None => 0 415 | }, 416 | &CqlBoolean(ref o) => match *o { 417 | Some(_) => std::mem::size_of::(), 418 | None => 0 419 | }, 420 | &CqlCounter(ref o) => match *o { 421 | Some(_) => std::mem::size_of::(), 422 | None => 0 423 | }, 424 | &CqlDecimal(_) => 0, 425 | &CqlDouble(ref o) => match *o { 426 | Some(_) => std::mem::size_of::(), 427 | None => 0 428 | }, 429 | &CqlFloat(ref o) => match *o { 430 | Some(_) => std::mem::size_of::(), 431 | None => 0 432 | }, 433 | &CqlInet(ref o) => match *o { 434 | Some(ref ip) => match *ip { 435 | IpAddr::Ipv4(_) => 5, 436 | IpAddr::Ipv6(_) => 17 437 | }, 438 | None => 0 439 | }, 440 | &CqlInt(ref o) => match *o { 441 | Some(_) => std::mem::size_of::(), 442 | None => 0 443 | }, 444 | &CqlList(ref o) => match *o { 445 | Some(ref v) => { 446 | if v.len() == 0 { 0 } 447 | else { 448 | // Lists contain [short bytes] elements, hence the 2 449 | v.len() * (2 + v[0].len(version)) 450 | } 451 | }, 452 | None => 0 453 | }, 454 | &CqlMap(ref o) => match *o { 455 | Some(ref v) => { 456 | if v.len() == 0 { 0 } 457 | else { 458 | // Maps contain [short bytes] elements, hence the 2 459 | v.len() * (2 + v[0].len(version)) 460 | } 461 | }, 462 | None => 0 463 | }, 464 | &CqlSet(ref o) => match *o { 465 | Some(ref v) => { 466 | if v.len() == 0 { 0 } 467 | else { 468 | // Sets contain [short bytes] elements, hence the 2 469 | v.len() * (2 + v[0].len(version)) 470 | } 471 | }, 472 | None => 0 473 | }, 474 | &CqlTimestamp(ref o) => match *o { 475 | Some(_) => std::mem::size_of::(), 476 | None => 0 477 | }, 478 | &CqlUuid(ref o) => match *o { 479 | Some(ref u) => u.as_bytes().len(), 480 | None => 0 481 | }, 482 | &CqlTimeUuid(ref o) => match *o { 483 | Some(ref u) => u.as_bytes().len(), 484 | None => 0 485 | }, 486 | &CqlVarchar(ref o) => match *o { 487 | Some(ref s) => s.len() as usize, 488 | None => 0 489 | }, 490 | &CqlVarint(_) => 0, 491 | _ => 0 492 | } 493 | } 494 | 495 | } 496 | 497 | 498 | impl<'a, T:CqlSerializable<'a>, V:CqlSerializable<'a>> CqlSerializable<'a> for Pair { 499 | fn serialize_size(&'a self, buf: &mut S, bytes_size: CqlBytesSize, version: u8) -> RCResult<()> { 500 | self.key.serialize_size(buf, bytes_size, version); 501 | self.value.serialize_size(buf, bytes_size, version); 502 | Ok(()) 503 | } 504 | 505 | fn serialize(&'a self, buf: &mut S, version: u8) -> RCResult<()> { 506 | self.serialize_size(buf, Cqli32, version) 507 | } 508 | 509 | fn len(&'a self, version: u8) -> usize { 510 | 0 511 | } 512 | } 513 | 514 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cql; 3 | 4 | macro_rules! assert_response( 5 | ($resp:expr) => ( 6 | if match $resp.opcode { cql::OpcodeResponse::OpcodeError => true, _ => false } { 7 | panic!("Test failed at assertion: {}", 8 | match $resp.body { cql::CqlResponseBody::ResponseError(_, message) => message, _ => Cow::Borrowed("Ooops!")}); 9 | } 10 | ); 11 | ); 12 | 13 | macro_rules! try_test( 14 | ($call: expr, $msg: expr) => { 15 | match $call { 16 | Ok(val) => val, 17 | Err(ref err) => panic!("Test failed at library call: {}", err.description()) 18 | }; 19 | } 20 | ); 21 | 22 | mod test_cql; -------------------------------------------------------------------------------- /tests/test_cql/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | 3 | use std::borrow::Cow; 4 | use std::io::Write; 5 | use cql; 6 | 7 | pub fn to_hex_string(bytes: &Vec) -> String { 8 | let strs: Vec = bytes.iter() 9 | .map(|b| format!("{:02X}", b)) 10 | .collect(); 11 | strs.connect(" ") 12 | } 13 | 14 | #[test] 15 | fn test() { 16 | println!("Connecting ...!"); 17 | let creds = vec![Cow::Borrowed("cassandra"), Cow::Borrowed("cassandra")]; 18 | let mut client = try_test!(cql::connect("127.0.0.1", 9042, Some(&creds)), "Error connecting to server at 127.0.0.1:9042"); 19 | println!("Connected with CQL binary version v{}", client.version); 20 | 21 | let mut q = "create keyspace if not exists rust with replication = {'class': 'SimpleStrategy', 'replication_factor':1}"; 22 | println!("cql::Query: {}", q); 23 | let mut response = try_test!(client.exec_query(q, cql::Consistency::One), "Error creating keyspace"); 24 | assert_response!(response); 25 | println!("Result: {:?} \n", response); 26 | 27 | q = "create table if not exists rust.test (id text primary key, f32 float, f64 double, i32 int, i64 bigint, b boolean, ip inet)"; 28 | println!("cql::Query: {}", q); 29 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error creating table rust.test"); 30 | assert_response!(response); 31 | println!("Result: {:?} \n", response); 32 | 33 | q = "insert into rust.test (id, f32, f64, i32, i64, b, ip) values ('asdf', 1.2345, 3.14159, 47, 59, true, '127.0.0.1')"; 34 | println!("cql::Query: {}", q); 35 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error inserting into table test"); 36 | assert_response!(response); 37 | println!("Result: {:?} \n", response); 38 | 39 | q = "select * from rust.test"; 40 | println!("cql::Query: {}", q); 41 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error selecting from table test"); 42 | assert_response!(response); 43 | println!("Result: {:?} \n", response); 44 | 45 | q = "insert into rust.test (id, f32) values (?, ?)"; 46 | println!("Create prepared: {}", q); 47 | let preps = try_test!(client.prepared_statement(q), "Error creating prepared statement"); 48 | println!("Created prepared with id = {}", to_hex_string(&preps.id)); 49 | 50 | println!("Execute prepared"); 51 | let params: &[cql::CqlValue] = &[cql::CqlVarchar(Some(Cow::Borrowed("ttrwe"))), cql::CqlFloat(Some(15.1617))]; 52 | response = try_test!(client.exec_prepared(&preps.id, params, cql::Consistency::One), "Error executing prepared statement"); 53 | assert_response!(response); 54 | println!("Result: {:?} \n", response); 55 | 56 | q = "select * from rust.test"; 57 | println!("cql::Query: {}", q); 58 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error selecting from table test"); 59 | assert_response!(response); 60 | println!("Result: {:?} \n", response); 61 | 62 | println!("Execute batch"); 63 | let params2: Vec = vec![cql::CqlVarchar(Some(Cow::Borrowed("batch2"))), cql::CqlFloat(Some(666.65))]; 64 | let q_vec = vec![cql::QueryStr(Cow::Borrowed("insert into rust.test (id, f32) values ('batch1', 34.56)")), 65 | cql::QueryPrepared(preps.id, params2)]; 66 | response = try_test!(client.exec_batch(cql::BatchType::Logged, q_vec, cql::Consistency::One), "Error executing batch cql::Query"); 67 | assert_response!(response); 68 | println!("Result: {:?} \n", response); 69 | 70 | q = "select * from rust.test"; 71 | println!("cql::Query: {}", q); 72 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error selecting from table test"); 73 | assert_response!(response); 74 | println!("Result: {:?} \n", response); 75 | 76 | q = "create table if not exists rust.test2 (id text primary key, l list, m map, s set)"; 77 | println!("cql::Query: {}", q); 78 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error creating table test2"); 79 | assert_response!(response); 80 | println!("Result: {:?} \n", response); 81 | 82 | q = "insert into rust.test2 (id, l, m, s) values ('asdf', [1,2,3,4,5], {0: 'aa', 1: 'bbb', 3: 'cccc'}, {1.234, 2.345, 3.456, 4.567})"; 83 | println!("cql::Query: {}", q); 84 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error inserting into table test2"); 85 | assert_response!(response); 86 | println!("Result: {:?} \n", response); 87 | 88 | q = "select * from rust.test2"; 89 | println!("cql::Query: {}", q); 90 | response = try_test!(client.exec_query(q, cql::Consistency::One), "Error selecting from table test2"); 91 | assert_response!(response); 92 | println!("Result: {:?} \n", response); 93 | } 94 | --------------------------------------------------------------------------------