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
@@ -13,41 +13,173 @@ module ActiveRecord
13
13
  @config = original.config.dup
14
14
  end
15
15
 
16
+ # Expands a connection string into a hash.
17
+ class ConnectionUrlResolver # :nodoc:
18
+
19
+ # == Example
20
+ #
21
+ # url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
22
+ # ConnectionUrlResolver.new(url).to_hash
23
+ # # => {
24
+ # "adapter" => "postgresql",
25
+ # "host" => "localhost",
26
+ # "port" => 9000,
27
+ # "database" => "foo_test",
28
+ # "username" => "foo",
29
+ # "password" => "bar",
30
+ # "pool" => "5",
31
+ # "timeout" => "3000"
32
+ # }
33
+ def initialize(url)
34
+ raise "Database URL cannot be empty" if url.blank?
35
+ @uri = URI.parse(url)
36
+ @adapter = @uri.scheme.gsub('-', '_')
37
+ @adapter = "postgresql" if @adapter == "postgres"
38
+
39
+ if @uri.opaque
40
+ @uri.opaque, @query = @uri.opaque.split('?', 2)
41
+ else
42
+ @query = @uri.query
43
+ end
44
+ @authority = url =~ %r{\A[^:]*://}
45
+ end
46
+
47
+ # Converts the given URL to a full connection hash.
48
+ def to_hash
49
+ config = raw_config.reject { |_,value| value.blank? }
50
+ config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String }
51
+ config
52
+ end
53
+
54
+ private
55
+
56
+ def uri
57
+ @uri
58
+ end
59
+
60
+ def uri_parser
61
+ @uri_parser ||= URI::Parser.new
62
+ end
63
+
64
+ # Converts the query parameters of the URI into a hash.
65
+ #
66
+ # "localhost?pool=5&reap_frequency=2"
67
+ # # => { "pool" => "5", "reap_frequency" => "2" }
68
+ #
69
+ # returns empty hash if no query present.
70
+ #
71
+ # "localhost"
72
+ # # => {}
73
+ def query_hash
74
+ Hash[(@query || '').split("&").map { |pair| pair.split("=") }]
75
+ end
76
+
77
+ def raw_config
78
+ if uri.opaque
79
+ query_hash.merge({
80
+ "adapter" => @adapter,
81
+ "database" => uri.opaque })
82
+ else
83
+ query_hash.merge({
84
+ "adapter" => @adapter,
85
+ "username" => uri.user,
86
+ "password" => uri.password,
87
+ "port" => uri.port,
88
+ "database" => database_from_path,
89
+ "host" => uri.hostname })
90
+ end
91
+ end
92
+
93
+ # Returns name of the database.
94
+ # Sqlite3's handling of a leading slash is in transition as of
95
+ # Rails 4.1.
96
+ def database_from_path
97
+ if @authority && @adapter == 'sqlite3'
98
+ # 'sqlite3:///foo' is relative, for backwards compatibility.
99
+
100
+ database_name = uri.path.sub(%r{^/}, "")
101
+
102
+ msg = "Paths in SQLite3 database URLs of the form `sqlite3:///path` will be treated as absolute in Rails 4.2. " \
103
+ "Please switch to `sqlite3:#{database_name}`."
104
+ ActiveSupport::Deprecation.warn(msg)
105
+
106
+ database_name
107
+
108
+ elsif @adapter == 'sqlite3'
109
+ # 'sqlite3:/foo' is absolute, because that makes sense. The
110
+ # corresponding relative version, 'sqlite3:foo', is handled
111
+ # elsewhere, as an "opaque".
112
+
113
+ uri.path
114
+ else
115
+ # Only SQLite uses a filename as the "database" name; for
116
+ # anything else, a leading slash would be silly.
117
+
118
+ uri.path.sub(%r{^/}, "")
119
+ end
120
+ end
121
+ end
122
+
16
123
  ##
17
- # Builds a ConnectionSpecification from user input
124
+ # Builds a ConnectionSpecification from user input.
18
125
  class Resolver # :nodoc:
19
- attr_reader :config, :klass, :configurations
126
+ attr_reader :configurations
20
127
 
21
- def initialize(config, configurations)
22
- @config = config
128
+ # Accepts a hash two layers deep, keys on the first layer represent
129
+ # environments such as "production". Keys must be strings.
130
+ def initialize(configurations)
23
131
  @configurations = configurations
24
132
  end
25
133
 
