activerecord 4.0.4 → 4.1.16

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1632 -1797
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/examples/performance.rb +30 -18
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +4 -0
  9. data/lib/active_record/associations/alias_tracker.rb +49 -29
  10. data/lib/active_record/associations/association.rb +9 -17
  11. data/lib/active_record/associations/association_scope.rb +59 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +6 -1
  14. data/lib/active_record/associations/builder/association.rb +84 -54
  15. data/lib/active_record/associations/builder/belongs_to.rb +90 -58
  16. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +119 -25
  18. data/lib/active_record/associations/builder/has_many.rb +3 -3
  19. data/lib/active_record/associations/builder/has_one.rb +5 -7
  20. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  21. data/lib/active_record/associations/collection_association.rb +121 -111
  22. data/lib/active_record/associations/collection_proxy.rb +73 -18
  23. data/lib/active_record/associations/has_many_association.rb +14 -11
  24. data/lib/active_record/associations/has_many_through_association.rb +33 -6
  25. data/lib/active_record/associations/has_one_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +46 -104
  27. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  28. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  29. data/lib/active_record/associations/join_dependency.rb +208 -168
  30. data/lib/active_record/associations/preloader/association.rb +69 -27
  31. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  35. data/lib/active_record/associations/preloader.rb +63 -49
  36. data/lib/active_record/associations/singular_association.rb +6 -5
  37. data/lib/active_record/associations/through_association.rb +30 -9
  38. data/lib/active_record/associations.rb +116 -42
  39. data/lib/active_record/attribute_assignment.rb +6 -3
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  41. data/lib/active_record/attribute_methods/dirty.rb +35 -26
  42. data/lib/active_record/attribute_methods/primary_key.rb +8 -1
  43. data/lib/active_record/attribute_methods/read.rb +56 -29
  44. data/lib/active_record/attribute_methods/serialization.rb +44 -12
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +13 -1
  46. data/lib/active_record/attribute_methods/write.rb +59 -26
  47. data/lib/active_record/attribute_methods.rb +82 -43
  48. data/lib/active_record/autosave_association.rb +209 -194
  49. data/lib/active_record/base.rb +6 -2
  50. data/lib/active_record/callbacks.rb +2 -2
  51. data/lib/active_record/coders/json.rb +13 -0
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -24
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -13
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +6 -3
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +90 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +45 -70
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -0
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -96
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +74 -66
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +231 -43
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -5
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +24 -17
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +22 -15
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -4
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -44
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -14
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +37 -12
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +20 -11
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +98 -52
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -60
  76. data/lib/active_record/connection_handling.rb +39 -5
  77. data/lib/active_record/core.rb +38 -54
  78. data/lib/active_record/counter_cache.rb +9 -10
  79. data/lib/active_record/dynamic_matchers.rb +6 -2
  80. data/lib/active_record/enum.rb +199 -0
  81. data/lib/active_record/errors.rb +22 -5
  82. data/lib/active_record/fixture_set/file.rb +2 -1
  83. data/lib/active_record/fixtures.rb +173 -76
  84. data/lib/active_record/gem_version.rb +15 -0
  85. data/lib/active_record/inheritance.rb +23 -9
  86. data/lib/active_record/integration.rb +54 -1
  87. data/lib/active_record/locking/optimistic.rb +7 -2
  88. data/lib/active_record/locking/pessimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +6 -13
  90. data/lib/active_record/migration/command_recorder.rb +8 -2
  91. data/lib/active_record/migration.rb +91 -56
  92. data/lib/active_record/model_schema.rb +7 -14
  93. data/lib/active_record/nested_attributes.rb +25 -13
  94. data/lib/active_record/no_touching.rb +52 -0
  95. data/lib/active_record/null_relation.rb +26 -6
  96. data/lib/active_record/persistence.rb +23 -29
  97. data/lib/active_record/querying.rb +15 -12
  98. data/lib/active_record/railtie.rb +12 -61
  99. data/lib/active_record/railties/databases.rake +37 -56
  100. data/lib/active_record/readonly_attributes.rb +0 -6
  101. data/lib/active_record/reflection.rb +230 -79
  102. data/lib/active_record/relation/batches.rb +74 -24
  103. data/lib/active_record/relation/calculations.rb +52 -48
  104. data/lib/active_record/relation/delegation.rb +54 -39
  105. data/lib/active_record/relation/finder_methods.rb +210 -67
  106. data/lib/active_record/relation/merger.rb +15 -12
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +17 -0
  109. data/lib/active_record/relation/predicate_builder.rb +81 -40
  110. data/lib/active_record/relation/query_methods.rb +185 -108
  111. data/lib/active_record/relation/spawn_methods.rb +8 -5
  112. data/lib/active_record/relation.rb +79 -84
  113. data/lib/active_record/result.rb +45 -6
  114. data/lib/active_record/runtime_registry.rb +5 -0
  115. data/lib/active_record/sanitization.rb +4 -4
  116. data/lib/active_record/schema_dumper.rb +18 -6
  117. data/lib/active_record/schema_migration.rb +31 -18
  118. data/lib/active_record/scoping/default.rb +5 -18
  119. data/lib/active_record/scoping/named.rb +14 -29
  120. data/lib/active_record/scoping.rb +5 -0
  121. data/lib/active_record/store.rb +67 -18
  122. data/lib/active_record/tasks/database_tasks.rb +66 -26
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -10
  124. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  125. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  126. data/lib/active_record/timestamp.rb +6 -6
  127. data/lib/active_record/transactions.rb +10 -12
  128. data/lib/active_record/validations/presence.rb +1 -1
  129. data/lib/active_record/validations/uniqueness.rb +19 -9
  130. data/lib/active_record/version.rb +4 -7
  131. data/lib/active_record.rb +5 -7
  132. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  133. data/lib/rails/generators/active_record/migration.rb +18 -0
  134. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  135. data/lib/rails/generators/active_record.rb +2 -8
  136. metadata +18 -30
  137. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  138. data/lib/active_record/associations/join_helper.rb +0 -45
  139. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  141. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  142. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  143. data/lib/active_record/test_case.rb +0 -96
