cassandra-cql 1.0.2 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +5 -5
- data/lib/cassandra-cql.rb +10 -2
- data/lib/cassandra-cql/0.8.rb +7 -0
- data/lib/cassandra-cql/0.8/result.rb +23 -0
- data/lib/cassandra-cql/0.8/statement.rb +38 -0
- data/lib/cassandra-cql/1.0.rb +7 -0
- data/lib/cassandra-cql/1.0/result.rb +6 -0
- data/lib/cassandra-cql/1.0/statement.rb +6 -0
- data/lib/cassandra-cql/database.rb +21 -3
- data/lib/cassandra-cql/row.rb +0 -5
- data/lib/cassandra-cql/statement.rb +8 -5
- data/lib/cassandra-cql/types/date_type.rb +16 -0
- data/lib/cassandra-cql/version.rb +1 -1
- data/spec/comparator_spec.rb +37 -33
- data/spec/database_spec.rb +25 -0
- data/spec/row_spec.rb +0 -6
- data/spec/rowkey_spec.rb +25 -15
- data/spec/spec_helper.rb +7 -4
- data/spec/validation_spec.rb +25 -15
- data/vendor/0.8/gen-rb/cassandra.rb +2210 -0
- data/vendor/0.8/gen-rb/cassandra_constants.rb +10 -0
- data/vendor/0.8/gen-rb/cassandra_types.rb +811 -0
- data/vendor/{gen-rb → 1.0/gen-rb}/cassandra.rb +0 -0
- data/vendor/{gen-rb → 1.0/gen-rb}/cassandra_constants.rb +0 -0
- data/vendor/{gen-rb → 1.0/gen-rb}/cassandra_types.rb +0 -0
- metadata +30 -20
data/Rakefile
CHANGED
@@ -6,12 +6,12 @@ require 'rspec/core'
|
|
6
6
|
require 'rspec/core/rake_task'
|
7
7
|
|
8
8
|
CassandraBinaries = {
|
9
|
-
'0.8' => 'http://archive.apache.org/dist/cassandra/0.8.
|
10
|
-
'1.0' => 'http://archive.apache.org/dist/cassandra/1.0.
|
9
|
+
'0.8' => 'http://archive.apache.org/dist/cassandra/0.8.8/apache-cassandra-0.8.8-bin.tar.gz',
|
10
|
+
'1.0' => 'http://archive.apache.org/dist/cassandra/1.0.5/apache-cassandra-1.0.5-bin.tar.gz',
|
11
11
|
}
|
12
12
|
|
13
13
|
CASSANDRA_VERSION = ENV['CASSANDRA_VERSION'] || '1.0'
|
14
|
-
CASSANDRA_HOME = File.dirname(__FILE__) + '/tmp'
|
14
|
+
CASSANDRA_HOME = ENV['CASSANDRA_HOME'] || File.dirname(__FILE__) + '/tmp'
|
15
15
|
CASSANDRA_PIDFILE = ENV['CASSANDRA_PIDFILE'] || "#{CASSANDRA_HOME}/cassandra.pid"
|
16
16
|
|
17
17
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
@@ -111,12 +111,12 @@ namespace :cassandra do
|
|
111
111
|
env = setup_environment
|
112
112
|
sh("kill $(cat #{CASSANDRA_PIDFILE})")
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
desc "Delete all data files in #{CASSANDRA_HOME}"
|
116
116
|
task :clean do
|
117
117
|
sh("rm -rf #{File.join(CASSANDRA_HOME, "cassandra-#{CASSANDRA_VERSION}", 'data')}")
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
end
|
121
121
|
|
122
122
|
desc "Start Cassandra"
|
data/lib/cassandra-cql.rb
CHANGED
@@ -14,8 +14,13 @@ See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
15
15
|
=end
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
module CassandraCQL; end;
|
18
|
+
unless CassandraCQL.respond_to?(:CASSANDRA_VERSION)
|
19
|
+
require "cassandra-cql/1.0"
|
20
|
+
end
|
21
|
+
|
22
|
+
here = File.expand_path(File.dirname(__FILE__))
|
23
|
+
require "#{here}/../vendor/#{CassandraCQL.CASSANDRA_VERSION}/gen-rb/cassandra"
|
19
24
|
|
20
25
|
require 'bigdecimal'
|
21
26
|
require 'date'
|
@@ -39,3 +44,6 @@ require 'cassandra-cql/schema'
|
|
39
44
|
require 'cassandra-cql/statement'
|
40
45
|
require 'cassandra-cql/result'
|
41
46
|
require 'cassandra-cql/row'
|
47
|
+
|
48
|
+
require "cassandra-cql/#{CassandraCQL.CASSANDRA_VERSION}/result"
|
49
|
+
require "cassandra-cql/#{CassandraCQL.CASSANDRA_VERSION}/statement"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CassandraCQL
|
2
|
+
module V08
|
3
|
+
class ResultSchema < CassandraCQL::ResultSchema
|
4
|
+
def initialize(column_family)
|
5
|
+
type_slice = lambda {|type| type[type.rindex('.')+1..-1] }
|
6
|
+
|
7
|
+
@names = Hash.new(type_slice.call(column_family.comparator_type))
|
8
|
+
@values = Hash.new(type_slice.call(column_family.default_validation_class))
|
9
|
+
column_family.columns.each_pair do |name, type|
|
10
|
+
@values[name] = type_slice.call(type)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Result < CassandraCQL::Result
|
16
|
+
def initialize(result, column_family)
|
17
|
+
@result = result
|
18
|
+
@schema = ResultSchema.new(column_family) if rows?
|
19
|
+
@cursor = 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module CassandraCQL
|
2
|
+
module V08
|
3
|
+
class Statement < CassandraCQL::Statement
|
4
|
+
SCHEMA_CHANGE_RE = /\s*(create|drop|alter)\s+(\w+)/i
|
5
|
+
COLFAM_RE = /\s*select.*from\s+'?(\w+)/i
|
6
|
+
|
7
|
+
def execute(bind_vars=[], options={})
|
8
|
+
column_family = nil
|
9
|
+
if @statement =~ COLFAM_RE
|
10
|
+
column_family = @handle.schema.column_families[$1].dup
|
11
|
+
end
|
12
|
+
|
13
|
+
sanitized_query = self.class.sanitize(@statement, bind_vars)
|
14
|
+
compression_type = CassandraCQL::Thrift::Compression::NONE
|
15
|
+
if options[:compression]
|
16
|
+
compression_type = CassandraCQL::Thrift::Compression::GZIP
|
17
|
+
sanitized_query = Utility.compress(sanitized_query)
|
18
|
+
end
|
19
|
+
|
20
|
+
res = V08::Result.new(@handle.execute_cql_query(sanitized_query, compression_type), column_family)
|
21
|
+
|
22
|
+
# Change our keyspace if required
|
23
|
+
if @statement =~ KS_CHANGE_RE
|
24
|
+
@handle.keyspace = $1
|
25
|
+
elsif @statement =~ KS_DROP_RE
|
26
|
+
@handle.keyspace = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# We let ints be fetched for now because they'll probably be deprecated later
|
30
|
+
if res.void?
|
31
|
+
nil
|
32
|
+
else
|
33
|
+
res
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -42,6 +42,7 @@ module CassandraCQL
|
|
42
42
|
obj = self
|
43
43
|
@connection.add_callback(:post_connect) do
|
44
44
|
execute("USE #{@keyspace}")
|
45
|
+
@connection.login(@auth_request) if @auth_request
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
@@ -60,12 +61,19 @@ module CassandraCQL
|
|
60
61
|
|
61
62
|
def reset!
|
62
63
|
disconnect!
|
63
|
-
|
64
|
+
connect!
|
64
65
|
end
|
65
66
|
alias_method :reconnect!, :reset!
|
66
67
|
|
68
|
+
def statement_class
|
69
|
+
return @statement_class if @statement_class
|
70
|
+
|
71
|
+
version_module = 'V' + CassandraCQL.CASSANDRA_VERSION.gsub('.', '')
|
72
|
+
return @statement_class = CassandraCQL.const_get(version_module).const_get(:Statement)
|
73
|
+
end
|
74
|
+
|
67
75
|
def prepare(statement, options={}, &block)
|
68
|
-
stmt =
|
76
|
+
stmt = statement_class.new(self, statement)
|
69
77
|
if block_given?
|
70
78
|
yield stmt
|
71
79
|
else
|
@@ -74,7 +82,7 @@ module CassandraCQL
|
|
74
82
|
end
|
75
83
|
|
76
84
|
def execute(statement, *bind_vars)
|
77
|
-
result =
|
85
|
+
result = statement_class.new(self, statement).execute(bind_vars)
|
78
86
|
if block_given?
|
79
87
|
yield result
|
80
88
|
else
|
@@ -103,5 +111,15 @@ module CassandraCQL
|
|
103
111
|
# TODO: This should be replaced with a CQL call that doesn't exist yet
|
104
112
|
Schema.new(@connection.describe_keyspace(@keyspace))
|
105
113
|
end
|
114
|
+
|
115
|
+
def login!(username, password)
|
116
|
+
request = CassandraCQL::Thrift::AuthenticationRequest.new
|
117
|
+
request.credentials = {'username' => username, 'password' => password}
|
118
|
+
ret = @connection.login(request)
|
119
|
+
# To avoid a double login on the initial connect, we set
|
120
|
+
# @auth_request after the first successful login.
|
121
|
+
@auth_request = request
|
122
|
+
ret
|
123
|
+
end
|
106
124
|
end
|
107
125
|
end
|
data/lib/cassandra-cql/row.rb
CHANGED
@@ -23,7 +23,6 @@ module CassandraCQL
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def [](obj)
|
26
|
-
# Rows include the row key so we skip the first one
|
27
26
|
column_index = obj.kind_of?(Fixnum) ? obj : column_names.index(obj)
|
28
27
|
return nil if column_index.nil?
|
29
28
|
column_values[column_index]
|
@@ -51,9 +50,5 @@ module CassandraCQL
|
|
51
50
|
def to_hash
|
52
51
|
Hash[([column_names, column_values]).transpose]
|
53
52
|
end
|
54
|
-
|
55
|
-
def key
|
56
|
-
ColumnFamily.cast(@row.key, @schema.values[@row.key])
|
57
|
-
end
|
58
53
|
end
|
59
54
|
end
|
@@ -31,18 +31,21 @@ module CassandraCQL
|
|
31
31
|
@handle = handle
|
32
32
|
prepare(statement)
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def prepare(statement)
|
36
36
|
@statement = statement
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def execute(bind_vars=[], options={})
|
40
|
+
sanitized_query = self.class.sanitize(@statement, bind_vars)
|
41
|
+
compression_type = CassandraCQL::Thrift::Compression::NONE
|
40
42
|
if options[:compression]
|
41
|
-
|
42
|
-
|
43
|
-
res = Result.new(@handle.execute_cql_query(self.class.sanitize(@statement, bind_vars), CassandraCQL::Thrift::Compression::NONE))
|
43
|
+
compression_type = CassandraCQL::Thrift::Compression::GZIP
|
44
|
+
sanitized_query = Utility.compress(sanitized_query)
|
44
45
|
end
|
45
46
|
|
47
|
+
res = Result.new(@handle.execute_cql_query(sanitized_query, compression_type))
|
48
|
+
|
46
49
|
# Change our keyspace if required
|
47
50
|
if @statement =~ KS_CHANGE_RE
|
48
51
|
@handle.keyspace = $1
|
@@ -1,3 +1,19 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2011 Inside Systems, Inc.
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
you may not use this file except in compliance with the License.
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
See the License for the specific language governing permissions and
|
14
|
+
limitations under the License.
|
15
|
+
=end
|
16
|
+
|
1
17
|
module CassandraCQL
|
2
18
|
module Types
|
3
19
|
class DateType < AbstractType
|
data/spec/comparator_spec.rb
CHANGED
@@ -97,23 +97,24 @@ describe "Comparator Roundtrip tests" do
|
|
97
97
|
end
|
98
98
|
res.class.should eq(Time)
|
99
99
|
end
|
100
|
-
|
101
100
|
end
|
102
101
|
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
if CASSANDRA_VERSION.to_f >= 1.0
|
103
|
+
context "with decimal comparator" do
|
104
|
+
let(:cf_name) { "comparator_cf_decimal" }
|
105
|
+
before(:each) { create_column_family(cf_name, 'DecimalType') }
|
106
106
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
107
|
+
def test_for_value(value)
|
108
|
+
create_and_fetch_column(cf_name, value).should eq(value)
|
109
|
+
create_and_fetch_column(cf_name, value*-1).should eq(value*-1)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should return a small decimal" do
|
113
|
+
test_for_value(15.333)
|
114
|
+
end
|
115
|
+
it "should return a huge decimal" do
|
116
|
+
test_for_value(BigDecimal.new('129182739481237481341234123411.1029348102934810293481039'))
|
117
|
+
end
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
@@ -151,26 +152,29 @@ describe "Comparator Roundtrip tests" do
|
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
154
|
-
|
155
|
-
|
156
|
-
|
155
|
+
if CASSANDRA_VERSION.to_f >= 1.0
|
156
|
+
#Int32Type was added in 1.0 (CASSANDRA-3031)
|
157
|
+
context "with int comparator" do
|
158
|
+
let(:cf_name) { "comparator_cf_int" }
|
159
|
+
before(:each) { create_column_family(cf_name, 'Int32Type') }
|
157
160
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
161
|
+
def test_for_value(value)
|
162
|
+
create_and_fetch_column(cf_name, value).should eq(value)
|
163
|
+
create_and_fetch_column(cf_name, value*-1).should eq(value*-1)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should properly convert integer values that fit into 1 byte" do
|
167
|
+
test_for_value(1)
|
168
|
+
end
|
169
|
+
it "should properly convert integer values that fit into 2 bytes" do
|
170
|
+
test_for_value(2**8 + 80)
|
171
|
+
end
|
172
|
+
it "should properly convert integer values that fit into 3 bytes" do
|
173
|
+
test_for_value(2**16 + 622)
|
174
|
+
end
|
175
|
+
it "should properly convert integer values that fit into 4 bytes" do
|
176
|
+
test_for_value(2**24 + 45820)
|
177
|
+
end
|
174
178
|
end
|
175
179
|
end
|
176
180
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path('spec_helper.rb', File.dirname(__FILE__))
|
2
|
+
include CassandraCQL
|
3
|
+
|
4
|
+
describe "Database" do
|
5
|
+
before do
|
6
|
+
@connection = setup_cassandra_connection
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "reset!" do
|
10
|
+
it "should create a new connection" do
|
11
|
+
@connection.should_receive(:connect!)
|
12
|
+
@connection.reset!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "login!" do
|
17
|
+
it "should call login! on connection" do
|
18
|
+
creds = { 'username' => 'myuser', 'password' => 'mypass' }
|
19
|
+
@connection.connection.should_receive(:login) do |auth|
|
20
|
+
auth.credentials.should eq(creds)
|
21
|
+
end
|
22
|
+
@connection.login!(creds['username'], creds['password'])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/row_spec.rb
CHANGED
@@ -34,12 +34,6 @@ describe "basic methods" do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
context "key" do
|
38
|
-
it "should return the cql_result row key" do
|
39
|
-
@row.key.should eq(@row.row.key)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
37
|
context "checking casting" do
|
44
38
|
it "should return column_values for to_a" do
|
45
39
|
@row.to_a.should eq(@row.column_values)
|
data/spec/rowkey_spec.rb
CHANGED
@@ -56,7 +56,11 @@ describe "Validation Roundtrip tests" do
|
|
56
56
|
|
57
57
|
context "with blob row_key_validation" do
|
58
58
|
let(:cf_name) { "row_key_validation_cf_blob" }
|
59
|
-
|
59
|
+
if CASSANDRA_VERSION.to_f == 0.8
|
60
|
+
before(:each) { create_column_family(cf_name, 'bytea') }
|
61
|
+
else
|
62
|
+
before(:each) { create_column_family(cf_name, 'blob') }
|
63
|
+
end
|
60
64
|
|
61
65
|
it "should return a blob" do
|
62
66
|
bytes = "binary\x00"
|
@@ -78,20 +82,22 @@ describe "Validation Roundtrip tests" do
|
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
81
|
-
|
82
|
-
|
83
|
-
|
85
|
+
if CASSANDRA_VERSION.to_f >= 1.0
|
86
|
+
context "with decimal row_key_validation" do
|
87
|
+
let(:cf_name) { "row_key_validation_cf_decimal" }
|
88
|
+
before(:each) { create_column_family(cf_name, 'decimal') }
|
84
89
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
90
|
+
def test_for_value(value)
|
91
|
+
create_and_fetch_column(cf_name, value*-1).should eq(value*-1)
|
92
|
+
create_and_fetch_column(cf_name, value).should eq(value)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should return a small decimal" do
|
96
|
+
test_for_value(15.333)
|
97
|
+
end
|
98
|
+
it "should return a huge decimal" do
|
99
|
+
test_for_value(BigDecimal.new('129182739481237481341234123411.1029348102934810293481039'))
|
100
|
+
end
|
95
101
|
end
|
96
102
|
end
|
97
103
|
|
@@ -167,7 +173,11 @@ describe "Validation Roundtrip tests" do
|
|
167
173
|
|
168
174
|
context "with timestamp row_key_validation" do
|
169
175
|
let(:cf_name) { "row_key_validation_cf_timestamp" }
|
170
|
-
|
176
|
+
if CASSANDRA_VERSION.to_f == 0.8
|
177
|
+
before(:each) { create_column_family(cf_name, 'date') }
|
178
|
+
else
|
179
|
+
before(:each) { create_column_family(cf_name, 'timestamp') }
|
180
|
+
end
|
171
181
|
|
172
182
|
it "should return a timestamp" do
|
173
183
|
uuid = UUID.new
|