activerecord 4.1.8 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +1165 -1591
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +84 -43
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
- 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 +87 -30
- data/lib/active_record/associations/collection_proxy.rb +33 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -12
- data/lib/active_record/associations/preloader/association.rb +14 -10
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +37 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +16 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +20 -12
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -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 +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +57 -95
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +30 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
- data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +19 -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 +109 -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 -385
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- 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 +134 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -40
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +10 -12
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +62 -74
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- 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 +79 -47
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +18 -8
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +48 -27
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +19 -14
- data/lib/active_record/railties/databases.rake +55 -56
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +281 -117
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +71 -48
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +42 -12
- data/lib/active_record/relation/query_methods.rb +130 -73
- data/lib/active_record/relation/spawn_methods.rb +10 -3
- data/lib/active_record/relation.rb +57 -25
- 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 +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- 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 +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -8
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +54 -28
- 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 +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -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 +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +110 -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 +5 -3
- data/lib/active_record/validations/uniqueness.rb +24 -20
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +5 -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 +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- 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
|
@@ -57,8 +57,8 @@ module ActiveRecord
|
|
57
57
|
# * <tt>:database</tt> - The name of the database. No default, must be provided.
|
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
|
-
# * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/
|
61
|
-
# * <tt>:variables</tt> - (Optional) A hash session variables to send as
|
60
|
+
# * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/sql-mode.html)
|
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
|
|
@@ -273,7 +245,7 @@ module ActiveRecord
|
|
273
245
|
return @client_encoding if @client_encoding
|
274
246
|
|
275
247
|
result = exec_query(
|
276
|
-
"
|
248
|
+
"select @@character_set_client",
|
277
249
|
'SCHEMA')
|
278
250
|
@client_encoding = ENCODINGS[result.rows.last.last]
|
279
251
|
end
|
@@ -294,125 +266,76 @@ 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
|
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
|
315
284
|
end
|
316
|
-
end
|
317
|
-
|
318
|
-
class Date < Type
|
319
|
-
def type; :date; end
|
320
285
|
|
321
|
-
def
|
322
|
-
|
323
|
-
|
324
|
-
# FIXME: probably we can improve this since we know it is mysql
|
325
|
-
# specific
|
326
|
-
ConnectionAdapters::Column.value_to_date value
|
286
|
+
def has_precision?
|
287
|
+
precision || 0
|
327
288
|
end
|
328
289
|
end
|
329
290
|
|
330
|
-
class
|
331
|
-
def
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
291
|
+
class Time < Type::Time # :nodoc:
|
292
|
+
def cast_value(value)
|
293
|
+
if Mysql::Time === value
|
294
|
+
new_time(
|
295
|
+
2000,
|
296
|
+
01,
|
297
|
+
01,
|
298
|
+
value.hour,
|
299
|
+
value.minute,
|
300
|
+
value.second,
|
301
|
+
value.second_part)
|
302
|
+
else
|
303
|
+
super
|
304
|
+
end
|
339
305
|
end
|
340
306
|
end
|
341
307
|
|
342
|
-
class
|
343
|
-
|
308
|
+
class << self
|
309
|
+
TYPES = Type::HashLookupTypeMap.new # :nodoc:
|
344
310
|
|
345
|
-
|
346
|
-
return if value.nil?
|
311
|
+
delegate :register_type, :alias_type, to: :TYPES
|
347
312
|
|
348
|
-
|
349
|
-
|
350
|
-
|
313
|
+
def find_type(field)
|
314
|
+
if field.type == Mysql::Field::TYPE_TINY && field.length > 1
|
315
|
+
TYPES.lookup(Mysql::Field::TYPE_LONG)
|
316
|
+
else
|
317
|
+
TYPES.lookup(field.type)
|
318
|
+
end
|
351
319
|
end
|
352
320
|
end
|
353
321
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
def type_cast(value)
|
358
|
-
return if value.nil?
|
359
|
-
|
360
|
-
value.to_f
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
class Decimal < Type
|
365
|
-
def type_cast(value)
|
366
|
-
return if value.nil?
|
367
|
-
|
368
|
-
ConnectionAdapters::Column.value_to_decimal value
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
class Boolean < Type
|
373
|
-
def type_cast(value)
|
374
|
-
return if value.nil?
|
375
|
-
|
376
|
-
ConnectionAdapters::Column.value_to_boolean value
|
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
|
387
|
-
|
388
|
-
def self.alias_type(new, old)
|
389
|
-
TYPES[new] = TYPES[old]
|
390
|
-
end
|
391
|
-
|
392
|
-
def self.find_type(field)
|
393
|
-
if field.type == Mysql::Field::TYPE_TINY && field.length > 1
|
394
|
-
TYPES[Mysql::Field::TYPE_LONG]
|
395
|
-
else
|
396
|
-
TYPES.fetch(field.type) { Fields::Identity.new }
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
register_type Mysql::Field::TYPE_TINY, Fields::Boolean.new
|
401
|
-
register_type Mysql::Field::TYPE_LONG, Fields::Integer.new
|
322
|
+
register_type Mysql::Field::TYPE_TINY, Type::Boolean.new
|
323
|
+
register_type Mysql::Field::TYPE_LONG, Type::Integer.new
|
402
324
|
alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
|
403
325
|
alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
|
404
326
|
|
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
|
327
|
+
register_type Mysql::Field::TYPE_DATE, Type::Date.new
|
408
328
|
register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
|
409
329
|
register_type Mysql::Field::TYPE_TIME, Fields::Time.new
|
410
|
-
register_type Mysql::Field::TYPE_FLOAT,
|
330
|
+
register_type Mysql::Field::TYPE_FLOAT, Type::Float.new
|
331
|
+
end
|
411
332
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
333
|
+
def initialize_type_map(m) # :nodoc:
|
334
|
+
super
|
335
|
+
m.register_type %r(time)i, Fields::Time.new
|
336
|
+
m.register_type(%r(datetime)i) do |sql_type|
|
337
|
+
precision = extract_precision(sql_type)
|
338
|
+
Fields::DateTime.new(precision: precision)
|
416
339
|
end
|
417
340
|
end
|
418
341
|
|
@@ -431,7 +354,7 @@ module ActiveRecord
|
|
431
354
|
fields << field_name
|
432
355
|
|
433
356
|
if field.decimals > 0
|
434
|
-
types[field_name] =
|
357
|
+
types[field_name] = Type::Decimal.new
|
435
358
|
else
|
436
359
|
types[field_name] = Fields.find_type field
|
437
360
|
end
|
@@ -447,7 +370,7 @@ module ActiveRecord
|
|
447
370
|
end
|
448
371
|
end
|
449
372
|
|
450
|
-
def execute_and_free(sql, name = nil)
|
373
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
451
374
|
result = execute(sql, name)
|
452
375
|
ret = yield result
|
453
376
|
result.free
|
@@ -460,7 +383,7 @@ module ActiveRecord
|
|
460
383
|
end
|
461
384
|
alias :create :insert_sql
|
462
385
|
|
463
|
-
def exec_delete(sql, name, binds)
|
386
|
+
def exec_delete(sql, name, binds) # :nodoc:
|
464
387
|
affected_rows = 0
|
465
388
|
|
466
389
|
exec_query(sql, name, binds) do |n|
|
@@ -497,7 +420,7 @@ module ActiveRecord
|
|
497
420
|
stmt.execute(*type_casted_binds.map { |_, val| val })
|
498
421
|
rescue Mysql::Error => e
|
499
422
|
# Older versions of MySQL leave the prepared statement in a bad
|
500
|
-
# place when an error occurs. To support older
|
423
|
+
# place when an error occurs. To support older MySQL versions, we
|
501
424
|
# need to close the statement and delete the statement from the
|
502
425
|
# cache.
|
503
426
|
stmt.close
|
@@ -553,7 +476,7 @@ module ActiveRecord
|
|
553
476
|
|
554
477
|
def select(sql, name = nil, binds = [])
|
555
478
|
@connection.query_with_result = true
|
556
|
-
rows =
|
479
|
+
rows = super
|
557
480
|
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
558
481
|
rows
|
559
482
|
end
|
@@ -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,33 +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
|
-
|
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)
|
146
169
|
end
|
147
|
-
|
148
|
-
ret = ActiveRecord::Result.new(fields, result.values, types)
|
149
|
-
result.clear
|
150
|
-
return ret
|
151
170
|
end
|
152
171
|
|
153
172
|
def exec_delete(sql, name = 'SQL', binds = [])
|
154
|
-
|
155
|
-
exec_cache(sql, name, binds)
|
156
|
-
affected = result.cmd_tuples
|
157
|
-
result.clear
|
158
|
-
affected
|
173
|
+
execute_and_clear(sql, name, binds) {|result| result.cmd_tuples }
|
159
174
|
end
|
160
175
|
alias :exec_update :exec_delete
|
161
176
|
|
@@ -208,7 +223,7 @@ module ActiveRecord
|
|
208
223
|
end
|
209
224
|
|
210
225
|
# Aborts a transaction.
|
211
|
-
def
|
226
|
+
def exec_rollback_db_transaction
|
212
227
|
execute "ROLLBACK"
|
213
228
|
end
|
214
229
|
end
|
@@ -0,0 +1,100 @@
|
|
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, :limit, 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
|
+
when ::Date, ::DateTime, ::Time then subtype.type_cast_for_schema(value)
|
84
|
+
else value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO
|
89
|
+
# for a list of all cases in which strings will be quoted.
|
90
|
+
def string_requires_quoting?(string)
|
91
|
+
string.empty? ||
|
92
|
+
string == "NULL" ||
|
93
|
+
string =~ /[\{\}"\\\s]/ ||
|
94
|
+
string.include?(delimiter)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
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
|