26
- def spec
27
- case config
28
- when nil
29
- raise AdapterNotSpecified unless defined?(Rails.env)
30
- resolve_string_connection Rails.env
31
- when Symbol, String
32
- resolve_string_connection config.to_s
33
- when Hash
34
- resolve_hash_connection config
134
+ # Returns a hash with database connection information.
135
+ #
136
+ # == Examples
137
+ #
138
+ # Full hash Configuration.
139
+ #
140
+ # configurations = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
141
+ # Resolver.new(configurations).resolve(:production)
142
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3"}
143
+ #
144
+ # Initialized with URL configuration strings.
145
+ #
146
+ # configurations = { "production" => "postgresql://localhost/foo" }
147
+ # Resolver.new(configurations).resolve(:production)
148
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
149
+ #
150
+ def resolve(config)
151
+ if config
152
+ resolve_connection config
153
+ elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call
154
+ resolve_symbol_connection env.to_sym
155
+ else
156
+ raise AdapterNotSpecified
35
157
  end
36
158
  end
37
159
 
38
- private
39
- def resolve_string_connection(spec) # :nodoc:
40
- hash = configurations.fetch(spec) do |k|
41
- connection_url_to_hash(k)
160
+ # Expands each key in @configurations hash into fully resolved hash
161
+ def resolve_all
162
+ config = configurations.dup
163
+ config.each do |key, value|
164
+ config[key] = resolve(value) if value
42
165
  end
43
-
44
- raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
45
-
46
- resolve_hash_connection hash
166
+ config
47
167
  end
48
168
 
49
- def resolve_hash_connection(spec) # :nodoc:
50
- spec = spec.symbolize_keys
169
+ # Returns an instance of ConnectionSpecification for a given adapter.
170
+ # Accepts a hash one layer deep that contains all connection information.
171
+ #
172
+ # == Example
173
+ #
174
+ # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
175
+ # spec = Resolver.new(config).spec(:production)
176
+ # spec.adapter_method
177
+ # # => "sqlite3"
178
+ # spec.config
179
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" }
180
+ #
181
+ def spec(config)
182
+ spec = resolve(config).symbolize_keys
51
183
 
52
184
  raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
53
185
 
@@ -55,41 +187,97 @@ module ActiveRecord
55
187
  begin
56
188
  require path_to_adapter
57
189
  rescue Gem::LoadError => e
58
- raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile."
190
+ raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)."
59
191
  rescue LoadError => e
60
192
  raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
61
193
  end
62
194
 
63
195
  adapter_method = "#{spec[:adapter]}_connection"
64
-
65
196
  ConnectionSpecification.new(spec, adapter_method)
66
197
  end
67
198
 
68
- def connection_url_to_hash(url) # :nodoc:
69
- config = URI.parse url
70
- adapter = config.scheme
71
- adapter = "postgresql" if adapter == "postgres"
72
- spec = { :adapter => adapter,
73
- :username => config.user,
74
- :password => config.password,
75
- :port => config.port,
76
- :database => config.path.sub(%r{^/},""),
77
- :host => config.host }
78
-
79
- spec.reject!{ |_,value| value.blank? }
80
-
81
- uri_parser = URI::Parser.new
199
+ private
82
200
 
83
- spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) }
201
+ # Returns fully resolved connection, accepts hash, string or symbol.
202
+ # Always returns a hash.
203
+ #
204
+ # == Examples
205
+ #
206
+ # Symbol representing current environment.
207
+ #
208
+ # Resolver.new("production" => {}).resolve_connection(:production)
209
+ # # => {}
210
+ #
211
+ # One layer deep hash of connection values.
212
+ #
213
+ # Resolver.new({}).resolve_connection("adapter" => "sqlite3")
214
+ # # => { "adapter" => "sqlite3" }
215
+ #
216
+ # Connection URL.
217
+ #
218
+ # Resolver.new({}).resolve_connection("postgresql://localhost/foo")
219
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
220
+ #
221
+ def resolve_connection(spec)
222
+ case spec
223
+ when Symbol
224
+ resolve_symbol_connection spec
225
+ when String
226
+ resolve_string_connection spec
227
+ when Hash
228
+ resolve_hash_connection spec
229
+ end
230
+ end
84
231
 
85
- if config.query
86
- options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
232
+ def resolve_string_connection(spec)
233
+ # Rails has historically accepted a string to mean either
234
+ # an environment key or a URL spec, so we have deprecated
235
+ # this ambiguous behaviour and in the future this function
236
+ # can be removed in favor of resolve_url_connection.
237
+ if configurations.key?(spec) || spec !~ /:/
238
+ ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \
239
+ "for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead"
240
+ resolve_symbol_connection(spec)
241
+ else
242
+ resolve_url_connection(spec)
243
+ end
244
+ end
87
245
 
