activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,96 @@
1
+ require 'uri'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class ConnectionSpecification #:nodoc:
6
+ attr_reader :config, :adapter_method
7
+
8
+ def initialize(config, adapter_method)
9
+ @config, @adapter_method = config, adapter_method
10
+ end
11
+
12
+ def initialize_dup(original)
13
+ @config = original.config.dup
14
+ end
15
+
16
+ ##
17
+ # Builds a ConnectionSpecification from user input
18
+ class Resolver # :nodoc:
19
+ attr_reader :config, :klass, :configurations
20
+
21
+ def initialize(config, configurations)
22
+ @config = config
23
+ @configurations = configurations
24
+ end
25
+
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
35
+ end
36
+ end
37
+
38
+ private
39
+ def resolve_string_connection(spec) # :nodoc:
40
+ hash = configurations.fetch(spec) do |k|
41
+ connection_url_to_hash(k)
42
+ end
43
+
44
+ raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
45
+
46
+ resolve_hash_connection hash
47
+ end
48
+
49
+ def resolve_hash_connection(spec) # :nodoc:
50
+ spec = spec.symbolize_keys
51
+
52
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
53
+
54
+ path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
55
+ begin
56
+ require path_to_adapter
57
+ 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."
59
+ rescue LoadError => e
60
+ 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
+ end
62
+
63
+ adapter_method = "#{spec[:adapter]}_connection"
64
+
65
+ ConnectionSpecification.new(spec, adapter_method)
66
+ end
67
+
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.hostname }
78
+
79
+ spec.reject!{ |_,value| value.blank? }
80
+
81
+ uri_parser = URI::Parser.new
82
+
83
+ spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) }
84
+
85
+ if config.query
86
+ options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
87
+
88
+ spec.merge!(options)
89
+ end
90
+
91
+ spec
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -4,16 +4,18 @@ gem 'mysql2', '~> 0.3.10'
4
4
  require 'mysql2'
5
5
 
6
6
  module ActiveRecord
7
- class Base
7
+ module ConnectionHandling # :nodoc:
8
8
  # Establishes a connection to the database that's used by all Active Record objects.
9
- def self.mysql2_connection(config)
9
+ def mysql2_connection(config)
10
+ config = config.symbolize_keys
11
+
10
12
  config[:username] = 'root' if config[:username].nil?
11
13
 
12
14
  if Mysql2::Client.const_defined? :FOUND_ROWS
13
15
  config[:flags] = Mysql2::Client::FOUND_ROWS
14
16
  end
15
17
 
16
- client = Mysql2::Client.new(config.symbolize_keys)
18
+ client = Mysql2::Client.new(config)
17
19
  options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
18
20
  ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
19
21
  end
@@ -36,6 +38,15 @@ module ActiveRecord
36
38
  configure_connection
37
39
  end
38
40
 
41
+ MAX_INDEX_LENGTH_FOR_UTF8MB4 = 191
42
+ def initialize_schema_migrations_table
43
+ if @config[:encoding] == 'utf8mb4'
44
+ ActiveRecord::SchemaMigration.create_table(MAX_INDEX_LENGTH_FOR_UTF8MB4)
45
+ else
46
+ ActiveRecord::SchemaMigration.create_table
47
+ end
48
+ end
49
+
39
50
  def supports_explain?
40
51
  true
41
52
  end
@@ -52,8 +63,8 @@ module ActiveRecord
52
63
  end
53
64
  end
54
65
 
55
- def new_column(field, default, type, null, collation) # :nodoc:
56
- Column.new(field, default, type, null, collation)
66
+ def new_column(field, default, type, null, collation, extra = "") # :nodoc:
67
+ Column.new(field, default, type, null, collation, strict_mode?, extra)
57
68
  end
58
69
 
59
70
  def error_number(exception)
@@ -74,24 +85,22 @@ module ActiveRecord
74
85
  end
75
86
 
76
87
  def reconnect!
88
+ super
77
89
  disconnect!
78
90
  connect
79
91
  end
92
+ alias :reset! :reconnect!
80
93
 
81
94
  # Disconnects from the database if already connected.
82
95
  # Otherwise, this method does nothing.
83
96
  def disconnect!
97
+ super
84
98
  unless @connection.nil?
85
99
  @connection.close
86
100
  @connection = nil
87
101
  end
88
102
  end
89
103
 
90
- def reset!
91
- disconnect!
92
- connect
93
- end
94
-
95
104
  # DATABASE STATEMENTS ======================================