@@ -6,10 +6,6 @@ module ActiveRecord
6
6
  module OID
7
7
  class Type
8
8
  def type; end
9
-
10
- def type_cast_for_write(value)
11
- value
12
- end
13
9
  end
14
10
 
15
11
  class Identity < Type
@@ -31,6 +27,9 @@ module ActiveRecord
31
27
  class Bytea < Type
32
28
  def type_cast(value)
33
29
  return if value.nil?
30
+ # This is a flawed heuristic, but it avoids truncation;
31
+ # we really shouldn’t be calling this with already-unescaped values
32
+ return value if value.dup.force_encoding("BINARY") =~ /\x00/
34
33
  PGconn.unescape_bytea value
35
34
  end
36
35
  end
@@ -141,7 +140,7 @@ module ActiveRecord
141
140
  case @subtype
142
141
  when :date
143
142
  from = ConnectionAdapters::Column.value_to_date(extracted[:from])
144
- from -= 1.day if extracted[:exclude_start]
143
+ from += 1.day if extracted[:exclude_start]
145
144
  to = ConnectionAdapters::Column.value_to_date(extracted[:to])
146
145
  when :decimal
147
146
  from = BigDecimal.new(extracted[:from].to_s)
@@ -152,7 +151,7 @@ module ActiveRecord
152
151
  to = ConnectionAdapters::Column.string_to_time(extracted[:to])
153
152
  when :integer
154
153
  from = to_integer(extracted[:from]) rescue value ? 1 : 0
155
- from -= 1 if extracted[:exclude_start]
154
+ from += 1 if extracted[:exclude_start]
156
155
  to = to_integer(extracted[:to]) rescue value ? 1 : 0
157
156
  else
158
157
  return value
@@ -214,9 +213,14 @@ module ActiveRecord
214
213
 
215
214
  class Float < Type
216
215
  def type_cast(value)
217
- return if value.nil?
218
-
219
- value.to_f
216
+ case value
217
+ when nil; nil
218
+ when 'Infinity'; ::Float::INFINITY
219
+ when '-Infinity'; -::Float::INFINITY
220
+ when 'NaN'; ::Float::NAN
221
+ else
222
+ value.to_f
223
+ end
220
224
  end
221
225
  end
222
226
 
