activerecord 3.2.22.5 → 4.2.11.3

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 (236) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1632 -609
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +37 -41
  5. data/examples/performance.rb +31 -19
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +56 -42
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -36
  10. data/lib/active_record/associations/association.rb +73 -55
  11. data/lib/active_record/associations/association_scope.rb +143 -82
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +125 -31
  15. data/lib/active_record/associations/builder/belongs_to.rb +89 -61
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +23 -17
  21. data/lib/active_record/associations/collection_association.rb +251 -177
  22. data/lib/active_record/associations/collection_proxy.rb +963 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +113 -22
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -39
  26. data/lib/active_record/associations/has_one_association.rb +43 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +62 -33
  38. data/lib/active_record/associations/preloader.rb +101 -79
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +30 -16
  41. data/lib/active_record/associations.rb +463 -345
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +142 -151
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +137 -57
  47. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +73 -106
  50. data/lib/active_record/attribute_methods/serialization.rb +44 -94
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
  52. data/lib/active_record/attribute_methods/write.rb +57 -44
  53. data/lib/active_record/attribute_methods.rb +301 -141
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +246 -217
  58. data/lib/active_record/base.rb +70 -474
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
  75. data/lib/active_record/connection_adapters/column.rb +31 -245
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
  114. data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +157 -105
  119. data/lib/active_record/dynamic_matchers.rb +119 -63
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +94 -36
  122. data/lib/active_record/explain.rb +15 -63
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +9 -5
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +302 -215
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +143 -70
  129. data/lib/active_record/integration.rb +65 -12
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +73 -52
  133. data/lib/active_record/locking/pessimistic.rb +5 -5
  134. data/lib/active_record/log_subscriber.rb +24 -21
  135. data/lib/active_record/migration/command_recorder.rb +124 -32
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +511 -213
  138. data/lib/active_record/model_schema.rb +91 -117
  139. data/lib/active_record/nested_attributes.rb +184 -130
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +276 -117
  143. data/lib/active_record/query_cache.rb +19 -37
  144. data/lib/active_record/querying.rb +28 -18
  145. data/lib/active_record/railtie.rb +73 -40
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +4 -3
  148. data/lib/active_record/railties/databases.rake +141 -416
  149. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  150. data/lib/active_record/readonly_attributes.rb +1 -4
  151. data/lib/active_record/reflection.rb +513 -154
  152. data/lib/active_record/relation/batches.rb +91 -43
  153. data/lib/active_record/relation/calculations.rb +199 -161
  154. data/lib/active_record/relation/delegation.rb +116 -25
  155. data/lib/active_record/relation/finder_methods.rb +362 -248
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -43
  160. data/lib/active_record/relation/query_methods.rb +928 -167
  161. data/lib/active_record/relation/spawn_methods.rb +48 -149
  162. data/lib/active_record/relation.rb +352 -207
  163. data/lib/active_record/result.rb +101 -10
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +56 -59
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +106 -63
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +50 -57
  170. data/lib/active_record/scoping/named.rb +73 -109
  171. data/lib/active_record/scoping.rb +58 -123
  172. data/lib/active_record/serialization.rb +6 -2
  173. data/lib/active_record/serializers/xml_serializer.rb +12 -22
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +168 -15
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +23 -16
  181. data/lib/active_record/transactions.rb +125 -79
  182. data/lib/active_record/type/big_integer.rb +13 -0
  183. data/lib/active_record/type/binary.rb +50 -0
  184. data/lib/active_record/type/boolean.rb +31 -0
  185. data/lib/active_record/type/date.rb +50 -0
  186. data/lib/active_record/type/date_time.rb +54 -0
  187. data/lib/active_record/type/decimal.rb +64 -0
  188. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  189. data/lib/active_record/type/decorator.rb +14 -0
  190. data/lib/active_record/type/float.rb +19 -0
  191. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  192. data/lib/active_record/type/integer.rb +59 -0
  193. data/lib/active_record/type/mutable.rb +16 -0
  194. data/lib/active_record/type/numeric.rb +36 -0
  195. data/lib/active_record/type/serialized.rb +62 -0
  196. data/lib/active_record/type/string.rb +40 -0
  197. data/lib/active_record/type/text.rb +11 -0
  198. data/lib/active_record/type/time.rb +26 -0
  199. data/lib/active_record/type/time_value.rb +38 -0
  200. data/lib/active_record/type/type_map.rb +64 -0
  201. data/lib/active_record/type/unsigned_integer.rb +15 -0
  202. data/lib/active_record/type/value.rb +110 -0
  203. data/lib/active_record/type.rb +23 -0
  204. data/lib/active_record/validations/associated.rb +24 -16
  205. data/lib/active_record/validations/presence.rb +67 -0
  206. data/lib/active_record/validations/uniqueness.rb +123 -64
  207. data/lib/active_record/validations.rb +36 -29
  208. data/lib/active_record/version.rb +5 -7
  209. data/lib/active_record.rb +66 -46
  210. data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
  211. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
  212. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  213. data/lib/rails/generators/active_record/migration.rb +11 -8
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
  215. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  216. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  217. data/lib/rails/generators/active_record.rb +3 -11
  218. metadata +101 -45
  219. data/examples/associations.png +0 -0
  220. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  221. data/lib/active_record/associations/join_helper.rb +0 -55
  222. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  223. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  226. data/lib/active_record/dynamic_finder_match.rb +0 -68
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/fixtures/file.rb +0 -65
  229. data/lib/active_record/identity_map.rb +0 -162
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -360
  232. data/lib/active_record/test_case.rb +0 -73
  233. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  234. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  235. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  236. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -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]
