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.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2176
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/association_scope.rb +53 -21
  7. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  8. data/lib/active_record/associations/builder/association.rb +16 -5
  9. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  10. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  11. data/lib/active_record/associations/builder/has_one.rb +2 -2
  12. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  13. data/lib/active_record/associations/collection_association.rb +32 -44
  14. data/lib/active_record/associations/collection_proxy.rb +1 -10
  15. data/lib/active_record/associations/has_many_association.rb +60 -14
  16. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  17. data/lib/active_record/associations/has_one_association.rb +0 -1
  18. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  19. data/lib/active_record/associations/join_dependency.rb +7 -9
  20. data/lib/active_record/associations/preloader/association.rb +9 -5
  21. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/singular_association.rb +16 -1
  24. data/lib/active_record/associations/through_association.rb +6 -22
  25. data/lib/active_record/associations.rb +58 -33
  26. data/lib/active_record/attribute.rb +131 -0
  27. data/lib/active_record/attribute_assignment.rb +19 -11
  28. data/lib/active_record/attribute_decorators.rb +66 -0
  29. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  30. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  31. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  32. data/lib/active_record/attribute_methods/read.rb +14 -57
  33. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  35. data/lib/active_record/attribute_methods/write.rb +8 -23
  36. data/lib/active_record/attribute_methods.rb +53 -90
  37. data/lib/active_record/attribute_set/builder.rb +32 -0
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attributes.rb +122 -0
  40. data/lib/active_record/autosave_association.rb +11 -21
  41. data/lib/active_record/base.rb +9 -19
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  52. data/lib/active_record/connection_adapters/column.rb +13 -244
  53. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  55. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  56. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  59. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  60. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  85. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  86. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  87. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  88. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  89. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  90. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  91. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  93. data/lib/active_record/connection_handling.rb +1 -1
  94. data/lib/active_record/core.rb +119 -22
  95. data/lib/active_record/counter_cache.rb +60 -6
  96. data/lib/active_record/enum.rb +9 -10
  97. data/lib/active_record/errors.rb +27 -26
  98. data/lib/active_record/explain.rb +1 -1
  99. data/lib/active_record/fixtures.rb +52 -45
  100. data/lib/active_record/gem_version.rb +3 -3
  101. data/lib/active_record/inheritance.rb +33 -8
  102. data/lib/active_record/integration.rb +4 -4
  103. data/lib/active_record/locking/optimistic.rb +34 -16
  104. data/lib/active_record/migration/command_recorder.rb +19 -2
  105. data/lib/active_record/migration/join_table.rb +1 -1
  106. data/lib/active_record/migration.rb +22 -32
  107. data/lib/active_record/model_schema.rb +39 -48
  108. data/lib/active_record/nested_attributes.rb +8 -18
  109. data/lib/active_record/persistence.rb +39 -22
  110. data/lib/active_record/query_cache.rb +3 -3
  111. data/lib/active_record/querying.rb +1 -8
  112. data/lib/active_record/railtie.rb +17 -10
  113. data/lib/active_record/railties/databases.rake +47 -42
  114. data/lib/active_record/readonly_attributes.rb +0 -1
  115. data/lib/active_record/reflection.rb +225 -92
  116. data/lib/active_record/relation/batches.rb +0 -2
  117. data/lib/active_record/relation/calculations.rb +28 -32
  118. data/lib/active_record/relation/delegation.rb +1 -1
  119. data/lib/active_record/relation/finder_methods.rb +42 -20
  120. data/lib/active_record/relation/merger.rb +0 -1
  121. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  122. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/query_methods.rb +98 -62
  125. data/lib/active_record/relation/spawn_methods.rb +6 -7
  126. data/lib/active_record/relation.rb +35 -11
  127. data/lib/active_record/result.rb +16 -9
  128. data/lib/active_record/sanitization.rb +8 -1
  129. data/lib/active_record/schema.rb +0 -1
  130. data/lib/active_record/schema_dumper.rb +51 -9
  131. data/lib/active_record/schema_migration.rb +4 -0
  132. data/lib/active_record/scoping/default.rb +5 -4
  133. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  134. data/lib/active_record/statement_cache.rb +79 -5
  135. data/lib/active_record/store.rb +5 -5
  136. data/lib/active_record/tasks/database_tasks.rb +37 -5
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  138. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  139. data/lib/active_record/timestamp.rb +9 -7
  140. data/lib/active_record/transactions.rb +35 -21
  141. data/lib/active_record/type/binary.rb +40 -0
  142. data/lib/active_record/type/boolean.rb +19 -0
  143. data/lib/active_record/type/date.rb +46 -0
  144. data/lib/active_record/type/date_time.rb +43 -0
  145. data/lib/active_record/type/decimal.rb +40 -0
  146. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  147. data/lib/active_record/type/float.rb +19 -0
  148. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  149. data/lib/active_record/type/integer.rb +23 -0
  150. data/lib/active_record/type/mutable.rb +16 -0
  151. data/lib/active_record/type/numeric.rb +36 -0
  152. data/lib/active_record/type/serialized.rb +51 -0
  153. data/lib/active_record/type/string.rb +36 -0
  154. data/lib/active_record/type/text.rb +11 -0
  155. data/lib/active_record/type/time.rb +26 -0
  156. data/lib/active_record/type/time_value.rb +38 -0
  157. data/lib/active_record/type/type_map.rb +48 -0
  158. data/lib/active_record/type/value.rb +101 -0
  159. data/lib/active_record/type.rb +20 -0
  160. data/lib/active_record/validations/uniqueness.rb +9 -23
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record.rb +2 -1
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. 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 error
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 type; end
300
-
301
- def type_cast_for_write(value)
302
- value
303
- end
304
- end
305
-
306
- class Identity < Type
307
- def type_cast(value); value; end
308
- end
309
-
310
- class Integer < Type
311
- def type_cast(value)
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 Decimal < Type
365
- def type_cast(value)
366
- return if value.nil?
367
-
368
- ConnectionAdapters::Column.value_to_decimal value
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 Boolean < Type
373
- def type_cast(value)
374
- return if value.nil?
300
+ class << self
301
+ TYPES = Type::HashLookupTypeMap.new # :nodoc:
375
302
 
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
303
+ delegate :register_type, :alias_type, to: :TYPES
387
304
 
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 }
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, Fields::Boolean.new
401
- register_type Mysql::Field::TYPE_LONG, Fields::Integer.new
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::TYPE_VAR_STRING, Fields::Identity.new
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, Fields::Float.new
322
+ register_type Mysql::Field::TYPE_FLOAT, Type::Float.new
323
+ end
411
324
 