@@ -229,11 +233,22 @@ module ActiveRecord
229
233
  end
230
234
 
231
235
  class Hstore < Type
236
+ def type_cast_for_write(value)
237
+ # roundtrip to ensure uniform uniform types
238
+ # TODO: This is not an efficient solution.
239
+ stringified = ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
240
+ type_cast(stringified)
241
+ end
242
+
232
243
  def type_cast(value)
233
244
  return if value.nil?
234
245
 
235
246
  ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
236
247
  end
248
+
249
+ def accessor
250
+ ActiveRecord::Store::StringKeyedHashAccessor
251
+ end
237
252
  end
238
253
 
239
254
  class Cidr < Type
@@ -245,11 +260,22 @@ module ActiveRecord
245
260
  end
246
261
 
247
262
  class Json < Type
263
+ def type_cast_for_write(value)
264
+ # roundtrip to ensure uniform uniform types
265
+ # TODO: This is not an efficient solution.
266
+ stringified = ConnectionAdapters::PostgreSQLColumn.json_to_string(value)
267
+ type_cast(stringified)
268
+ end
269
+
248
270
  def type_cast(value)
249
271
  return if value.nil?
250
272
 
251
273
  ConnectionAdapters::PostgreSQLColumn.string_to_json value
252
274
  end
275
+
276
+ def accessor
277
+ ActiveRecord::Store::StringKeyedHashAccessor
278
+ end
253
279
  end
254
280
 
255
281
  class TypeMap
@@ -289,17 +315,15 @@ module ActiveRecord
289
315
  end
290
316
  end
291
317
 
292
- TYPE_MAP = TypeMap.new # :nodoc:
293
-
294
- # When the PG adapter connects, the pg_type table is queried. The
318
+ # When the PG adapter connects, the pg_type table is queried. The
295
319
  # key of this hash maps to the `typname` column from the table.
296
- # TYPE_MAP is then dynamically built with oids as the key and type
320
+ # type_map is then dynamically built with oids as the key and type
297
321
  # objects as values.
298
322
  NAMES = Hash.new { |h,k| # :nodoc:
299
323
  h[k] = OID::Identity.new
300
324
  }
301
325
 
302
- # Register an OID type named +name+ with a typcasting object in
326
+ # Register an OID type named +name+ with a typecasting object in
303
327
  # +type+. +name+ should correspond to the `typname` column in
304
328
  # the `pg_type` table.
305
329
  def self.register_type(name, type)
@@ -16,14 +16,15 @@ module ActiveRecord
16
16
 
17
17
  # Quotes PostgreSQL-specific data types for SQL input.
18
18
  def quote(value, column = nil) #:nodoc:
19
- return super unless column
19
+ return super unless column && column.type
20
20
 
21
21
  sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
22
22
 
23
23
  case value
24
24
  when Range
25
25
  if /range$/ =~ sql_type
26
- "'#{PostgreSQLColumn.range_to_string(value)}'::#{sql_type}"
26
+ escaped = quote_string(PostgreSQLColumn.range_to_string(value))
27
+ "'#{escaped}'::#{sql_type}"
27
28
  else
28
29
  super
29
30
  end
@@ -70,8 +71,8 @@ module ActiveRecord
70
71
  when 'xml' then "xml '#{quote_string(value)}'"
71
72
  when /^bit/
72
73
  case value
73
- when /^[01]*$/ then "B'#{value}'" # Bit-string notation
74
- when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
74
+ when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
75
+ when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
75
76
  end
76
77
  else
77
78
  super
@@ -86,8 +87,11 @@ module ActiveRecord
86
87
 
87
88
  case value
88
89
  when Range
89
- return super(value, column) unless /range$/ =~ column.sql_type
90
- PostgreSQLColumn.range_to_string(value)
90
+ if /range$/ =~ column.sql_type
91
+ PostgreSQLColumn.range_to_string(value)
92
+ else
93
+ super(value, column)
94
+ end
91
95
  when NilClass
92
96
  if column.array && array_member
93
97
  'NULL'
@@ -101,12 +105,21 @@ module ActiveRecord
101
105
  when 'point' then PostgreSQLColumn.point_to_string(value)
102
106
  when 'json' then PostgreSQLColumn.json_to_string(value)
103
107
  else