@@ -36,6 +34,12 @@ module ActiveRecord
36
34
  default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
37
35
  options = [host, username, password, database, port, socket, default_flags]
38
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, error)
40
+ else
41
+ raise
42
+ end
39
43
  end
40
44
  end
41
45
 
@@ -53,6 +57,8 @@ module ActiveRecord
53
57
  # * <tt>:database</tt> - The name of the database. No default, must be provided.
54
58
  # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
55
59
  # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.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)
61
+ # * <tt>:variables</tt> - (Optional) A hash session variables to send as <tt>SET @@SESSION.key = value</tt> on each database connection. Use the value +:default+ to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
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.
58
64
  # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
@@ -60,36 +66,7 @@ module ActiveRecord
60
66
  # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
61
67
  #
62
68
  class MysqlAdapter < AbstractMysqlAdapter
63
-
64
- class Column < AbstractMysqlAdapter::Column #:nodoc:
65
- def self.string_to_time(value)
66
- return super unless Mysql::Time === value
67
- new_time(
68
- value.year,
69
- value.month,
70
- value.day,
71
- value.hour,
72
- value.minute,
73
- value.second,
74
- value.second_part)
75
- end
76
-
77
- def self.string_to_dummy_time(v)
78
- return super unless Mysql::Time === v
79
- new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
80
- end
81
-
82
- def self.string_to_date(v)
83
- return super unless Mysql::Time === v
84
- new_date(v.year, v.month, v.day)
85
- end
86
-
87
- def adapter
88
- MysqlAdapter
89
- end
90
- end
91
-
92
- ADAPTER_NAME = 'MySQL'
69
+ ADAPTER_NAME = 'MySQL'.freeze
93
70
 
94
71
  class StatementPool < ConnectionAdapters::StatementPool
95
72
  def initialize(connection, max = 1000)
@@ -111,7 +88,7 @@ module ActiveRecord
111
88
  end
112
89
 
113
90
  def clear
114
- cache.values.each do |hash|
91
+ cache.each_value do |hash|
115
92
  hash[:stmt].close
116
93
  end
