hallelujah-cassandra-cql 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +203 -0
- data/README.rdoc +71 -0
- data/Rakefile +151 -0
- data/hallelujah-cassandra-cql.gemspec +33 -0
- data/lib/cassandra-cql.rb +49 -0
- 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/1.1.rb +7 -0
- data/lib/cassandra-cql/1.1/result.rb +6 -0
- data/lib/cassandra-cql/1.1/statement.rb +6 -0
- data/lib/cassandra-cql/database.rb +127 -0
- data/lib/cassandra-cql/result.rb +133 -0
- data/lib/cassandra-cql/row.rb +54 -0
- data/lib/cassandra-cql/schema.rb +108 -0
- data/lib/cassandra-cql/statement.rb +116 -0
- data/lib/cassandra-cql/types/abstract_type.rb +47 -0
- data/lib/cassandra-cql/types/ascii_type.rb +25 -0
- data/lib/cassandra-cql/types/boolean_type.rb +25 -0
- data/lib/cassandra-cql/types/bytes_type.rb +21 -0
- data/lib/cassandra-cql/types/date_type.rb +25 -0
- data/lib/cassandra-cql/types/decimal_type.rb +25 -0
- data/lib/cassandra-cql/types/double_type.rb +25 -0
- data/lib/cassandra-cql/types/float_type.rb +25 -0
- data/lib/cassandra-cql/types/integer_type.rb +27 -0
- data/lib/cassandra-cql/types/long_type.rb +27 -0
- data/lib/cassandra-cql/types/utf8_type.rb +25 -0
- data/lib/cassandra-cql/types/uuid_type.rb +27 -0
- data/lib/cassandra-cql/utility.rb +37 -0
- data/lib/cassandra-cql/uuid.rb +21 -0
- data/lib/cassandra-cql/version.rb +19 -0
- data/spec/column_family_spec.rb +105 -0
- data/spec/comparator_spec.rb +249 -0
- data/spec/conf/0.8/cassandra.in.sh +41 -0
- data/spec/conf/0.8/cassandra.yaml +61 -0
- data/spec/conf/0.8/log4j-server.properties +40 -0
- data/spec/conf/0.8/schema.txt +10 -0
- data/spec/conf/1.0/cassandra.in.sh +41 -0
- data/spec/conf/1.0/cassandra.yaml +416 -0
- data/spec/conf/1.0/log4j-server.properties +40 -0
- data/spec/conf/1.0/schema.txt +10 -0
- data/spec/conf/1.1/cassandra.in.sh +41 -0
- data/spec/conf/1.1/cassandra.yaml +560 -0
- data/spec/conf/1.1/log4j-server.properties +44 -0
- data/spec/conf/1.1/schema.txt +10 -0
- data/spec/database_spec.rb +25 -0
- data/spec/result_spec.rb +173 -0
- data/spec/row_spec.rb +49 -0
- data/spec/rowkey_spec.rb +233 -0
- data/spec/schema_spec.rb +51 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/statement_spec.rb +226 -0
- data/spec/utility_spec.rb +26 -0
- data/spec/uuid_spec.rb +26 -0
- data/spec/validation_spec.rb +272 -0
- 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/1.0/gen-rb/cassandra.rb +2212 -0
- data/vendor/1.0/gen-rb/cassandra_constants.rb +10 -0
- data/vendor/1.0/gen-rb/cassandra_types.rb +854 -0
- data/vendor/1.1/gen-rb/cassandra.rb +2511 -0
- data/vendor/1.1/gen-rb/cassandra_constants.rb +13 -0
- data/vendor/1.1/gen-rb/cassandra_types.rb +928 -0
- metadata +230 -0
@@ -0,0 +1,49 @@
|
|
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
|
+
|
17
|
+
module CassandraCQL; end;
|
18
|
+
unless CassandraCQL.respond_to?(:CASSANDRA_VERSION)
|
19
|
+
require "cassandra-cql/1.1"
|
20
|
+
end
|
21
|
+
|
22
|
+
here = File.expand_path(File.dirname(__FILE__))
|
23
|
+
require "#{here}/../vendor/#{CassandraCQL.CASSANDRA_VERSION}/gen-rb/cassandra"
|
24
|
+
|
25
|
+
require 'bigdecimal'
|
26
|
+
require 'date'
|
27
|
+
require 'thrift_client'
|
28
|
+
require 'cassandra-cql/types/abstract_type'
|
29
|
+
require 'cassandra-cql/types/ascii_type'
|
30
|
+
require 'cassandra-cql/types/boolean_type'
|
31
|
+
require 'cassandra-cql/types/bytes_type'
|
32
|
+
require 'cassandra-cql/types/date_type'
|
33
|
+
require 'cassandra-cql/types/decimal_type'
|
34
|
+
require 'cassandra-cql/types/double_type'
|
35
|
+
require 'cassandra-cql/types/float_type'
|
36
|
+
require 'cassandra-cql/types/integer_type'
|
37
|
+
require 'cassandra-cql/types/long_type'
|
38
|
+
require 'cassandra-cql/types/utf8_type'
|
39
|
+
require 'cassandra-cql/types/uuid_type'
|
40
|
+
require 'cassandra-cql/utility'
|
41
|
+
require 'cassandra-cql/uuid'
|
42
|
+
require 'cassandra-cql/database'
|
43
|
+
require 'cassandra-cql/schema'
|
44
|
+
require 'cassandra-cql/statement'
|
45
|
+
require 'cassandra-cql/result'
|
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
|
@@ -0,0 +1,127 @@
|
|
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
|
+
|
17
|
+
module CassandraCQL
|
18
|
+
module Error
|
19
|
+
class InvalidRequestException < Exception; end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Database
|
23
|
+
attr_reader :connection, :schema, :keyspace
|
24
|
+
|
25
|
+
def initialize(servers, options={}, thrift_client_options={})
|
26
|
+
@options = {
|
27
|
+
:keyspace => 'system'
|
28
|
+
}.merge(options)
|
29
|
+
|
30
|
+
@thrift_client_options = {
|
31
|
+
:exception_class_overrides => CassandraCQL::Thrift::InvalidRequestException
|
32
|
+
}.merge(thrift_client_options)
|
33
|
+
|
34
|
+
@keyspace = @options[:keyspace]
|
35
|
+
@cql_version = @options[:cql_version]
|
36
|
+
@servers = servers
|
37
|
+
connect!
|
38
|
+
execute("USE #{@keyspace}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def connect!
|
42
|
+
@connection = ThriftClient.new(CassandraCQL::Thrift::Client, @servers, @thrift_client_options)
|
43
|
+
obj = self
|
44
|
+
@connection.add_callback(:post_connect) do
|
45
|
+
@connection.set_cql_version(@cql_version) if @cql_version
|
46
|
+
execute("USE #{@keyspace}")
|
47
|
+
@connection.login(@auth_request) if @auth_request
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def disconnect!
|
52
|
+
@connection.disconnect! if active?
|
53
|
+
end
|
54
|
+
|
55
|
+
def active?
|
56
|
+
# TODO: This should be replaced with a CQL call that doesn't exist yet
|
57
|
+
@connection.describe_version
|
58
|
+
true
|
59
|
+
rescue Exception
|
60
|
+
false
|
61
|
+
end
|
62
|
+
alias_method :ping, :active?
|
63
|
+
|
64
|
+
def reset!
|
65
|
+
disconnect!
|
66
|
+
connect!
|
67
|
+
end
|
68
|
+
alias_method :reconnect!, :reset!
|
69
|
+
|
70
|
+
def statement_class
|
71
|
+
return @statement_class if @statement_class
|
72
|
+
|
73
|
+
version_module = 'V' + CassandraCQL.CASSANDRA_VERSION.gsub('.', '')
|
74
|
+
return @statement_class = CassandraCQL.const_get(version_module).const_get(:Statement)
|
75
|
+
end
|
76
|
+
|
77
|
+
def prepare(statement, options={}, &block)
|
78
|
+
stmt = statement_class.new(self, statement)
|
79
|
+
if block_given?
|
80
|
+
yield stmt
|
81
|
+
else
|
82
|
+
stmt
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def execute(statement, *bind_vars)
|
87
|
+
result = statement_class.new(self, statement).execute(bind_vars)
|
88
|
+
if block_given?
|
89
|
+
yield result
|
90
|
+
else
|
91
|
+
result
|
92
|
+
end
|
93
|
+
rescue CassandraCQL::Thrift::InvalidRequestException
|
94
|
+
raise Error::InvalidRequestException.new($!.why)
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute_cql_query(cql, compression=CassandraCQL::Thrift::Compression::NONE)
|
98
|
+
@connection.execute_cql_query(cql, compression)
|
99
|
+
rescue CassandraCQL::Thrift::InvalidRequestException
|
100
|
+
raise Error::InvalidRequestException.new($!.why)
|
101
|
+
end
|
102
|
+
|
103
|
+
def keyspace=(ks)
|
104
|
+
@keyspace = (ks.nil? ? nil : ks.to_s)
|
105
|
+
end
|
106
|
+
|
107
|
+
def keyspaces
|
108
|
+
# TODO: This should be replaced with a CQL call that doesn't exist yet
|
109
|
+
@connection.describe_keyspaces.map { |keyspace| Schema.new(keyspace) }
|
110
|
+
end
|
111
|
+
|
112
|
+
def schema
|
113
|
+
# TODO: This should be replaced with a CQL call that doesn't exist yet
|
114
|
+
Schema.new(@connection.describe_keyspace(@keyspace))
|
115
|
+
end
|
116
|
+
|
117
|
+
def login!(username, password)
|
118
|
+
request = CassandraCQL::Thrift::AuthenticationRequest.new
|
119
|
+
request.credentials = {'username' => username, 'password' => password}
|
120
|
+
ret = @connection.login(request)
|
121
|
+
# To avoid a double login on the initial connect, we set
|
122
|
+
# @auth_request after the first successful login.
|
123
|
+
@auth_request = request
|
124
|
+
ret
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,133 @@
|
|
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
|
+
|
17
|
+
module CassandraCQL
|
18
|
+
module Error
|
19
|
+
class InvalidResultType < Exception; end
|
20
|
+
class InvalidCursor < Exception; end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ResultSchema
|
24
|
+
attr_reader :names, :values
|
25
|
+
|
26
|
+
def initialize(schema)
|
27
|
+
# When https://issues.apache.org/jira/browse/CASSANDRA-3436 is resolve, no more need to split/last
|
28
|
+
@names = Hash.new(schema.default_name_type.split(".").last)
|
29
|
+
schema.name_types.each_pair { |key, type|
|
30
|
+
@names[key] = type.split(".").last
|
31
|
+
}
|
32
|
+
@values = Hash.new(schema.default_value_type.split(".").last)
|
33
|
+
schema.value_types.each_pair { |key, type|
|
34
|
+
@values[key] = type.split(".").last
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Result
|
40
|
+
attr_reader :result, :schema, :cursor
|
41
|
+
|
42
|
+
def initialize(result)
|
43
|
+
@result = result
|
44
|
+
@schema = ResultSchema.new(result.schema) if rows?
|
45
|
+
@cursor = 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def void?
|
49
|
+
@result.type == CassandraCQL::Thrift::CqlResultType::VOID
|
50
|
+
end
|
51
|
+
|
52
|
+
def int?
|
53
|
+
@result.type == CassandraCQL::Thrift::CqlResultType::INT
|
54
|
+
end
|
55
|
+
|
56
|
+
def rows?
|
57
|
+
@result.type == CassandraCQL::Thrift::CqlResultType::ROWS
|
58
|
+
end
|
59
|
+
|
60
|
+
def rows
|
61
|
+
@result.rows.size
|
62
|
+
end
|
63
|
+
|
64
|
+
def cursor=(cursor)
|
65
|
+
@cursor = cursor.to_i
|
66
|
+
rescue Exception => e
|
67
|
+
raise Error::InvalidCursor, e.to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def fetch_row
|
71
|
+
case @result.type
|
72
|
+
when CassandraCQL::Thrift::CqlResultType::ROWS
|
73
|
+
return nil if @cursor >= rows
|
74
|
+
|
75
|
+
row = Row.new(@result.rows[@cursor], @schema)
|
76
|
+
@cursor += 1
|
77
|
+
return row
|
78
|
+
when CassandraCQL::Thrift::CqlResultType::VOID
|
79
|
+
return nil
|
80
|
+
when CassandraCQL::Thrift::CqlResultType::INT
|
81
|
+
return @result.num
|
82
|
+
else
|
83
|
+
raise Error::InvalidResultType, "Expects one of 0, 1, 2; was #{@result.type} "
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def fetch
|
88
|
+
if block_given?
|
89
|
+
while row = fetch_row
|
90
|
+
yield row
|
91
|
+
end
|
92
|
+
else
|
93
|
+
fetch_row
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def fetch_hash
|
98
|
+
if block_given?
|
99
|
+
while row = fetch_row
|
100
|
+
if row.kind_of?(Fixnum)
|
101
|
+
yield({row => row})
|
102
|
+
else
|
103
|
+
yield row.to_hash
|
104
|
+
end
|
105
|
+
end
|
106
|
+
else
|
107
|
+
if (row = fetch_row).kind_of?(Fixnum)
|
108
|
+
{row => row}
|
109
|
+
else
|
110
|
+
row.to_hash
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def fetch_array
|
116
|
+
if block_given?
|
117
|
+
while row = fetch_row
|
118
|
+
if row.kind_of?(Fixnum)
|
119
|
+
yield [row]
|
120
|
+
else
|
121
|
+
yield row.to_a
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
if (row = fetch_row).kind_of?(Fixnum)
|
126
|
+
[row]
|
127
|
+
else
|
128
|
+
row.to_a
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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
|
+
|
17
|
+
module CassandraCQL
|
18
|
+
class Row
|
19
|
+
attr_reader :row
|
20
|
+
|
21
|
+
def initialize(row, schema)
|
22
|
+
@row, @schema = row, schema
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](obj)
|
26
|
+
column_index = obj.kind_of?(Fixnum) ? obj : column_names.index(obj)
|
27
|
+
return nil if column_index.nil?
|
28
|
+
column_values[column_index]
|
29
|
+
end
|
30
|
+
|
31
|
+
def column_names
|
32
|
+
@names ||= @row.columns.map do |column|
|
33
|
+
ColumnFamily.cast(column.name, @schema.names[column.name])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def column_values
|
38
|
+
@values ||= @row.columns.map { |column| ColumnFamily.cast(column.value, @schema.values[column.name]) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def columns
|
42
|
+
@row.columns.size
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_a
|
46
|
+
column_values
|
47
|
+
end
|
48
|
+
|
49
|
+
# TODO: This should be an ordered hash
|
50
|
+
def to_hash
|
51
|
+
Hash[([column_names, column_values]).transpose]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|