cassandra-cql 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|