├── .travis.yml ├── .gitignore ├── pkg.rs ├── Makefile ├── README.md ├── Cargo.toml ├── chan.rs ├── src ├── cql_client.rc ├── cql_client.rs └── lib.rs ├── cql.rs └── examples └── hello.rs /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.sw[a-z] 3 | target 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /pkg.rs: -------------------------------------------------------------------------------- 1 | #[pkg(id = "org.desti.cql_client", 2 | vers="0.0.1")]; 3 | 4 | #[pkg_crate(file="src/cql_client.rc")]; 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | cql: cql.rs lib 2 | rustc -O -L./ $< -o cql 3 | 4 | lib: src/cql_client.rs 5 | rustc --lib -O $< --out-dir=./ 6 | 7 | all: cql 8 | 9 | clean: 10 | rm libcql_client* cql 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rql 2 | ======== 3 | 4 | Cassandra Query Language version 3(cql3) v3 native protocol implementation with Rust. 5 | 6 | [1] https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v3.spec 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "cql" 4 | version = "0.0.3" 5 | authors = ["Annie Lai "] 6 | description = "Apache Cassandra client library" 7 | 8 | homepage = "https://github.com/ng8eke/rql" 9 | repository = "https://github.com/ng8eke/rql" 10 | keywords = ["cql", "cassandra"] 11 | 12 | license = "MIT" 13 | 14 | [dependencies] 15 | byteorder = "1.2" 16 | log = "0.4" 17 | 18 | [profile.bench] 19 | debug = true 20 | -------------------------------------------------------------------------------- /chan.rs: -------------------------------------------------------------------------------- 1 | use core::pipes; 2 | use core::pipes::Select2; 3 | 4 | use core::task; 5 | 6 | fn main() { 7 | let (port, chan) = pipes::stream(); 8 | let (port2, chan2) = pipes::stream(); 9 | 10 | chan.send(~"Hello"); 11 | chan2.send(~"world"); 12 | 13 | loop { 14 | let msg = match (port, port2).select() { 15 | Left(a) => { 16 | a 17 | }, 18 | Right(b) => { 19 | b 20 | }, 21 | }; 22 | io::println(msg); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/cql_client.rc: -------------------------------------------------------------------------------- 1 | #[link(name="cql_client", 2 | vers="0.0.1")]; 3 | 4 | #[crate_type="lib"]; 5 | #[warn(non_camel_case_types)]; 6 | 7 | extern mod std; 8 | 9 | pub mod cql_client; 10 | 11 | pub use cql_client::{CQL_VERSION, connect}; 12 | pub use cql_client::{ConsistencyAny, ConsistencyOne, 13 | ConsistencyTwo, ConsistencyThree, ConsistencyQuorum, 14 | ConsistencyAll, ConsistencyLocalQuorum, ConsistencyEachQuorum, ConsistencyUnknown}; 15 | 16 | pub use cql_client::CqlColumn; 17 | pub use cql_client::{CqlString, Cqli32, Cqli64, CqlBlob, CqlBool, 18 | CqlCounter, Cqlf32, Cqlf64, CqlTimestamp, CqlBigint}; 19 | 20 | pub use cql_client::CqlResponseBody; 21 | pub use cql_client::{ResponseError, 22 | ResultVoid, ResultRows, ResultKeyspace, ResultPrepared, 23 | ResultSchemaChange, ResponseEmpty}; 24 | 25 | pub use cql_client::{CqlRow, CqlRows}; 26 | 27 | //pub use cql_client::{ColumnCustom, ColumnASCII, ColumnBIGINT, ColumnBlob, ColumnBoolean, ColumnCounter, ColumnDecimal, ColumnDOUBLE, ColumnFLOAT, ColumnINT, ColumnTEXT, ColumnTimestamp, ColumnUUID, ColumnVARCHAR, ColumnVarint, ColumnTimeUUID, ColumnInet, ColumnList, ColumnMap, ColumnSet, ColumnUnknown}; 28 | 29 | //pub use cql_client::Opcode; 30 | //pub use cql_client::{OpcodeError OpcodeStartup, OpcodeReady, OpcodeAuthenticate, OpcodeCredentials, OpcodeOptions, OpcodeSupported, OpcodeQuery, OpcodeResult, OpcodePrepare, OpcodeExecute, OpcodeRegister, OpcodeEvent, OpcodeUnknown}; 31 | -------------------------------------------------------------------------------- /cql.rs: -------------------------------------------------------------------------------- 1 | extern mod std; 2 | extern mod cql_client (name="cql_client", vers="0.0.1"); 3 | 4 | use core::rand; 5 | use std::time; 6 | 7 | static str_seq:&'static str = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"; 8 | static str_seq_len:uint = 26 * 2; 9 | 10 | fn rand_str(size: uint) -> ~str { 11 | let mut bytes:~[u8] = ~[]; 12 | for uint::range(0, size) |_| { 13 | bytes.push(str_seq[rand::random::() % str_seq_len]); 14 | } 15 | str::from_bytes(bytes) 16 | } 17 | 18 | fn time_diff(start: time::Timespec, end: time::Timespec) -> float { 19 | return (end.sec - start.sec) as float + (end.nsec - start.nsec) as float/1e9f; 20 | } 21 | 22 | fn main() { 23 | let res = cql_client::connect(~"127.0.0.1", 9042, None); 24 | if res.is_err() { 25 | io::println(fmt!("%?", res.get_err())); 26 | fail!(~"Failed to connect"); 27 | } 28 | 29 | let client = res.get(); 30 | 31 | let mut res; 32 | res = client.query(~"create keyspace rust with replication = \ 33 | {'class': 'SimpleStrategy', 'replication_factor':1}", cql_client::ConsistencyOne); 34 | io::println(fmt!("%?", res)); 35 | 36 | res = client.query(~"create table rust.test (id text primary key, value float)", 37 | cql_client::ConsistencyOne); 38 | io::println(fmt!("%?", res)); 39 | 40 | res = client.query(~"insert into rust.test (id, value) values ('asdf', 1.2345)", 41 | cql_client::ConsistencyOne); 42 | io::println(fmt!("%?", res)); 43 | 44 | res = client.query(~"select * from rust.test", 45 | cql_client::ConsistencyOne); 46 | io::println(fmt!("%?", res)); 47 | } 48 | -------------------------------------------------------------------------------- /examples/hello.rs: -------------------------------------------------------------------------------- 1 | extern crate cql; 2 | 3 | fn run() -> cql::Result<()> { 4 | let mut client = cql::Client::new("localhost:9042")?; 5 | 6 | eprintln!("ready"); 7 | 8 | let res = client.options(); 9 | println!("options: {:?}", res); 10 | 11 | let res = client.query( 12 | "create keyspace rust with replication = \ 13 | {'class': 'SimpleStrategy', 'replication_factor':1}", 14 | cql::Consistency::One, 15 | Vec::new(), 16 | )?; 17 | println!("create: {:?}", res); 18 | 19 | let res = client.query( 20 | "create table rust.test (v1 text primary key, v2 float, v3 list, v4 varint)", 21 | cql::Consistency::One, 22 | Vec::new(), 23 | )?; 24 | println!("create table: {:?}", res); 25 | 26 | let prepare_id = 27 | client.prepare("insert into rust.test (v1, v2, v3, v4) values ('asdf', ?, ?, ?)")?; 28 | println!("prepare: {:?}", prepare_id); 29 | 30 | let res = client.execute( 31 | prepare_id, 32 | cql::Consistency::One, 33 | vec![ 34 | cql::Value::CqlFloat(1.2345), 35 | cql::Value::CqlList(vec![cql::Value::CqlBoolean(false)]), 36 | cql::Value::CqlVarInt(123), 37 | ], 38 | )?; 39 | println!("execute: {:?}", res); 40 | 41 | let res = client.query("select * from rust.test", cql::Consistency::One, Vec::new())?; 42 | println!("select: {:?}", res); 43 | 44 | Ok(()) 45 | } 46 | 47 | fn main() { 48 | match run() { 49 | Ok(()) => {} 50 | Err(e) => { 51 | println!("Error while running CQL: {:?}", e); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/cql_client.rs: -------------------------------------------------------------------------------- 1 | #[link(name="cql_client", 2 | vers="0.0.1")]; 3 | 4 | #[crate_type="lib"]; 5 | #[warn(non_camel_case_types)]; 6 | 7 | extern mod std; 8 | 9 | use core::io::{ReaderUtil, WriterUtil}; 10 | 11 | use std::{net_ip, net_tcp, uv_global_loop, bigint}; 12 | use std::io_util; 13 | 14 | pub static CQL_VERSION:u8 = 0x01; 15 | 16 | enum OpcodeRequest { 17 | //requests 18 | OpcodeStartup = 0x01, 19 | OpcodeCredentials = 0x04, 20 | OpcodeOptions = 0x05, 21 | OpcodeQuery = 0x07, 22 | OpcodePrepare = 0x09, 23 | OpcodeRegister = 0x0B, 24 | } 25 | 26 | enum OpcodeResponse { 27 | //responces 28 | OpcodeError = 0x00, 29 | OpcodeReady = 0x02, 30 | OpcodeAuthenticate = 0x03, 31 | OpcodeSupported = 0x06, 32 | OpcodeResult = 0x08, 33 | OpcodeExecute = 0x0A, 34 | OpcodeEvent = 0x0C, 35 | 36 | OpcodeUnknown 37 | } 38 | 39 | fn OpcodeResponse(val: u8) -> OpcodeResponse { 40 | match val { 41 | /* requests 42 | 0x01 => OpcodeStartup, 43 | 0x04 => OpcodeCredentials, 44 | 0x05 => OpcodeOptions, 45 | 0x07 => OpcodeQuery, 46 | 0x09 => OpcodePrepare, 47 | 0x0B => OpcodeRegister, 48 | */ 49 | 50 | 0x00 => OpcodeError, 51 | 0x02 => OpcodeReady, 52 | 0x03 => OpcodeAuthenticate, 53 | 0x06 => OpcodeSupported, 54 | 0x08 => OpcodeResult, 55 | 0x0A => OpcodeExecute, 56 | 0x0C => OpcodeEvent, 57 | 58 | _ => OpcodeUnknown 59 | } 60 | } 61 | 62 | pub enum Consistency { 63 | ConsistencyAny = 0x0000, 64 | ConsistencyOne = 0x0001, 65 | ConsistencyTwo = 0x0002, 66 | ConsistencyThree = 0x0003, 67 | ConsistencyQuorum = 0x0004, 68 | ConsistencyAll = 0x0005, 69 | ConsistencyLocalQuorum = 0x0006, 70 | ConsistencyEachQuorum = 0x0007, 71 | ConsistencyUnknown, 72 | } 73 | 74 | pub fn Consistency(val: u16) -> Consistency { 75 | match val { 76 | 0 => ConsistencyAny, 77 | 1 => ConsistencyOne, 78 | 2 => ConsistencyTwo, 79 | 3 => ConsistencyThree, 80 | 4 => ConsistencyQuorum, 81 | 5 => ConsistencyAll, 82 | 6 => ConsistencyLocalQuorum, 83 | 7 => ConsistencyEachQuorum, 84 | _ => ConsistencyUnknown 85 | } 86 | } 87 | 88 | pub enum CqlColumnType { 89 | ColumnCustom = 0x0000, 90 | ColumnASCII = 0x0001, 91 | ColumnBigInt = 0x0002, 92 | ColumnBlob = 0x0003, 93 | ColumnBoolean = 0x0004, 94 | ColumnCounter = 0x0005, 95 | ColumnDecimal = 0x0006, 96 | ColumnDouble = 0x0007, 97 | ColumnFloat = 0x0008, 98 | ColumnInt = 0x0009, 99 | ColumnText = 0x000A, 100 | ColumnTimestamp = 0x000B, 101 | ColumnUUID = 0x000C, 102 | ColumnVarChar = 0x000D, 103 | ColumnVarint = 0x000E, 104 | ColumnTimeUUID = 0x000F, 105 | ColumnInet = 0x0010, 106 | ColumnList = 0x0020, 107 | ColumnMap = 0x0021, 108 | ColumnSet = 0x0022, 109 | ColumnUnknown, 110 | } 111 | 112 | fn CqlColumnType(val: u16) -> CqlColumnType { 113 | match val { 114 | 0x0000 => ColumnCustom, 115 | 0x0001 => ColumnASCII, 116 | 0x0002 => ColumnBigInt, 117 | 0x0003 => ColumnBlob, 118 | 0x0004 => ColumnBoolean, 119 | 0x0005 => ColumnCounter, 120 | 0x0006 => ColumnDecimal, 121 | 0x0007 => ColumnDouble, 122 | 0x0008 => ColumnFloat, 123 | 0x0009 => ColumnInt, 124 | 0x000A => ColumnText, 125 | 0x000B => ColumnTimestamp, 126 | 0x000C => ColumnUUID, 127 | 0x000D => ColumnVarChar, 128 | 0x000E => ColumnVarint, 129 | 0x000F => ColumnTimeUUID, 130 | 0x0010 => ColumnInet, 131 | 0x0020 => ColumnList, 132 | 0x0021 => ColumnMap, 133 | 0x0022 => ColumnSet, 134 | _ => ColumnUnknown 135 | } 136 | } 137 | 138 | struct CqlError { 139 | err_name: ~str, 140 | err_msg: ~str, 141 | } 142 | 143 | fn CqlError(name: ~str, msg: ~str) -> CqlError { 144 | return CqlError{err_name: name, err_msg: msg}; 145 | } 146 | 147 | trait CqlSerializable { 148 | fn len(&self) -> uint; 149 | fn serialize(&self, buf: &T); 150 | } 151 | 152 | trait CqlReader { 153 | fn read_cql_str(&self) -> ~str; 154 | fn read_cql_long_str(&self) -> Option<~str>; 155 | fn read_cql_rows(&self) -> CqlRows; 156 | 157 | fn read_cql_metadata(&self) -> CqlMetadata; 158 | fn read_cql_response(&self) -> CqlResponse; 159 | } 160 | 161 | impl CqlReader for T { 162 | fn read_cql_str(&self) -> ~str { 163 | let len = self.read_be_u16() as uint; 164 | str::from_bytes(self.read_bytes(len)) 165 | } 166 | 167 | fn read_cql_long_str(&self) -> Option<~str> { 168 | let len = self.read_be_i32() as uint; 169 | if(len == -1) { 170 | None 171 | } else { 172 | Some(str::from_bytes(self.read_bytes(len))) 173 | } 174 | } 175 | 176 | fn read_cql_metadata(&self) -> CqlMetadata { 177 | let flags = self.read_be_u32(); 178 | let column_count = self.read_be_u32(); 179 | let (keyspace, table) = 180 | if flags == 0x0001 { 181 | let keyspace_str = self.read_cql_str(); 182 | let table_str = self.read_cql_str(); 183 | (keyspace_str, table_str) 184 | } else { 185 | (~"", ~"") 186 | }; 187 | 188 | let mut row_metadata:~[CqlColMetadata] = ~[]; 189 | for u32::range(0, column_count) |_| { 190 | let (keyspace, table) = 191 | if flags == 0x0001 { 192 | (~"", ~"") 193 | } else { 194 | let keyspace_str = self.read_cql_str(); 195 | let table_str = self.read_cql_str(); 196 | (keyspace_str, table_str) 197 | }; 198 | let col_name = self.read_cql_str(); 199 | let type_key = self.read_be_u16(); 200 | let type_name = 201 | if type_key >= 0x20 { 202 | CqlColumnType(self.read_be_u16()) 203 | } else { 204 | ColumnUnknown 205 | }; 206 | 207 | row_metadata.push(CqlColMetadata { 208 | keyspace: keyspace, 209 | table: table, 210 | col_name: col_name, 211 | col_type: CqlColumnType(type_key), 212 | col_type_name: type_name 213 | }); 214 | } 215 | 216 | CqlMetadata { 217 | flags: flags, 218 | column_count: column_count, 219 | keyspace: keyspace, 220 | table: table, 221 | row_metadata: row_metadata, 222 | } 223 | } 224 | 225 | fn read_cql_rows(&self) -> CqlRows { 226 | let metadata = @self.read_cql_metadata(); 227 | let rows_count = self.read_be_u32(); 228 | 229 | let mut rows:~[CqlRow] = ~[]; 230 | for u32::range(0, rows_count) |_| { 231 | let mut row = CqlRow{ cols: ~[], metadata: metadata }; 232 | for metadata.row_metadata.each |meta| { 233 | let col = match meta.col_type { 234 | ColumnASCII => CqlString(self.read_cql_long_str()), 235 | ColumnVarChar => CqlString(self.read_cql_long_str()), 236 | ColumnText => CqlString(self.read_cql_long_str()), 237 | 238 | ColumnInt => Cqli32({ 239 | match self.read_be_i32() { 240 | -1 => None, 241 | 4 => Some(self.read_be_i32()), 242 | len => fail!(fmt!("Invalid length with i32: %?", len)), 243 | } 244 | }), 245 | ColumnBigInt => Cqli64(Some(self.read_be_i64())), 246 | ColumnFloat => Cqlf32(unsafe{ 247 | match self.read_be_i32() { 248 | -1 => None, 249 | 4 => Some(cast::transmute(self.read_be_u32())), 250 | len => fail!(fmt!("Invalid length with f32: %?", len)), 251 | } 252 | }), 253 | ColumnDouble => Cqlf64(unsafe{ 254 | match self.read_be_i32() { 255 | -1 => None, 256 | 4 => Some(cast::transmute(self.read_be_u64())), 257 | len => fail!(fmt!("Invalid length with f64: %?", len)), 258 | } 259 | }), 260 | 261 | ColumnList => CqlList({ 262 | match self.read_be_i32() { 263 | -1 => None, 264 | len => { 265 | let data = self.read_bytes(len as uint); 266 | io::println(fmt!("%?", data)); 267 | fail!(fmt!("List parse not implemented")); 268 | }, 269 | } 270 | }), 271 | 272 | 273 | /* 274 | ColumnCustom => , 275 | ColumnBlob => , 276 | ColumnBoolean => , 277 | ColumnCounter => , 278 | ColumnDecimal => , 279 | ColumnTimestamp => , 280 | ColumnUUID => , 281 | ColumnVarint => , 282 | ColumnTimeUUID => , 283 | ColumnInet => , 284 | ColumnList => , 285 | ColumnMap => , 286 | ColumnSet => , 287 | */ 288 | unknown => { 289 | io::println(fmt!("Unknown type id: %?", unknown)); 290 | match self.read_be_i32() { 291 | -1 => (), 292 | len => { self.read_bytes(len as uint); }, 293 | } 294 | CqlUnknown 295 | } 296 | }; 297 | 298 | row.cols.push(col); 299 | } 300 | rows.push(row); 301 | } 302 | 303 | CqlRows { 304 | metadata: metadata, 305 | rows: rows, 306 | } 307 | } 308 | 309 | fn read_cql_response(&self) -> CqlResponse { 310 | let header_data = self.read_bytes(8); 311 | 312 | let version = header_data[0]; 313 | let flags = header_data[1]; 314 | let stream = header_data[2] as i8; 315 | let opcode = OpcodeResponse(header_data[3]); 316 | let length = (header_data[4] as uint << 24) + 317 | (header_data[5] as uint << 16) + 318 | (header_data[6] as uint << 8) + 319 | (header_data[7] as uint); 320 | 321 | let body_data = self.read_bytes(length); 322 | let reader = io_util::BufReader::new(body_data); 323 | 324 | let body = match opcode { 325 | OpcodeReady => ResponseReady, 326 | OpcodeAuthenticate => { 327 | ResponseAuth(reader.read_cql_str()) 328 | } 329 | OpcodeError => { 330 | let code = reader.read_be_u32(); 331 | let msg = reader.read_cql_str(); 332 | ResponseError(code, msg) 333 | }, 334 | OpcodeResult => { 335 | let code = reader.read_be_u32(); 336 | match code { 337 | 0x0001 => { 338 | ResultVoid 339 | }, 340 | 0x0002 => { 341 | ResultRows(reader.read_cql_rows()) 342 | }, 343 | 0x0003 => { 344 | let msg = reader.read_cql_str(); 345 | ResultKeyspace(msg) 346 | }, 347 | 0x0004 => { 348 | let id = reader.read_u8(); 349 | let metadata = reader.read_cql_metadata(); 350 | ResultPrepared(id, metadata) 351 | }, 352 | 0x0005 => { 353 | let change = reader.read_cql_str(); 354 | let keyspace = reader.read_cql_str(); 355 | let table = reader.read_cql_str(); 356 | ResultSchemaChange(change, keyspace, table) 357 | }, 358 | _ => { 359 | fail!(fmt!("Unknown code for result: %?", code)); 360 | }, 361 | } 362 | } 363 | _ => { 364 | fail!(~"Invalid response from server"); 365 | },//ResponseEmpty, 366 | }; 367 | 368 | if *reader.pos != length { 369 | debug!("Data is not fully readed: specification might be changed %? != %?", 370 | reader.pos, length); 371 | } 372 | 373 | return CqlResponse { 374 | version: version, 375 | flags: flags, 376 | stream: stream, 377 | opcode: opcode, 378 | body: body, 379 | }; 380 | } 381 | } 382 | 383 | struct CqlPair { 384 | key: ~str, 385 | value: ~str, 386 | } 387 | 388 | impl CqlSerializable for CqlPair { 389 | fn serialize(&self, buf: &T) { 390 | buf.write_be_u16(self.key.len() as u16); 391 | buf.write(str::to_bytes(self.key)); 392 | buf.write_be_u16(self.value.len() as u16); 393 | buf.write(str::to_bytes(self.value)); 394 | } 395 | 396 | fn len(&self) -> uint { 397 | return 4 + self.key.len() + self.value.len(); 398 | } 399 | } 400 | 401 | struct CqlStringMap { 402 | pairs: ~[CqlPair], 403 | } 404 | 405 | impl CqlSerializable for CqlStringMap { 406 | fn serialize(&self, buf: &T) { 407 | buf.write_be_u16(self.pairs.len() as u16); 408 | for self.pairs.each |pair| { 409 | pair.serialize(buf); 410 | } 411 | } 412 | 413 | fn len(&self) -> uint { 414 | let mut len = 2u; 415 | for self.pairs.each |pair| { 416 | len += pair.len(); 417 | } 418 | len 419 | } 420 | } 421 | 422 | struct CqlColMetadata { 423 | keyspace: ~str, 424 | table: ~str, 425 | col_name: ~str, 426 | col_type: CqlColumnType, 427 | col_type_name: CqlColumnType, 428 | } 429 | 430 | struct CqlMetadata { 431 | flags: u32, 432 | column_count: u32, 433 | keyspace: ~str, 434 | table: ~str, 435 | row_metadata: ~[CqlColMetadata], 436 | } 437 | 438 | pub enum CqlColumn { 439 | CqlString(Option<~str>), 440 | 441 | Cqli32(Option), 442 | Cqli64(Option), 443 | 444 | CqlBlob(Option<~[u8]>), 445 | CqlBool(Option), 446 | 447 | CqlCounter(Option), 448 | 449 | Cqlf32(Option), 450 | Cqlf64(Option), 451 | 452 | CqlTimestamp(u64), 453 | CqlBigint(bigint::BigInt), 454 | 455 | CqlList(Option<~[CqlColumn]>), 456 | 457 | CqlUnknown, 458 | } 459 | 460 | pub struct CqlRow { 461 | cols: ~[CqlColumn], 462 | metadata: @CqlMetadata, 463 | } 464 | 465 | impl CqlRow { 466 | fn get_column(&self, col_name: ~str) -> Option { 467 | let mut i = 0; 468 | let len = self.metadata.row_metadata.len(); 469 | while i < len { 470 | if self.metadata.row_metadata[i].col_name == col_name { 471 | return Some(copy self.cols[i]); 472 | } 473 | i += 1; 474 | } 475 | None 476 | } 477 | } 478 | 479 | pub struct CqlRows { 480 | metadata: @CqlMetadata, 481 | rows: ~[CqlRow], 482 | } 483 | 484 | pub enum CqlRequestBody { 485 | RequestStartup(CqlStringMap), 486 | RequestCred(~[~str]), 487 | RequestQuery(~str, Consistency), 488 | RequestOptions, 489 | } 490 | 491 | pub enum CqlResponseBody { 492 | ResponseError(u32, ~str), 493 | ResponseReady, 494 | ResponseAuth(~str), 495 | 496 | ResultVoid, 497 | ResultRows(CqlRows), 498 | ResultKeyspace(~str), 499 | ResultPrepared(u8, CqlMetadata), 500 | ResultSchemaChange(~str, ~str, ~str), 501 | ResultUnknown, 502 | 503 | ResponseEmpty, 504 | } 505 | 506 | struct CqlRequest { 507 | version: u8, 508 | flags: u8, 509 | stream: i8, 510 | opcode: OpcodeRequest, 511 | body: CqlRequestBody, 512 | } 513 | 514 | struct CqlResponse { 515 | version: u8, 516 | flags: u8, 517 | stream: i8, 518 | opcode: OpcodeResponse, 519 | body: CqlResponseBody, 520 | } 521 | 522 | impl CqlSerializable for CqlRequest { 523 | fn serialize(&self, buf: &T) { 524 | buf.write_u8(self.version); 525 | buf.write_u8(self.flags); 526 | buf.write_i8(self.stream); 527 | buf.write_u8(self.opcode as u8); 528 | buf.write_be_u32((self.len()-8) as u32); 529 | 530 | match self.body { 531 | RequestStartup(ref map) => { 532 | map.serialize(buf) 533 | }, 534 | RequestQuery(ref query_str, ref consistency) => { 535 | buf.write_be_u32(query_str.len() as u32); 536 | buf.write(str::to_bytes(*query_str)); 537 | buf.write_be_u16(*consistency as u16); 538 | }, 539 | _ => (), 540 | } 541 | } 542 | fn len(&self) -> uint { 543 | 8 + match self.body { 544 | RequestStartup(ref map) => { 545 | map.len() 546 | }, 547 | RequestQuery(ref query_str, _) => { 548 | 4 + query_str.len() + 2 549 | }, 550 | _ => { 551 | 0 552 | } 553 | } 554 | } 555 | } 556 | 557 | fn Startup() -> CqlRequest { 558 | let body = CqlStringMap { 559 | pairs:~[CqlPair{key: ~"CQL_VERSION", value: ~"3.0.0"}], 560 | }; 561 | return CqlRequest { 562 | version: CQL_VERSION, 563 | flags: 0x00, 564 | stream: 0x01, 565 | opcode: OpcodeStartup, 566 | body: RequestStartup(body), 567 | }; 568 | } 569 | 570 | fn Auth(creds: ~[~str]) -> CqlRequest { 571 | return CqlRequest { 572 | version: CQL_VERSION, 573 | flags: 0x00, 574 | stream: 0x01, 575 | opcode: OpcodeOptions, 576 | body: RequestCred(creds), 577 | }; 578 | } 579 | 580 | fn Options() -> CqlRequest { 581 | return CqlRequest { 582 | version: CQL_VERSION, 583 | flags: 0x00, 584 | stream: 0x01, 585 | opcode: OpcodeOptions, 586 | body: RequestOptions, 587 | }; 588 | } 589 | 590 | fn Query(stream: i8, query_str: ~str, con: Consistency) -> CqlRequest { 591 | return CqlRequest { 592 | version: CQL_VERSION, 593 | flags: 0x00, 594 | stream: stream, 595 | opcode: OpcodeQuery, 596 | body: RequestQuery(query_str, con), 597 | }; 598 | } 599 | 600 | pub struct CqlClient { 601 | socket: net_tcp::TcpSocketBuf, 602 | } 603 | 604 | impl CqlClient { 605 | pub fn query(&self, query_str: ~str, con: Consistency) -> CqlResponse { 606 | let q = Query(0x01, query_str, con); 607 | 608 | let writer = io::BytesWriter(); 609 | 610 | q.serialize::(&writer); 611 | self.socket.write(*writer.bytes); 612 | self.socket.read_cql_response() 613 | } 614 | } 615 | 616 | pub fn connect(ip: ~str, port: uint, creds:Option<~[~str]>) -> 617 | result::Result { 618 | let task = @uv_global_loop::get(); 619 | let addr = net_ip::v4::parse_addr(ip); 620 | 621 | let res = net_tcp::connect(addr, port, task); 622 | if(res.is_err()) { 623 | return result::Err(CqlError(~"Error", ~"Failed to connect to server")); 624 | } 625 | 626 | let socket = res.unwrap(); 627 | let buf = net_tcp::socket_buf(socket); 628 | 629 | let msg_startup = Startup(); 630 | msg_startup.serialize::(&buf); 631 | 632 | let response = buf.read_cql_response(); 633 | let opcode = copy response.opcode; 634 | match response.body { 635 | ResponseReady => { 636 | result::Ok(CqlClient { socket: buf }) 637 | }, 638 | ResponseAuth(_) => { 639 | match(creds) { 640 | Some(cred) => { 641 | let msg_auth = Auth(cred); 642 | msg_auth.serialize::(&buf); 643 | let response = buf.read_cql_response(); 644 | match response.body { 645 | ResponseReady => result::Ok(CqlClient { socket: buf }), 646 | ResponseError(_, ref msg) => { 647 | result::Err(CqlError(~"Error", copy *msg)) 648 | } 649 | _ => { 650 | result::Err(CqlError(~"Error", ~"Server returned unknown message")) 651 | }, 652 | } 653 | }, 654 | None => { 655 | result::Err(CqlError(~"Error", ~"Credential should be provided")) 656 | }, 657 | } 658 | 659 | } 660 | _ => result::Err(CqlError(~"Error", fmt!("Invalid opcode: %?", opcode))) 661 | } 662 | } 663 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | #[macro_use] 3 | extern crate log; 4 | 5 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 6 | use std::intrinsics::transmute; 7 | use std::io; 8 | use std::io::Write; 9 | use std::net::TcpStream; 10 | use std::rc::Rc; 11 | use std::string::FromUtf8Error; 12 | 13 | pub static CQL_VERSION: u8 = 0x03; 14 | 15 | #[derive(Clone, Copy, Debug)] 16 | enum Opcode { 17 | // req 18 | Startup = 0x01, 19 | Cred = 0x04, 20 | Opts = 0x05, 21 | Query = 0x07, 22 | Prepare = 0x09, 23 | Execute = 0x0A, 24 | Register = 0x0B, 25 | 26 | // resp 27 | Error = 0x00, 28 | Ready = 0x02, 29 | Auth = 0x03, 30 | Supported = 0x06, 31 | Result = 0x08, 32 | Event = 0x0C, 33 | } 34 | 35 | fn opcode(val: u8) -> Opcode { 36 | use Opcode::*; 37 | match val { 38 | // req 39 | 0x01 => Startup, 40 | 0x04 => Cred, 41 | 0x05 => Opts, 42 | 0x07 => Query, 43 | 0x09 => Prepare, 44 | 0x0A => Execute, 45 | 0x0B => Register, 46 | 47 | // resp 48 | 0x00 => Error, 49 | 0x02 => Ready, 50 | 0x03 => Auth, 51 | 0x06 => Supported, 52 | 0x08 => Result, 53 | 0x0C => Event, 54 | _ => Error, 55 | } 56 | } 57 | 58 | #[derive(Clone, Debug)] 59 | pub enum Consistency { 60 | Any = 0x0000, 61 | One = 0x0001, 62 | Two = 0x0002, 63 | Three = 0x0003, 64 | Quorum = 0x0004, 65 | All = 0x0005, 66 | LocalQuorum = 0x0006, 67 | EachQuorum = 0x0007, 68 | Unknown, 69 | } 70 | 71 | pub fn consistency(val: u16) -> Consistency { 72 | use Consistency::*; 73 | match val { 74 | 0 => Any, 75 | 1 => One, 76 | 2 => Two, 77 | 3 => Three, 78 | 4 => Quorum, 79 | 5 => All, 80 | 6 => LocalQuorum, 81 | 7 => EachQuorum, 82 | _ => Unknown, 83 | } 84 | } 85 | 86 | #[derive(Clone, Copy, Debug)] 87 | pub enum ColumnType { 88 | Custom = 0x0000, 89 | Ascii = 0x0001, 90 | Bigint = 0x0002, 91 | Blob = 0x0003, 92 | Boolean = 0x0004, 93 | Counter = 0x0005, 94 | Decimal = 0x0006, 95 | Double = 0x0007, 96 | Float = 0x0008, 97 | Int = 0x0009, 98 | Text = 0x000A, 99 | Timestamp = 0x000B, 100 | UUID = 0x000C, 101 | VarChar = 0x000D, 102 | VarInt = 0x000E, 103 | TimeUUID = 0x000F, 104 | Inet = 0x0010, 105 | List = 0x0020, 106 | Map = 0x0021, 107 | Set = 0x0022, 108 | UDT = 0x0030, 109 | Tuple = 0x0031, 110 | Unknown = 0xffff, 111 | } 112 | 113 | fn column_type(val: u16) -> ColumnType { 114 | use ColumnType::*; 115 | 116 | match val { 117 | 0x0000 => Custom, 118 | 0x0001 => Ascii, 119 | 0x0002 => Bigint, 120 | 0x0003 => Blob, 121 | 0x0004 => Boolean, 122 | 0x0005 => Counter, 123 | 0x0006 => Decimal, 124 | 0x0007 => Double, 125 | 0x0008 => Float, 126 | 0x0009 => Int, 127 | 0x000A => Text, 128 | 0x000B => Timestamp, 129 | 0x000C => UUID, 130 | 0x000D => VarChar, 131 | 0x000E => VarInt, 132 | 0x000F => TimeUUID, 133 | 0x0010 => Inet, 134 | 0x0020 => List, 135 | 0x0021 => Map, 136 | 0x0022 => Set, 137 | 0x0030 => UDT, 138 | 0x0031 => Tuple, 139 | _ => Unknown, 140 | } 141 | } 142 | 143 | #[derive(Debug)] 144 | pub enum Error { 145 | Protocol, 146 | Unimplemented, 147 | UnexpectedEOF, 148 | Io(io::Error), 149 | Utf8(FromUtf8Error), 150 | } 151 | 152 | impl From for Error { 153 | fn from(err: io::Error) -> Self { 154 | Error::Io(err) 155 | } 156 | } 157 | 158 | impl From for Error { 159 | fn from(err: FromUtf8Error) -> Self { 160 | Error::Utf8(err) 161 | } 162 | } 163 | 164 | pub type Result = std::result::Result; 165 | 166 | fn parse_varint(v: &[u8]) -> i64 { 167 | let start = 8 - v.len(); 168 | let is_positive = (v[0] & 0x80) == 0; 169 | let mut buf = if is_positive { [0u8; 8] } else { [255u8; 8] }; 170 | 171 | buf[start..].copy_from_slice(v); 172 | let mut slice: &[u8] = &buf; 173 | slice.read_i64::().unwrap() 174 | } 175 | 176 | trait CqlSerializable { 177 | fn len_(&self) -> usize; 178 | fn serialize(&self, buf: &mut T) -> Result<()>; 179 | 180 | fn to_vec(&self) -> Result> { 181 | let mut s = Vec::with_capacity(self.len_()); 182 | self.serialize(&mut s)?; 183 | Ok(s) 184 | } 185 | } 186 | 187 | trait CqlReader: io::Read { 188 | fn read_full(&mut self, buf: &mut [u8]) -> Result<()> { 189 | let mut nread = 0usize; 190 | while nread < buf.len() { 191 | match self.read(&mut buf[nread..])? { 192 | 0 => return Err(Error::UnexpectedEOF), 193 | n => nread += n, 194 | } 195 | } 196 | Ok(()) 197 | } 198 | 199 | fn read_bytes(&mut self, len: usize) -> Result> { 200 | let mut vec = vec![0; len]; 201 | self.read_full(vec.as_mut_slice())?; 202 | Ok(vec) 203 | } 204 | 205 | fn read_short(&mut self) -> Result { 206 | let val = self.read_u16::()?; 207 | Ok(val) 208 | } 209 | 210 | fn read_int(&mut self) -> Result { 211 | let val = self.read_i32::()?; 212 | Ok(val) 213 | } 214 | 215 | fn read_cql_str_len(&mut self, len: usize) -> Result { 216 | let bytes = self.read_bytes(len)?; 217 | Ok(String::from_utf8(bytes)?) 218 | } 219 | 220 | fn read_cql_str(&mut self) -> Result { 221 | let len = self.read_short()?; 222 | let bytes = self.read_bytes(usize::from(len))?; 223 | let s = String::from_utf8(bytes)?; 224 | Ok(s) 225 | } 226 | 227 | fn read_cql_string_list(&mut self) -> Result> { 228 | let len = self.read_short()?; 229 | let mut v = Vec::with_capacity(usize::from(len)); 230 | for _ in 0..len { 231 | v.push(self.read_cql_str()?); 232 | } 233 | Ok(v) 234 | } 235 | 236 | fn read_cql_string_multimap(&mut self) -> Result { 237 | let len = self.read_short()?; 238 | let mut v = Vec::with_capacity(usize::from(len)); 239 | for _ in 0..len { 240 | let key = self.read_cql_str()?; 241 | let values = self.read_cql_string_list()?; 242 | v.push((key, values)); 243 | } 244 | Ok(v) 245 | } 246 | 247 | fn read_cql_col_type(&mut self) -> Result { 248 | let type_key = column_type(self.read_short()?); 249 | let ty = match type_key { 250 | ColumnType::Custom => { 251 | let name = self.read_cql_str()?; 252 | CqlColDescr::Custom(name) 253 | } 254 | ColumnType::List => { 255 | let ty = self.read_cql_col_type()?; 256 | CqlColDescr::List(Box::new(ty)) 257 | } 258 | ColumnType::Map => { 259 | let key_ty = self.read_cql_col_type()?; 260 | let val_ty = self.read_cql_col_type()?; 261 | CqlColDescr::Map(Box::new((key_ty, val_ty))) 262 | } 263 | ColumnType::Set => { 264 | let ty = self.read_cql_col_type()?; 265 | CqlColDescr::Set(Box::new(ty)) 266 | } 267 | ColumnType::Tuple => { 268 | let n = self.read_short()?; 269 | let mut ty_list = Vec::with_capacity(usize::from(n)); 270 | for _ in 0..n { 271 | ty_list.push(self.read_cql_col_type()?); 272 | } 273 | CqlColDescr::Tuple(ty_list.into()) 274 | } 275 | ty => CqlColDescr::Single(ty), 276 | }; 277 | Ok(ty) 278 | } 279 | 280 | fn read_cql_col_metadata(&mut self, flags: u32) -> Result { 281 | let (keyspace, table) = if flags == 0x0001 { 282 | (None, None) 283 | } else { 284 | let keyspace_str = self.read_cql_str()?; 285 | let table_str = self.read_cql_str()?; 286 | (Some(keyspace_str), Some(table_str)) 287 | }; 288 | let col_name = self.read_cql_str()?; 289 | let col_type = self.read_cql_col_type()?; 290 | 291 | Ok(CqlColMetadata { 292 | keyspace, 293 | table, 294 | col_name, 295 | col_type, 296 | }) 297 | } 298 | 299 | fn read_cql_metadata(&mut self) -> Result { 300 | let flags = self.read_u32::()?; 301 | let column_count = self.read_u32::()?; 302 | let (keyspace, table) = if flags == 0x0001 { 303 | let keyspace_str = self.read_cql_str()?; 304 | let table_str = self.read_cql_str()?; 305 | (Some(keyspace_str), Some(table_str)) 306 | } else { 307 | (None, None) 308 | }; 309 | 310 | let mut row_metadata = Vec::with_capacity(column_count as usize); 311 | for _ in 0..column_count { 312 | row_metadata.push(self.read_cql_col_metadata(flags)?); 313 | } 314 | 315 | Ok(Metadata { 316 | flags: flags, 317 | column_count: column_count, 318 | keyspace: keyspace, 319 | table: table, 320 | row_metadata: row_metadata, 321 | }) 322 | } 323 | 324 | fn read_cql_rows(&mut self) -> Result { 325 | let metadata = Rc::new(self.read_cql_metadata()?); 326 | let rows_count = self.read_u32::()?; 327 | let col_count = metadata.row_metadata.len(); 328 | 329 | let mut rows: Vec = Vec::with_capacity(rows_count as usize); 330 | for _ in 0..rows_count { 331 | let mut cols = Vec::with_capacity(col_count); 332 | 333 | for meta in metadata.row_metadata.iter() { 334 | cols.push(self.read_cql_col(&meta.col_type)?); 335 | } 336 | 337 | rows.push(Row { 338 | cols, 339 | metadata: metadata.clone(), 340 | }); 341 | } 342 | 343 | Ok(Rows { 344 | metadata: metadata, 345 | rows: rows, 346 | }) 347 | } 348 | 349 | fn read_cql_result(&mut self) -> Result { 350 | use ResponseResult::*; 351 | 352 | let code = self.read_u32::()?; 353 | let res = match code { 354 | 0x0001 => Void, 355 | 0x0002 => Rows(self.read_cql_rows()?), 356 | 0x0003 => { 357 | let msg = self.read_cql_str()?; 358 | Keyspace(msg) 359 | } 360 | 0x0004 => { 361 | let len = self.read_short()?; 362 | let id = self.read_bytes(usize::from(len))?; 363 | let metadata = self.read_cql_metadata()?; 364 | Prepared(id, metadata) 365 | } 366 | 0x0005 => { 367 | let change_type = self.read_cql_str()?; 368 | let target = self.read_cql_str()?; 369 | let ks_name = self.read_cql_str()?; 370 | 371 | let name = match target.as_str() { 372 | "KEYSPACE" => None, 373 | "TABLE" | "TYPE" => { 374 | let target_name = self.read_cql_str()?; 375 | Some(target_name) 376 | } 377 | _ => { 378 | return Err(Error::Protocol); 379 | } 380 | }; 381 | SchemaChange(change_type, target, ks_name, name) 382 | } 383 | _ => return Err(Error::Protocol), 384 | }; 385 | Ok(res) 386 | } 387 | 388 | fn read_cql_body(&mut self, opcode: Opcode) -> Result { 389 | let body = match opcode { 390 | Opcode::Ready => ResponseBody::Ready, 391 | Opcode::Auth => ResponseBody::Auth(self.read_cql_str()?), 392 | Opcode::Error => { 393 | let code = self.read_u32::()?; 394 | let msg = self.read_cql_str()?; 395 | 396 | match code { 397 | 0x2400 => { 398 | let _ks = self.read_cql_str()?; 399 | let _namespace = self.read_cql_str()?; 400 | } 401 | _ => (), 402 | } 403 | ResponseBody::Error(code, msg) 404 | } 405 | Opcode::Result => ResponseBody::Result(self.read_cql_result()?), 406 | Opcode::Supported => ResponseBody::Supported(self.read_cql_string_multimap()?), 407 | _ => return Err(Error::Protocol), 408 | }; 409 | Ok(body) 410 | } 411 | 412 | fn read_cql_response(&mut self) -> Result { 413 | let header_data = self.read_bytes(9)?; 414 | let mut header_reader = io::Cursor::new(header_data.as_slice()); 415 | 416 | // 9-byte header 417 | let version = header_reader.read_u8()?; 418 | let flags = header_reader.read_u8()?; 419 | let stream = header_reader.read_i16::()?; 420 | let opcode = opcode(header_reader.read_u8()?); 421 | let length = header_reader.read_u32::()?; 422 | eprintln!("len: {:?}, opcode: {:?}", length, opcode); 423 | 424 | let body_data = self.read_bytes(length as usize)?; 425 | let mut reader = io::Cursor::new(body_data.as_slice()); 426 | 427 | let body = reader.read_cql_body(opcode)?; 428 | eprintln!("body: {:?}", body); 429 | // println!("byte: {:?} {:?}", header_data, body_data); 430 | 431 | if reader.position() != length as u64 { 432 | eprintln!("short: {} != {}", reader.position(), length); 433 | } 434 | 435 | Ok(Response { 436 | header: FrameHeader { 437 | version, 438 | flags, 439 | stream, 440 | opcode, 441 | }, 442 | body: body, 443 | }) 444 | } 445 | 446 | fn read_cql_varint(&mut self, len: usize) -> Result { 447 | let v = self.read_bytes(len)?; 448 | if v.len() > 10 { 449 | //TODO: add bigint? 450 | return Err(Error::Protocol); 451 | } 452 | Ok(parse_varint(&v)) 453 | } 454 | 455 | fn read_cql_col_ty(&mut self, col_type: ColumnType, len: usize) -> Result { 456 | use ColumnType::*; 457 | use Value::*; 458 | 459 | trace!("ty: {:?}, len: {:?}", col_type, len); 460 | 461 | let col = match col_type { 462 | Ascii => CqlAscii(self.read_cql_str_len(len)?), 463 | Bigint => CqlBigint(match len { 464 | 8 => self.read_i64::()?, 465 | _len => return Err(Error::Protocol), 466 | }), 467 | Blob => CqlBlob(self.read_bytes(len)?), 468 | Boolean => CqlBoolean(match len { 469 | 1 => self.read_u8()? != 0, 470 | _len => return Err(Error::Protocol), 471 | }), 472 | //TODO 473 | Counter => return Err(Error::Unimplemented), 474 | Decimal => { 475 | let scale = self.read_int()?; 476 | let unscaled = self.read_cql_varint(len)?; 477 | CqlDecimal(scale, unscaled) 478 | } 479 | Double => unsafe { 480 | match len { 481 | 8 => CqlDouble(transmute(self.read_u64::()?)), 482 | _len => return Err(Error::Protocol), 483 | } 484 | }, 485 | Float => unsafe { 486 | match len { 487 | 4 => CqlFloat(transmute(self.read_u32::()?)), 488 | _len => return Err(Error::Protocol), 489 | } 490 | }, 491 | Int => match len { 492 | 4 => CqlInt(self.read_int()?), 493 | _len => return Err(Error::Protocol), 494 | }, 495 | Text => CqlText(self.read_cql_str_len(len)?), 496 | Timestamp => match len { 497 | 8 => CqlTimestamp(self.read_i64::()?), 498 | _len => return Err(Error::Protocol), 499 | }, 500 | UUID => match len { 501 | 16 => { 502 | let mut v = [0u8; 16]; 503 | self.read_full(&mut v)?; 504 | CqlUUID(v) 505 | } 506 | _len => return Err(Error::Protocol), 507 | }, 508 | VarChar => CqlVarChar(self.read_cql_str_len(len)?), 509 | VarInt => CqlVarInt(self.read_cql_varint(len)?), 510 | TimeUUID => match len { 511 | 16 => { 512 | let mut v = [0u8; 16]; 513 | self.read_full(&mut v)?; 514 | CqlTimeUUID(v) 515 | } 516 | _len => return Err(Error::Protocol), 517 | }, 518 | Inet => CqlInet(match len { 519 | 4 => { 520 | let mut v = [0u8; 4]; 521 | self.read_full(&mut v)?; 522 | std::net::IpAddr::V4(v.into()) 523 | } 524 | 16 => { 525 | let mut v = [0u8; 16]; 526 | self.read_full(&mut v)?; 527 | std::net::IpAddr::V6(v.into()) 528 | } 529 | _len => return Err(Error::Protocol), 530 | }), 531 | Custom | List | Map | Set | UDT | Tuple => { 532 | unreachable!("non-singular type on read_cql_col_ty: {:?}", col_type); 533 | } 534 | _ => { 535 | self.read_bytes(len)?; 536 | CqlUnknown 537 | } 538 | }; 539 | Ok(col) 540 | } 541 | 542 | fn read_cql_col(&mut self, col_type: &CqlColDescr) -> Result { 543 | let len = match self.read_int()? { 544 | -1 => return Ok(Value::CqlNull), 545 | len => len as usize, 546 | }; 547 | 548 | match *col_type { 549 | CqlColDescr::Custom(ref name) => { 550 | let data = self.read_bytes(len)?; 551 | Ok(Value::CqlCustom(name.clone(), data)) 552 | } 553 | CqlColDescr::Single(ty) => self.read_cql_col_ty(ty, len), 554 | CqlColDescr::List(ref ty) => { 555 | let n = self.read_int()? as usize; 556 | let mut l = Vec::with_capacity(n); 557 | for _ in 0..n { 558 | l.push(self.read_cql_col(ty)?); 559 | } 560 | Ok(Value::CqlList(l)) 561 | } 562 | CqlColDescr::Map(ref ty_tup) => { 563 | let n = self.read_int()? as usize; 564 | let mut l = Vec::with_capacity(n); 565 | for _ in 0..n { 566 | let key = self.read_cql_col(&ty_tup.0)?; 567 | let val = self.read_cql_col(&ty_tup.1)?; 568 | l.push((key, val)); 569 | } 570 | Ok(Value::CqlMap(l)) 571 | } 572 | CqlColDescr::Tuple(ref ty_list) => { 573 | let n = self.read_int()? as usize; 574 | let mut l = Vec::with_capacity(n); 575 | for _ in 0..n { 576 | let mut row = Vec::with_capacity(ty_list.len()); 577 | for ty in ty_list.iter() { 578 | row.push(self.read_cql_col(ty)?); 579 | } 580 | l.push(row) 581 | } 582 | Ok(Value::CqlTuple(l)) 583 | } 584 | CqlColDescr::Set(ref ty) => { 585 | let n = self.read_int()? as usize; 586 | let mut l = Vec::with_capacity(n); 587 | for _ in 0..n { 588 | l.push(self.read_cql_col(ty)?); 589 | } 590 | Ok(Value::CqlSet(l)) 591 | } 592 | } 593 | } 594 | } 595 | 596 | impl<'a, T: io::Read> CqlReader for T {} 597 | 598 | struct ShortString<'a>(&'a str); 599 | impl<'a> CqlSerializable for ShortString<'a> { 600 | fn serialize(&self, buf: &mut T) -> Result<()> { 601 | buf.write_u16::(self.0.len() as u16)?; 602 | buf.write_all(self.0.as_bytes())?; 603 | Ok(()) 604 | } 605 | fn len_(&self) -> usize { 606 | self.0.len() + 2 607 | } 608 | } 609 | 610 | struct LongString<'a>(&'a str); 611 | impl<'a> CqlSerializable for LongString<'a> { 612 | fn serialize(&self, buf: &mut T) -> Result<()> { 613 | buf.write_u32::(self.0.len() as u32)?; 614 | buf.write_all(self.0.as_bytes())?; 615 | Ok(()) 616 | } 617 | fn len_(&self) -> usize { 618 | self.0.len() + 4 619 | } 620 | } 621 | 622 | #[derive(Debug)] 623 | struct Pair { 624 | key: String, 625 | value: String, 626 | } 627 | 628 | impl CqlSerializable for Pair { 629 | fn serialize(&self, buf: &mut T) -> Result<()> { 630 | ShortString(&self.key).serialize(buf)?; 631 | ShortString(&self.value).serialize(buf) 632 | } 633 | 634 | fn len_(&self) -> usize { 635 | ShortString(&self.key).len_() + ShortString(&self.value).len_() 636 | } 637 | } 638 | 639 | #[derive(Debug)] 640 | pub struct StringMap { 641 | pairs: Vec, 642 | } 643 | 644 | impl CqlSerializable for StringMap { 645 | fn serialize(&self, buf: &mut T) -> Result<()> { 646 | buf.write_u16::(self.pairs.len() as u16)?; 647 | for pair in self.pairs.iter() { 648 | pair.serialize(buf)?; 649 | } 650 | Ok(()) 651 | } 652 | 653 | fn len_(&self) -> usize { 654 | let mut len = 2usize; 655 | for pair in self.pairs.iter() { 656 | len += pair.len_(); 657 | } 658 | len 659 | } 660 | } 661 | 662 | #[derive(Debug)] 663 | struct CqlColMetadata { 664 | keyspace: Option, 665 | table: Option, 666 | col_name: String, 667 | col_type: CqlColDescr, 668 | } 669 | 670 | #[derive(Debug)] 671 | enum CqlColDescr { 672 | Custom(String), 673 | Single(ColumnType), 674 | List(Box), 675 | Map(Box<(CqlColDescr, CqlColDescr)>), 676 | Set(Box), 677 | //UDT, 678 | Tuple(Box<[CqlColDescr]>), 679 | } 680 | 681 | #[derive(Debug)] 682 | pub struct Metadata { 683 | flags: u32, 684 | column_count: u32, 685 | keyspace: Option, 686 | table: Option, 687 | row_metadata: Vec, 688 | } 689 | 690 | #[derive(Clone, Debug)] 691 | pub enum Value { 692 | CqlNull, 693 | 694 | CqlCustom(String, Vec), 695 | CqlAscii(String), 696 | CqlBigint(i64), 697 | CqlBlob(Vec), 698 | CqlBoolean(bool), 699 | CqlCounter(u64), 700 | CqlDecimal(i32, i64), 701 | CqlDouble(f64), 702 | CqlFloat(f32), 703 | CqlInt(i32), 704 | CqlText(String), 705 | CqlTimestamp(i64), 706 | CqlUUID([u8; 16]), 707 | CqlVarChar(String), 708 | CqlVarInt(i64), 709 | CqlTimeUUID([u8; 16]), 710 | CqlInet(std::net::IpAddr), 711 | CqlList(Vec), 712 | CqlMap(Vec<(Value, Value)>), 713 | CqlSet(Vec), 714 | CqlUDT, 715 | CqlTuple(Vec>), 716 | CqlUnknown, 717 | } 718 | 719 | impl CqlSerializable for Value { 720 | fn serialize(&self, buf: &mut T) -> Result<()> { 721 | use Value::*; 722 | 723 | match self { 724 | CqlNull => { 725 | buf.write_i32::(-1)?; 726 | return Ok(()); 727 | } 728 | _ => (), 729 | } 730 | 731 | let len = self.len_() - 4; 732 | buf.write_u32::(len as u32)?; 733 | match self { 734 | CqlNull => unreachable!(), 735 | CqlCustom(ref _name, ref v) => buf.write_all(&v)?, 736 | CqlAscii(ref v) => buf.write_all(v.as_bytes())?, 737 | CqlBigint(v) => buf.write_i64::(*v)?, 738 | CqlBlob(ref v) => buf.write_all(&v)?, 739 | CqlBoolean(ref b) => buf.write_u8(*b as u8)?, 740 | CqlCounter(_) => return Err(Error::Unimplemented), 741 | CqlDecimal(_, _) => return Err(Error::Unimplemented), 742 | CqlDouble(v) => { 743 | let b: u64 = unsafe { transmute(*v) }; 744 | buf.write_u64::(b)?; 745 | } 746 | CqlFloat(v) => { 747 | let b: u32 = unsafe { transmute(*v) }; 748 | buf.write_u32::(b)?; 749 | } 750 | CqlInt(v) => buf.write_i32::(*v)?, 751 | CqlText(ref v) => buf.write_all(v.as_bytes())?, 752 | CqlTimestamp(v) => buf.write_i64::(*v)?, 753 | CqlUUID(ref v) => buf.write_all(v)?, 754 | CqlVarChar(ref v) => buf.write_all(v.as_bytes())?, 755 | CqlVarInt(v) => { 756 | //TODO: compress varint 757 | buf.write_i64::(*v)? 758 | } 759 | CqlTimeUUID(ref v) => buf.write_all(v)?, 760 | CqlInet(ref v) => match v { 761 | std::net::IpAddr::V4(v) => buf.write_all(&v.octets())?, 762 | std::net::IpAddr::V6(v) => buf.write_all(&v.octets())?, 763 | }, 764 | CqlList(v) => { 765 | buf.write_u32::(v.len() as u32)?; 766 | for item in v { 767 | item.serialize(buf)?; 768 | } 769 | } 770 | CqlMap(v) => { 771 | buf.write_u32::(v.len() as u32)?; 772 | for (ref k, ref v) in v { 773 | k.serialize(buf)?; 774 | v.serialize(buf)?; 775 | } 776 | } 777 | CqlSet(v) => { 778 | buf.write_u32::(v.len() as u32)?; 779 | for item in v { 780 | item.serialize(buf)?; 781 | } 782 | } 783 | CqlUDT => return Err(Error::Unimplemented), 784 | CqlTuple(v) => { 785 | buf.write_u32::(v.len() as u32)?; 786 | for tup in v { 787 | for item in tup { 788 | item.serialize(buf)?; 789 | } 790 | } 791 | } 792 | CqlUnknown => return Err(Error::Unimplemented), 793 | }; 794 | Ok(()) 795 | } 796 | 797 | fn len_(&self) -> usize { 798 | use std::mem::size_of; 799 | use Value::*; 800 | 801 | let body_len = match self { 802 | CqlNull => 0, 803 | CqlCustom(ref _name, ref v) => v.len(), 804 | CqlAscii(ref v) => v.len(), 805 | CqlBigint(_) => size_of::(), 806 | CqlBlob(ref v) => v.len(), 807 | CqlBoolean(_) => size_of::(), 808 | CqlCounter(_) => size_of::(), 809 | CqlDecimal(_, _) => unimplemented!(), 810 | CqlDouble(_) => size_of::(), 811 | CqlFloat(_) => size_of::(), 812 | CqlInt(_) => size_of::(), 813 | CqlText(ref v) => v.len(), 814 | CqlTimestamp(_) => size_of::(), 815 | CqlUUID(_) => 16, 816 | CqlVarChar(ref v) => v.len(), 817 | CqlVarInt(_) => { 818 | //TODO: compress varint 819 | size_of::() 820 | } 821 | CqlTimeUUID(_) => 16, 822 | CqlInet(ref v) => match *v { 823 | std::net::IpAddr::V4(_) => 4, 824 | std::net::IpAddr::V6(_) => 16, 825 | }, 826 | CqlList(v) => 4 + v.iter().map(|item| item.len_()).sum::(), 827 | CqlMap(v) => { 828 | 4 + v.iter() 829 | .map(|tup| tup.0.len_() + tup.1.len_()) 830 | .sum::() 831 | } 832 | CqlSet(v) => 4 + v.iter().map(|item| item.len_()).sum::(), 833 | CqlUDT => unimplemented!(), 834 | CqlTuple(v) => { 835 | 4 + v.iter() 836 | .map(|t| -> usize { t.iter().map(|c| c.len_()).sum::() }) 837 | .sum::() 838 | } 839 | CqlUnknown => unimplemented!(), 840 | }; 841 | 4 + body_len 842 | } 843 | } 844 | 845 | #[derive(Debug)] 846 | pub struct Row { 847 | cols: Vec, 848 | metadata: Rc, 849 | } 850 | 851 | impl Row { 852 | pub fn get_column(&self, col_name: &str) -> Option { 853 | self.metadata 854 | .row_metadata 855 | .iter() 856 | .position(|m| m.col_name == col_name) 857 | .map(|i| self.cols[i].clone()) 858 | } 859 | } 860 | 861 | #[derive(Debug)] 862 | pub struct Rows { 863 | metadata: Rc, 864 | rows: Vec, 865 | } 866 | 867 | struct BodyStartup { 868 | body: StringMap, 869 | } 870 | impl CqlSerializable for BodyStartup { 871 | fn serialize(&self, buf: &mut T) -> Result<()> { 872 | self.body.serialize(buf) 873 | } 874 | 875 | fn len_(&self) -> usize { 876 | self.body.len_() 877 | } 878 | } 879 | 880 | struct QueryParams { 881 | con: Consistency, 882 | params: Vec, 883 | } 884 | impl CqlSerializable for QueryParams { 885 | fn serialize(&self, buf: &mut T) -> Result<()> { 886 | buf.write_u16::(self.con.clone() as u16)?; 887 | buf.write_u8(0x01)?; 888 | 889 | buf.write_u16::(self.params.len() as u16)?; 890 | for v in &self.params { 891 | v.serialize(buf)?; 892 | } 893 | Ok(()) 894 | } 895 | fn len_(&self) -> usize { 896 | 3 + 2 + self.params.iter().map(|v| v.len_()).sum::() 897 | } 898 | } 899 | 900 | struct BodyQuery { 901 | query: String, 902 | params: QueryParams, 903 | } 904 | impl CqlSerializable for BodyQuery { 905 | fn serialize(&self, buf: &mut T) -> Result<()> { 906 | LongString(&self.query).serialize(buf)?; 907 | self.params.serialize(buf) 908 | } 909 | 910 | fn len_(&self) -> usize { 911 | LongString(&self.query).len_() + self.params.len_() 912 | } 913 | } 914 | 915 | struct BodyExecute { 916 | id: Vec, 917 | params: QueryParams, 918 | } 919 | impl CqlSerializable for BodyExecute { 920 | fn serialize(&self, buf: &mut T) -> Result<()> { 921 | buf.write_u16::(self.id.len() as u16)?; 922 | buf.write_all(&self.id)?; 923 | self.params.serialize(buf) 924 | } 925 | 926 | fn len_(&self) -> usize { 927 | 2 + self.id.len() + self.params.len_() 928 | } 929 | } 930 | 931 | struct BodyPrepare { 932 | query: String, 933 | } 934 | impl CqlSerializable for BodyPrepare { 935 | fn serialize(&self, buf: &mut T) -> Result<()> { 936 | LongString(&self.query).serialize(buf) 937 | } 938 | 939 | fn len_(&self) -> usize { 940 | LongString(&self.query).len_() 941 | } 942 | } 943 | 944 | struct BodyEmpty; 945 | impl CqlSerializable for BodyEmpty { 946 | fn serialize(&self, _buf: &mut T) -> Result<()> { 947 | Ok(()) 948 | } 949 | 950 | fn len_(&self) -> usize { 951 | 0 952 | } 953 | } 954 | 955 | type StringMultiMap = Vec<(String, Vec)>; 956 | 957 | #[derive(Debug)] 958 | pub enum ResponseBody { 959 | Error(u32, String), 960 | Ready, 961 | Auth(String), 962 | Supported(StringMultiMap), 963 | Result(ResponseResult), 964 | } 965 | 966 | #[derive(Debug)] 967 | pub enum ResponseResult { 968 | Void, 969 | Rows(Rows), 970 | Keyspace(String), 971 | Prepared(Vec, Metadata), 972 | SchemaChange(String, String, String, Option), 973 | } 974 | 975 | #[derive(Debug)] 976 | pub struct FrameHeader { 977 | version: u8, 978 | flags: u8, 979 | stream: i16, 980 | opcode: Opcode, 981 | } 982 | impl FrameHeader { 983 | fn new(stream: i16, opcode: Opcode) -> Self { 984 | Self { 985 | version: CQL_VERSION, 986 | flags: 0x00, 987 | stream, 988 | opcode, 989 | } 990 | } 991 | } 992 | 993 | #[derive(Debug)] 994 | struct Request { 995 | header: FrameHeader, 996 | body: B, 997 | } 998 | 999 | impl CqlSerializable for Request { 1000 | fn serialize(&self, buf: &mut T) -> Result<()> { 1001 | let header = &self.header; 1002 | buf.write_u8(header.version)?; 1003 | buf.write_u8(header.flags)?; 1004 | buf.write_i16::(header.stream)?; 1005 | buf.write_u8(header.opcode.clone() as u8)?; 1006 | 1007 | buf.write_u32::(self.body.len_() as u32)?; 1008 | self.body.serialize(buf)?; 1009 | Ok(()) 1010 | } 1011 | 1012 | fn len_(&self) -> usize { 1013 | self.body.len_() + 9 1014 | } 1015 | } 1016 | 1017 | #[derive(Debug)] 1018 | pub struct Response { 1019 | header: FrameHeader, 1020 | body: ResponseBody, 1021 | } 1022 | 1023 | fn startup() -> Request { 1024 | let body = StringMap { 1025 | pairs: vec![Pair { 1026 | key: "CQL_VERSION".to_owned(), 1027 | value: "3.0.0".to_owned(), 1028 | }], 1029 | }; 1030 | Request { 1031 | header: FrameHeader::new(1, Opcode::Startup), 1032 | body: BodyStartup { body }, 1033 | } 1034 | } 1035 | 1036 | /* 1037 | #[allow(unused)] 1038 | fn auth(creds: Vec>) -> Request { 1039 | return Request { 1040 | header: FrameHeader::new(1, Opcode::Auth), 1041 | body: RequestBody::RequestCred(creds), 1042 | }; 1043 | } 1044 | */ 1045 | 1046 | #[allow(unused)] 1047 | fn options() -> Request { 1048 | Request { 1049 | header: FrameHeader::new(1, Opcode::Opts), 1050 | body: BodyEmpty, 1051 | } 1052 | } 1053 | 1054 | fn query(stream: i16, query_str: &str, con: Consistency, params: Vec) -> Request { 1055 | Request { 1056 | header: FrameHeader::new(stream, Opcode::Query), 1057 | body: BodyQuery { 1058 | query: query_str.to_owned(), 1059 | params: QueryParams { con, params }, 1060 | }, 1061 | } 1062 | } 1063 | 1064 | fn prepare(stream: i16, query_str: &str) -> Request { 1065 | Request { 1066 | header: FrameHeader::new(stream, Opcode::Prepare), 1067 | body: BodyPrepare { 1068 | query: query_str.to_owned(), 1069 | }, 1070 | } 1071 | } 1072 | 1073 | fn execute(stream: i16, id: Vec, con: Consistency, params: Vec) -> Request { 1074 | Request { 1075 | header: FrameHeader::new(stream, Opcode::Execute), 1076 | body: BodyExecute { 1077 | id: id.clone(), 1078 | params: QueryParams { con, params }, 1079 | }, 1080 | } 1081 | } 1082 | 1083 | pub struct Client { 1084 | socket: TcpStream, 1085 | } 1086 | 1087 | impl Client { 1088 | pub fn new(addr: &str) -> Result { 1089 | let mut socket = TcpStream::connect(addr)?; 1090 | let msg_startup = startup().to_vec()?; 1091 | 1092 | socket.write_all(&msg_startup)?; 1093 | let response = socket.read_cql_response()?; 1094 | match response.body { 1095 | ResponseBody::Ready => Ok(Client { socket: socket }), 1096 | /* 1097 | Auth(_) => { 1098 | match(creds) { 1099 | Some(cred) => { 1100 | let msg_auth = Auth(cred); 1101 | msg_auth.serialize::(&buf); 1102 | let response = buf.read_cql_response(); 1103 | match response.body { 1104 | Ready => result::Ok(Client { socket: buf }), 1105 | Error(_, ref msg) => { 1106 | result::Err(Error(~"Error", copy *msg)) 1107 | } 1108 | _ => { 1109 | result::Err(Error(~"Error", ~"Server returned unknown message")) 1110 | }, 1111 | } 1112 | }, 1113 | None => { 1114 | result::Err(Error(~"Error", ~"Credential should be provided")) 1115 | }, 1116 | } 1117 | } 1118 | */ 1119 | ResponseBody::Auth(_) => Err(Error::Unimplemented), 1120 | _ => Err(Error::Protocol), 1121 | } 1122 | } 1123 | 1124 | pub fn options(&mut self) -> Result { 1125 | let msg = options().to_vec()?; 1126 | self.send(&msg) 1127 | } 1128 | 1129 | //TODO: signature 1130 | pub fn query( 1131 | &mut self, 1132 | query_str: &str, 1133 | con: Consistency, 1134 | values: Vec, 1135 | ) -> Result { 1136 | let msg = query(0, query_str, con, values).to_vec()?; 1137 | self.send(&msg) 1138 | } 1139 | 1140 | pub fn prepare(&mut self, query_str: &str) -> Result> { 1141 | let msg = prepare(0, query_str).to_vec()?; 1142 | let resp = self.send(&msg)?; 1143 | match resp.body { 1144 | ResponseBody::Result(res) => match res { 1145 | ResponseResult::Prepared(id, _) => Ok(id), 1146 | _ => Err(Error::Protocol), 1147 | }, 1148 | _ => Err(Error::Protocol), 1149 | } 1150 | } 1151 | 1152 | pub fn execute( 1153 | &mut self, 1154 | id: Vec, 1155 | con: Consistency, 1156 | values: Vec, 1157 | ) -> Result { 1158 | let msg = execute(0, id, con, values).to_vec()?; 1159 | self.send(&msg) 1160 | } 1161 | 1162 | fn send(&mut self, data: &[u8]) -> Result { 1163 | self.socket.write_all(data)?; 1164 | self.socket.read_cql_response() 1165 | } 1166 | } 1167 | 1168 | #[cfg(test)] 1169 | mod tests { 1170 | use super::*; 1171 | 1172 | #[test] 1173 | fn test_parse_varint() { 1174 | assert_eq!(0, parse_varint(&[0])); 1175 | assert_eq!(1, parse_varint(&[1])); 1176 | assert_eq!(127, parse_varint(&[0x7f])); 1177 | assert_eq!(128, parse_varint(&[0x00, 0x80])); 1178 | assert_eq!(129, parse_varint(&[0x00, 0x81])); 1179 | 1180 | assert_eq!(-1, parse_varint(&[0xff])); 1181 | assert_eq!(-128, parse_varint(&[0x80])); 1182 | assert_eq!(-129, parse_varint(&[0xff, 0x7f])); 1183 | } 1184 | 1185 | #[test] 1186 | fn resp_ready() { 1187 | let v = vec![131, 0, 0, 1, 2, 0, 0, 0, 0]; 1188 | let resp = v.as_slice().read_cql_response(); 1189 | assert!(resp.is_ok()) 1190 | } 1191 | 1192 | #[test] 1193 | fn resp_error() { 1194 | let v = vec![ 1195 | 131, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 36, 0, 0, 35, 67, 97, 110, 110, 111, 116, 32, 97, 1196 | 100, 100, 32, 101, 120, 105, 115, 116, 105, 110, 103, 32, 107, 101, 121, 115, 112, 97, 1197 | 99, 101, 32, 34, 114, 117, 115, 116, 34, 0, 4, 114, 117, 115, 116, 0, 0, 1198 | ]; 1199 | let resp = v.as_slice().read_cql_response(); 1200 | assert!(resp.is_ok()) 1201 | } 1202 | 1203 | #[test] 1204 | fn resp_schema_change() { 1205 | let v = vec![ 1206 | 131, 0, 0, 0, 8, 0, 0, 0, 29, 0, 0, 0, 5, 0, 7, 67, 82, 69, 65, 84, 69, 68, 0, 8, 75, 1207 | 69, 89, 83, 80, 65, 67, 69, 0, 4, 114, 117, 115, 116, 1208 | ]; 1209 | let resp = v.as_slice().read_cql_response(); 1210 | assert!(resp.is_ok()) 1211 | } 1212 | 1213 | #[test] 1214 | fn resp_schema_change_table() { 1215 | let v = vec![ 1216 | 131, 0, 0, 0, 8, 0, 0, 0, 32, 0, 0, 0, 5, 0, 7, 67, 82, 69, 65, 84, 69, 68, 0, 5, 84, 1217 | 65, 66, 76, 69, 0, 4, 114, 117, 115, 116, 0, 4, 116, 101, 115, 116, 1218 | ]; 1219 | let resp = v.as_slice().read_cql_response(); 1220 | assert!(resp.is_ok()) 1221 | } 1222 | 1223 | #[test] 1224 | fn resp_result_void() { 1225 | let v = vec![131, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 1]; 1226 | let resp = v.as_slice().read_cql_response(); 1227 | assert!(resp.is_ok()) 1228 | } 1229 | 1230 | #[test] 1231 | fn resp_result_select() { 1232 | let v = vec![ 1233 | 131, 0, 0, 0, 8, 0, 0, 0, 59, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 4, 114, 117, 115, 1234 | 116, 0, 4, 116, 101, 115, 116, 0, 2, 105, 100, 0, 13, 0, 5, 118, 97, 108, 117, 101, 0, 1235 | 8, 0, 0, 0, 1, 0, 0, 0, 4, 97, 115, 100, 102, 0, 0, 0, 4, 63, 158, 4, 25, 1236 | ]; 1237 | let resp = v.as_slice().read_cql_response(); 1238 | assert!(resp.is_ok()) 1239 | } 1240 | } 1241 | --------------------------------------------------------------------------------