activerecord 4.1.15 → 4.2.0.beta1
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 +634 -2176
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- 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/has_and_belongs_to_many.rb +2 -11
- 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 +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- 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 +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- 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 -20
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -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 +76 -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 +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- 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 +22 -32
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -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/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -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 +51 -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 +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record.rb +2 -1
- 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 +71 -14
- 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,7 +57,7 @@ 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/sql-mode.html)
|
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
61
|
# * <tt>:variables</tt> - (Optional) A hash session variables to send as `SET @@SESSION.key = value` 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.
|
@@ -66,35 +66,6 @@ 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
69
|
ADAPTER_NAME = 'MySQL'
|
99
70
|
|
100
71
|
class StatementPool < ConnectionAdapters::StatementPool
|
@@ -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
|
@@ -222,6 +189,7 @@ module ActiveRecord
|
|
222
189
|
|
223
190
|
# Clears the prepared statements cache.
|
224
191
|
def clear_cache!
|
192
|
+
super
|
225
193
|
@statements.clear
|
226
194
|
end
|
227
195
|
|
@@ -294,126 +262,70 @@ module ActiveRecord
|
|
294
262
|
@connection.insert_id
|
295
263
|
end
|
296
264
|
|
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
|
265
|
+
module Fields # :nodoc:
|
266
|
+
class DateTime < Type::DateTime # :nodoc:
|
267
|
+
def cast_value(value)
|
268
|
+
if Mysql::Time === value
|
269
|
+
new_time(
|
270
|
+
value.year,
|
271
|
+
value.month,
|
272
|
+
value.day,
|
273
|
+
value.hour,
|
274
|
+
value.minute,
|
275
|
+
value.second,
|
276
|
+
value.second_part)
|
277
|
+
else
|
278
|
+
super
|
279
|
+
end
|
361
280
|
end
|
362
281
|
end
|
363
282
|
|
364
|
-
class
|
365
|
-
def
|
366
|
-
|
367
|
-
|
368
|
-
|
283
|
+
class Time < Type::Time # :nodoc:
|
284
|
+
def cast_value(value)
|
285
|
+
if Mysql::Time === value
|
286
|
+
new_time(
|
287
|
+
2000,
|
288
|
+
01,
|
289
|
+
01,
|
290
|
+
value.hour,
|
291
|
+
value.minute,
|
292
|
+
value.second,
|
293
|
+
value.second_part)
|
294
|
+
else
|
295
|
+
super
|
296
|
+
end
|
369
297
|
end
|
370
298
|
end
|
371
299
|
|
372
|
-
class
|
373
|
-
|
374
|
-
return if value.nil?
|
300
|
+
class << self
|
301
|
+
TYPES = Type::HashLookupTypeMap.new # :nodoc:
|
375
302
|
|
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
|
303
|
+
delegate :register_type, :alias_type, to: :TYPES
|
387
304
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
TYPES[Mysql::Field::TYPE_LONG]
|
395
|
-
else
|
396
|
-
TYPES.fetch(field.type) { Fields::Identity.new }
|
305
|
+
def find_type(field)
|
306
|
+
if field.type == Mysql::Field::TYPE_TINY && field.length > 1
|
307
|
+
TYPES.lookup(Mysql::Field::TYPE_LONG)
|
308
|
+
else
|
309
|
+
TYPES.lookup(field.type)
|
310
|
+
end
|
397
311
|
end
|
398
312
|
end
|
399
313
|
|
400
|
-
register_type Mysql::Field::TYPE_TINY,
|
401
|
-
register_type Mysql::Field::TYPE_LONG,
|
314
|
+
register_type Mysql::Field::TYPE_TINY, Type::Boolean.new
|
315
|
+
register_type Mysql::Field::TYPE_LONG, Type::Integer.new
|
402
316
|
alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
|
403
317
|
alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
|
404
318
|
|
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
|
319
|
+
register_type Mysql::Field::TYPE_DATE, Type::Date.new
|
408
320
|
register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
|
409
321
|
register_type Mysql::Field::TYPE_TIME, Fields::Time.new
|
410
|
-
register_type Mysql::Field::TYPE_FLOAT,
|
322
|
+
register_type Mysql::Field::TYPE_FLOAT, Type::Float.new
|
323
|
+
end
|
411
324
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
end
|
325
|
+
def initialize_type_map(m) # :nodoc:
|
326
|
+
super
|
327
|
+
m.register_type %r(datetime)i, Fields::DateTime.new
|
328
|
+
m.register_type %r(time)i, Fields::Time.new
|
417
329
|
end
|
418
330
|
|
419
331
|
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
|
@@ -431,7 +343,7 @@ module ActiveRecord
|
|
431
343
|
fields << field_name
|
432
344
|
|
433
345
|
if field.decimals > 0
|
434
|
-
types[field_name] =
|
346
|
+
types[field_name] = Type::Decimal.new
|
435
347
|
else
|
436
348
|
types[field_name] = Fields.find_type field
|
437
349
|
end
|
@@ -447,7 +359,7 @@ module ActiveRecord
|
|
447
359
|
end
|
448
360
|
end
|
449
361
|
|
450
|
-
def execute_and_free(sql, name = nil)
|
362
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
451
363
|
result = execute(sql, name)
|
452
364
|
ret = yield result
|
453
365
|
result.free
|
@@ -460,7 +372,7 @@ module ActiveRecord
|
|
460
372
|
end
|
461
373
|
alias :create :insert_sql
|
462
374
|
|
463
|
-
def exec_delete(sql, name, binds)
|
375
|
+
def exec_delete(sql, name, binds) # :nodoc:
|
464
376
|
affected_rows = 0
|
465
377
|
|
466
378
|
exec_query(sql, name, binds) do |n|
|
@@ -497,7 +409,7 @@ module ActiveRecord
|
|
497
409
|
stmt.execute(*type_casted_binds.map { |_, val| val })
|
498
410
|
rescue Mysql::Error => e
|
499
411
|
# Older versions of MySQL leave the prepared statement in a bad
|
500
|
-
# place when an error occurs. To support older
|
412
|
+
# place when an error occurs. To support older MySQL versions, we
|
501
413
|
# need to close the statement and delete the statement from the
|
502
414
|
# cache.
|
503
415
|
stmt.close
|
@@ -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
|
@@ -134,28 +161,20 @@ module ActiveRecord
|
|
134
161
|
end
|
135
162
|
|
136
163
|
def exec_query(sql, name = 'SQL', binds = [])
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
164
|
+
execute_and_clear(sql, name, binds) do |result|
|
165
|
+
types = {}
|
166
|
+
fields = result.fields
|
167
|
+
fields.each_with_index do |fname, i|
|
168
|
+
ftype = result.ftype i
|
169
|
+
fmod = result.fmod i
|
170
|
+
types[fname] = get_oid_type(ftype, fmod, fname)
|
171
|
+
end
|
172
|
+
ActiveRecord::Result.new(fields, result.values, types)
|
146
173
|
end
|
147
|
-
|
148
|
-
ret = ActiveRecord::Result.new(fields, result.values, types)
|
149
|
-
result.clear
|
150
|
-
return ret
|
151
174
|
end
|
152
175
|
|
153
176
|
def exec_delete(sql, name = 'SQL', binds = [])
|
154
|
-
|
155
|
-
exec_cache(sql, name, binds)
|
156
|
-
affected = result.cmd_tuples
|
157
|
-
result.clear
|
158
|
-
affected
|
177
|
+
execute_and_clear(sql, name, binds) {|result| result.cmd_tuples }
|
159
178
|
end
|
160
179
|
alias :exec_update :exec_delete
|
161
180
|
|
@@ -0,0 +1,96 @@
|
|
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
|
+
type_cast_array(value, :type_cast_from_user)
|
38
|
+
end
|
39
|
+
|
40
|
+
def type_cast_for_database(value)
|
41
|
+
if value.is_a?(::Array)
|
42
|
+
cast_value_for_database(value)
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def type_cast_array(value, method)
|
51
|
+
if value.is_a?(::Array)
|
52
|
+
value.map { |item| type_cast_array(item, method) }
|
53
|
+
else
|
54
|
+
@subtype.public_send(method, value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def cast_value_for_database(value)
|
59
|
+
if value.is_a?(::Array)
|
60
|
+
casted_values = value.map { |item| cast_value_for_database(item) }
|
61
|
+
"{#{casted_values.join(delimiter)}}"
|
62
|
+
else
|
63
|
+
quote_and_escape(subtype.type_cast_for_database(value))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
|
68
|
+
|
69
|
+
def quote_and_escape(value)
|
70
|
+
case value
|
71
|
+
when ::String
|
72
|
+
if string_requires_quoting?(value)
|
73
|
+
value = value.gsub(/\\/, ARRAY_ESCAPE)
|
74
|
+
value.gsub!(/"/,"\\\"")
|
75
|
+
%("#{value}")
|
76
|
+
else
|
77
|
+
value
|
78
|
+
end
|
79
|
+
when nil then "NULL"
|
80
|
+
else value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO
|
85
|
+
# for a list of all cases in which strings will be quoted.
|
86
|
+
def string_requires_quoting?(string)
|
87
|
+
string.empty? ||
|
88
|
+
string == "NULL" ||
|
89
|
+
string =~ /[\{\}"\\\s]/ ||
|
90
|
+
string.include?(delimiter)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
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
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module OID # :nodoc:
|
5
|
+
class Bytea < Type::Binary # :nodoc:
|
6
|
+
def type_cast_from_database(value)
|
7
|
+
return if value.nil?
|
8
|
+
PGconn.unescape_bytea(super)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module OID # :nodoc:
|
5
|
+
class Cidr < Type::Value # :nodoc:
|
6
|
+
def type
|
7
|
+
:cidr
|
8
|
+
end
|
9
|
+
|
10
|
+
def type_cast_for_schema(value)
|
11
|
+
subnet_mask = value.instance_variable_get(:@mask_addr)
|
12
|
+
|
13
|
+
# If the subnet mask is equal to /32, don't output it
|
14
|
+
if subnet_mask == (2**32 - 1)
|
15
|
+
"\"#{value.to_s}\""
|
16
|
+
else
|
17
|
+
"\"#{value.to_s}/#{subnet_mask.to_s(2).count('1')}\""
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def type_cast_for_database(value)
|
22
|
+
if IPAddr === value
|
23
|
+
"#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
|
24
|
+
else
|
25
|
+
value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def cast_value(value)
|
30
|
+
if value.nil?
|
31
|
+
nil
|
32
|
+
elsif String === value
|
33
|
+
begin
|
34
|
+
IPAddr.new(value)
|
35
|
+
rescue ArgumentError
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
else
|
39
|
+
value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|