88
- spec.merge!(options)
246
+ # Takes the environment such as `:production` or `:development`.
247
+ # This requires that the @configurations was initialized with a key that
248
+ # matches.
249
+ #
250
+ # Resolver.new("production" => {}).resolve_symbol_connection(:production)
251
+ # # => {}
252
+ #
253
+ def resolve_symbol_connection(spec)
254
+ if config = configurations[spec.to_s]
255
+ resolve_connection(config)
256
+ else
257
+ raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
89
258
  end
259
+ end
90
260
 
261
+ # Accepts a hash. Expands the "url" key that contains a
262
+ # URL database connection to a full connection
263
+ # hash and merges with the rest of the hash.
264
+ # Connection details inside of the "url" key win any merge conflicts
265
+ def resolve_hash_connection(spec)
266
+ if spec["url"] && spec["url"] !~ /^jdbc:/
267
+ connection_hash = resolve_string_connection(spec.delete("url"))
268
+ spec.merge!(connection_hash)
269
+ end
91
270
  spec
92
271
  end
272
+
273
+ # Takes a connection URL.
274
+ #
275
+ # Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
276
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
277
+ #
278
+ def resolve_url_connection(url)
279
+ ConnectionUrlResolver.new(url).to_hash
280
+ end
93
281
  end
94
282
  end
95
283
  end
@@ -1,6 +1,6 @@
1
1
  require 'active_record/connection_adapters/abstract_mysql_adapter'
2
2
 
3
- gem 'mysql2', '~> 0.3.10'
3
+ gem 'mysql2', '~> 0.3.13'
4
4
  require 'mysql2'
5
5
 
6
6
  module ActiveRecord
@@ -18,6 +18,12 @@ module ActiveRecord
18
18
  client = Mysql2::Client.new(config)
19
19
  options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
20
20
  ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
21
+ rescue Mysql2::Error => error
22
+ if error.message.include?("Unknown database")
23
+ raise ActiveRecord::NoDatabaseError.new(error.message)
24
+ else
25
+ raise error
26
+ end
21
27
  end
22
28
  end
23
29
 
@@ -229,8 +235,7 @@ module ActiveRecord
229
235
 
230
236
  alias exec_without_stmt exec_query
231
237
 
232
- # Returns an array of record hashes with the column names as keys and
233
- # column values as values.
238
+ # Returns an ActiveRecord::Result instance.
234
239
  def select(sql, name = nil, binds = [])
235
240
  exec_query(sql, name)
236
241
  end
@@ -267,8 +272,8 @@ module ActiveRecord
267
272
  super
268
273
  end
269
274
 
270
- def version
271
- @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
275
+ def full_version
276
+ @full_version ||= @connection.server_info[:version]
272
277
  end
273
278
 
274
279
  def set_field_encoding field_name
@@ -34,6 +34,12 @@ module ActiveRecord
34
34
  default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
35
35
  options = [host, username, password, database, port, socket, default_flags]
36
36
  ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
37
+ rescue Mysql::Error => error
38
+ if error.message.include?("Unknown database")
39
+ raise ActiveRecord::NoDatabaseError.new(error.message)
40
+ else
41
+ raise error
42
+ end
37
43
  end
38
44
  end
39
45
 
@@ -51,7 +57,7 @@ module ActiveRecord
51
57
  # * <tt>:database</tt> - The name of the database. No default, must be provided.
52
58
  # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
53
59
  # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
54
- # * <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)
60
+ # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/sql-mode.html)
55
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).
56
62
  # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
57
63
  # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
@@ -160,12 +166,6 @@ module ActiveRecord
160
166
 
161
167
  # QUOTING ==================================================
162
168
 
163
- def type_cast(value, column)
164
- return super unless value == true || value == false
165
-
166
- value ? 1 : 0
167
- end
168
-
169
169
  def quote_string(string) #:nodoc:
170
170
  @connection.quote(string)
171
171
  end
@@ -425,14 +425,19 @@ module ActiveRecord
425
425
 
426
426
  if result
427
427
  types = {}
428
+ fields = []
428
429
  result.fetch_fields.each { |field|
430
+ field_name = field.name
431
+ fields << field_name
432
+
429
433
  if field.decimals > 0
430
- types[field.name] = Fields::Decimal.new
434
+ types[field_name] = Fields::Decimal.new
431
435
  else
432
- types[field.name] = Fields.find_type field
436
+ types[field_name] = Fields.find_type field
433
437
  end
434
438
  }