117
94
  cache.clear
@@ -119,14 +96,14 @@ module ActiveRecord
119
96
 
120
97
  private
121
98
  def cache
122
- @cache[$$]
99
+ @cache[Process.pid]
123
100
  end
124
101
  end
125
102
 
126
103
  def initialize(connection, logger, connection_options, config)
127
104
  super
128
105
  @statements = StatementPool.new(@connection,
129
- config.fetch(:statement_limit) { 1000 })
106
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
130
107
  @client_encoding = nil
131
108
  connect
132
109
  end
@@ -150,27 +127,19 @@ module ActiveRecord
150
127
  end
151
128
  end
152
129
 
153
- def new_column(field, default, type, null, collation) # :nodoc:
154
- Column.new(field, default, type, null, collation)
155
- end
156
-
157
130
  def error_number(exception) # :nodoc:
158
131
  exception.errno if exception.respond_to?(:errno)
159
132
  end
160
133
 
161
134
  # QUOTING ==================================================
162
135
 
163
- def type_cast(value, column)
164
- return super unless value == true || value == false
165
-
166
- value ? 1 : 0
167
- end
168
-
169
136
  def quote_string(string) #:nodoc:
170
137
  @connection.quote(string)
171
138
  end
172
139
 
140
+ #--
173
141
  # CONNECTION MANAGEMENT ====================================
142
+ #++
174
143
 
175
144
  def active?
176
145
  if @connection.respond_to?(:stat)
@@ -190,14 +159,15 @@ module ActiveRecord
190
159
  end
191
160
 
192
161
  def reconnect!
162
+ super
193
163
  disconnect!
194
- clear_cache!
195
164
  connect
196
165
  end
197
166
 
198
167
  # Disconnects from the database if already connected. Otherwise, this
199
168
  # method does nothing.
200
169
  def disconnect!
170
+ super
201
171
  @connection.close rescue nil
202
172
  end
203
173
 
@@ -210,83 +180,78 @@ module ActiveRecord
210
180
  end
211
181
  end
212
182
 
183
+ #--
213
184
  # DATABASE STATEMENTS ======================================
185
+ #++
214
186
 
215
- def select_rows(sql, name = nil)
187
+ def select_rows(sql, name = nil, binds = [])
216
188
  @connection.query_with_result = true
217
- rows = exec_query(sql, name).rows
189
+ rows = exec_query(sql, name, binds).rows
218
190
  @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
219
191
  rows
220
192
  end
221
193
 
222
194
  # Clears the prepared statements cache.
223
195
  def clear_cache!
196
+ super
224
197
  @statements.clear
225
198
  end
226
199
 
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
200
+ # Taken from here:
201
+ # https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
202
+ # Author: TOMITA Masahiro <tommy@tmtm.org>
203
+ ENCODINGS = {
204
+ "armscii8" => nil,
205
+ "ascii" => Encoding::US_ASCII,
206
+ "big5" => Encoding::Big5,
207
+ "binary" => Encoding::ASCII_8BIT,
208
+ "cp1250" => Encoding::Windows_1250,
209
+ "cp1251" => Encoding::Windows_1251,
210
+ "cp1256" => Encoding::Windows_1256,
211
+ "cp1257" => Encoding::Windows_1257,
212
+ "cp850" => Encoding::CP850,
213
+ "cp852" => Encoding::CP852,
214
+ "cp866" => Encoding::IBM866,
215
+ "cp932" => Encoding::Windows_31J,
216
+ "dec8" => nil,
217
+ "eucjpms" => Encoding::EucJP_ms,
218
+ "euckr" => Encoding::EUC_KR,
219
+ "gb2312" => Encoding::EUC_CN,
220
+ "gbk" => Encoding::GBK,
221
+ "geostd8" => nil,
222
+ "greek" => Encoding::ISO_8859_7,
223
+ "hebrew" => Encoding::ISO_8859_8,
224
+ "hp8" => nil,
225
+ "keybcs2" => nil,
226
+ "koi8r" => Encoding::KOI8_R,
227
+ "koi8u" => Encoding::KOI8_U,
228
+ "latin1" => Encoding::ISO_8859_1,
229
+ "latin2" => Encoding::ISO_8859_2,
230
+ "latin5" => Encoding::ISO_8859_9,
231
+ "latin7" => Encoding::ISO_8859_13,
232
+ "macce" => Encoding::MacCentEuro,
233
+ "macroman" => Encoding::MacRoman,
234
+ "sjis" => Encoding::SHIFT_JIS,
235
+ "swe7" => nil,
236
+ "tis620" => Encoding::TIS_620,
237
+ "ucs2" => Encoding::UTF_16BE,
238
+ "ujis" => Encoding::EucJP_ms,
239
+ "utf8" => Encoding::UTF_8,
240
+ "utf8mb4" => Encoding::UTF_8,
241
+ }
273
242
 
