activerecord 2.3.5 → 2.3.6

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 (90) hide show
  1. data/CHANGELOG +33 -0
  2. data/Rakefile +1 -1
  3. data/examples/performance.sql +85 -0
  4. data/lib/active_record.rb +1 -2
  5. data/lib/active_record/association_preload.rb +9 -2
  6. data/lib/active_record/associations.rb +48 -38
  7. data/lib/active_record/associations/association_collection.rb +15 -11
  8. data/lib/active_record/associations/association_proxy.rb +16 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +11 -1
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -10
  11. data/lib/active_record/associations/has_many_association.rb +5 -0
  12. data/lib/active_record/associations/has_many_through_association.rb +5 -5
  13. data/lib/active_record/associations/has_one_association.rb +10 -1
  14. data/lib/active_record/attribute_methods.rb +5 -1
  15. data/lib/active_record/autosave_association.rb +66 -35
  16. data/lib/active_record/base.rb +77 -36
  17. data/lib/active_record/batches.rb +13 -9
  18. data/lib/active_record/calculations.rb +6 -3
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
  20. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +64 -10
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -0
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +31 -1
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -66
  28. data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
  29. data/lib/active_record/dirty.rb +2 -2
  30. data/lib/active_record/fixtures.rb +1 -0
  31. data/lib/active_record/locking/optimistic.rb +34 -1
  32. data/lib/active_record/migration.rb +5 -0
  33. data/lib/active_record/nested_attributes.rb +64 -52
  34. data/lib/active_record/reflection.rb +66 -1
  35. data/lib/active_record/schema.rb +5 -1
  36. data/lib/active_record/schema_dumper.rb +3 -0
  37. data/lib/active_record/serializers/json_serializer.rb +1 -1
  38. data/lib/active_record/validations.rb +13 -1
  39. data/lib/active_record/version.rb +1 -1
  40. data/test/cases/active_schema_test_mysql.rb +22 -0
  41. data/test/cases/associations/belongs_to_associations_test.rb +13 -0
  42. data/test/cases/associations/eager_load_nested_include_test.rb +8 -7
  43. data/test/cases/associations/eager_test.rb +7 -1
  44. data/test/cases/associations/has_many_associations_test.rb +26 -0
  45. data/test/cases/associations/inverse_associations_test.rb +566 -0
  46. data/test/cases/associations_test.rb +10 -0
  47. data/test/cases/autosave_association_test.rb +86 -10
  48. data/test/cases/base_test.rb +29 -0
  49. data/test/cases/batches_test.rb +20 -0
  50. data/test/cases/calculations_test.rb +2 -3
  51. data/test/cases/encoding_test.rb +6 -0
  52. data/test/cases/finder_test.rb +19 -3
  53. data/test/cases/fixtures_test.rb +5 -0
  54. data/test/cases/json_serialization_test.rb +14 -0
  55. data/test/cases/locking_test.rb +48 -3
  56. data/test/cases/migration_test.rb +115 -0
  57. data/test/cases/modules_test.rb +28 -0
  58. data/test/cases/named_scope_test.rb +1 -1
  59. data/test/cases/nested_attributes_test.rb +239 -7
  60. data/test/cases/query_cache_test.rb +7 -1
  61. data/test/cases/reflection_test.rb +47 -7
  62. data/test/cases/schema_test_postgresql.rb +2 -2
  63. data/test/cases/validations_i18n_test.rb +6 -36
  64. data/test/cases/validations_test.rb +33 -1
  65. data/test/cases/yaml_serialization_test.rb +11 -0
  66. data/test/fixtures/faces.yml +11 -0
  67. data/test/fixtures/fixture_database.sqlite +0 -0
  68. data/test/fixtures/fixture_database.sqlite3 +0 -0
  69. data/test/fixtures/fixture_database_2.sqlite +0 -0
  70. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  71. data/test/fixtures/interests.yml +33 -0
  72. data/test/fixtures/men.yml +5 -0
  73. data/test/fixtures/zines.yml +5 -0
  74. data/test/models/author.rb +3 -0
  75. data/test/models/bird.rb +6 -0
  76. data/test/models/company_in_module.rb +17 -0
  77. data/test/models/event_author.rb +5 -0
  78. data/test/models/face.rb +7 -0
  79. data/test/models/interest.rb +5 -0
  80. data/test/models/invoice.rb +4 -0
  81. data/test/models/line_item.rb +3 -0
  82. data/test/models/man.rb +9 -0
  83. data/test/models/parrot.rb +6 -0
  84. data/test/models/pirate.rb +10 -0
  85. data/test/models/ship.rb +10 -1
  86. data/test/models/ship_part.rb +3 -1
  87. data/test/models/zine.rb +3 -0
  88. data/test/schema/schema.rb +41 -0
  89. metadata +37 -11
  90. data/lib/active_record/i18n_interpolation_deprecation.rb +0 -26