435
- result_set = ActiveRecord::Result.new(types.keys, result.to_a, types)
439
+
440
+ result_set = ActiveRecord::Result.new(fields, result.to_a, types)
436
441
  result.free
437
442
  else
438
443
  result_set = ActiveRecord::Result.new([], [])
@@ -468,15 +473,17 @@ module ActiveRecord
468
473
 
469
474
  def begin_db_transaction #:nodoc:
470
475
  exec_query "BEGIN"
471
- rescue Mysql::Error
472
- # Transactions aren't supported
473
476
  end
474
477
 
475
478
  private
476
479
 
477
480
  def exec_stmt(sql, name, binds)
478
481
  cache = {}
479
- log(sql, name, binds) do
482
+ type_casted_binds = binds.map { |col, val|
483
+ [col, type_cast(val, col)]
484
+ }
485
+
486
+ log(sql, name, type_casted_binds) do
480
487
  if binds.empty?
481
488
  stmt = @connection.prepare(sql)
482
489
  else
@@ -487,7 +494,7 @@ module ActiveRecord
487
494
  end
488
495
 
489
496
  begin
490
- stmt.execute(*binds.map { |col, val| type_cast(val, col) })
497
+ stmt.execute(*type_casted_binds.map { |_, val| val })
491
498
  rescue Mysql::Error => e
492
499
  # Older versions of MySQL leave the prepared statement in a bad
493
500
  # place when an error occurs. To support older mysql versions, we
@@ -551,9 +558,9 @@ module ActiveRecord
551
558
  rows
552
559
  end
553
560
 
554
- # Returns the version of the connected MySQL server.
555
- def version
556
- @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
561
+ # Returns the full version of the connected MySQL server.
562
+ def full_version
563
+ @full_version ||= @connection.server_info
557
564
  end
558
565
 
559
566
  def set_field_encoding field_name
@@ -2,6 +2,13 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  class PostgreSQLColumn < Column
4
4
  module ArrayParser
5
+
6
+ DOUBLE_QUOTE = '"'
7
+ BACKSLASH = "\\"
8
+ COMMA = ','
9
+ BRACKET_OPEN = '{'
10
+ BRACKET_CLOSE = '}'
11
+
5
12
  private
6
13
  # Loads pg_array_parser if available. String parsing can be
7
14
  # performed quicker by a native extension, which will not create
@@ -12,18 +19,18 @@ module ActiveRecord
12
19
  include PgArrayParser
13
20
  rescue LoadError
14
21
  def parse_pg_array(string)
15
- parse_data(string, 0)
22
+ parse_data(string)
16
23
  end
17
24
  end
18
25
 
19
- def parse_data(string, index)
20
- local_index = index
26
+ def parse_data(string)
27
+ local_index = 0
21
28
  array = []
22
29
  while(local_index < string.length)
23
30
  case string[local_index]
24
- when '{'
31
+ when BRACKET_OPEN
25
32
  local_index,array = parse_array_contents(array, string, local_index + 1)
26
- when '}'
33
+ when BRACKET_CLOSE
27
34
  return array
28
35
  end
29
36
  local_index += 1
@@ -33,9 +40,9 @@ module ActiveRecord
33
40
  end
34
41
 
35
42
  def parse_array_contents(array, string, index)
36
- is_escaping = false
37
- is_quoted = false
38
- was_quoted = false
43
+ is_escaping = false
44
+ is_quoted = false
45
+ was_quoted = false
39
46
  current_item = ''
40
47
 
41
48
  local_index = index
@@ -47,29 +54,29 @@ module ActiveRecord
47
54
  else
48
55
  if is_quoted
49
56
  case token
50
- when '"'
57
+ when DOUBLE_QUOTE
51
58
  is_quoted = false
52
59
  was_quoted = true
53
- when "\\"
60
+ when BACKSLASH
54
61
  is_escaping = true
55
62
  else
56
63
  current_item << token
57
64
  end
58
65
  else
59
66
  case token
60
- when "\\"
67
+ when BACKSLASH
61
68
  is_escaping = true
62
- when ','
69
+ when COMMA
63
70
  add_item_to_array(array, current_item, was_quoted)
64
71
  current_item = ''
65
72
  was_quoted = false
66
- when '"'
73
+ when DOUBLE_QUOTE
67
74
  is_quoted = true