274
243
  # Get the client encoding for this database
275
244
  def client_encoding
276
245
  return @client_encoding if @client_encoding
277
246
 
278
247
  result = exec_query(
279
- "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
248
+ "select @@character_set_client",
280
249
  'SCHEMA')
281
250
  @client_encoding = ENCODINGS[result.rows.last.last]
282
251
  end
283
252
 
284
253
  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?
254
+ if without_prepared_statement?(binds)
290
255
  result_set, affected_rows = exec_without_stmt(sql, name)
291
256
  else
292
257
  result_set, affected_rows = exec_stmt(sql, name, binds)
@@ -301,6 +266,79 @@ module ActiveRecord
301
266
  @connection.insert_id
302
267
  end
303
268
 
269
+ module Fields # :nodoc:
270
+ class DateTime < Type::DateTime # :nodoc:
271
+ def cast_value(value)
272
+ if Mysql::Time === value
273
+ new_time(
274
+ value.year,
275
+ value.month,
276
+ value.day,
277
+ value.hour,
278
+ value.minute,
279
+ value.second,
280
+ value.second_part)
281
+ else
282
+ super
283
+ end
284
+ end
285
+
286
+ def has_precision?
287
+ precision || 0
288
+ end
289
+ end
290
+
291
+ class Time < Type::Time # :nodoc:
292
+ def cast_value(value)
293
+ if Mysql::Time === value
294
+ new_time(
295
+ 2000,
296
+ 01,
297
+ 01,
298
+ value.hour,
299
+ value.minute,
300
+ value.second,
301
+ value.second_part)
302
+ else
303
+ super
304
+ end
305
+ end
306
+ end
307
+
308
+ class << self
309
+ TYPES = Type::HashLookupTypeMap.new # :nodoc:
310
+
311
+ delegate :register_type, :alias_type, to: :TYPES
312
+
313
+ def find_type(field)
314
+ if field.type == Mysql::Field::TYPE_TINY && field.length > 1
315
+ TYPES.lookup(Mysql::Field::TYPE_LONG)
316
+ else
317
+ TYPES.lookup(field.type)
318
+ end
319
+ end
320
+ end
321
+
322
+ register_type Mysql::Field::TYPE_TINY, Type::Boolean.new
323
+ register_type Mysql::Field::TYPE_LONG, Type::Integer.new
324
+ alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
325
+ alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
326
+
327
+ register_type Mysql::Field::TYPE_DATE, Type::Date.new
328
+ register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
329
+ register_type Mysql::Field::TYPE_TIME, Fields::Time.new
330
+ register_type Mysql::Field::TYPE_FLOAT, Type::Float.new
331
+ end
332
+
333
+ def initialize_type_map(m) # :nodoc:
334
+ super
335
+ m.register_type %r(time)i, Fields::Time.new
336
+ m.register_type(%r(datetime)i) do |sql_type|
337
+ precision = extract_precision(sql_type)
338
+ Fields::DateTime.new(precision: precision)
339
+ end
340
+ end
341
+
304
342
  def exec_without_stmt(sql, name = 'SQL') # :nodoc:
