bigrecord 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +44 -0
  3. data/Rakefile +17 -0
  4. data/VERSION +1 -0
  5. data/doc/bigrecord_specs.rdoc +36 -0
  6. data/doc/getting_started.rdoc +157 -0
  7. data/examples/bigrecord.yml +25 -0
  8. data/generators/bigrecord/bigrecord_generator.rb +17 -0
  9. data/generators/bigrecord/templates/bigrecord.rake +47 -0
  10. data/generators/bigrecord_migration/bigrecord_migration_generator.rb +13 -0
  11. data/generators/bigrecord_migration/templates/migration.rb +9 -0
  12. data/generators/bigrecord_model/bigrecord_model_generator.rb +28 -0
  13. data/generators/bigrecord_model/templates/migration.rb +13 -0
  14. data/generators/bigrecord_model/templates/model.rb +7 -0
  15. data/generators/bigrecord_model/templates/model_spec.rb +12 -0
  16. data/init.rb +9 -0
  17. data/install.rb +22 -0
  18. data/lib/big_record/abstract_base.rb +1088 -0
  19. data/lib/big_record/action_view_extensions.rb +266 -0
  20. data/lib/big_record/ar_associations/association_collection.rb +194 -0
  21. data/lib/big_record/ar_associations/association_proxy.rb +158 -0
  22. data/lib/big_record/ar_associations/belongs_to_association.rb +57 -0
  23. data/lib/big_record/ar_associations/belongs_to_many_association.rb +57 -0
  24. data/lib/big_record/ar_associations/has_and_belongs_to_many_association.rb +164 -0
  25. data/lib/big_record/ar_associations/has_many_association.rb +191 -0
  26. data/lib/big_record/ar_associations/has_one_association.rb +80 -0
  27. data/lib/big_record/ar_associations.rb +1608 -0
  28. data/lib/big_record/ar_reflection.rb +223 -0
  29. data/lib/big_record/attribute_methods.rb +75 -0
  30. data/lib/big_record/base.rb +618 -0
  31. data/lib/big_record/br_associations/association_collection.rb +194 -0
  32. data/lib/big_record/br_associations/association_proxy.rb +153 -0
  33. data/lib/big_record/br_associations/belongs_to_association.rb +52 -0
  34. data/lib/big_record/br_associations/belongs_to_many_association.rb +293 -0
  35. data/lib/big_record/br_associations/cached_item_proxy.rb +194 -0
  36. data/lib/big_record/br_associations/cached_item_proxy_factory.rb +62 -0
  37. data/lib/big_record/br_associations/has_and_belongs_to_many_association.rb +168 -0
  38. data/lib/big_record/br_associations/has_one_association.rb +80 -0
  39. data/lib/big_record/br_associations.rb +978 -0
  40. data/lib/big_record/br_reflection.rb +151 -0
  41. data/lib/big_record/callbacks.rb +367 -0
  42. data/lib/big_record/connection_adapters/abstract/connection_specification.rb +279 -0
  43. data/lib/big_record/connection_adapters/abstract/database_statements.rb +175 -0
  44. data/lib/big_record/connection_adapters/abstract/quoting.rb +58 -0
  45. data/lib/big_record/connection_adapters/abstract_adapter.rb +190 -0
  46. data/lib/big_record/connection_adapters/column.rb +491 -0
  47. data/lib/big_record/connection_adapters/hbase_adapter.rb +432 -0
  48. data/lib/big_record/connection_adapters/view.rb +27 -0
  49. data/lib/big_record/connection_adapters.rb +10 -0
  50. data/lib/big_record/deletion.rb +73 -0
  51. data/lib/big_record/dynamic_schema.rb +92 -0
  52. data/lib/big_record/embedded.rb +71 -0
  53. data/lib/big_record/embedded_associations/association_proxy.rb +148 -0
  54. data/lib/big_record/family_span_columns.rb +89 -0
  55. data/lib/big_record/fixtures.rb +1025 -0
  56. data/lib/big_record/migration.rb +380 -0
  57. data/lib/big_record/routing_ext.rb +65 -0
  58. data/lib/big_record/timestamp.rb +51 -0
  59. data/lib/big_record/validations.rb +830 -0
  60. data/lib/big_record.rb +125 -0
  61. data/lib/bigrecord.rb +1 -0
  62. data/rails/init.rb +9 -0
  63. data/spec/connections/bigrecord.yml +13 -0
  64. data/spec/connections/cassandra/connection.rb +2 -0
  65. data/spec/connections/hbase/connection.rb +2 -0
  66. data/spec/debug.log +281 -0
  67. data/spec/integration/br_associations_spec.rb +80 -0
  68. data/spec/lib/animal.rb +12 -0
  69. data/spec/lib/book.rb +10 -0
  70. data/spec/lib/broken_migrations/duplicate_name/20090706182535_add_animals_table.rb +14 -0
  71. data/spec/lib/broken_migrations/duplicate_name/20090706193019_add_animals_table.rb +9 -0
  72. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_books_table.rb +9 -0
  73. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_companies_table.rb +9 -0
  74. data/spec/lib/company.rb +14 -0
  75. data/spec/lib/embedded/web_link.rb +12 -0
  76. data/spec/lib/employee.rb +33 -0
  77. data/spec/lib/migrations/20090706182535_add_animals_table.rb +13 -0
  78. data/spec/lib/migrations/20090706190623_add_books_table.rb +15 -0
  79. data/spec/lib/migrations/20090706193019_add_companies_table.rb +14 -0
  80. data/spec/lib/migrations/20090706194512_add_employees_table.rb +13 -0
  81. data/spec/lib/migrations/20090706195741_add_zoos_table.rb +13 -0
  82. data/spec/lib/novel.rb +5 -0
  83. data/spec/lib/zoo.rb +17 -0
  84. data/spec/spec.opts +4 -0
  85. data/spec/spec_helper.rb +55 -0
  86. data/spec/unit/abstract_base_spec.rb +287 -0
  87. data/spec/unit/adapters/abstract_adapter_spec.rb +56 -0
  88. data/spec/unit/adapters/adapter_shared_spec.rb +51 -0
  89. data/spec/unit/adapters/hbase_adapter_spec.rb +15 -0
  90. data/spec/unit/ar_associations_spec.rb +8 -0
  91. data/spec/unit/base_spec.rb +6 -0
  92. data/spec/unit/br_associations_spec.rb +58 -0
  93. data/spec/unit/embedded_spec.rb +43 -0
  94. data/spec/unit/find_spec.rb +34 -0
  95. data/spec/unit/hash_helper_spec.rb +44 -0
  96. data/spec/unit/migration_spec.rb +144 -0
  97. data/spec/unit/model_spec.rb +315 -0
  98. data/spec/unit/validations_spec.rb +182 -0
  99. data/tasks/bigrecord_tasks.rake +47 -0
  100. data/tasks/data_store.rb +46 -0
  101. data/tasks/gem.rb +22 -0
  102. data/tasks/rdoc.rb +8 -0
  103. data/tasks/spec.rb +34 -0
  104. metadata +189 -0