412
- Mysql::Field.constants.grep(/TYPE/).map { |class_name|
413
- Mysql::Field.const_get class_name
414
- }.reject { |const| TYPES.key? const }.each do |const|
415
- register_type const, Fields::Identity.new
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] = Fields::Decimal.new
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 mysql versions, we
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
- class PostgreSQLColumn < Column
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
- private
13
- # Loads pg_array_parser if available. String parsing can be
14
- # performed quicker by a native extension, which will not create
15
- # a large amount of Ruby objects that will need to be garbage
16
- # collected. pg_array_parser has a C and Java extension
17
- begin
18
- require 'pg_array_parser'
19
- include PgArrayParser
20
- rescue LoadError
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
- def parse_data(string)
27
- local_index = 0
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
- array
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
- class PostgreSQLAdapter < AbstractAdapter
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
- exec_query(sql, name, binds).rows
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
- result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
138
- exec_cache(sql, name, binds)
139
-
140
- types = {}
141
- fields = result.fields
142
- fields.each_with_index do |fname, i|
143
- ftype = result.ftype i
144
- fmod = result.fmod i
145
- types[fname] = get_oid_type(ftype, fmod, fname)
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
- result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
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,13 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class BitVarying < OID::Bit # :nodoc:
6
+ def type
7
+ :bit_varying
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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