68
- when '{'
75
+ when BRACKET_OPEN
69
76
  internal_items = []
70
77
  local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
71
78
  array.push(internal_items)
72
- when '}'
79
+ when BRACKET_CLOSE
73
80
  add_item_to_array(array, current_item, was_quoted)
74
81
  return local_index,array
75
82
  else
@@ -17,8 +17,8 @@ module ActiveRecord
17
17
  return string unless String === string
18
18
 
19
19
  case string
20
- when 'infinity'; 1.0 / 0.0
21
- when '-infinity'; -1.0 / 0.0
20
+ when 'infinity'; Float::INFINITY
21
+ when '-infinity'; -Float::INFINITY
22
22
  when / BC$/
23
23
  super("-" + string.sub(/ BC$/, ""))
24
24
  else
@@ -67,7 +67,7 @@ module ActiveRecord
67
67
  end
68
68
  end
69
69
 
70
- def array_to_string(value, column, adapter, should_be_quoted = false)
70
+ def array_to_string(value, column, adapter)
71
71
  casted_values = value.map do |val|
72
72
  if String === val
73
73
  if val == "NULL"
@@ -119,7 +119,7 @@ module ActiveRecord
119
119
  end
120
120
 
121
121
  def string_to_array(string, oid)
122
- parse_pg_array(string).map{|val| oid.type_cast val}
122
+ parse_pg_array(string).map {|val| type_cast_array(oid, val)}
123
123
  end
124
124
 
125
125
  private
@@ -154,6 +154,14 @@ module ActiveRecord
154
154
  "\"#{value}\""
155
155
  end
156
156
  end
157
+
158
+ def type_cast_array(oid, value)
159
+ if ::Array === value
160
+ value.map {|item| type_cast_array(oid, item)}
161
+ else
162
+ oid.type_cast value
163
+ end
164
+ end
157
165
  end
158
166
  end
159
167
  end
@@ -134,35 +134,28 @@ module ActiveRecord
134
134
  end
135
135
 
136
136
  def exec_query(sql, name = 'SQL', binds = [])
137
- log(sql, name, binds) do
138
- result = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) :
139
- exec_cache(sql, binds)
140
-
141
- types = {}
142
- fields = result.fields
143
- fields.each_with_index do |fname, i|
144
- ftype = result.ftype i
145
- fmod = result.fmod i
146
- types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod|
147
- warn "unknown OID: #{fname}(#{oid}) (#{sql})"
148
- OID::Identity.new
149
- }
150
- end
151
-
152
- ret = ActiveRecord::Result.new(fields, result.values, types)
153
- result.clear
154
- return ret
137
+ result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
138
+ exec_cache(sql, name, binds)
139
+
140
+ types = {}
141
+ fields = result.fields
142
+ fields.each_with_index do |fname, i|
143
+ ftype = result.ftype i
144
+ fmod = result.fmod i
145
+ types[fname] = get_oid_type(ftype, fmod, fname)
155
146
  end
147
+
148
+ ret = ActiveRecord::Result.new(fields, result.values, types)
149
+ result.clear
150
+ return ret
156
151
  end
157
152
 
158
153
  def exec_delete(sql, name = 'SQL', binds = [])
159
- log(sql, name, binds) do
160
- result = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) :
161
- exec_cache(sql, binds)
162
- affected = result.cmd_tuples
163
- result.clear
164
- affected
165
- end
154
+ result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
155
+ exec_cache(sql, name, binds)
156
+ affected = result.cmd_tuples
157
+ result.clear
158
+ affected
166
159
  end
167
160
  alias :exec_update :exec_delete
168
161
 
@@ -218,25 +211,6 @@ module ActiveRecord
218
211
  def rollback_db_transaction
219
212
  execute "ROLLBACK"
220
213
  end
221
-
222
- def outside_transaction?
223
- message = "#outside_transaction? is deprecated. This method was only really used " \
224
- "internally, but you can use #transaction_open? instead."
225
- ActiveSupport::Deprecation.warn message
226
- @connection.transaction_status == PGconn::PQTRANS_IDLE
227
- end
228
-
229
- def create_savepoint
230
- execute("SAVEPOINT #{current_savepoint_name}")
231
- end
232
-
233
- def rollback_to_savepoint
234
- execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
235
- end
236
-
237
- def release_savepoint
238
- execute("RELEASE SAVEPOINT #{current_savepoint_name}")
239
- end
240
214
  end
241
215
  end
242
216
  end