104
- return super(value, column) unless column.array
105
- PostgreSQLColumn.array_to_string(value, column, self)
108
+ if column.array
109
+ PostgreSQLColumn.array_to_string(value, column, self)
110
+ else
111
+ super(value, column)
112
+ end
106
113
  end
107
114
  when String
108
- return super(value, column) unless 'bytea' == column.sql_type
109
- { :value => value, :format => 1 }
115
+ if 'bytea' == column.sql_type
116
+ # Return a bind param hash with format as binary.
117
+ # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
118
+ # for more information
119
+ { value: value, format: 1 }
120
+ else
121
+ super(value, column)
122
+ end
110
123
  when Hash
111
124
  case column.sql_type
112
125
  when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
@@ -114,8 +127,11 @@ module ActiveRecord
114
127
  else super(value, column)
115
128
  end
116
129
  when IPAddr
117
- return super(value, column) unless ['inet','cidr'].include? column.sql_type
118
- PostgreSQLColumn.cidr_to_string(value)
130
+ if %w(inet cidr).include? column.sql_type
131
+ PostgreSQLColumn.cidr_to_string(value)
132
+ else
133
+ super(value, column)
134
+ end
119
135
  else
120
136
  super(value, column)
121
137
  end
@@ -167,6 +183,15 @@ module ActiveRecord
167
183
  end
168
184
  result
169
185
  end
186
+
187
+ # Does not quote function default values for UUID columns
188
+ def quote_default_value(value, column) #:nodoc:
189
+ if column.type == :uuid && value =~ /\(\)/
190
+ value
191
+ else
192
+ quote(value, column)
193
+ end
194
+ end
170
195
  end
171
196
  end
172
197
  end
@@ -12,7 +12,7 @@ module ActiveRecord
12
12
 
13
13
  def visit_ColumnDefinition(o)
14
14
  sql = super
15
- if o.primary_key? && o.type == :uuid
15
+ if o.primary_key? && o.type != :primary_key
16
16
  sql << " PRIMARY KEY "
17
17
  add_column_options!(sql, column_options(o))
18
18
  end
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  end
47
47
 
48
48
  # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
49
- # <tt>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
49
+ # <tt>:encoding</tt> (defaults to utf8), <tt>:collation</tt>, <tt>:ctype</tt>,
50
50
  # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
51
51
  # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
52
52
  #
@@ -56,8 +56,8 @@ module ActiveRecord
56
56
  def create_database(name, options = {})
57
57
  options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
58
58
 
59
- option_string = options.sum do |key, value|
60
- case key
59
+ option_string = options.inject("") do |memo, (key, value)|
60
+ memo += case key
61
61
  when :owner
62
62
  " OWNER = \"#{value}\""
63
63
  when :template
@@ -185,13 +185,15 @@ module ActiveRecord
185
185
  def columns(table_name)
186
186
  # Limit, precision, and scale are all handled by the superclass.
187
187
  column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
188
- oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) {
189
- OID::Identity.new
190
- }
188
+ oid = get_oid_type(oid.to_i, fmod.to_i, column_name)
191
189
  PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
192
190
  end
193
191
  end
194
192
 
193
+ def column_for(table_name, column_name) #:nodoc:
194
+ columns(table_name).detect { |c| c.name == column_name.to_s }
195
+ end
196
+
195
197
  # Returns the current database name.
196
198
  def current_database
197
199
  query('select current_database()', 'SCHEMA')[0][0]
@@ -380,7 +382,10 @@ module ActiveRecord
380
382
  pk, seq = pk_and_sequence_for(new_name)
381
383
  if seq == "#{table_name}_#{pk}_seq"
382
384
  new_seq = "#{new_name}_#{pk}_seq"
385
+ idx = "#{table_name}_pkey"
386
+ new_idx = "#{new_name}_pkey"
383
387
  execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
388
+ execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
384
389
  end
385
390
 
386
391
  rename_table_indexes(table_name, new_name)
@@ -408,13 +413,16 @@ module ActiveRecord
408
413
  # Changes the default value of a table column.
409
414
  def change_column_default(table_name, column_name, default)
410
415
  clear_cache!
411
- execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
416
+ column = column_for(table_name, column_name)
417
+
418
+ execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}" if column
412
419
  end