@@ -59,19 +59,23 @@ module ActiveRecord
59
59
  start = options.delete(:start).to_i
60
60
  batch_size = options.delete(:batch_size) || 1000
61
61
 
62
- with_scope(:find => options.merge(:order => batch_order, :limit => batch_size)) do
63
- records = find(:all, :conditions => [ "#{table_name}.#{primary_key} >= ?", start ])
62
+ proxy = scoped(options.merge(:order => batch_order, :limit => batch_size))
63
+ records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} >= ?", start ])
64
64
 
65
- while records.any?
66
- yield records
65
+ while records.any?
66
+ yield records
67
67
 
68
- break if records.size < batch_size
69
- records = find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", records.last.id ])
70
- end
68
+ break if records.size < batch_size
69
+
70
+ last_value = records.last.id
71
+
72
+ raise "You must include the primary key if you define a select" unless last_value.present?
73
+
74
+ records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", last_value ])
71
75
  end
72
76
  end
73
-
74
-
77
+
78
+
75
79
  private
76
80
  def batch_order
77
81
  "#{table_name}.#{primary_key} ASC"
@@ -294,12 +294,15 @@ module ActiveRecord
294
294
  end
295
295
 
296
296
  def type_cast_calculated_value(value, column, operation = nil)
297
- operation = operation.to_s.downcase
298
- case operation
297
+ if value.is_a?(String) || value.nil?
298
+ case operation.to_s.downcase
299
299
  when 'count' then value.to_i
300
300
  when 'sum' then type_cast_using_column(value || '0', column)
301
- when 'avg' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d
301
+ when 'avg' then value.try(:to_d)
302
302
  else type_cast_using_column(value, column)
303
+ end
304
+ else
305
+ value
303
306
  end
304
307
  end
305
308
 
@@ -10,8 +10,8 @@ module ActiveRecord
10
10
  ##
11
11
  # :singleton-method:
12
12
  # The connection handler
13
- cattr_accessor :connection_handler, :instance_writer => false
14
- @@connection_handler = ConnectionAdapters::ConnectionHandler.new
13
+ superclass_delegating_accessor :connection_handler
14
+ self.connection_handler = ConnectionAdapters::ConnectionHandler.new
15
15
 
16
16
  # Returns the connection currently associated with the class. This can
17
17
  # also be used to "borrow" the connection to do database work that isn't
@@ -54,7 +54,7 @@ module ActiveRecord
54
54
  raise AdapterNotSpecified unless defined? RAILS_ENV
55
55
  establish_connection(RAILS_ENV)
56
56
  when ConnectionSpecification
57
- @@connection_handler.establish_connection(name, spec)
57
+ self.connection_handler.establish_connection(name, spec)
58
58
  when Symbol, String
59
59
  if configuration = configurations[spec.to_s]
60
60
  establish_connection(configuration)
@@ -0,0 +1,57 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module DatabaseLimits
4
+
5
+ # the maximum length of a table alias
6
+ def table_alias_length
7
+ 255
8
+ end
9
+
10
+ # the maximum length of a column name
11
+ def column_name_length
12
+ 64
13
+ end
14
+
15
+ # the maximum length of a table name
16
+ def table_name_length
17
+ 64
18
+ end
19
+
20
+ # the maximum length of an index name
21
+ def index_name_length
22
+ 64
23
+ end
24
+
25
+ # the maximum number of columns per table
26
+ def columns_per_table
27
+ 1024
28
+ end
29
+
30
+ # the maximum number of indexes per table
31
+ def indexes_per_table
32
+ 16
33
+ end
34
+
35
+ # the maximum number of columns in a multicolumn index
36
+ def columns_per_multicolumn_index
37
+ 16
38
+ end
39
+
40
+ # the maximum number of elements in an IN (x,y,z) clause
41
+ def in_clause_length
42
+ 65535
43
+ end
44
+
45
+ # the maximum length of a SQL query
46
+ def sql_query_length
47
+ 1048575
48
+ end
49
+
50
+ # maximum number of joins in a single query
51
+ def joins_per_query
52
+ 256
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
 