96
105
 
97
106
  def explain(arel, binds = [])
@@ -177,7 +186,7 @@ module ActiveRecord
177
186
  # # as values.
178
187
  # def select_one(sql, name = nil)
179
188
  # result = execute(sql, name)
180
- # result.each(:as => :hash) do |r|
189
+ # result.each(as: :hash) do |r|
181
190
  # return r
182
191
  # end
183
192
  # end
@@ -198,7 +207,7 @@ module ActiveRecord
198
207
 
199
208
  # Returns an array of arrays containing the field values.
200
209
  # Order is the same as that returned by +columns+.
201
- def select_rows(sql, name = nil)
210
+ def select_rows(sql, name = nil, binds = [])
202
211
  execute(sql, name).to_a
203
212
  end
204
213
 
@@ -223,7 +232,7 @@ module ActiveRecord
223
232
  # Returns an array of record hashes with the column names as keys and
224
233
  # column values as values.
225
234
  def select(sql, name = nil, binds = [])
226
- exec_query(sql, name).to_a
235
+ exec_query(sql, name)
227
236
  end
228
237
 
229
238
  def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
@@ -232,7 +241,7 @@ module ActiveRecord
232
241
  end
233
242
  alias :create :insert_sql
234
243
 
235
- def exec_insert(sql, name, binds)
244
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
236
245
  execute to_sql(sql, binds), name
237
246
  end
238
247
 
@@ -255,26 +264,16 @@ module ActiveRecord
255
264
 
256
265
  def configure_connection
257
266
  @connection.query_options.merge!(:as => :array)
258
-
259
- # By default, MySQL 'where id is null' selects the last inserted id.
260
- # Turn this off. http://dev.rubyonrails.org/ticket/6778
261
- variable_assignments = ['SQL_AUTO_IS_NULL=0']
262
- encoding = @config[:encoding]
263
-
264
- # make sure we set the encoding
265
- variable_assignments << "NAMES '#{encoding}'" if encoding
266
-
267
- # increase timeout so mysql server doesn't disconnect us
268
- wait_timeout = @config[:wait_timeout]
269
- wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
270
- variable_assignments << "@@wait_timeout = #{wait_timeout}"
271
-
272
- execute("SET #{variable_assignments.join(', ')}", :skip_logging)
267
+ super
273
268
  end
274
269
 
275
270
  def version
276
271
  @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
277
272
  end
273
+
274
+ def set_field_encoding field_name
275
+ field_name
276
+ end
278
277
  end
279
278
  end
280
279
  end
@@ -2,13 +2,11 @@ require 'active_record/connection_adapters/abstract_mysql_adapter'
2
2
  require 'active_record/connection_adapters/statement_pool'
3
3
  require 'active_support/core_ext/hash/keys'
4
4
 
5
- gem 'mysql', '~> 2.8'
5
+ gem 'mysql', '~> 2.9'
6
6
  require 'mysql'
7
7
 
8
8
  class Mysql
9
9
  class Time
10
- ###
11
- # This monkey patch is for test_additional_columns_from_join_table
12
10
  def to_date
13
11
  Date.new(year, month, day)
14
12
  end
@@ -18,9 +16,9 @@ class Mysql
18
16
  end
19
17
 
20
18
  module ActiveRecord
21
- class Base
19
+ module ConnectionHandling # :nodoc:
22
20
  # Establishes a connection to the database that's used by all Active Record objects.
23
- def self.mysql_connection(config) # :nodoc:
21
+ def mysql_connection(config)
24
22
  config = config.symbolize_keys
25
23
  host = config[:host]
26
24
  port = config[:port]
@@ -53,6 +51,8 @@ module ActiveRecord
53
51
  # * <tt>:database</tt> - The name of the database. No default, must be provided.
54
52
  # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
55
53
  # * <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)
55
+ # * <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
56
  # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
57
57
  # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
58
58
  # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
@@ -119,14 +119,14 @@ module ActiveRecord
119
119
 
120
120
  private
121
121
  def cache
122
- @cache[$$]
122
+ @cache[Process.pid]
123
123
  end
124
124
  end
125
125
 
126
126
  def initialize(connection, logger, connection_options, config)
127
127
  super
128
128
  @statements = StatementPool.new(@connection,
129
- config.fetch(:statement_limit) { 1000 })
129
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
130
130
  @client_encoding = nil
131
131
  connect
132
132
  end