413
420
 
414
421
  def change_column_null(table_name, column_name, null, default = nil)
415
422
  clear_cache!
416
423
  unless null || default.nil?
417
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
424
+ column = column_for(table_name, column_name)
425
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
418
426
  end
419
427
  execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
420
428
  end
@@ -484,11 +492,12 @@ module ActiveRecord
484
492
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
485
493
  # requires that the ORDER BY include the distinct column.
486
494
  def columns_for_distinct(columns, orders) #:nodoc:
487
- order_columns = orders.map{ |s|
495
+ order_columns = orders.reject(&:blank?).map{ |s|
488
496
  # Convert Arel node to string
489
497
  s = s.to_sql unless s.is_a?(String)
490
498
  # Remove any ASC/DESC modifiers
491
- s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
499
+ s.gsub(/\s+(?:ASC|DESC)\b/i, '')
500
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
492
501
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
493
502
 
494
503
  [super, *order_columns].join(', ')
@@ -20,8 +20,8 @@ module ActiveRecord
20
20
  VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
21
21
  :client_encoding, :options, :application_name, :fallback_application_name,
22
22
  :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
23
- :tty, :sslmode, :requiressl, :sslcert, :sslkey, :sslrootcert, :sslcrl,
24
- :requirepeer, :krbsrvname, :gsslib, :service]
23
+ :tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
24
+ :sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
25
25
 
26
26
  # Establishes a connection to the database that's used by all Active Record objects
27
27
  def postgresql_connection(config)
@@ -96,7 +96,7 @@ module ActiveRecord
96
96
  $1
97
97
  # Character types
98
98
  when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
99
- $1
99
+ $1.gsub(/''/, "'")
100
100
  # Binary data types
101
101
  when /\A'(.*)'::bytea\z/m
102
102
  $1
@@ -141,6 +141,14 @@ module ActiveRecord
141
141
  end
142
142
  end
143
143
 
144
+ def type_cast_for_write(value)
145
+ if @oid_type.respond_to?(:type_cast_for_write)
146
+ @oid_type.type_cast_for_write(value)
147
+ else
148
+ super
149
+ end
150
+ end
151
+
144
152
  def type_cast(value)
145
153
  return if value.nil?
146
154
  return super if encoded?
@@ -148,6 +156,10 @@ module ActiveRecord
148
156
  @oid_type.type_cast value
149
157
  end
150
158
 
159
+ def accessor
160
+ @oid_type.accessor
161
+ end
162
+
151
163
  private
152
164
 
153
165
  def has_default_function?(default_value, default)
@@ -356,10 +368,10 @@ module ActiveRecord
356
368
  # end
357
369
  #
358
370
  # By default, this will use the +uuid_generate_v4()+ function from the
359
- # +uuid-ossp+ extension, which MUST be enabled on your databse. To enable
371
+ # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
360
372
  # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
361
- # migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
362
- # set the +:default+ option to nil:
373
+ # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
374
+ # set the +:default+ option to +nil+:
363
375
  #
364
376
  # create_table :stuffs, id: false do |t|
365
377
  # t.primary_key :id, :uuid, default: nil
@@ -370,11 +382,10 @@ module ActiveRecord
370
382
  # You may also pass a different UUID generation function from +uuid-ossp+
371
383
  # or another library.
372
384
  #
373
- # Note that setting the UUID primary key default value to +nil+
374
- # will require you to assure that you always provide a UUID value
375
- # before saving a record (as primary keys cannot be nil). This might be
376
- # done via the SecureRandom.uuid method and a +before_save+ callback,
377
- # for instance.
385
+ # Note that setting the UUID primary key default value to +nil+ will
386
+ # require you to assure that you always provide a UUID value before saving
387
+ # a record (as primary keys cannot be +nil+). This might be done via the
388
+ # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
378
389
  def primary_key(name, type = :primary_key, options = {})
379
390
  return super unless type == :uuid
380
391
  options[:default] = options.fetch(:default, 'uuid_generate_v4()')
@@ -437,6 +448,7 @@ module ActiveRecord
437
448
  include ReferentialIntegrity
438
449
  include SchemaStatements
439
450
  include DatabaseStatements
451
+ include Savepoints
440
452
 
441
453
  # Returns 'PostgreSQL' as adapter name for identification purposes.
442
454
  def adapter_name
@@ -561,7 +573,8 @@ module ActiveRecord
561
573
  raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
562
574
  end
563
575
 
564
- initialize_type_map
576
+ @type_map = OID::TypeMap.new
577
+ initialize_type_map(type_map)
565
578
  @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
566
579
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
567
580
  end
@@ -632,12 +645,6 @@ module ActiveRecord
632
645
  true
633
646
  end
634
647
 
635
- # Returns true, since this connection adapter supports savepoints.
636
- def supports_savepoints?
637
- true
638
- end
639
-
640
- # Returns true.
641
648
  def supports_explain?
642
649
  true
643
650
  end
@@ -749,65 +756,92 @@ module ActiveRecord
749
756
 
750
757
  private
751
758
 
759
+ def type_map
760
+ @type_map
761
+ end
762
+
763
+ def get_oid_type(oid, fmod, column_name)
764
+ type_map.fetch(oid, fmod) {
765
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
766
+ type_map[oid] = OID::Identity.new
767
+ }
768
+ end
769
+
752
770
  def reload_type_map
753
- OID::TYPE_MAP.clear
754
- initialize_type_map
771
+ type_map.clear
772
+ initialize_type_map(type_map)
755
773
  end
756
774
 
757
- def initialize_type_map
775
+ def add_oid(row, records_by_oid, type_map)
776
+ return type_map if type_map.key? row['type_elem'].to_i
777
+
778
+ if OID.registered_type? row['typname']
779
+ # this composite type is explicitly registered
780
+ vector = OID::NAMES[row['typname']]
781
+ else
782
+ # use the default for composite types
783
+ unless type_map.key? row['typelem'].to_i
784
+ add_oid records_by_oid[row['typelem']], records_by_oid, type_map
785
+ end
786
+
787
+ vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i]
788
+ end
789
+
790
+ type_map[row['oid'].to_i] = vector
791
+ type_map
792
+ end
793
+
794
+ def initialize_type_map(type_map)
758
795
  result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