14
14
  def dirties_query_cache(base, *method_names)
15
15
  method_names.each do |method_name|
16
- base.class_eval <<-end_code, __FILE__, __LINE__
16
+ base.class_eval <<-end_code, __FILE__, __LINE__ + 1
17
17
  def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args)
18
18
  clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
19
19
  #{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args)
@@ -11,12 +11,12 @@ module ActiveRecord
11
11
  when String, ActiveSupport::Multibyte::Chars
12
12
  value = value.to_s
13
13
  if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
14
- "#{quoted_string_prefix}'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
14
+ "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
15
15
  elsif column && [:integer, :float].include?(column.type)
16
16
  value = column.type == :integer ? value.to_i : value.to_f
17
17
  value.to_s
18
18
  else
19
- "#{quoted_string_prefix}'#{quote_string(value)}'" # ' (for ruby-mode)
19
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
20
20
  end
21
21
  when NilClass then "NULL"
22
22
  when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  if value.acts_like?(:date) || value.acts_like?(:time)
29
29
  "'#{quoted_date(value)}'"
30
30
  else
31
- "#{quoted_string_prefix}'#{quote_string(value.to_yaml)}'"
31
+ "'#{quote_string(value.to_yaml)}'"
32
32
  end
33
33
  end
34
34
  end
@@ -60,10 +60,6 @@ module ActiveRecord
60
60
  def quoted_date(value)
61
61
  value.to_s(:db)
62
62
  end
63
-
64
- def quoted_string_prefix
65
- ''
66
- end
67
63
  end
68
64
  end
69
65
  end
@@ -256,7 +256,7 @@ module ActiveRecord
256
256
  end
257
257
  end
258
258
 
259
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
259
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
260
260
  end
261
261
 
262
262
  # Abstract representation of a column definition. Instances of this type
@@ -8,11 +8,6 @@ module ActiveRecord
8
8
  {}
9
9
  end
10
10
 
11
- # This is the maximum length a table alias can be
12
- def table_alias_length
13
- 255
14
- end
15
-
16
11
  # Truncates a table alias according to the limits of the current adapter.
17
12
  def table_alias_for(table_name)
18
13
  table_name[0..table_alias_length-1].gsub(/\./, '_')
@@ -101,7 +96,7 @@ module ActiveRecord
101
96
  table_definition = TableDefinition.new(self)
102
97
  table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
103
98
 
104
- yield table_definition
99
+ yield table_definition if block_given?
105
100
 
106
101
  if options[:force] && table_exists?(table_name)
107
102
  drop_table(table_name, options)
@@ -246,18 +241,32 @@ module ActiveRecord
246
241
  # name.
247
242
  #
248
243
  # ===== Examples
244
+ #
249
245
  # ====== Creating a simple index
250
246
  # add_index(:suppliers, :name)
251
247
  # generates
252
248
  # CREATE INDEX suppliers_name_index ON suppliers(name)
249
+ #
253
250
  # ====== Creating a unique index
254
251
  # add_index(:accounts, [:branch_id, :party_id], :unique => true)
255
252
  # generates
256
253
  # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
254
+ #
257
255
  # ====== Creating a named index
258
256
  # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
259
257
  # generates
260
258
  # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
259
+ #
260
+ # ====== Creating an index with specific key length
261
+ # add_index(:accounts, :name, :name => 'by_name', :length => 10)
262
+ # generates
263
+ # CREATE INDEX by_name ON accounts(name(10))
264
+ #
265
+ # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
266
+ # generates
267
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
268
+ #
269
+ # Note: SQLite doesn't support index length
261
270
  def add_index(table_name, column_name, options = {})
262
271
  column_names = Array(column_name)
263
272
  index_name = index_name(table_name, :column => column_names)
@@ -268,7 +277,17 @@ module ActiveRecord
268
277
  else
269
278
  index_type = options
270
279
  end