@@ -150,8 +150,8 @@ module ActiveRecord
150
150
  end
151
151
  end
152
152
 
153
- def new_column(field, default, type, null, collation) # :nodoc:
154
- Column.new(field, default, type, null, collation)
153
+ def new_column(field, default, type, null, collation, extra = "") # :nodoc:
154
+ Column.new(field, default, type, null, collation, strict_mode?, extra)
155
155
  end
156
156
 
157
157
  def error_number(exception) # :nodoc:
@@ -190,14 +190,15 @@ module ActiveRecord
190
190
  end
191
191
 
192
192
  def reconnect!
193
+ super
193
194
  disconnect!
194
- clear_cache!
195
195
  connect
196
196
  end
197
197
 
198
198
  # Disconnects from the database if already connected. Otherwise, this
199
199
  # method does nothing.
200
200
  def disconnect!
201
+ super
201
202
  @connection.close rescue nil
202
203
  end
203
204
 
@@ -212,9 +213,9 @@ module ActiveRecord
212
213
 
213
214
  # DATABASE STATEMENTS ======================================
214
215
 
215
- def select_rows(sql, name = nil)
216
+ def select_rows(sql, name = nil, binds = [])
216
217
  @connection.query_with_result = true
217
- rows = exec_query(sql, name).rows
218
+ rows = exec_query(sql, name, binds).rows
218
219
  @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
219
220
  rows
220
221
  end
@@ -224,52 +225,48 @@ module ActiveRecord
224
225
  @statements.clear
225
226
  end
226
227
 
227
- if "<3".respond_to?(:encode)
228
- # Taken from here:
229
- # https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
230
- # Author: TOMITA Masahiro <tommy@tmtm.org>
231
- ENCODINGS = {
232
- "armscii8" => nil,
233
- "ascii" => Encoding::US_ASCII,
234
- "big5" => Encoding::Big5,
235
- "binary" => Encoding::ASCII_8BIT,
236
- "cp1250" => Encoding::Windows_1250,
237
- "cp1251" => Encoding::Windows_1251,
238
- "cp1256" => Encoding::Windows_1256,
239
- "cp1257" => Encoding::Windows_1257,
240
- "cp850" => Encoding::CP850,
241
- "cp852" => Encoding::CP852,
242
- "cp866" => Encoding::IBM866,
243
- "cp932" => Encoding::Windows_31J,
244
- "dec8" => nil,
245
- "eucjpms" => Encoding::EucJP_ms,
246
- "euckr" => Encoding::EUC_KR,
247
- "gb2312" => Encoding::EUC_CN,
248
- "gbk" => Encoding::GBK,
249
- "geostd8" => nil,
250
- "greek" => Encoding::ISO_8859_7,
251
- "hebrew" => Encoding::ISO_8859_8,
252
- "hp8" => nil,
253
- "keybcs2" => nil,
254
- "koi8r" => Encoding::KOI8_R,
255
- "koi8u" => Encoding::KOI8_U,
256
- "latin1" => Encoding::ISO_8859_1,
257
- "latin2" => Encoding::ISO_8859_2,
258
- "latin5" => Encoding::ISO_8859_9,
259
- "latin7" => Encoding::ISO_8859_13,
260
- "macce" => Encoding::MacCentEuro,
261
- "macroman" => Encoding::MacRoman,
262
- "sjis" => Encoding::SHIFT_JIS,
263
- "swe7" => nil,
264
- "tis620" => Encoding::TIS_620,
265
- "ucs2" => Encoding::UTF_16BE,
266
- "ujis" => Encoding::EucJP_ms,
267
- "utf8" => Encoding::UTF_8,
268
- "utf8mb4" => Encoding::UTF_8,
269
- }
270
- else
271
- ENCODINGS = Hash.new { |h,k| h[k] = k }
272
- end
228
+ # Taken from here:
229
+ # https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
230
+ # Author: TOMITA Masahiro <tommy@tmtm.org>
231
+ ENCODINGS = {
232
+ "armscii8" => nil,
233
+ "ascii" => Encoding::US_ASCII,
234
+ "big5" => Encoding::Big5,
235
+ "binary" => Encoding::ASCII_8BIT,
236
+ "cp1250" => Encoding::Windows_1250,
237
+ "cp1251" => Encoding::Windows_1251,
238
+ "cp1256" => Encoding::Windows_1256,
239
+ "cp1257" => Encoding::Windows_1257,
240
+ "cp850" => Encoding::CP850,
241
+ "cp852" => Encoding::CP852,
242
+ "cp866" => Encoding::IBM866,
243
+ "cp932" => Encoding::Windows_31J,
244
+ "dec8" => nil,
245
+ "eucjpms" => Encoding::EucJP_ms,
246
+ "euckr" => Encoding::EUC_KR,
247
+ "gb2312" => Encoding::EUC_CN,
248
+ "gbk" => Encoding::GBK,
249
+ "geostd8" => nil,
250
+ "greek" => Encoding::ISO_8859_7,
251
+ "hebrew" => Encoding::ISO_8859_8,
252
+ "hp8" => nil,
253
+ "keybcs2" => nil,
254
+ "koi8r" => Encoding::KOI8_R,
255
+ "koi8u" => Encoding::KOI8_U,
256
+ "latin1" => Encoding::ISO_8859_1,
257
+ "latin2" => Encoding::ISO_8859_2,
258
+ "latin5" => Encoding::ISO_8859_9,
259
+ "latin7" => Encoding::ISO_8859_13,
260
+ "macce" => Encoding::MacCentEuro,
261
+ "macroman" => Encoding::MacRoman,
262
+ "sjis" => Encoding::SHIFT_JIS,
263
+ "swe7" => nil,
264
+ "tis620" => Encoding::TIS_620,
265
+ "ucs2" => Encoding::UTF_16BE,
266
+ "ujis" => Encoding::EucJP_ms,
267
+ "utf8" => Encoding::UTF_8,
268
+ "utf8mb4" => Encoding::UTF_8,
269
+ }
273
270
 