759
796
  leaves, nodes = result.partition { |row| row['typelem'] == '0' }
760
797
 
761
798
  # populate the leaf nodes
762
799
  leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
763
- OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']]
800
+ type_map[row['oid'].to_i] = OID::NAMES[row['typname']]
764
801
  end
765
802
 
803
+ records_by_oid = result.group_by { |row| row['oid'] }
804
+
766
805
  arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
767
806
 
768
807
  # populate composite types
769
- nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
770
- if OID.registered_type? row['typname']
771
- # this composite type is explicitly registered
772
- vector = OID::NAMES[row['typname']]
773
- else
774
- # use the default for composite types
775
- vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
776
- end
777
-
778
- OID::TYPE_MAP[row['oid'].to_i] = vector
808
+ nodes.each do |row|
809
+ add_oid row, records_by_oid, type_map
779
810
  end
780
811
 
781
812
  # populate array types
782
- arrays.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
783
- array = OID::Array.new OID::TYPE_MAP[row['typelem'].to_i]
784
- OID::TYPE_MAP[row['oid'].to_i] = array
813
+ arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
814
+ array = OID::Array.new type_map[row['typelem'].to_i]
815
+ type_map[row['oid'].to_i] = array
785
816
  end
786
817
  end
787
818
 
788
819
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
789
820
 
790
- def exec_no_cache(sql, binds)
791
- @connection.async_exec(sql)
821
+ def exec_no_cache(sql, name, binds)
822
+ log(sql, name, binds) { @connection.async_exec(sql, []) }
792
823
  end
793
824
 
794
- def exec_cache(sql, binds)
795
- stmt_key = prepare_statement sql
825
+ def exec_cache(sql, name, binds)
826
+ stmt_key = prepare_statement(sql)
827
+ type_casted_binds = binds.map { |col, val|
828
+ [col, type_cast(val, col)]
829
+ }
830
+
831
+ log(sql, name, type_casted_binds, stmt_key) do
832
+ @connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
833
+ @connection.block
834
+ @connection.get_last_result
835
+ end
836
+ rescue ActiveRecord::StatementInvalid => e
837
+ pgerror = e.original_exception
796
838
 
