Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:simplereach/helenus

* 'master' of github.com:simplereach/helenus:
  fix failling test
  add history lines for merged requests
  wrong version
  bump C* versions
  bump version
  fix deserialization of collection types
  fix (datetype): uses long if post-epoch date, datestring for pre, to account for https://issues.apache.org/jira/browse/CASSANDRA-6212
  Fix Thrift readme example
  fix: escapes date as iso string in utc to work with all versions of cql
  fix: handles pre-epoch dates for cassandra < 2.*
  fix: separated the different timestamps (current, future, pre-epoch) out into their own tests
  fix: escapeCql handles invalid date objects tests: adds tests for timestamps
  feat: adds Date support to escapeCQL function
  • Loading branch information...
commit 26de4fd18a47a4aeeb0a97a66226d80e9f330db2 2 parents 9f5b60a + 425d24c
@devdazed devdazed authored
View
4 .travis.yml
@@ -5,8 +5,8 @@ node_js:
env:
- CASSANDRA_VERSION=1.1.12
- - CASSANDRA_VERSION=1.2.11
- - CASSANDRA_VERSION=2.0.2
+ - CASSANDRA_VERSION=1.2.12
+ - CASSANDRA_VERSION=2.0.3
before_install:
- sudo apt-get install libjna-java
View
10 History.md
@@ -236,4 +236,12 @@
0.6.7 / 2013-11-04
==================
- * Add support for tunable consistency on a per-query basis for CQL [ @mrvisser #130 ]
+ * Add support for tunable consistency on a per-query basis for CQL [ @mrvisser #130 ]
+
+ 0.6.8 / 2013-12-15
+==================
+
+ * Fixed issues with Deserialization of collection types
+ * README Updates [ @Rudeg #132 ]
+ * Add Date Type Support [ @tim-dev #131 ]
+
View
2  Readme.md
@@ -99,7 +99,7 @@ If you do not want to use CQL, you can make calls using the thrift driver
//get what we just put in
//the driver will return a helenus.Row object just like CQL
- cf.get('foo', {consistency:helenus.ConsistencyLevel.ONE} function(err, row){
+ cf.get('foo', {consistency:helenus.ConsistencyLevel.ONE}, function(err, row){
if(err){
throw(err);
}
View
22 lib/connection.js
@@ -16,6 +16,10 @@ var util = require('util'),
* @memberOf Connection
*/
var NOOP = function(){};
+/**
+ * Adds zero left-padding to numbers, when necessary
+ */
+var zero_pad = function(n){ return n < 10 ? "0" + n : n }
/**
* Default port for cassandra
@@ -102,6 +106,24 @@ function escapeCQL(val) {
return val.toString();
}
+ if(val instanceof Date){
+ _val = val.getTime()
+ if(isNaN(_val)){
+ throw errors.create(new Error('Invalid Date'));
+ }
+ // C* < 2.0.2 doesn't work with negative longs
+ if(_val<0){
+ return "'" + val.getUTCFullYear()
+ + '-' + zero_pad(val.getUTCMonth() + 1)
+ + '-' + zero_pad(val.getUTCDate())
+ + ' ' + zero_pad(val.getUTCHours())
+ + ':' + zero_pad(val.getUTCMinutes())
+ + ':' + zero_pad(val.getUTCSeconds())
+ + "'"
+ }
+ return _val
+ }
+
if(typeof val === 'number' || typeof val === 'boolean'){
return val.toString();
}
View
44 lib/marshal/deserializers.js
@@ -46,50 +46,6 @@ Deserializers.decodeBinary = function(val){
return new Buffer(val, 'binary');
};
-// https://github.com/simplereach/helenus/issues/96
-Deserializers.decodeCollection = function (deserializers) {
- return function(str) {
- if(str === null || str === undefined) {
- return null;
- }
-
- var buf = new Buffer(str, 'binary'),
- pos = 3, len, keys = [], vals = [], i = 0, result;
-
- while(pos < buf.length){
- if(i > deserializers.length - 1) {
- i = 0;
- }
-
- len = buf.readUInt16BE(pos);
- pos += 2;
-
- if(i == 0 && deserializers.length == 2) {
- keys.push(deserializers[i](buf.slice(pos, len + pos)));
- }
- else if(i == 0 && deserializers.length == 1 || i == 1 && deserializers.length == 2) {
- vals.push(deserializers[i](buf.slice(pos, len + pos)));
- }
-
- i += 1;
-
- pos += len;
- }
-
- if(keys.length === vals.length) {
- result = {}
- for(var i = 0, len = keys.length; i < len; i++) {
- result[keys[i]] = vals[i];
- }
- }
- else {
- result = vals;
- }
-
- return result;
- };
-};
-
/**
* Decodes a Long (Int64)
* @static
View
105 lib/marshal/index.js
@@ -58,15 +58,18 @@ function getType(str){
}
function getMapType(str){
- return 'MapType';
+ var index = str.indexOf('<');
+ return index > 0 ? getCompositeTypes(str.substring(index + 1, str.length - 1)) : str;
}
function getSetType(str){
- return 'SetType';
+ var index = str.indexOf('<');
+ return index > 0 ? getType(str.substring(index + 1, str.length - 1)) : str;
}
function getListType(str){
- return 'ListType';
+ var index = str.indexOf('<');
+ return index > 0 ? getType(str.substring(index + 1, str.length - 1)) : str;
}
@@ -108,23 +111,23 @@ function getCompositeTypes(str){
*/
function parseTypeString(type){
if (type.indexOf('CompositeType') > -1){
- return getCompositeTypes(type);
+ return { baseType: 'CompositeType', subType: getCompositeTypes(type) };
} else if(type.indexOf('ReversedType') > -1){
- return getType(getInnerType(type));
+ return { baseType: 'ReversedType', subType: getType(getInnerType(type)) };
}
else if(type.indexOf('org.apache.cassandra.db.marshal.SetType') > -1){
- return getSetType(type);
+ return { baseType: 'SetType', subType: getType(getInnerType(type)) };
}
else if(type.indexOf('org.apache.cassandra.db.marshal.ListType') > -1){
- return getListType(type);
+ return { baseType: 'ListType', subType: getType(getInnerType(type)) };
}
else if(type.indexOf('org.apache.cassandra.db.marshal.MapType') > -1){
- return getMapType(type);
+ return { baseType: 'MapType', subType: getCompositeTypes(type) };
}
else if(type === null || type === undefined) {
- return 'BytesType';
+ return { baseType: null, subType: 'BytesType' };
} else {
- return getType(type);
+ return {baseType: null, subType: getType(type) };
}
}
@@ -224,20 +227,74 @@ function compositeDeserializer(deserializers){
}
/**
+ * Descodes SetType and ListType
+ * @private
+ * @memberOf Marshal
+ */
+function listDeserializer(deserializer){
+ return function(str){
+ var buf = new Buffer(str, 'binary'),
+ pos = 2, len, vals = [], key, value;
+
+ while( pos < buf.length){
+ len = buf.readUInt16BE(pos);
+ pos += 2
+ value = deserializer(buf.slice(pos, len + pos));
+ pos += len
+
+ vals.push(value)
+ }
+
+ return vals;
+ };
+}
+
+/**
+ * Descodes MapType
+ * @private
+ * @memberOf Marshal
+ */
+function mapDeserializer(deserializers){
+ return function(str){
+ var buf = new Buffer(str, 'binary'),
+ pos = 2, len, vals = {}, key, value;
+
+ while( pos < buf.length){
+ len = buf.readUInt16BE(pos);
+ pos += 2
+ key = deserializers[0](buf.slice(pos, len + pos))
+ pos += len
+
+ len = buf.readUInt16BE(pos);
+ pos += 2;
+ value = deserializers[1](buf.slice(pos, len + pos))
+ pos += len
+ vals[key] = value;
+ }
+
+ return vals;
+ };
+}
+
+/**
* Gets the serializer(s) for a specific type
* @private
* @memberOf Marshal
*/
function getSerializer(type){
- if (Array.isArray(type)){
- var i = 0, typeLength = type.length, serializers = [];
+ if (type.baseType === 'CompositeType'){
+ var i = 0, typeLength = type.subType.length, serializers = [];
for(; i < typeLength; i += 1){
- serializers.push( getSerializer(type[i]));
+ serializers.push( getSerializer(type.subType[i]));
}
return compositeSerializer(serializers);
+ } else if (type.baseType === 'ListType' || type.baseType == 'SetType' || type.baseType === 'MapType'){
+ return function(){
+ throw('Serializing MapType, ListType and SetType is not currently supported');
+ }
} else {
- return TYPES[type.replace(/^\s+|\s+$/g,'')].ser;
+ return TYPES[type.subType.replace(/^\s+|\s+$/g,'')].ser;
}
}
@@ -247,16 +304,23 @@ function getSerializer(type){
* @memberOf Marshal
*/
function getDeserializer(type){
- if (Array.isArray(type)){
- var i = 0, typeLength = type.length, deserializers = [];
+ if (type.baseType === 'CompositeType'){
+ var i = 0, typeLength = type.subType.length, deserializers = [];
+
for(; i < typeLength; i += 1){
- deserializers.push( getDeserializer(type[i]));
+ deserializers.push( TYPES[type.subType[i].subType].de );
}
return compositeDeserializer(deserializers);
+ } else if (type.baseType === 'ListType' || type.baseType == 'SetType'){
+ var subtypeDeserializer = getDeserializer({ baseType:null, subType: type.subType });
+ return listDeserializer(subtypeDeserializer);
+ } else if (type.baseType === 'MapType'){
+ var subtypeDeserializers = type.subType.map(function(t){ return getDeserializer(t) });
+ return mapDeserializer(subtypeDeserializers);
} else {
return function(val) {
- return val !== null ? TYPES[type].de(val) : null;
+ return val !== null ? TYPES[type.subType].de(val) : null;
};
}
}
@@ -269,7 +333,10 @@ function getDeserializer(type){
var Marshal = function(type){
var parsedType = parseTypeString(type);
this.type = parsedType;
- this.isComposite = Array.isArray(parsedType);
+ this.isComposite = parsedType.baseType === 'CompositeType'
+ this.isMap = parsedType.baseType === 'MapType';
+ this.isList = parsedType.baseType === 'ListType';
+ this.isSet = parsedType.baseType === 'SetType';
/**
* Serializes data for the type specified
View
2  package.json
@@ -1,6 +1,6 @@
{
"name": "helenus"
- , "version": "0.6.7"
+ , "version": "0.6.8"
, "description": "NodeJS Bindings for Cassandra"
, "keywords": ["cassandra"]
, "author": "Russell Bradberry <rbradberry@gmail.com>"
View
19 test/cql3.js
@@ -67,6 +67,14 @@ function testResultless(){
return testCql.apply(testCql, args);
}
+function makeDates(dates_array){
+ var ret = [], i = 0, len = dates_array.length;
+ for(i;i<len;i++){
+ ret.push(new Date(dates_array[i]));
+ }
+ return ret;
+}
+
module.exports = {
'setUp':function(test, assert){
Helenus = require('helenus');
@@ -293,6 +301,17 @@ module.exports = {
assert.strictEqual(res[0].get('varnumber').value, -8388607);//test a 3 byte-long variable integer
}),
+ 'test cql timestamp create table':testResultless(config['timestamp_create']),
+ 'test cql timestamp update1':testResultless(config['timestamp_update#cql'], makeDates(config['timestamp_update#vals1'])),
+ 'test cql timestamp update2':testResultless(config['timestamp_update#cql'], makeDates(config['timestamp_update#vals2'])),
+ 'test cql timestamp update3':testResultless(config['timestamp_update#cql'], makeDates(config['timestamp_update#vals3'])),
+ 'test cql timestamp invalid date': function(test, assert) {
+ assert.throws(function(){
+ conn.cql(config['timestamp_invalid#cql'], makeDates(config['timestamp_invalid#vals']))
+ }, Error)
+ test.finish();
+ },
+
'test cql drop keyspace':testResultless(config['drop_ks#cql']),
'tearDown':function(test, assert){
View
7 test/helpers/cql3.json
@@ -51,6 +51,13 @@
"integers_select2#cql" : "SELECT * FROM integers where number = -1",
"integers_select3#cql" : "SELECT * FROM integers where number = -2",
+ "timestamp_create" : "CREATE TABLE timestamps (ts1 timestamp, ts2 timestamp, PRIMARY KEY (ts1))",
+ "timestamp_update#cql" : "UPDATE timestamps SET ts2 = ? WHERE ts1 = ?",
+ "timestamp_update#vals1" : [null, null],
+ "timestamp_update#vals2" : [null, "2100-01-01"],
+ "timestamp_update#vals3" : [null, "1969-07-20"],
+ "timestamp_invalid#cql" : "UPDATE timestamps SET ts2 = ? ts1 old = ?",
+ "timestamp_invalid#vals" : [null, "THIS ISNT A DATE"],
"prepare#cql" : "SELECT * FROM ? WHERE KEY = ?",
"error#cql" : "SOME INVALID CQL"
Please sign in to comment.
Something went wrong with that request. Please try again.