305
343
  # Some queries, like SHOW CREATE TABLE don't work through the prepared
306
344
  # statement API. For those queries, we need to use this method. :'(
@@ -309,8 +347,20 @@ module ActiveRecord
309
347
  affected_rows = @connection.affected_rows
310
348
 
311
349
  if result
312
- cols = result.fetch_fields.map { |field| field.name }
313
- result_set = ActiveRecord::Result.new(cols, result.to_a)
350
+ types = {}
351
+ fields = []
352
+ result.fetch_fields.each { |field|
353
+ field_name = field.name
354
+ fields << field_name
355
+
356
+ if field.decimals > 0
357
+ types[field_name] = Type::Decimal.new
358
+ else
359
+ types[field_name] = Fields.find_type field
360
+ end
361
+ }
362
+
363
+ result_set = ActiveRecord::Result.new(fields, result.to_a, types)
314
364
  result.free
315
365
  else
316
366
  result_set = ActiveRecord::Result.new([], [])
@@ -320,7 +370,7 @@ module ActiveRecord
320
370
  end
321
371
  end
322
372
 
323
- def execute_and_free(sql, name = nil)
373
+ def execute_and_free(sql, name = nil) # :nodoc:
324
374
  result = execute(sql, name)
325
375
  ret = yield result
326
376
  result.free
@@ -333,7 +383,7 @@ module ActiveRecord
333
383
  end
334
384
  alias :create :insert_sql
335
385
 
336
- def exec_delete(sql, name, binds)
386
+ def exec_delete(sql, name, binds) # :nodoc:
337
387
  affected_rows = 0
338
388
 
339
389
  exec_query(sql, name, binds) do |n|
@@ -346,15 +396,17 @@ module ActiveRecord
346
396
 
347
397
  def begin_db_transaction #:nodoc:
348
398
  exec_query "BEGIN"
349
- rescue Mysql::Error
350
- # Transactions aren't supported
351
399
  end
352
400
 
353
401
  private
354
402
 
355
403
  def exec_stmt(sql, name, binds)
356
404
  cache = {}
357
- log(sql, name, binds) do
405
+ type_casted_binds = binds.map { |col, val|
406
+ [col, type_cast(val, col)]
407
+ }
408
+
409
+ log(sql, name, type_casted_binds) do
358
410
  if binds.empty?
359
411
  stmt = @connection.prepare(sql)
360
412
  else
@@ -365,10 +417,10 @@ module ActiveRecord
365
417
  end
366
418
 
367
419
  begin
368
- stmt.execute(*binds.map { |col, val| type_cast(val, col) })
420
+ stmt.execute(*type_casted_binds.map { |_, val| val })
369
421
  rescue Mysql::Error => e
370
422
  # Older versions of MySQL leave the prepared statement in a bad
371
- # place when an error occurs. To support older mysql versions, we
423
+ # place when an error occurs. To support older MySQL versions, we
372
424
  # need to close the statement and delete the statement from the
373
425
  # cache.
374
426
  stmt.close
@@ -381,12 +433,12 @@ module ActiveRecord
381
433
  cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
382
434
  field.name
383
435
  }
436
+ metadata.free
384
437
  end
385
438
 
386
439
  result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
387
440
  affected_rows = stmt.affected_rows
388
441
 
389
- stmt.result_metadata.free if cols
390
442
  stmt.free_result
391
443
  stmt.close if binds.empty?
392
444
 
@@ -416,25 +468,30 @@ module ActiveRecord
416
468
  configure_connection
417
469
  end
418
470
 
471
+ # Many Rails applications monkey-patch a replacement of the configure_connection method
472
+ # and don't call 'super', so leave this here even though it looks superfluous.
419
473
  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)