271
- quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
280
+
281
+ if index_name.length > index_name_length
282
+ @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.")
283
+ return
284
+ end
285
+ if index_exists?(table_name, index_name, false)
286
+ @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.")
287
+ return
288
+ end
289
+ quoted_column_names = quoted_columns_for_index(column_names, options).join(", ")
290
+
272
291
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
273
292
  end
274
293
 
@@ -283,7 +302,28 @@ module ActiveRecord
283
302
  # Remove the index named by_branch_party in the accounts table.
284
303
  # remove_index :accounts, :name => :by_branch_party
285
304
  def remove_index(table_name, options = {})
286
- execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} ON #{table_name}"
305
+ index_name = index_name(table_name, options)
306
+ unless index_exists?(table_name, index_name, true)
307
+ @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.")
308
+ return
309
+ end
310
+ remove_index!(table_name, index_name)
311
+ end
312
+
313
+ def remove_index!(table_name, index_name) #:nodoc:
314
+ execute "DROP INDEX #{quote_column_name(index_name)} ON #{table_name}"
315
+ end
316
+
317
+ # Rename an index.
318
+ #
319
+ # Rename the index_people_on_last_name index to index_users_on_last_name
320
+ # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
321
+ def rename_index(table_name, old_name, new_name)
322
+ # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
323
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
324
+ return unless old_index_def
325
+ remove_index(table_name, :name => old_name)
326
+ add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique)
287
327
  end
288
328
 
289
329
  def index_name(table_name, options) #:nodoc:
@@ -300,6 +340,15 @@ module ActiveRecord
300
340
  end
301
341
  end
302
342
 
343
+ # Verify the existence of an index.
344
+ #
345
+ # The default argument is returned if the underlying implementation does not define the indexes method,
346
+ # as there's no way to determine the correct answer in that case.
347
+ def index_exists?(table_name, index_name, default)
348
+ return default unless respond_to?(:indexes)
349
+ indexes(table_name).detect { |i| i.name == index_name }
350
+ end
351
+
303
352
  # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
304
353
  # entire structure of the database.
305
354
  def structure_dump
@@ -336,12 +385,12 @@ module ActiveRecord
336
385
  end
337
386
  end
338
387
 
339
- def assume_migrated_upto_version(version)
388
+ def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path)
340
389
  version = version.to_i
341
390
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
342
391
 
343
392
  migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
344
- versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename|
393
+ versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename|
345
394
  filename.split('/').last.split('_').first.to_i
346
395
  end
347
396
 
@@ -426,6 +475,11 @@ module ActiveRecord
426
475
  end
427
476
 
428
477
  protected
478
+ # Overridden by the mysql adapter for supporting index lengths
479
+ def quoted_columns_for_index(column_names, options = {})
480
+ column_names.map {|name| quote_column_name(name) }
481
+ end
482
+
429
483
  def options_include_default?(options)
430
484
  options.include?(:default) && !(options[:null] == false && options[:default].nil?)
431
485
  end
@@ -11,6 +11,7 @@ require 'active_record/connection_adapters/abstract/quoting'
11
11
  require 'active_record/connection_adapters/abstract/connection_pool'
12
12
  require 'active_record/connection_adapters/abstract/connection_specification'
13
13
  require 'active_record/connection_adapters/abstract/query_cache'
14
+ require 'active_record/connection_adapters/abstract/database_limits'
14
15
 
15
16
  module ActiveRecord
16
17
  module ConnectionAdapters # :nodoc:
@@ -29,6 +30,7 @@ module ActiveRecord
29
30
  # notably, the instance methods provided by SchemaStatement are very useful.
30
31
  class AbstractAdapter
31
32
  include Quoting, DatabaseStatements, SchemaStatements
33
+ include DatabaseLimits
32
34
  include QueryCache
33
35
  include ActiveSupport::Callbacks
34
36
  define_callbacks :checkout, :checkin
@@ -454,10 +454,11 @@ module ActiveRecord
454
454
  if current_index != row[2]
455
455
  next if row[2] == "PRIMARY" # skip the primary key
456
456
  current_index = row[2]
457
- indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
457
+ indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], [])
458
458
  end
459
459
 
460
460
  indexes.last.columns << row[4]
461
+ indexes.last.lengths << row[7]
461
462
  end
462
463
  result.free
463
464
  indexes
@@ -480,6 +481,13 @@ module ActiveRecord
480
481
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
481
482
  end