@@ -0,0 +1,279 @@
1
+ require 'set'
2
+
3
+ module BigRecord
4
+ class Base
5
+ class ConnectionSpecification #:nodoc:
6
+ attr_reader :config, :adapter_method
7
+ def initialize (config, adapter_method)
8
+ @config, @adapter_method = config, adapter_method
9
+ end
10
+ end
11
+
12
+ # Check for activity after at least +verification_timeout+ seconds.
13
+ # Defaults to 0 (always check.)
14
+ cattr_accessor :verification_timeout, :instance_writer => false
15
+ @@verification_timeout = 0
16
+
17
+ # The class -> [adapter_method, config] map
18
+ @@defined_connections = {}
19
+
20
+ # The class -> thread id -> adapter cache. (class -> adapter if not allow_concurrency)
21
+ @@active_connections = {}
22
+
23
+ class << self
24
+ # Retrieve the connection cache.
25
+ def thread_safe_active_connections #:nodoc:
26
+ @@active_connections[Thread.current.object_id] ||= {}
27
+ end
28
+
29
+ def single_threaded_active_connections #:nodoc:
30
+ @@active_connections
31
+ end
32
+
33
+ # pick up the right active_connection method from @@allow_concurrency
34
+ if @@allow_concurrency
35
+ alias_method :active_connections, :thread_safe_active_connections
36
+ else
37
+ alias_method :active_connections, :single_threaded_active_connections
38
+ end
39
+
40
+ # set concurrency support flag (not thread safe, like most of the methods in this file)
41
+ def allow_concurrency=(threaded) #:nodoc:
42
+ logger.debug "allow_concurrency=#{threaded}" if logger
43
+ return if @@allow_concurrency == threaded
44
+ clear_all_cached_connections!
45
+ @@allow_concurrency = threaded
46
+ method_prefix = threaded ? "thread_safe" : "single_threaded"
47
+ sing = (class << self; self; end)
48
+ [:active_connections, :scoped_methods].each do |method|
49
+ sing.send(:alias_method, method, "#{method_prefix}_#{method}")
50
+ end
51
+ log_connections if logger
52
+ end
53
+
54
+ def active_connection_name #:nodoc:
55
+ @active_connection_name ||=
56
+ if active_connections[name] || @@defined_connections[name]
57
+ name
58
+ elsif self == BigRecord::Base
59
+ nil
60
+ else
61
+ superclass.active_connection_name
62
+ end
63
+ end
64
+
65
+ def clear_active_connection_name #:nodoc:
66
+ @active_connection_name = nil
67
+ subclasses.each { |klass| klass.clear_active_connection_name }
68
+ end
69
+
70
+ # Returns the connection currently associated with the class. This can
71
+ # also be used to "borrow" the connection to do database work unrelated
72
+ # to any of the specific Active Records.
73
+ def connection
74
+ if @active_connection_name && (conn = active_connections[@active_connection_name])
75
+ conn
76
+ else
77
+ # retrieve_connection sets the cache key.
78
+ conn = retrieve_connection
79
+ active_connections[@active_connection_name] = conn
80
+ end
81
+ end
82
+
83
+ # Clears the cache which maps classes to connections.
84
+ def clear_active_connections!
85
+ clear_cache!(@@active_connections) do |name, conn|
86
+ conn.disconnect!
87
+ end
88
+ end
89
+
90
+ # Clears the cache which maps classes
91
+ def clear_reloadable_connections!
92
+ @@active_connections.each do |name, conn|
93
+ if conn.requires_reloading?
94
+ conn.disconnect!
95
+ @@active_connections.delete(name)
96
+ end
97
+ end
98
+ end
99
+
100
+ # Verify active connections.
101
+ def verify_active_connections! #:nodoc:
102
+ if @@allow_concurrency
103
+ remove_stale_cached_threads!(@@active_connections) do |name, conn|
104
+ conn.disconnect!
105
+ end
106
+ end
107
+
108
+ active_connections.each_value do |connection|
109
+ connection.verify!(@@verification_timeout)
110
+ end
111
+ end
112
+
113
+ private
114
+ def clear_cache!(cache, thread_id = nil, &block)
115
+ if cache
116
+ if @@allow_concurrency
117
+ thread_id ||= Thread.current.object_id
118
+ thread_cache, cache = cache, cache[thread_id]
119
+ return unless cache
120
+ end
121
+
122
+ cache.each(&block) if block_given?
123
+ cache.clear
124
+ end
125
+ ensure
126
+ if thread_cache && @@allow_concurrency
127
+ thread_cache.delete(thread_id)
128
+ end
129
+ end
130
+
131
+ # Remove stale threads from the cache.
132
+ def remove_stale_cached_threads!(cache, &block)
133
+ stale = Set.new(cache.keys)
134
+
135
+ Thread.list.each do |thread|
136
+ stale.delete(thread.object_id) if thread.alive?
137
+ end
138
+
139
+ stale.each do |thread_id|
140
+ clear_cache!(cache, thread_id, &block)
141
+ end
142
+ end
143
+
144
+ def clear_all_cached_connections!
145
+ if @@allow_concurrency
146
+ @@active_connections.each_value do |connection_hash_for_thread|
147
+ connection_hash_for_thread.each_value {|conn| conn.disconnect! }
148
+ connection_hash_for_thread.clear
149
+ end
150
+ else
151
+ @@active_connections.each_value {|conn| conn.disconnect! }
152
+ end
153
+ @@active_connections.clear
154
+ end
155
+ end
156
+
157
+ # Returns the connection currently associated with the class. This can
158
+ # also be used to "borrow" the connection to do database work that isn't
159
+ # easily done without going straight to SQL.
160
+ def connection
161
+ self.class.connection
162
+ end
163
+
164
+ # Establishes the connection to the database. Accepts a hash as input where
165
+ # the :adapter key must be specified with the name of a database adapter (in lower-case)
166
+ # example for regular databases (MySQL, Postgresql, etc):
167
+ #
168
+ # ActiveRecord::Base.establish_connection(
169
+ # :adapter => "mysql",
170
+ # :host => "localhost",
171
+ # :username => "myuser",
172
+ # :password => "mypass",
173
+ # :database => "somedatabase"
174
+ # )
175
+ #
176
+ # Example for SQLite database:
177
+ #
178
+ # ActiveRecord::Base.establish_connection(
179
+ # :adapter => "sqlite",
180
+ # :database => "path/to/dbfile"
181
+ # )
182
+ #
183
+ # Also accepts keys as strings (for parsing from yaml for example):
184
+ # ActiveRecord::Base.establish_connection(
185
+ # "adapter" => "sqlite",
186
+ # "database" => "path/to/dbfile"
187
+ # )
188
+ #
189
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
190
+ # may be returned on an error.
191
+ def self.establish_connection(spec = nil)
192
+ case spec
193
+ when nil
194
+ raise AdapterNotSpecified unless defined? RAILS_ENV
195
+ establish_connection(RAILS_ENV)
196
+ when ConnectionSpecification
197
+ clear_active_connection_name
198
+ @active_connection_name = name
199
+ @@defined_connections[name] = spec
200
+ when Symbol, String
201
+ if configuration = configurations[spec.to_s]
202
+ establish_connection(configuration)
203
+ else
204
+ raise AdapterNotSpecified, "#{spec} database is not configured"
205
+ end
206
+ else
207
+ spec = spec.symbolize_keys
208
+ unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
209
+ adapter_method = "#{spec[:adapter]}_connection"
210
+ unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
211
+ remove_connection
212
+ establish_connection(ConnectionSpecification.new(spec, adapter_method))
213
+ end
214
+ end
215
+
216
+ # Locate the connection of the nearest super class. This can be an
217
+ # active or defined connections: if it is the latter, it will be
218
+ # opened and set as the active connection for the class it was defined
219
+ # for (not necessarily the current class).
220
+ def self.retrieve_connection #:nodoc:
221
+ # Name is nil if establish_connection hasn't been called for
222
+ # some class along the inheritance chain up to AR::Base yet.
223
+ if name = active_connection_name
224
+ if conn = active_connections[name]
225
+ # Verify the connection.
226
+ conn.verify!(@@verification_timeout)
227
+ elsif spec = @@defined_connections[name]
228
+ # Activate this connection specification.
229
+ klass = name.constantize
230
+ klass.connection = spec
231
+ conn = active_connections[name]
232
+ end
233
+ end
234
+
235
+ conn or raise ConnectionNotEstablished
236
+ end
237
+
238
+ # Returns true if a connection that's accessible to this class have already been opened.
239
+ def self.connected?
240
+ active_connections[active_connection_name] ? true : false
241
+ end
242
+
243
+ # Remove the connection for this class. This will close the active
244
+ # connection and the defined connection (if they exist). The result
245
+ # can be used as argument for establish_connection, for easy
246
+ # re-establishing of the connection.
247
+ def self.remove_connection(klass=self)
248
+ spec = @@defined_connections[klass.name]
249
+ konn = active_connections[klass.name]
250
+ @@defined_connections.delete_if { |key, value| value == spec }
251
+ active_connections.delete_if { |key, value| value == konn }
252
+ konn.disconnect! if konn
253
+ spec.config if spec
254
+ end
255
+
256
+ # Set the connection for the class.
257
+ def self.connection=(spec) #:nodoc:
258
+ if spec.kind_of?(BigRecord::ConnectionAdapters::AbstractAdapter)
259
+ active_connections[name] = spec
260
+ elsif spec.kind_of?(ConnectionSpecification)
261
+ config = spec.config.reverse_merge(:allow_concurrency => @@allow_concurrency)
262
+ self.connection = self.send(spec.adapter_method, config)
263
+ elsif spec.nil?
264
+ raise ConnectionNotEstablished
265
+ else
266
+ establish_connection spec
267
+ end
268
+ end
269
+
270
+ # connection state logging
271
+ def self.log_connections #:nodoc:
272
+ if logger
273
+ logger.info "Defined connections: #{@@defined_connections.inspect}"
274
+ logger.info "Active connections: #{active_connections.inspect}"
275
+ logger.info "Active connection name: #{@active_connection_name}"
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,175 @@
1
+ module BigRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module DatabaseStatements
4
+ # # Returns an array of record hashes with the column names as keys and
5
+ # # column values as values.
6
+ # def select_all(sql, name = nil)
7
+ # select(sql, name)
8
+ # end
9
+ #
10
+ # # Returns a record hash with the column names as keys and column values
11
+ # # as values.
12
+ # def select_one(sql, name = nil)
13
+ # result = select_all(sql, name)
14
+ # result.first if result
15
+ # end
16
+ #
17
+ # # Returns a single value from a record
18
+ # def select_value(sql, name = nil)
19
+ # if result = select_one(sql, name)
20
+ # result.values.first
21
+ # end
22
+ # end
23
+ #
24
+ # # Returns an array of the values of the first column in a select:
25
+ # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
26
+ # def select_values(sql, name = nil)
27
+ # result = select_rows(sql, name)
28
+ # result.map { |v| v[0] }
29
+ # end
30
+ #
31
+ # # Returns an array of arrays containing the field values.
32
+ # # Order is the same as that returned by #columns.
33
+ # def select_rows(sql, name = nil)
34
+ # raise NotImplementedError, "select_rows is an abstract method"
35
+ # end
36
+ #
37
+ # # Executes the SQL statement in the context of this connection.
38
+ # def execute(sql, name = nil)
39
+ # raise NotImplementedError, "execute is an abstract method"
40
+ # end
41
+ #
42
+ # # Returns the last auto-generated ID from the affected table.
43
+ # def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
44
+ # insert_sql(sql, name, pk, id_value, sequence_name)
45
+ # end
46
+ #
47
+ # # Executes the update statement and returns the number of rows affected.
48
+ # def update(sql, name = nil)
49
+ # update_sql(sql, name)
50
+ # end
51
+ #
52
+ # # Executes the delete statement and returns the number of rows affected.
53
+ # def delete(sql, name = nil)
54
+ # delete_sql(sql, name)
55
+ # end
56
+ #
57
+ # # Wrap a block in a transaction. Returns result of block.
58
+ # def transaction(start_db_transaction = true)
59
+ # transaction_open = false
60
+ # begin
61
+ # if block_given?
62
+ # if start_db_transaction
63
+ # begin_db_transaction
64
+ # transaction_open = true
65
+ # end
66
+ # yield
67
+ # end
68
+ # rescue Exception => database_transaction_rollback
69
+ # if transaction_open
70
+ # transaction_open = false
71
+ # rollback_db_transaction
72
+ # end
73
+ # raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
74
+ # end
75
+ # ensure
76
+ # if transaction_open
77
+ # begin
78
+ # commit_db_transaction
79
+ # rescue Exception => database_transaction_rollback
80
+ # rollback_db_transaction
81
+ # raise
82
+ # end
83
+ # end
84
+ # end
85
+ #
86
+ # # Begins the transaction (and turns off auto-committing).
87
+ # def begin_db_transaction() end
88
+ #
89
+ # # Commits the transaction (and turns on auto-committing).
90
+ # def commit_db_transaction() end
91
+ #
92
+ # # Rolls back the transaction (and turns on auto-committing). Must be
93
+ # # done if the transaction block raises an exception or returns false.
94
+ # def rollback_db_transaction() end
95
+ #
96
+ # # Alias for #add_limit_offset!.
97
+ # def add_limit!(sql, options)
98
+ # add_limit_offset!(sql, options) if options
99
+ # end
100
+ #
101
+ # # Appends +LIMIT+ and +OFFSET+ options to an SQL statement.
102
+ # # This method *modifies* the +sql+ parameter.
103
+ # # ===== Examples
104
+ # # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
105
+ # # generates
106
+ # # SELECT * FROM suppliers LIMIT 10 OFFSET 50
107
+ # def add_limit_offset!(sql, options)
108
+ # if limit = options[:limit]
109
+ # sql << " LIMIT #{limit}"
110
+ # if offset = options[:offset]
111
+ # sql << " OFFSET #{offset}"
112
+ # end
113
+ # end
114
+ # end
115
+ #
116
+ # # Appends a locking clause to an SQL statement.
117
+ # # This method *modifies* the +sql+ parameter.
118
+ # # # SELECT * FROM suppliers FOR UPDATE
119
+ # # add_lock! 'SELECT * FROM suppliers', :lock => true
120
+ # # add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
121
+ # def add_lock!(sql, options)
122
+ # case lock = options[:lock]
123
+ # when true; sql << ' FOR UPDATE'
124
+ # when String; sql << " #{lock}"
125
+ # end
126
+ # end
127
+ #
128
+ # def default_sequence_name(table, column)
129
+ # nil
130
+ # end
131
+ #
132
+ # # Set the sequence to the max value of the table's column.
133
+ # def reset_sequence!(table, column, sequence = nil)
134
+ # # Do nothing by default. Implement for PostgreSQL, Oracle, ...
135
+ # end
136
+
137
+ # Inserts the given fixture into the table. Overridden in adapters that require
138
+ # something beyond a simple insert (eg. Oracle).
139
+ def insert_fixture(fixture, table_name)
140
+ # execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
141
+ attributes = fixture.to_hash.dup
142
+ id = attributes.delete("id")
143
+ raise ArgumentError, "the id is missing" unless id
144
+ update(table_name, id, attributes, Time.now.to_bigrecord_timestamp)
145
+ end
146
+
147
+ # def empty_insert_statement(table_name)
148
+ # "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
149
+ # end
150
+ #
151
+ # protected
152
+ # # Returns an array of record hashes with the column names as keys and
153
+ # # column values as values.
154
+ # def select(sql, name = nil)
155
+ # raise NotImplementedError, "select is an abstract method"
156
+ # end
157
+ #
158
+ # # Returns the last auto-generated ID from the affected table.
159
+ # def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
160
+ # execute(sql, name)
161
+ # id_value
162
+ # end
163
+ #
164
+ # # Executes the update statement and returns the number of rows affected.
165
+ # def update_sql(sql, name = nil)
166
+ # execute(sql, name)
167
+ # end
168
+ #
169
+ # # Executes the delete statement and returns the number of rows affected.
170
+ # def delete_sql(sql, name = nil)
171
+ # update_sql(sql, name)
172
+ # end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,58 @@
1
+ module BigRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module Quoting
4
+ # Quotes the column value to help prevent
5
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
6
+ def quote(value, column = nil)
7
+ # records are quoted as their primary key
8
+ return value.quoted_id if value.respond_to?(:quoted_id)
9
+
10
+ case value
11
+ when String, ActiveSupport::Multibyte::Chars
12
+ value = value.to_s
13
+ if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
14
+ "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
15
+ elsif column && [:integer, :float].include?(column.type)
16
+ value = column.type == :integer ? value.to_i : value.to_f
17
+ value.to_s
18
+ else
19
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
20
+ end
21
+ when NilClass then "NULL"
22
+ when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
23
+ when FalseClass then (column && column.type == :integer ? '0' : quoted_false)
24
+ when Float, Fixnum, Bignum then value.to_s
25
+ # BigDecimals need to be output in a non-normalized form and quoted.
26
+ when BigDecimal then value.to_s('F')
27
+ when Date then "'#{value.to_s}'"
28
+ when Time, DateTime then "'#{quoted_date(value)}'"
29
+ else "'#{quote_string(value.to_yaml)}'"
30
+ end
31
+ end
32
+
33
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
34
+ # characters.
35
+ def quote_string(s)
36
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
37
+ end
38
+
39
+ # Returns a quoted form of the column name. This is highly adapter
40
+ # specific.
41
+ def quote_column_name(name)
42
+ name
43
+ end
44
+
45
+ def quoted_true
46
+ "'t'"
47
+ end
48
+
49
+ def quoted_false
50
+ "'f'"
51
+ end
52
+
53
+ def quoted_date(value)
54
+ value.strftime("%Y-%m-%d %H:%M:%S")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,190 @@
1
+ require 'benchmark'
2
+ require 'date'
3
+ require 'bigdecimal'
4
+ require 'bigdecimal/util'
5
+
6
+ module BigRecord
7
+ module ConnectionAdapters # :nodoc:
8
+ # All the concrete database adapters follow the interface laid down in this class.
9
+ # You can use this interface directly by borrowing the database connection from the Base with
10
+ # Base.connection.
11
+ #
12
+ # Most of the methods in the adapter are useful during migrations. Most
13
+ # notably, SchemaStatements#create_table, SchemaStatements#drop_table,
14
+ # SchemaStatements#add_index, SchemaStatements#remove_index,
15
+ # SchemaStatements#add_column, SchemaStatements#change_column and
16
+ # SchemaStatements#remove_column are very useful.
17
+ class AbstractAdapter
18
+ include Quoting, DatabaseStatements#, SchemaStatements
19
+ @@row_even = true
20
+
21
+ def initialize(connection, logger = nil) #:nodoc:
22
+ @connection, @logger = connection, logger
23
+ @runtime = 0
24
+ @last_verification = 0
25
+ end
26
+
27
+ # Returns the human-readable name of the adapter. Use mixed case - one
28
+ # can always use downcase if needed.
29
+ def adapter_name
30
+ 'Abstract'
31
+ end
32
+
33
+ # Does this adapter support migrations? Backend specific, as the
34
+ # abstract adapter always returns +false+.
35
+ def supports_migrations?
36
+ false
37
+ end
38
+
39
+ # Does this adapter support using DISTINCT within COUNT? This is +true+
40
+ # for all adapters except sqlite.
41
+ def supports_count_distinct?
42
+ false
43
+ end
44
+
45
+ def supports_ddl_transactions?
46
+ false
47
+ end
48
+
49
+ # Should primary key values be selected from their corresponding
50
+ # sequence before the insert statement? If true, next_sequence_value
51
+ # is called before each insert to set the record's primary key.
52
+ # This is false for all adapters but Firebird.
53
+ def prefetch_primary_key?(table_name = nil)
54
+ false
55
+ end
56
+
57
+ def reset_runtime #:nodoc:
58
+ rt, @runtime = @runtime, 0
59
+ rt
60
+ end
61
+
62
+ # QUOTING ==================================================
63
+
64
+ # Override to return the quoted table name if the database needs it
65
+ def quote_table_name(name)
66
+ name
67
+ end
68
+
69
+ # REFERENTIAL INTEGRITY ====================================
70
+
71
+ # Override to turn off referential integrity while executing +&block+
72
+ def disable_referential_integrity(&block)
73
+ yield
74
+ end
75
+
76
+ # CONNECTION MANAGEMENT ====================================
77
+
78
+ # Is this connection active and ready to perform queries?
79
+ def active?
80
+ @active != false
81
+ end
82
+
83
+ # Close this connection and open a new one in its place.
84
+ def reconnect!
85
+ @active = true
86
+ end
87
+
88
+ # Close this connection
89
+ def disconnect!
90
+ @active = false
91
+ end
92
+
93
+ # Returns true if its safe to reload the connection between requests for development mode.
94
+ # This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
95
+ def requires_reloading?
96
+ false
97
+ end
98
+
99
+ # Lazily verify this connection, calling +active?+ only if it hasn't
100
+ # been called for +timeout+ seconds.
101
+ def verify!(timeout)
102
+ now = Time.now.to_i
103
+ if (now - @last_verification) > timeout
104
+ reconnect! unless active?
105
+ @last_verification = now
106
+ end
107
+ end
108
+
109
+ # Provides access to the underlying database connection. Useful for
110
+ # when you need to call a proprietary method such as postgresql's lo_*
111
+ # methods
112
+ def raw_connection
113
+ @connection
114
+ end
115
+
116
+ # DATABASE STATEMENTS ======================================
117
+
118
+ def update_raw(table_name, row, values, timestamp)
119
+ raise NotImplementedError
120
+ end
121
+
122
+ def update(table_name, row, values, timestamp)
123
+ raise NotImplementedError
124
+ end
125
+
126
+ def get_raw(table_name, row, column, options={})
127
+ raise NotImplementedError
128
+ end
129
+
130
+ def get(table_name, row, column, options={})
131
+ raise NotImplementedError
132
+ end
133
+
134
+ def get_columns_raw(table_name, row, columns, options={})
135
+ raise NotImplementedError
136
+ end
137
+
138
+ def get_columns(table_name, row, columns, options={})
139
+ raise NotImplementedError
140
+ end
141
+
142
+ def get_consecutive_rows_raw(table_name, start_row, limit, columns, stop_row = nil)
143
+ raise NotImplementedError
144
+ end
145
+
146
+ def get_consecutive_rows(table_name, start_row, limit, columns, stop_row = nil)
147
+ raise NotImplementedError
148
+ end
149
+
150
+ def delete(table_name, row)
151
+ raise NotImplementedError
152
+ end
153
+
154
+ def delete_all(table_name)
155
+ raise NotImplementedError
156
+ end
157
+
158
+ # SCHEMA STATEMENTS ========================================
159
+
160
+ def table_exists?(table_name)
161
+ raise NotImplementedError
162
+ end
163
+
164
+ def create_table(table_name, column_families)
165
+ raise NotImplementedError
166
+ end
167
+
168
+ def drop_table(table_name)
169
+ raise NotImplementedError
170
+ end
171
+
172
+ end # class AbstractAdapter
173
+ end # module ConnectionAdapters
174
+ end # module BigRecord
175
+
176
+
177
+ # Open the time class to add logic for the hbase timestamp
178
+ class Time
179
+ # Return this time is the hbase timestamp format, i.e. a 'long'. The 4 high bytes contain
180
+ # the number of seconds since epoch and the 4 low bytes contain the microseconds. That
181
+ # format is an arbitrary one and could have been something else.
182
+ def to_bigrecord_timestamp
183
+ (self.to_i << 32) + self.usec
184
+ end
185
+
186
+ def self.from_bigrecord_timestamp(timestamp)
187
+ Time.at(timestamp >> 32, timestamp & 0xFFFFFFFF)
188
+ end
189
+
190
+ end