797
- # Clear the queue
798
- @connection.get_last_result
799
- @connection.send_query_prepared(stmt_key, binds.map { |col, val|
800
- type_cast(val, col)
801
- })
802
- @connection.block
803
- @connection.get_last_result
804
- rescue PGError => e
805
839
  # Get the PG code for the failure. Annoyingly, the code for
806
840
  # prepared statements whose return value may have changed is
807
841
  # FEATURE_NOT_SUPPORTED. Check here for more details:
808
842
  # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
809
843
  begin
810
- code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
844
+ code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
811
845
  rescue
812
846
  raise e
813
847
  end
@@ -831,7 +865,13 @@ module ActiveRecord
831
865
  sql_key = sql_key(sql)
832
866
  unless @statements.key? sql_key
833
867
  nextkey = @statements.next_key
834
- @connection.prepare nextkey, sql
868
+ begin
869
+ @connection.prepare nextkey, sql
870
+ rescue => e
871
+ raise translate_exception_class(e, sql)
872
+ end
873
+ # Clear the queue
874
+ @connection.get_last_result
835
875
  @statements[sql_key] = nextkey
836
876
  end
837
877
  @statements[sql_key]
@@ -853,6 +893,12 @@ module ActiveRecord
853
893
  PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
854
894
 
855
895
  configure_connection
896
+ rescue ::PG::Error => error
897
+ if error.message.include?("does not exist")
898
+ raise ActiveRecord::NoDatabaseError.new(error.message)
899
+ else
900
+ raise error
901
+ end
856
902
  end
857
903
 
858
904
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -949,12 +995,12 @@ module ActiveRecord
949
995
  end
950
996
 
951
997
  def extract_table_ref_from_insert_sql(sql)
952
- sql[/into\s+([^\(]*).*values\s*\(/i]
998
+ sql[/into\s+([^\(]*).*values\s*\(/im]
953
999
  $1.strip if $1
954
1000
  end
955
1001
 
956
- def create_table_definition(name, temporary, options)
957
- TableDefinition.new native_database_types, name, temporary, options
1002
+ def create_table_definition(name, temporary, options, as = nil)
1003
+ TableDefinition.new native_database_types, name, temporary, options, as
958
1004
  end
959
1005
  end
960
1006
  end
@@ -1,4 +1,3 @@
1
- require 'active_support/deprecation/reporting'
2
1
 
3
2
  module ActiveRecord
4
3
  module ConnectionAdapters
@@ -16,13 +15,8 @@ module ActiveRecord
16
15
  prepare_default_proc
17
16
  end
18
17
 
19
- def primary_keys(table_name = nil)
20
- if table_name
21
- @primary_keys[table_name]
22
- else
23
- ActiveSupport::Deprecation.warn('call primary_keys with a table name!')
24
- @primary_keys.dup
25
- end
18
+ def primary_keys(table_name)
19
+ @primary_keys[table_name]
26
20
  end
27
21
 
28
22
  # A cached lookup for table existence.
@@ -41,34 +35,19 @@ module ActiveRecord
41
35
  end
42
36
  end
43
37
 
44
- def tables(name = nil)
45
- if name
46
- @tables[name]
47
- else
48
- ActiveSupport::Deprecation.warn('call tables with a name!')
49
- @tables.dup
50
- end
38
+ def tables(name)
39
+ @tables[name]
51
40
  end
52
41
 
53
42
  # Get the columns for a table
54
- def columns(table = nil)
55
- if table
56
- @columns[table]
57
- else
58
- ActiveSupport::Deprecation.warn('call columns with a table name!')
59
- @columns.dup
60
- end
43
+ def columns(table)
44
+ @columns[table]
61
45
  end
62
46
 
63
47
  # Get the columns for a table as a hash, key is the column name
64
48
  # value is the column object.
65
- def columns_hash(table = nil)
66
- if table
67
- @columns_hash[table]
68
- else
69
- ActiveSupport::Deprecation.warn('call columns_hash with a table name!')
70
- @columns_hash.dup
71
- end
49
+ def columns_hash(table)
50
+ @columns_hash[table]
72
51
  end
73
52
 
74
53
  # Clears out internal caches