274
271
  # Get the client encoding for this database
275
272
  def client_encoding
@@ -282,11 +279,7 @@ module ActiveRecord
282
279
  end
283
280
 
284
281
  def exec_query(sql, name = 'SQL', binds = [])
285
- # If the configuration sets prepared_statements:false, binds will
286
- # always be empty, since the bind variables will have been already
287
- # substituted and removed from binds by BindVisitor, so this will
288
- # effectively disable prepared statement usage completely.
289
- if binds.empty?
282
+ if without_prepared_statement?(binds)
290
283
  result_set, affected_rows = exec_without_stmt(sql, name)
291
284
  else
292
285
  result_set, affected_rows = exec_stmt(sql, name, binds)
@@ -301,6 +294,128 @@ module ActiveRecord
301
294
  @connection.insert_id
302
295
  end
303
296
 
297
+ module Fields
298
+ class Type
299
+ def type; end
300
+
301
+ def type_cast_for_write(value)
302
+ value
303
+ end
304
+ end
305
+
306
+ class Identity < Type
307
+ def type_cast(value); value; end
308
+ end
309
+
310
+ class Integer < Type
311
+ def type_cast(value)
312
+ return if value.nil?
313
+
314
+ value.to_i rescue value ? 1 : 0
315
+ end
316
+ end
317
+
318
+ class Date < Type
319
+ def type; :date; end
320
+
321
+ def type_cast(value)
322
+ return if value.nil?
323
+
324
+ # FIXME: probably we can improve this since we know it is mysql
325
+ # specific
326
+ ConnectionAdapters::Column.value_to_date value
327
+ end
328
+ end
329
+
330
+ class DateTime < Type
331
+ def type; :datetime; end
332
+
333
+ def type_cast(value)
334
+ return if value.nil?
335
+
336
+ # FIXME: probably we can improve this since we know it is mysql
337
+ # specific
338
+ ConnectionAdapters::Column.string_to_time value
339
+ end
340
+ end
341
+
342
+ class Time < Type
343
+ def type; :time; end
344
+
345
+ def type_cast(value)
346
+ return if value.nil?
347
+
348
+ # FIXME: probably we can improve this since we know it is mysql
349
+ # specific
350
+ ConnectionAdapters::Column.string_to_dummy_time value
351
+ end
352
+ end
353
+
354
+ class Float < Type
355
+ def type; :float; end
356
+
357
+ def type_cast(value)
358
+ return if value.nil?
359
+
360
+ value.to_f
361
+ end
362
+ end
363
+
364
+ class Decimal < Type
365
+ def type_cast(value)
366
+ return if value.nil?
367
+
368
+ ConnectionAdapters::Column.value_to_decimal value
369
+ end
370
+ end
371
+
372
+ class Boolean < Type
373
+ def type_cast(value)
374
+ return if value.nil?
375
+
376
+ ConnectionAdapters::Column.value_to_boolean value
377
+ end
378
+ end
379
+
380
+ TYPES = {}
381
+
382
+ # Register an MySQL +type_id+ with a typecasting object in
383
+ # +type+.
384
+ def self.register_type(type_id, type)
385
+ TYPES[type_id] = type
386
+ end
387
+
388
+ def self.alias_type(new, old)
389
+ TYPES[new] = TYPES[old]
390
+ end
391
+
392
+ def self.find_type(field)
393
+ if field.type == Mysql::Field::TYPE_TINY && field.length > 1
394
+ TYPES[Mysql::Field::TYPE_LONG]
395
+ else
396
+ TYPES.fetch(field.type) { Fields::Identity.new }
397
+ end
398
+ end
399
+
400
+ register_type Mysql::Field::TYPE_TINY, Fields::Boolean.new
401
+ register_type Mysql::Field::TYPE_LONG, Fields::Integer.new
402
+ alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
403
+ alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
404
+
405
+ register_type Mysql::Field::TYPE_VAR_STRING, Fields::Identity.new
406
+ register_type Mysql::Field::TYPE_BLOB, Fields::Identity.new
407
+ register_type Mysql::Field::TYPE_DATE, Fields::Date.new
408
+ register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
409
+ register_type Mysql::Field::TYPE_TIME, Fields::Time.new
410
+ register_type Mysql::Field::TYPE_FLOAT, Fields::Float.new
411
+
412
+ Mysql::Field.constants.grep(/TYPE/).map { |class_name|
413
+ Mysql::Field.const_get class_name
414
+ }.reject { |const| TYPES.key? const }.each do |const|
415
+ register_type const, Fields::Identity.new
416
+ end
417
+ end
418
+
304
419
  def exec_without_stmt(sql, name = 'SQL') # :nodoc:
305
420
  # Some queries, like SHOW CREATE TABLE don't work through the prepared
306
421
  # statement API. For those queries, we need to use this method. :'(
@@ -309,8 +424,15 @@ module ActiveRecord
309
424
  affected_rows = @connection.affected_rows
310
425
 
311
426
  if result
312
- cols = result.fetch_fields.map { |field| field.name }
313
- result_set = ActiveRecord::Result.new(cols, result.to_a)
427
+ types = {}
428
+ result.fetch_fields.each { |field|
429
+ if field.decimals > 0
430
+ types[field.name] = Fields::Decimal.new
431
+ else
432
+ types[field.name] = Fields.find_type field
433
+ end
434
+ }
435
+ result_set = ActiveRecord::Result.new(types.keys, result.to_a, types)
314
436
  result.free
315
437
  else
316
438
  result_set = ActiveRecord::Result.new([], [])
@@ -381,12 +503,12 @@ module ActiveRecord
381
503
  cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
382
504
  field.name
383
505
  }
506
+ metadata.free
384
507
  end
385
508
 
386
509
  result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
387
510
  affected_rows = stmt.affected_rows
388
511
 
389
- stmt.result_metadata.free if cols
390
512
  stmt.free_result
391
513
  stmt.close if binds.empty?
392
514
 
@@ -416,18 +538,15 @@ module ActiveRecord
416
538
  configure_connection
417
539
  end
418
540
 
541
+ # Many Rails applications monkey-patch a replacement of the configure_connection method
542
+ # and don't call 'super', so leave this here even though it looks superfluous.
419
543
  def configure_connection
420
- encoding = @config[:encoding]
421
- execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
422
-
423
- # By default, MySQL 'where id is null' selects the last inserted id.
424
- # Turn this off. http://dev.rubyonrails.org/ticket/6778
425
- execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
544
+ super
426
545
  end
427
546
 
428
547
  def select(sql, name = nil, binds = [])
429
548
  @connection.query_with_result = true
430
- rows = exec_query(sql, name, binds).to_a
549
+ rows = exec_query(sql, name, binds)
431
550
  @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
432
551
  rows
433
552
  end
@@ -436,6 +555,14 @@ module ActiveRecord
436
555
  def version
437
556
  @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
438
557
  end
558
+
559
+ def set_field_encoding field_name
560
+ field_name.force_encoding(client_encoding)
561
+ if internal_enc = Encoding.default_internal
562
+ field_name = field_name.encode!(internal_enc)
563
+ end
564
+ field_name
565
+ end
439
566
  end
440
567
  end
441
568
  end