activerecord 4.1.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

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