474
+ super
426
475
  end
427
476
 
428
477
  def select(sql, name = nil, binds = [])
429
478
  @connection.query_with_result = true
430
- rows = exec_query(sql, name, binds).to_a
479
+ rows = super
431
480
  @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
432
481
  rows
433
482
  end
434
483
 
435
- # Returns the version of the connected MySQL server.
436
- def version
437
- @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
484
+ # Returns the full version of the connected MySQL server.
485
+ def full_version
486
+ @full_version ||= @connection.server_info
487
+ end
488
+
489
+ def set_field_encoding field_name
490
+ field_name.force_encoding(client_encoding)
491
+ if internal_enc = Encoding.default_internal
492
+ field_name = field_name.encode!(internal_enc)
493
+ end
494
+ field_name
438
495
  end
439
496
  end
440
497
  end
@@ -0,0 +1,93 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module ArrayParser # :nodoc:
5
+
6
+ DOUBLE_QUOTE = '"'
7
+ BACKSLASH = "\\"
8
+ COMMA = ','
9
+ BRACKET_OPEN = '{'
10
+ BRACKET_CLOSE = '}'
11
+
12
+ def parse_pg_array(string) # :nodoc:
13
+ local_index = 0
14
+ array = []
15
+ while(local_index < string.length)
16
+ case string[local_index]
17
+ when BRACKET_OPEN
18
+ local_index,array = parse_array_contents(array, string, local_index + 1)
19
+ when BRACKET_CLOSE
20
+ return array
21
+ end
22
+ local_index += 1
23
+ end
24
+
25
+ array
26
+ end
27
+
28
+ private
29
+
30
+ def parse_array_contents(array, string, index)
31
+ is_escaping = false
32
+ is_quoted = false
33
+ was_quoted = false
34
+ current_item = ''
35
+
36
+ local_index = index
37
+ while local_index
38
+ token = string[local_index]
39
+ if is_escaping
40
+ current_item << token
41
+ is_escaping = false
42
+ else
43
+ if is_quoted
44
+ case token
45
+ when DOUBLE_QUOTE
46
+ is_quoted = false
47
+ was_quoted = true
48
+ when BACKSLASH
49
+ is_escaping = true
50
+ else
51
+ current_item << token
52
+ end
53
+ else
54
+ case token
55
+ when BACKSLASH
56
+ is_escaping = true
57
+ when COMMA
58
+ add_item_to_array(array, current_item, was_quoted)
59
+ current_item = ''
60
+ was_quoted = false
61
+ when DOUBLE_QUOTE
62
+ is_quoted = true
63
+ when BRACKET_OPEN
64
+ internal_items = []
65
+ local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
66
+ array.push(internal_items)
67
+ when BRACKET_CLOSE
68
+ add_item_to_array(array, current_item, was_quoted)
69
+ return local_index,array
70
+ else
71
+ current_item << token
72
+ end
73
+ end
74
+ end
75
+
76
+ local_index += 1
77
+ end
78
+ return local_index,array
79
+ end
80
+
81
+ def add_item_to_array(array, current_item, quoted)
82
+ return if !quoted && current_item.length == 0
83
+
84
+ if !quoted && current_item == 'NULL'
85
+ array.push nil
86
+ else
87
+ array.push current_item
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ # PostgreSQL-specific extensions to column definitions in a table.
4
+ class PostgreSQLColumn < Column #:nodoc:
5
+ attr_accessor :array
6
+
7
+ def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
8
+ if sql_type =~ /\[\]$/
9
+ @array = true
10
+ super(name, default, cast_type, sql_type[0..sql_type.length - 3], null)
11
+ else
12
+ @array = false
13
+ super(name, default, cast_type, sql_type, null)
14
+ end
15
+
16
+ @default_function = default_function
17
+ end
18
+ end
19
+ end
20
+ end