activerecord-hbase-adapter 0.0.7
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/activerecord-hbase-adapter.gemspec +37 -0
- data/lib/active_record/connection_adapters/hbase_adapter.rb +300 -0
- data/lib/activerecord-hbase-adapter/version.rb +7 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c9b0cf4fd299122fc45486397c36ccc43e2923c6
|
4
|
+
data.tar.gz: 109f0b4a74e3a763eee842152117aeee01a3ec21
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: efdc375a03df4c79831f209565ad5ce1a377c758054d3c753802a1ce168799cd71178e40b6ad5b0e63cb05c779f04cfe1a8b1feb588ab6fae59f33c3414dbfe2
|
7
|
+
data.tar.gz: 758e44e26fb0884febac15e40e503b2f809d6aa052aecc93601efaa06499c29ce486c321760da4064f96d7bdbd1ad87a87f306e0e3031afd3546634878a9166c
|
data/Gemfile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
2
|
+
require File.expand_path('../lib/activerecord-hbase-adapter/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Jean Lescure"]
|
6
|
+
gem.email = ["jean@agilityfeat.com"]
|
7
|
+
gem.description = %q{HBase ActiveRecord adapter based on HipsterSqlToHbase}
|
8
|
+
gem.summary = %q{HBase ActiveRecord adapter based on HipsterSqlToHbase}
|
9
|
+
gem.homepage = %q{http://github.com/jeanlescure/activerecord-hbase-adapter}
|
10
|
+
|
11
|
+
gem.files = [ 'Gemfile',
|
12
|
+
'activerecord-hbase-adapter.gemspec',
|
13
|
+
'lib/activerecord-hbase-adapter/version.rb',
|
14
|
+
'lib/active_record/connection_adapters/hbase_adapter.rb']
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.name = "activerecord-hbase-adapter"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = Activerecord::Hbase::Adapter::VERSION
|
19
|
+
|
20
|
+
if gem.respond_to? :specification_version then
|
21
|
+
gem.specification_version = 4
|
22
|
+
|
23
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
24
|
+
gem.add_runtime_dependency(%q<hipster_sql_to_hbase>, [">= 0"])
|
25
|
+
gem.add_runtime_dependency(%q<httparty>, [">= 0"])
|
26
|
+
gem.add_runtime_dependency(%q<msgpack>, [">= 0"])
|
27
|
+
else
|
28
|
+
gem.add_dependency(%q<hipster_sql_to_hbase>, [">= 0"])
|
29
|
+
gem.add_dependency(%q<httparty>, [">= 0"])
|
30
|
+
gem.add_dependency(%q<msgpack>, [">= 0"])
|
31
|
+
end
|
32
|
+
else
|
33
|
+
gem.add_dependency(%q<hipster_sql_to_hbase>, [">= 0"])
|
34
|
+
gem.add_dependency(%q<httparty>, [">= 0"])
|
35
|
+
gem.add_dependency(%q<msgpack>, [">= 0"])
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
2
|
+
require 'active_record/connection_adapters/hbase/error'
|
3
|
+
require 'active_record/connection_adapters/hbase/result'
|
4
|
+
require 'active_record/connection_adapters/hbase/client'
|
5
|
+
|
6
|
+
### FOR DEBUGGING ONLY ###
|
7
|
+
#$LOAD_PATH.unshift '/home/jean/adapter/lib/mysql2/lib'
|
8
|
+
#require 'mysql2'
|
9
|
+
### FOR DEBUGGING ONLY ###
|
10
|
+
|
11
|
+
require 'hipster_sql_to_hbase'
|
12
|
+
|
13
|
+
module ActiveRecord
|
14
|
+
module ConnectionHandling # :nodoc:
|
15
|
+
# Establishes a connection to the database that's used by all Active Record objects.
|
16
|
+
def hbase_connection(config)
|
17
|
+
config = config.symbolize_keys
|
18
|
+
|
19
|
+
config[:username] = 'root' if config[:username].nil?
|
20
|
+
|
21
|
+
### FOR DEBUGGING ONLY ###
|
22
|
+
#if Mysql2::Client.const_defined? :FOUND_ROWS
|
23
|
+
# config[:flags] = Mysql2::Client::FOUND_ROWS
|
24
|
+
#end
|
25
|
+
#config[:host] = config[:m2host]
|
26
|
+
#config[:port] = config[:m2port]
|
27
|
+
#client = Mysql2::Client.new(config)
|
28
|
+
### FOR DEBUGGING ONLY ###
|
29
|
+
|
30
|
+
client = HbaseRestIface::Client.new(config)
|
31
|
+
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
|
32
|
+
ConnectionAdapters::HbaseAdapter.new(client, logger, options, config)
|
33
|
+
rescue Hbase::Error => error
|
34
|
+
if error.message.include?("Unknown database")
|
35
|
+
raise ActiveRecord::NoDatabaseError.new(error.message)
|
36
|
+
else
|
37
|
+
raise error
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ConnectionAdapters
|
43
|
+
class HbaseAdapter < AbstractMysqlAdapter
|
44
|
+
|
45
|
+
|
46
|
+
class Column < AbstractMysqlAdapter::Column # :nodoc:
|
47
|
+
def adapter
|
48
|
+
HbaseAdapter
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
ADAPTER_NAME = 'Hbase'
|
53
|
+
|
54
|
+
def initialize(connection, logger, connection_options, config)
|
55
|
+
super
|
56
|
+
|
57
|
+
@visitor = BindSubstitution.new self
|
58
|
+
configure_connection
|
59
|
+
end
|
60
|
+
|
61
|
+
MAX_INDEX_LENGTH_FOR_UTF8MB4 = 191
|
62
|
+
def initialize_schema_migrations_table
|
63
|
+
if @config[:encoding] == 'utf8mb4'
|
64
|
+
ActiveRecord::SchemaMigration.create_table(MAX_INDEX_LENGTH_FOR_UTF8MB4)
|
65
|
+
else
|
66
|
+
ActiveRecord::SchemaMigration.create_table
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def supports_explain?
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
# HELPER METHODS ===========================================
|
75
|
+
|
76
|
+
def each_hash(result) # :nodoc:
|
77
|
+
|
78
|
+
if block_given?
|
79
|
+
result.each(:as => :hash, :symbolize_keys => true) do |row|
|
80
|
+
yield row
|
81
|
+
end
|
82
|
+
else
|
83
|
+
to_enum(:each_hash, result)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def new_column(field, default, type, null, collation, extra = "") # :nodoc:
|
88
|
+
Column.new(field, default, type, null, collation, strict_mode?, extra)
|
89
|
+
end
|
90
|
+
|
91
|
+
def error_number(exception)
|
92
|
+
exception.error_number if exception.respond_to?(:error_number)
|
93
|
+
end
|
94
|
+
|
95
|
+
# QUOTING ==================================================
|
96
|
+
|
97
|
+
def quote_string(string)
|
98
|
+
@connection.escape(string)
|
99
|
+
end
|
100
|
+
|
101
|
+
# CONNECTION MANAGEMENT ====================================
|
102
|
+
|
103
|
+
def active?
|
104
|
+
return false unless @connection
|
105
|
+
@connection.ping
|
106
|
+
end
|
107
|
+
|
108
|
+
def reconnect!
|
109
|
+
super
|
110
|
+
disconnect!
|
111
|
+
connect
|
112
|
+
end
|
113
|
+
alias :reset! :reconnect!
|
114
|
+
|
115
|
+
# Disconnects from the database if already connected.
|
116
|
+
# Otherwise, this method does nothing.
|
117
|
+
def disconnect!
|
118
|
+
super
|
119
|
+
unless @connection.nil?
|
120
|
+
@connection.close
|
121
|
+
@connection = nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# DATABASE STATEMENTS ======================================
|
126
|
+
|
127
|
+
def explain(arel, binds = [])
|
128
|
+
sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
|
129
|
+
start = Time.now
|
130
|
+
result = exec_query(sql, 'EXPLAIN', binds)
|
131
|
+
elapsed = Time.now - start
|
132
|
+
|
133
|
+
ExplainPrettyPrinter.new.pp(result, elapsed)
|
134
|
+
end
|
135
|
+
|
136
|
+
class ExplainPrettyPrinter # :nodoc:
|
137
|
+
# Pretty prints the result of a EXPLAIN in a way that resembles the output of the
|
138
|
+
# MySQL shell:
|
139
|
+
#
|
140
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
141
|
+
# | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
142
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
143
|
+
# | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
|
144
|
+
# | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
145
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
146
|
+
# 2 rows in set (0.00 sec)
|
147
|
+
#
|
148
|
+
# This is an exercise in Ruby hyperrealism :).
|
149
|
+
def pp(result, elapsed)
|
150
|
+
widths = compute_column_widths(result)
|
151
|
+
separator = build_separator(widths)
|
152
|
+
|
153
|
+
pp = []
|
154
|
+
|
155
|
+
pp << separator
|
156
|
+
pp << build_cells(result.columns, widths)
|
157
|
+
pp << separator
|
158
|
+
|
159
|
+
result.rows.each do |row|
|
160
|
+
pp << build_cells(row, widths)
|
161
|
+
end
|
162
|
+
|
163
|
+
pp << separator
|
164
|
+
pp << build_footer(result.rows.length, elapsed)
|
165
|
+
|
166
|
+
pp.join("\n") + "\n"
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def compute_column_widths(result)
|
172
|
+
[].tap do |widths|
|
173
|
+
result.columns.each_with_index do |column, i|
|
174
|
+
cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
|
175
|
+
widths << cells_in_column.map(&:length).max
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def build_separator(widths)
|
181
|
+
padding = 1
|
182
|
+
'+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
|
183
|
+
end
|
184
|
+
|
185
|
+
def build_cells(items, widths)
|
186
|
+
cells = []
|
187
|
+
items.each_with_index do |item, i|
|
188
|
+
item = 'NULL' if item.nil?
|
189
|
+
justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
|
190
|
+
cells << item.to_s.send(justifier, widths[i])
|
191
|
+
end
|
192
|
+
'| ' + cells.join(' | ') + ' |'
|
193
|
+
end
|
194
|
+
|
195
|
+
def build_footer(nrows, elapsed)
|
196
|
+
rows_label = nrows == 1 ? 'row' : 'rows'
|
197
|
+
"#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# FIXME: re-enable the following once a "better" query_cache solution is in core
|
202
|
+
#
|
203
|
+
# The overrides below perform much better than the originals in AbstractAdapter
|
204
|
+
# because we're able to take advantage of hbase's lazy-loading capabilities
|
205
|
+
#
|
206
|
+
# # Returns a record hash with the column names as keys and column values
|
207
|
+
# # as values.
|
208
|
+
# def select_one(sql, name = nil)
|
209
|
+
# result = execute(sql, name)
|
210
|
+
# result.each(as: :hash) do |r|
|
211
|
+
# return r
|
212
|
+
# end
|
213
|
+
# end
|
214
|
+
#
|
215
|
+
# # Returns a single value from a record
|
216
|
+
# def select_value(sql, name = nil)
|
217
|
+
# result = execute(sql, name)
|
218
|
+
# if first = result.first
|
219
|
+
# first.first
|
220
|
+
# end
|
221
|
+
# end
|
222
|
+
#
|
223
|
+
# # Returns an array of the values of the first column in a select:
|
224
|
+
# # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
225
|
+
# def select_values(sql, name = nil)
|
226
|
+
# execute(sql, name).map { |row| row.first }
|
227
|
+
# end
|
228
|
+
|
229
|
+
# Returns an array of arrays containing the field values.
|
230
|
+
# Order is the same as that returned by +columns+.
|
231
|
+
def select_rows(sql, name = nil, binds = [])
|
232
|
+
execute(sql, name).to_a
|
233
|
+
end
|
234
|
+
|
235
|
+
# Executes the SQL statement in the context of this connection.
|
236
|
+
def execute(sql, name = nil)
|
237
|
+
if @connection
|
238
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
239
|
+
# made since we established the connection
|
240
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
241
|
+
end
|
242
|
+
|
243
|
+
super
|
244
|
+
end
|
245
|
+
|
246
|
+
def exec_query(sql, name = 'SQL', binds = [])
|
247
|
+
result = execute(sql, name)
|
248
|
+
ActiveRecord::Result.new(result.fields, result.to_a)
|
249
|
+
end
|
250
|
+
|
251
|
+
alias exec_without_stmt exec_query
|
252
|
+
|
253
|
+
# Returns an ActiveRecord::Result instance.
|
254
|
+
def select(sql, name = nil, binds = [])
|
255
|
+
exec_query(sql, name)
|
256
|
+
end
|
257
|
+
|
258
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
259
|
+
super
|
260
|
+
id_value || @connection.last_id
|
261
|
+
end
|
262
|
+
alias :create :insert_sql
|
263
|
+
|
264
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
265
|
+
execute to_sql(sql, binds), name
|
266
|
+
end
|
267
|
+
|
268
|
+
def exec_delete(sql, name, binds)
|
269
|
+
execute to_sql(sql, binds), name
|
270
|
+
@connection.affected_rows
|
271
|
+
end
|
272
|
+
alias :exec_update :exec_delete
|
273
|
+
|
274
|
+
def last_inserted_id(result)
|
275
|
+
@connection.last_id
|
276
|
+
end
|
277
|
+
|
278
|
+
private
|
279
|
+
|
280
|
+
def connect
|
281
|
+
#@connection = Mysql2::Client.new(@config)
|
282
|
+
@connection = HbaseRestIface::Client.new(:host => @config[:hb_host], :port => @config[:hb_port])
|
283
|
+
configure_connection
|
284
|
+
end
|
285
|
+
|
286
|
+
def configure_connection
|
287
|
+
@connection.query_options.merge!(:as => :array)
|
288
|
+
super
|
289
|
+
end
|
290
|
+
|
291
|
+
def full_version
|
292
|
+
@full_version ||= @connection.info[:version]
|
293
|
+
end
|
294
|
+
|
295
|
+
def set_field_encoding field_name
|
296
|
+
field_name
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-hbase-adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jean Lescure
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hipster_sql_to_hbase
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httparty
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: msgpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: HBase ActiveRecord adapter based on HipsterSqlToHbase
|
56
|
+
email:
|
57
|
+
- jean@agilityfeat.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- Gemfile
|
63
|
+
- activerecord-hbase-adapter.gemspec
|
64
|
+
- lib/active_record/connection_adapters/hbase_adapter.rb
|
65
|
+
- lib/activerecord-hbase-adapter/version.rb
|
66
|
+
homepage: http://github.com/jeanlescure/activerecord-hbase-adapter
|
67
|
+
licenses: []
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.2.2
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: HBase ActiveRecord adapter based on HipsterSqlToHbase
|
89
|
+
test_files: []
|