482
483
 
484
+ def add_column(table_name, column_name, type, options = {})
485
+ add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
486
+ add_column_options!(add_column_sql, options)
487
+ add_column_position!(add_column_sql, options)
488
+ execute(add_column_sql)
489
+ end
490
+
483
491
  def change_column_default(table_name, column_name, default) #:nodoc:
484
492
  column = column_for(table_name, column_name)
485
493
  change_column table_name, column_name, column.sql_type, :default => default
@@ -508,6 +516,7 @@ module ActiveRecord
508
516
 
509
517
  change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
510
518
  add_column_options!(change_column_sql, options)
519
+ add_column_position!(change_column_sql, options)
511
520
  execute(change_column_sql)
512
521
  end
513
522
 
@@ -539,6 +548,13 @@ module ActiveRecord
539
548
  end
540
549
  end
541
550
 
551
+ def add_column_position!(sql, options)
552
+ if options[:first]
553
+ sql << " FIRST"
554
+ elsif options[:after]
555
+ sql << " AFTER #{quote_column_name(options[:after])}"
556
+ end
557
+ end
542
558
 
543
559
  # SHOW VARIABLES LIKE 'name'
544
560
  def show_variable(name)
@@ -571,6 +587,20 @@ module ActiveRecord
571
587
  where_sql
572
588
  end
573
589
 
590
+ protected
591
+ def quoted_columns_for_index(column_names, options = {})
592
+ length = options[:length] if options.is_a?(Hash)
593
+
594
+ quoted_column_names = case length
595
+ when Hash
596
+ column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
597
+ when Fixnum
598
+ column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
599
+ else
600
+ column_names.map {|name| quote_column_name(name) }
601
+ end
602
+ end
603
+
574
604
  private
575
605
  def connect
576
606
  encoding = @config[:encoding]
@@ -261,20 +261,12 @@ module ActiveRecord
261
261
  true
262
262
  end
263
263
 
264
- # Does PostgreSQL support standard conforming strings?
265
- def supports_standard_conforming_strings?
266
- # Temporarily set the client message level above error to prevent unintentional
267
- # error messages in the logs when working on a PostgreSQL database server that
268
- # does not support standard conforming strings.
269
- client_min_messages_old = client_min_messages
270
- self.client_min_messages = 'panic'
271
-
272
- # postgres-pr does not raise an exception when client_min_messages is set higher
273
- # than error and "SHOW standard_conforming_strings" fails, but returns an empty
274
- # PGresult instead.
275
- has_support = query('SHOW standard_conforming_strings')[0][0] rescue false
276
- self.client_min_messages = client_min_messages_old
277
- has_support
264
+ # Enable standard-conforming strings if available.
265
+ def set_standard_conforming_strings
266
+ old, self.client_min_messages = client_min_messages, 'panic'
267
+ execute('SET standard_conforming_strings = on') rescue nil
268
+ ensure
269
+ self.client_min_messages = old
278
270
  end
279
271
 
280
272
  def supports_insert_with_returning?
@@ -298,7 +290,7 @@ module ActiveRecord
298
290
  # QUOTING ==================================================
299
291
 
300
292
  # Escapes binary strings for bytea input to the database.
301
- def escape_bytea(value)
293
+ def escape_bytea(original_value)
302
294
  if @connection.respond_to?(:escape_bytea)
303
295
  self.class.instance_eval do
304
296
  define_method(:escape_bytea) do |value|
@@ -322,62 +314,40 @@ module ActiveRecord
322
314
  end
323
315
  end
324
316
  end
325
- escape_bytea(value)
317
+ escape_bytea(original_value)
326
318
  end
327
319
 
328
320
  # Unescapes bytea output from a database to the binary string it represents.
329
321
  # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
330
322
  # on escaped binary output from database drive.
331
- def unescape_bytea(value)
323
+ def unescape_bytea(original_value)
332
324
  # In each case, check if the value actually is escaped PostgreSQL bytea output
333
325
  # or an unescaped Active Record attribute that was just written.
334
- if PGconn.respond_to?(:unescape_bytea)
326
+ if @connection.respond_to?(:unescape_bytea)
335
327
  self.class.instance_eval do
336
328
  define_method(:unescape_bytea) do |value|
337
- if value =~ /\\\d{3}/
338
- PGconn.unescape_bytea(value)
339
- else
340
- value
341
- end
329
+ @connection.unescape_bytea(value) if value
342
330
  end
