activerecord 4.1.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -36,9 +36,9 @@ module ActiveRecord
|
|
36
36
|
ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
|
37
37
|
rescue Mysql::Error => error
|
38
38
|
if error.message.include?("Unknown database")
|
39
|
-
raise ActiveRecord::NoDatabaseError.new(error.message)
|
39
|
+
raise ActiveRecord::NoDatabaseError.new(error.message, error)
|
40
40
|
else
|
41
|
-
raise
|
41
|
+
raise
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -58,7 +58,7 @@ module ActiveRecord
|
|
58
58
|
# * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
|
59
59
|
# * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
|
60
60
|
# * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html)
|
61
|
-
# * <tt>:variables</tt> - (Optional) A hash session variables to send as
|
61
|
+
# * <tt>:variables</tt> - (Optional) A hash session variables to send as <tt>SET @@SESSION.key = value</tt> on each database connection. Use the value +:default+ to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
|
62
62
|
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
|
63
63
|
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
|
64
64
|
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
|
@@ -66,36 +66,7 @@ module ActiveRecord
|
|
66
66
|
# * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
|
67
67
|
#
|
68
68
|
class MysqlAdapter < AbstractMysqlAdapter
|
69
|
-
|
70
|
-
class Column < AbstractMysqlAdapter::Column #:nodoc:
|
71
|
-
def self.string_to_time(value)
|
72
|
-
return super unless Mysql::Time === value
|
73
|
-
new_time(
|
74
|
-
value.year,
|
75
|
-
value.month,
|
76
|
-
value.day,
|
77
|
-
value.hour,
|
78
|
-
value.minute,
|
79
|
-
value.second,
|
80
|
-
value.second_part)
|
81
|
-
end
|
82
|
-
|
83
|
-
def self.string_to_dummy_time(v)
|
84
|
-
return super unless Mysql::Time === v
|
85
|
-
new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.string_to_date(v)
|
89
|
-
return super unless Mysql::Time === v
|
90
|
-
new_date(v.year, v.month, v.day)
|
91
|
-
end
|
92
|
-
|
93
|
-
def adapter
|
94
|
-
MysqlAdapter
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
ADAPTER_NAME = 'MySQL'
|
69
|
+
ADAPTER_NAME = 'MySQL'.freeze
|
99
70
|
|
100
71
|
class StatementPool < ConnectionAdapters::StatementPool
|
101
72
|
def initialize(connection, max = 1000)
|
@@ -117,7 +88,7 @@ module ActiveRecord
|
|
117
88
|
end
|
118
89
|
|
119
90
|
def clear
|
120
|
-
cache.
|
91
|
+
cache.each_value do |hash|
|
121
92
|
hash[:stmt].close
|
122
93
|
end
|
123
94
|
cache.clear
|
@@ -156,10 +127,6 @@ module ActiveRecord
|
|
156
127
|
end
|
157
128
|
end
|
158
129
|
|
159
|
-
def new_column(field, default, type, null, collation, extra = "") # :nodoc:
|
160
|
-
Column.new(field, default, type, null, collation, strict_mode?, extra)
|
161
|
-
end
|
162
|
-
|
163
130
|
def error_number(exception) # :nodoc:
|
164
131
|
exception.errno if exception.respond_to?(:errno)
|
165
132
|
end
|
@@ -170,7 +137,9 @@ module ActiveRecord
|
|
170
137
|
@connection.quote(string)
|
171
138
|
end
|
172
139
|
|
140
|
+
#--
|
173
141
|
# CONNECTION MANAGEMENT ====================================
|
142
|
+
#++
|
174
143
|
|
175
144
|
def active?
|
176
145
|
if @connection.respond_to?(:stat)
|
@@ -211,7 +180,9 @@ module ActiveRecord
|
|
211
180
|
end
|
212
181
|
end
|
213
182
|
|
183
|
+
#--
|
214
184
|
# DATABASE STATEMENTS ======================================
|
185
|
+
#++
|
215
186
|
|
216
187
|
def select_rows(sql, name = nil, binds = [])
|
217
188
|
@connection.query_with_result = true
|
@@ -222,6 +193,7 @@ module ActiveRecord
|
|
222
193
|
|
223
194
|
# Clears the prepared statements cache.
|
224
195
|
def clear_cache!
|
196
|
+
super
|
225
197
|
@statements.clear
|
226
198
|
end
|
227
199
|
|
@@ -294,126 +266,70 @@ module ActiveRecord
|
|
294
266
|
@connection.insert_id
|
295
267
|
end
|
296
268
|
|
297
|
-
module Fields
|
298
|
-
class Type
|
299
|
-
def
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
return if value.nil?
|
313
|
-
|
314
|
-
value.to_i rescue value ? 1 : 0
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
class Date < Type
|
319
|
-
def type; :date; end
|
320
|
-
|
321
|
-
def type_cast(value)
|
322
|
-
return if value.nil?
|
323
|
-
|
324
|
-
# FIXME: probably we can improve this since we know it is mysql
|
325
|
-
# specific
|
326
|
-
ConnectionAdapters::Column.value_to_date value
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
class DateTime < Type
|
331
|
-
def type; :datetime; end
|
332
|
-
|
333
|
-
def type_cast(value)
|
334
|
-
return if value.nil?
|
335
|
-
|
336
|
-
# FIXME: probably we can improve this since we know it is mysql
|
337
|
-
# specific
|
338
|
-
ConnectionAdapters::Column.string_to_time value
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
class Time < Type
|
343
|
-
def type; :time; end
|
344
|
-
|
345
|
-
def type_cast(value)
|
346
|
-
return if value.nil?
|
347
|
-
|
348
|
-
# FIXME: probably we can improve this since we know it is mysql
|
349
|
-
# specific
|
350
|
-
ConnectionAdapters::Column.string_to_dummy_time value
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
class Float < Type
|
355
|
-
def type; :float; end
|
356
|
-
|
357
|
-
def type_cast(value)
|
358
|
-
return if value.nil?
|
359
|
-
|
360
|
-
value.to_f
|
269
|
+
module Fields # :nodoc:
|
270
|
+
class DateTime < Type::DateTime # :nodoc:
|
271
|
+
def cast_value(value)
|
272
|
+
if Mysql::Time === value
|
273
|
+
new_time(
|
274
|
+
value.year,
|
275
|
+
value.month,
|
276
|
+
value.day,
|
277
|
+
value.hour,
|
278
|
+
value.minute,
|
279
|
+
value.second,
|
280
|
+
value.second_part)
|
281
|
+
else
|
282
|
+
super
|
283
|
+
end
|
361
284
|
end
|
362
285
|
end
|
363
286
|
|
364
|
-
class
|
365
|
-
def
|
366
|
-
|
367
|
-
|
368
|
-
|
287
|
+
class Time < Type::Time # :nodoc:
|
288
|
+
def cast_value(value)
|
289
|
+
if Mysql::Time === value
|
290
|
+
new_time(
|
291
|
+
2000,
|
292
|
+
01,
|
293
|
+
01,
|
294
|
+
value.hour,
|
295
|
+
value.minute,
|
296
|
+
value.second,
|
297
|
+
value.second_part)
|
298
|
+
else
|
299
|
+
super
|
300
|
+
end
|
369
301
|
end
|
370
302
|
end
|
371
303
|
|
372
|
-
class
|
373
|
-
|
374
|
-
return if value.nil?
|
304
|
+
class << self
|
305
|
+
TYPES = Type::HashLookupTypeMap.new # :nodoc:
|
375
306
|
|
376
|
-
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
TYPES = {}
|
381
|
-
|
382
|
-
# Register an MySQL +type_id+ with a typecasting object in
|
383
|
-
# +type+.
|
384
|
-
def self.register_type(type_id, type)
|
385
|
-
TYPES[type_id] = type
|
386
|
-
end
|
307
|
+
delegate :register_type, :alias_type, to: :TYPES
|
387
308
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
TYPES[Mysql::Field::TYPE_LONG]
|
395
|
-
else
|
396
|
-
TYPES.fetch(field.type) { Fields::Identity.new }
|
309
|
+
def find_type(field)
|
310
|
+
if field.type == Mysql::Field::TYPE_TINY && field.length > 1
|
311
|
+
TYPES.lookup(Mysql::Field::TYPE_LONG)
|
312
|
+
else
|
313
|
+
TYPES.lookup(field.type)
|
314
|
+
end
|
397
315
|
end
|
398
316
|
end
|
399
317
|
|
400
|
-
register_type Mysql::Field::TYPE_TINY,
|
401
|
-
register_type Mysql::Field::TYPE_LONG,
|
318
|
+
register_type Mysql::Field::TYPE_TINY, Type::Boolean.new
|
319
|
+
register_type Mysql::Field::TYPE_LONG, Type::Integer.new
|
402
320
|
alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
|
403
321
|
alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
|
404
322
|
|
405
|
-
register_type Mysql::Field::
|
406
|
-
register_type Mysql::Field::TYPE_BLOB, Fields::Identity.new
|
407
|
-
register_type Mysql::Field::TYPE_DATE, Fields::Date.new
|
323
|
+
register_type Mysql::Field::TYPE_DATE, Type::Date.new
|
408
324
|
register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
|
409
325
|
register_type Mysql::Field::TYPE_TIME, Fields::Time.new
|
410
|
-
register_type Mysql::Field::TYPE_FLOAT,
|
326
|
+
register_type Mysql::Field::TYPE_FLOAT, Type::Float.new
|
327
|
+
end
|
411
328
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
end
|
329
|
+
def initialize_type_map(m) # :nodoc:
|
330
|
+
super
|
331
|
+
m.register_type %r(datetime)i, Fields::DateTime.new
|
332
|
+
m.register_type %r(time)i, Fields::Time.new
|
417
333
|
end
|
418
334
|
|
419
335
|
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
|
@@ -431,7 +347,7 @@ module ActiveRecord
|
|
431
347
|
fields << field_name
|
432
348
|
|
433
349
|
if field.decimals > 0
|
434
|
-
types[field_name] =
|
350
|
+
types[field_name] = Type::Decimal.new
|
435
351
|
else
|
436
352
|
types[field_name] = Fields.find_type field
|
437
353
|
end
|
@@ -447,7 +363,7 @@ module ActiveRecord
|
|
447
363
|
end
|
448
364
|
end
|
449
365
|
|
450
|
-
def execute_and_free(sql, name = nil)
|
366
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
451
367
|
result = execute(sql, name)
|
452
368
|
ret = yield result
|
453
369
|
result.free
|
@@ -460,7 +376,7 @@ module ActiveRecord
|
|
460
376
|
end
|
461
377
|
alias :create :insert_sql
|
462
378
|
|
463
|
-
def exec_delete(sql, name, binds)
|
379
|
+
def exec_delete(sql, name, binds) # :nodoc:
|
464
380
|
affected_rows = 0
|
465
381
|
|
466
382
|
exec_query(sql, name, binds) do |n|
|
@@ -497,7 +413,7 @@ module ActiveRecord
|
|
497
413
|
stmt.execute(*type_casted_binds.map { |_, val| val })
|
498
414
|
rescue Mysql::Error => e
|
499
415
|
# Older versions of MySQL leave the prepared statement in a bad
|
500
|
-
# place when an error occurs. To support older
|
416
|
+
# place when an error occurs. To support older MySQL versions, we
|
501
417
|
# need to close the statement and delete the statement from the
|
502
418
|
# cache.
|
503
419
|
stmt.close
|
@@ -553,14 +469,14 @@ module ActiveRecord
|
|
553
469
|
|
554
470
|
def select(sql, name = nil, binds = [])
|
555
471
|
@connection.query_with_result = true
|
556
|
-
rows =
|
472
|
+
rows = super
|
557
473
|
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
558
474
|
rows
|
559
475
|
end
|
560
476
|
|
561
|
-
# Returns the version of the connected MySQL server.
|
562
|
-
def
|
563
|
-
@
|
477
|
+
# Returns the full version of the connected MySQL server.
|
478
|
+
def full_version
|
479
|
+
@full_version ||= @connection.server_info
|
564
480
|
end
|
565
481
|
|
566
482
|
def set_field_encoding field_name
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
|
4
|
-
module ArrayParser
|
3
|
+
module PostgreSQL
|
4
|
+
module ArrayParser # :nodoc:
|
5
5
|
|
6
6
|
DOUBLE_QUOTE = '"'
|
7
7
|
BACKSLASH = "\\"
|
@@ -9,35 +9,23 @@ module ActiveRecord
|
|
9
9
|
BRACKET_OPEN = '{'
|
10
10
|
BRACKET_CLOSE = '}'
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def parse_pg_array(string)
|
22
|
-
parse_data(string)
|
12
|
+
def parse_pg_array(string) # :nodoc:
|
13
|
+
local_index = 0
|
14
|
+
array = []
|
15
|
+
while(local_index < string.length)
|
16
|
+
case string[local_index]
|
17
|
+
when BRACKET_OPEN
|
18
|
+
local_index,array = parse_array_contents(array, string, local_index + 1)
|
19
|
+
when BRACKET_CLOSE
|
20
|
+
return array
|
23
21
|
end
|
22
|
+
local_index += 1
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
array = []
|
29
|
-
while(local_index < string.length)
|
30
|
-
case string[local_index]
|
31
|
-
when BRACKET_OPEN
|
32
|
-
local_index,array = parse_array_contents(array, string, local_index + 1)
|
33
|
-
when BRACKET_CLOSE
|
34
|
-
return array
|
35
|
-
end
|
36
|
-
local_index += 1
|
37
|
-
end
|
25
|
+
array
|
26
|
+
end
|
38
27
|
|
39
|
-
|
40
|
-
end
|
28
|
+
private
|
41
29
|
|
42
30
|
def parse_array_contents(array, string, index)
|
43
31
|
is_escaping = false
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
# PostgreSQL-specific extensions to column definitions in a table.
|
4
|
+
class PostgreSQLColumn < Column #:nodoc:
|
5
|
+
attr_accessor :array
|
6
|
+
|
7
|
+
def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
|
8
|
+
if sql_type =~ /\[\]$/
|
9
|
+
@array = true
|
10
|
+
super(name, default, cast_type, sql_type[0..sql_type.length - 3], null)
|
11
|
+
else
|
12
|
+
@array = false
|
13
|
+
super(name, default, cast_type, sql_type, null)
|
14
|
+
end
|
15
|
+
|
16
|
+
@default_function = default_function
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
|
3
|
+
module PostgreSQL
|
4
4
|
module DatabaseStatements
|
5
5
|
def explain(arel, binds = [])
|
6
6
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
@@ -44,10 +44,32 @@ module ActiveRecord
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def select_value(arel, name = nil, binds = [])
|
48
|
+
arel, binds = binds_from_relation arel, binds
|
49
|
+
sql = to_sql(arel, binds)
|
50
|
+
execute_and_clear(sql, name, binds) do |result|
|
51
|
+
result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def select_values(arel, name = nil)
|
56
|
+
arel, binds = binds_from_relation arel, []
|
57
|
+
sql = to_sql(arel, binds)
|
58
|
+
execute_and_clear(sql, name, binds) do |result|
|
59
|
+
if result.nfields > 0
|
60
|
+
result.column_values(0)
|
61
|
+
else
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
47
67
|
# Executes a SELECT query and returns an array of rows. Each row is an
|
48
68
|
# array of field values.
|
49
69
|
def select_rows(sql, name = nil, binds = [])
|
50
|
-
|
70
|
+
execute_and_clear(sql, name, binds) do |result|
|
71
|
+
result.values
|
72
|
+
end
|
51
73
|
end
|
52
74
|
|
53
75
|
# Executes an INSERT query and returns the new record's ID
|
@@ -72,6 +94,11 @@ module ActiveRecord
|
|
72
94
|
super.insert
|
73
95
|
end
|
74
96
|
|
97
|
+
# The internal PostgreSQL identifier of the money data type.
|
98
|
+
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
|
99
|
+
# The internal PostgreSQL identifier of the BYTEA data type.
|
100
|
+
BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
|
101
|
+
|
75
102
|
# create a 2D array representing the result set
|
76
103
|
def result_as_array(res) #:nodoc:
|
77
104
|
# check if we have any binary column and if they need escaping
|
@@ -129,36 +156,21 @@ module ActiveRecord
|
|
129
156
|
end
|
130
157
|
end
|
131
158
|
|
132
|
-
def substitute_at(column, index)
|
133
|
-
Arel::Nodes::BindParam.new "$#{index + 1}"
|
134
|
-
end
|
135
|
-
|
136
159
|
def exec_query(sql, name = 'SQL', binds = [])
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
warn "unknown OID: #{fname}(#{oid}) (#{sql})"
|
147
|
-
OID::Identity.new
|
148
|
-
}
|
160
|
+
execute_and_clear(sql, name, binds) do |result|
|
161
|
+
types = {}
|
162
|
+
fields = result.fields
|
163
|
+
fields.each_with_index do |fname, i|
|
164
|
+
ftype = result.ftype i
|
165
|
+
fmod = result.fmod i
|
166
|
+
types[fname] = get_oid_type(ftype, fmod, fname)
|
167
|
+
end
|
168
|
+
ActiveRecord::Result.new(fields, result.values, types)
|
149
169
|
end
|
150
|
-
|
151
|
-
ret = ActiveRecord::Result.new(fields, result.values, types)
|
152
|
-
result.clear
|
153
|
-
return ret
|
154
170
|
end
|
155
171
|
|
156
172
|
def exec_delete(sql, name = 'SQL', binds = [])
|
157
|
-
|
158
|
-
exec_cache(sql, name, binds)
|
159
|
-
affected = result.cmd_tuples
|
160
|
-
result.clear
|
161
|
-
affected
|
173
|
+
execute_and_clear(sql, name, binds) {|result| result.cmd_tuples }
|
162
174
|
end
|
163
175
|
alias :exec_update :exec_delete
|
164
176
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module OID # :nodoc:
|
5
|
+
class Array < Type::Value # :nodoc:
|
6
|
+
include Type::Mutable
|
7
|
+
|
8
|
+
# Loads pg_array_parser if available. String parsing can be
|
9
|
+
# performed quicker by a native extension, which will not create
|
10
|
+
# a large amount of Ruby objects that will need to be garbage
|
11
|
+
# collected. pg_array_parser has a C and Java extension
|
12
|
+
begin
|
13
|
+
require 'pg_array_parser'
|
14
|
+
include PgArrayParser
|
15
|
+
rescue LoadError
|
16
|
+
require 'active_record/connection_adapters/postgresql/array_parser'
|
17
|
+
include PostgreSQL::ArrayParser
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :subtype, :delimiter
|
21
|
+
delegate :type, to: :subtype
|
22
|
+
|
23
|
+
def initialize(subtype, delimiter = ',')
|
24
|
+
@subtype = subtype
|
25
|
+
@delimiter = delimiter
|
26
|
+
end
|
27
|
+
|
28
|
+
def type_cast_from_database(value)
|
29
|
+
if value.is_a?(::String)
|
30
|
+
type_cast_array(parse_pg_array(value), :type_cast_from_database)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def type_cast_from_user(value)
|
37
|
+
if value.is_a?(::String)
|
38
|
+
value = parse_pg_array(value)
|
39
|
+
end
|
40
|
+
type_cast_array(value, :type_cast_from_user)
|
41
|
+
end
|
42
|
+
|
43
|
+
def type_cast_for_database(value)
|
44
|
+
if value.is_a?(::Array)
|
45
|
+
cast_value_for_database(value)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def type_cast_array(value, method)
|
54
|
+
if value.is_a?(::Array)
|
55
|
+
value.map { |item| type_cast_array(item, method) }
|
56
|
+
else
|
57
|
+
@subtype.public_send(method, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def cast_value_for_database(value)
|
62
|
+
if value.is_a?(::Array)
|
63
|
+
casted_values = value.map { |item| cast_value_for_database(item) }
|
64
|
+
"{#{casted_values.join(delimiter)}}"
|
65
|
+
else
|
66
|
+
quote_and_escape(subtype.type_cast_for_database(value))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
|
71
|
+
|
72
|
+
def quote_and_escape(value)
|
73
|
+
case value
|
74
|
+
when ::String
|
75
|
+
if string_requires_quoting?(value)
|
76
|
+
value = value.gsub(/\\/, ARRAY_ESCAPE)
|
77
|
+
value.gsub!(/"/,"\\\"")
|
78
|
+
%("#{value}")
|
79
|
+
else
|
80
|
+
value
|
81
|
+
end
|
82
|
+
when nil then "NULL"
|
83
|
+
else value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO
|
88
|
+
# for a list of all cases in which strings will be quoted.
|
89
|
+
def string_requires_quoting?(string)
|
90
|
+
string.empty? ||
|
91
|
+
string == "NULL" ||
|
92
|
+
string =~ /[\{\}"\\\s]/ ||
|
93
|
+
string.include?(delimiter)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module OID # :nodoc:
|
5
|
+
class Bit < Type::Value # :nodoc:
|
6
|
+
def type
|
7
|
+
:bit
|
8
|
+
end
|
9
|
+
|
10
|
+
def type_cast(value)
|
11
|
+
if ::String === value
|
12
|
+
case value
|
13
|
+
when /^0x/i
|
14
|
+
value[2..-1].hex.to_s(2) # Hexadecimal notation
|
15
|
+
else
|
16
|
+
value # Bit-string notation
|
17
|
+
end
|
18
|
+
else
|
19
|
+
value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def type_cast_for_database(value)
|
24
|
+
Data.new(super) if value
|
25
|
+
end
|
26
|
+
|
27
|
+
class Data
|
28
|
+
def initialize(value)
|
29
|
+
@value = value
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
value
|
34
|
+
end
|
35
|
+
|
36
|
+
def binary?
|
37
|
+
/\A[01]*\Z/ === value
|
38
|
+
end
|
39
|
+
|
40
|
+
def hex?
|
41
|
+
/\A[0-9A-F]*\Z/i === value
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
attr_reader :value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|