343
331
  end
344
- else
332
+ elsif PGconn.respond_to?(:unescape_bytea)
345
333
  self.class.instance_eval do
346
334
  define_method(:unescape_bytea) do |value|
347
- if value =~ /\\\d{3}/
348
- result = ''
349
- i, max = 0, value.size
350
- while i < max
351
- char = value[i]
352
- if char == ?\\
353
- if value[i+1] == ?\\
354
- char = ?\\
355
- i += 1
356
- else
357
- char = value[i+1..i+3].oct
358
- i += 3
359
- end
360
- end
361
- result << char
362
- i += 1
363
- end
364
- result
365
- else
366
- value
367
- end
335
+ PGconn.unescape_bytea(value) if value
368
336
  end
369
337
  end
338
+ else
339
+ raise 'Your PostgreSQL connection does not support unescape_bytea. Try upgrading to pg 0.9.0 or later.'
370
340
  end
371
- unescape_bytea(value)
341
+ unescape_bytea(original_value)
372
342
  end
373
343
 
374
344
  # Quotes PostgreSQL-specific data types for SQL input.
375
345
  def quote(value, column = nil) #:nodoc:
376
346
  if value.kind_of?(String) && column && column.type == :binary
377
- "#{quoted_string_prefix}'#{escape_bytea(value)}'"
378
- elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
379
- "xml E'#{quote_string(value)}'"
380
- elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
347
+ "'#{escape_bytea(value)}'"
348
+ elsif value.kind_of?(String) && column && column.sql_type == 'xml'
349
+ "xml '#{quote_string(value)}'"
350
+ elsif value.kind_of?(Numeric) && column && column.sql_type == 'money'
381
351
  # Not truly string input, so doesn't require (or allow) escape string syntax.
382
352
  "'#{value.to_s}'"
383
353
  elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/
@@ -393,7 +363,7 @@ module ActiveRecord
393
363
  end
394
364
 
395
365
  # Quotes strings for use in SQL input in the postgres driver for better performance.
396
- def quote_string(s) #:nodoc:
366
+ def quote_string(original_value) #:nodoc:
397
367
  if @connection.respond_to?(:escape)
398
368
  self.class.instance_eval do
399
369
  define_method(:quote_string) do |s|
@@ -413,7 +383,7 @@ module ActiveRecord
413
383
  remove_method(:quote_string)
414
384
  end
415
385
  end
416
- quote_string(s)
386
+ quote_string(original_value)
417
387
  end
418
388
 
419
389
  # Checks the following cases:
@@ -889,9 +859,12 @@ module ActiveRecord
889
859
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
890
860
  end
891
861
 
892
- # Drops an index from a table.
893
- def remove_index(table_name, options = {})
894
- execute "DROP INDEX #{quote_table_name(index_name(table_name, options))}"
862
+ def remove_index!(table_name, index_name) #:nodoc:
863
+ execute "DROP INDEX #{quote_table_name(index_name)}"
864
+ end
865
+
866
+ def index_name_length
867
+ 63
895
868
  end
896
869
 
897
870
  # Maps logical Rails types to PostgreSQL-specific data types.
@@ -971,17 +944,6 @@ module ActiveRecord
971
944
  # Ignore async_exec and async_query when using postgres-pr.
972
945
  @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)
973
946
 
974
- # Use escape string syntax if available. We cannot do this lazily when encountering
975
- # the first string, because that could then break any transactions in progress.
976
- # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
977
- # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
978
- # support escape string syntax. Don't override the inherited quoted_string_prefix.
979
- if supports_standard_conforming_strings?
980
- self.class.instance_eval do
981
- define_method(:quoted_string_prefix) { 'E' }
982
- end
983
- end
984
-
985
947
  # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
986
948
  # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
987
949
  # should know about this but can't detect it there, so deal with it here.
@@ -1011,6 +973,9 @@ module ActiveRecord
1011
973
  end
1012
974
  self.client_min_messages = @config[:min_messages] if @config[:min_messages]
1013
975
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
976
+
977
+ # Use standard-conforming strings if available so we don't have to do the E'...' dance.
978
+ set_standard_conforming_strings
1014
979
  end
1015
980
 
1016
981
  # Returns the current ID of a table's sequence.