activerecord 2.3.18 → 3.2.22

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 (454) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1014 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +222 -0
  5. data/examples/performance.rb +100 -126
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +93 -99
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +247 -0
  10. data/lib/active_record/associations/association_scope.rb +134 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +54 -61
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +17 -59
  13. data/lib/active_record/associations/builder/association.rb +55 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +88 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
  17. data/lib/active_record/associations/builder/has_many.rb +71 -0
  18. data/lib/active_record/associations/builder/has_one.rb +62 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +580 -0
  21. data/lib/active_record/associations/collection_proxy.rb +133 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +39 -119
  23. data/lib/active_record/associations/has_many_association.rb +60 -79
  24. data/lib/active_record/associations/has_many_through_association.rb +127 -206
  25. data/lib/active_record/associations/has_one_association.rb +55 -114
  26. data/lib/active_record/associations/has_one_through_association.rb +25 -26
  27. data/lib/active_record/associations/join_dependency/join_association.rb +159 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +214 -0
  31. data/lib/active_record/associations/join_helper.rb +55 -0
  32. data/lib/active_record/associations/preloader/association.rb +125 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  42. data/lib/active_record/associations/preloader.rb +181 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +693 -1337
  46. data/lib/active_record/attribute_assignment.rb +221 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
  48. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  49. data/lib/active_record/attribute_methods/dirty.rb +111 -0
  50. data/lib/active_record/attribute_methods/primary_key.rb +114 -0
  51. data/lib/active_record/attribute_methods/query.rb +39 -0
  52. data/lib/active_record/attribute_methods/read.rb +136 -0
  53. data/lib/active_record/attribute_methods/serialization.rb +120 -0
  54. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  55. data/lib/active_record/attribute_methods/write.rb +70 -0
  56. data/lib/active_record/attribute_methods.rb +211 -339
  57. data/lib/active_record/autosave_association.rb +179 -149
  58. data/lib/active_record/base.rb +401 -2907
  59. data/lib/active_record/callbacks.rb +91 -176
  60. data/lib/active_record/coders/yaml_column.rb +41 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +236 -119
  62. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +110 -58
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -74
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -35
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +71 -21
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +81 -311
  68. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +194 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +130 -83
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
  71. data/lib/active_record/connection_adapters/column.rb +296 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +280 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +272 -493
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +650 -405
  75. data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
  76. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +30 -9
  77. data/lib/active_record/connection_adapters/sqlite_adapter.rb +276 -147
  78. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  79. data/lib/active_record/counter_cache.rb +123 -0
  80. data/lib/active_record/dynamic_finder_match.rb +41 -14
  81. data/lib/active_record/dynamic_matchers.rb +84 -0
  82. data/lib/active_record/dynamic_scope_match.rb +13 -15
  83. data/lib/active_record/errors.rb +195 -0
  84. data/lib/active_record/explain.rb +86 -0
  85. data/lib/active_record/explain_subscriber.rb +25 -0
  86. data/lib/active_record/fixtures/file.rb +65 -0
  87. data/lib/active_record/fixtures.rb +695 -770
  88. data/lib/active_record/identity_map.rb +162 -0
  89. data/lib/active_record/inheritance.rb +174 -0
  90. data/lib/active_record/integration.rb +60 -0
  91. data/lib/active_record/locale/en.yml +9 -27
  92. data/lib/active_record/locking/optimistic.rb +76 -73
  93. data/lib/active_record/locking/pessimistic.rb +32 -10
  94. data/lib/active_record/log_subscriber.rb +72 -0
  95. data/lib/active_record/migration/command_recorder.rb +105 -0
  96. data/lib/active_record/migration.rb +415 -205
  97. data/lib/active_record/model_schema.rb +368 -0
  98. data/lib/active_record/nested_attributes.rb +153 -63
  99. data/lib/active_record/observer.rb +27 -103
  100. data/lib/active_record/persistence.rb +376 -0
  101. data/lib/active_record/query_cache.rb +49 -8
  102. data/lib/active_record/querying.rb +58 -0
  103. data/lib/active_record/railtie.rb +131 -0
  104. data/lib/active_record/railties/console_sandbox.rb +6 -0
  105. data/lib/active_record/railties/controller_runtime.rb +49 -0
  106. data/lib/active_record/railties/databases.rake +659 -0
  107. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  108. data/lib/active_record/readonly_attributes.rb +26 -0
  109. data/lib/active_record/reflection.rb +269 -120
  110. data/lib/active_record/relation/batches.rb +90 -0
  111. data/lib/active_record/relation/calculations.rb +372 -0
  112. data/lib/active_record/relation/delegation.rb +49 -0
  113. data/lib/active_record/relation/finder_methods.rb +402 -0
  114. data/lib/active_record/relation/predicate_builder.rb +63 -0
  115. data/lib/active_record/relation/query_methods.rb +417 -0
  116. data/lib/active_record/relation/spawn_methods.rb +180 -0
  117. data/lib/active_record/relation.rb +537 -0
  118. data/lib/active_record/result.rb +40 -0
  119. data/lib/active_record/sanitization.rb +194 -0
  120. data/lib/active_record/schema.rb +9 -6
  121. data/lib/active_record/schema_dumper.rb +55 -32
  122. data/lib/active_record/scoping/default.rb +142 -0
  123. data/lib/active_record/scoping/named.rb +200 -0
  124. data/lib/active_record/scoping.rb +152 -0
  125. data/lib/active_record/serialization.rb +8 -91
  126. data/lib/active_record/serializers/xml_serializer.rb +43 -197
  127. data/lib/active_record/session_store.rb +129 -103
  128. data/lib/active_record/store.rb +52 -0
  129. data/lib/active_record/test_case.rb +30 -23
  130. data/lib/active_record/timestamp.rb +95 -52
  131. data/lib/active_record/transactions.rb +212 -66
  132. data/lib/active_record/translation.rb +22 -0
  133. data/lib/active_record/validations/associated.rb +43 -0
  134. data/lib/active_record/validations/uniqueness.rb +180 -0
  135. data/lib/active_record/validations.rb +43 -1106
  136. data/lib/active_record/version.rb +5 -4
  137. data/lib/active_record.rb +121 -48
  138. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  139. data/lib/rails/generators/active_record/migration/templates/migration.rb +34 -0
  140. data/lib/rails/generators/active_record/migration.rb +15 -0
  141. data/lib/rails/generators/active_record/model/model_generator.rb +47 -0
  142. data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
  143. data/lib/rails/generators/active_record/model/templates/model.rb +12 -0
  144. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  145. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  146. data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
  147. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
  148. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
  149. data/lib/rails/generators/active_record.rb +25 -0
  150. metadata +187 -363
  151. data/CHANGELOG +0 -5904
  152. data/README +0 -351
  153. data/RUNNING_UNIT_TESTS +0 -36
  154. data/Rakefile +0 -268
  155. data/install.rb +0 -30
  156. data/lib/active_record/association_preload.rb +0 -406
  157. data/lib/active_record/associations/association_collection.rb +0 -533
  158. data/lib/active_record/associations/association_proxy.rb +0 -288
  159. data/lib/active_record/batches.rb +0 -85
  160. data/lib/active_record/calculations.rb +0 -321
  161. data/lib/active_record/dirty.rb +0 -183
  162. data/lib/active_record/named_scope.rb +0 -197
  163. data/lib/active_record/serializers/json_serializer.rb +0 -91
  164. data/lib/activerecord.rb +0 -2
  165. data/test/assets/example.log +0 -1
  166. data/test/assets/flowers.jpg +0 -0
  167. data/test/cases/aaa_create_tables_test.rb +0 -24
  168. data/test/cases/active_schema_test_mysql.rb +0 -122
  169. data/test/cases/active_schema_test_postgresql.rb +0 -24
  170. data/test/cases/adapter_test.rb +0 -144
  171. data/test/cases/aggregations_test.rb +0 -167
  172. data/test/cases/ar_schema_test.rb +0 -32
  173. data/test/cases/associations/belongs_to_associations_test.rb +0 -438
  174. data/test/cases/associations/callbacks_test.rb +0 -161
  175. data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
  176. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
  177. data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
  178. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
  179. data/test/cases/associations/eager_singularization_test.rb +0 -145
  180. data/test/cases/associations/eager_test.rb +0 -852
  181. data/test/cases/associations/extension_test.rb +0 -62
  182. data/test/cases/associations/habtm_join_table_test.rb +0 -56
  183. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
  184. data/test/cases/associations/has_many_associations_test.rb +0 -1273
  185. data/test/cases/associations/has_many_through_associations_test.rb +0 -360
  186. data/test/cases/associations/has_one_associations_test.rb +0 -330
  187. data/test/cases/associations/has_one_through_associations_test.rb +0 -209
  188. data/test/cases/associations/inner_join_association_test.rb +0 -93
  189. data/test/cases/associations/inverse_associations_test.rb +0 -566
  190. data/test/cases/associations/join_model_test.rb +0 -712
  191. data/test/cases/associations_test.rb +0 -282
  192. data/test/cases/attribute_methods_test.rb +0 -305
  193. data/test/cases/autosave_association_test.rb +0 -1218
  194. data/test/cases/base_test.rb +0 -2166
  195. data/test/cases/batches_test.rb +0 -81
  196. data/test/cases/binary_test.rb +0 -30
  197. data/test/cases/calculations_test.rb +0 -360
  198. data/test/cases/callbacks_observers_test.rb +0 -38
  199. data/test/cases/callbacks_test.rb +0 -438
  200. data/test/cases/class_inheritable_attributes_test.rb +0 -32
  201. data/test/cases/column_alias_test.rb +0 -17
  202. data/test/cases/column_definition_test.rb +0 -70
  203. data/test/cases/connection_pool_test.rb +0 -25
  204. data/test/cases/connection_test_firebird.rb +0 -8
  205. data/test/cases/connection_test_mysql.rb +0 -65
  206. data/test/cases/copy_table_test_sqlite.rb +0 -80
  207. data/test/cases/counter_cache_test.rb +0 -84
  208. data/test/cases/database_statements_test.rb +0 -12
  209. data/test/cases/datatype_test_postgresql.rb +0 -204
  210. data/test/cases/date_time_test.rb +0 -37
  211. data/test/cases/default_test_firebird.rb +0 -16
  212. data/test/cases/defaults_test.rb +0 -111
  213. data/test/cases/deprecated_finder_test.rb +0 -30
  214. data/test/cases/dirty_test.rb +0 -316
  215. data/test/cases/finder_respond_to_test.rb +0 -76
  216. data/test/cases/finder_test.rb +0 -1098
  217. data/test/cases/fixtures_test.rb +0 -661
  218. data/test/cases/helper.rb +0 -68
  219. data/test/cases/i18n_test.rb +0 -46
  220. data/test/cases/inheritance_test.rb +0 -262
  221. data/test/cases/invalid_date_test.rb +0 -24
  222. data/test/cases/json_serialization_test.rb +0 -219
  223. data/test/cases/lifecycle_test.rb +0 -193
  224. data/test/cases/locking_test.rb +0 -350
  225. data/test/cases/method_scoping_test.rb +0 -704
  226. data/test/cases/migration_test.rb +0 -1649
  227. data/test/cases/migration_test_firebird.rb +0 -124
  228. data/test/cases/mixin_test.rb +0 -96
  229. data/test/cases/modules_test.rb +0 -109
  230. data/test/cases/multiple_db_test.rb +0 -85
  231. data/test/cases/named_scope_test.rb +0 -372
  232. data/test/cases/nested_attributes_test.rb +0 -840
  233. data/test/cases/pk_test.rb +0 -119
  234. data/test/cases/pooled_connections_test.rb +0 -103
  235. data/test/cases/query_cache_test.rb +0 -129
  236. data/test/cases/readonly_test.rb +0 -107
  237. data/test/cases/reflection_test.rb +0 -234
  238. data/test/cases/reload_models_test.rb +0 -22
  239. data/test/cases/repair_helper.rb +0 -50
  240. data/test/cases/reserved_word_test_mysql.rb +0 -176
  241. data/test/cases/sanitize_test.rb +0 -25
  242. data/test/cases/schema_authorization_test_postgresql.rb +0 -75
  243. data/test/cases/schema_dumper_test.rb +0 -211
  244. data/test/cases/schema_test_postgresql.rb +0 -178
  245. data/test/cases/serialization_test.rb +0 -47
  246. data/test/cases/sp_test_mysql.rb +0 -16
  247. data/test/cases/synonym_test_oracle.rb +0 -17
  248. data/test/cases/timestamp_test.rb +0 -75
  249. data/test/cases/transactions_test.rb +0 -543
  250. data/test/cases/unconnected_test.rb +0 -32
  251. data/test/cases/validations_i18n_test.rb +0 -925
  252. data/test/cases/validations_test.rb +0 -1684
  253. data/test/cases/xml_serialization_test.rb +0 -240
  254. data/test/cases/yaml_serialization_test.rb +0 -11
  255. data/test/config.rb +0 -5
  256. data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
  257. data/test/connections/jdbc_jdbch2/connection.rb +0 -18
  258. data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
  259. data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
  260. data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
  261. data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
  262. data/test/connections/native_db2/connection.rb +0 -25
  263. data/test/connections/native_firebird/connection.rb +0 -26
  264. data/test/connections/native_frontbase/connection.rb +0 -27
  265. data/test/connections/native_mysql/connection.rb +0 -25
  266. data/test/connections/native_openbase/connection.rb +0 -21
  267. data/test/connections/native_oracle/connection.rb +0 -27
  268. data/test/connections/native_postgresql/connection.rb +0 -21
  269. data/test/connections/native_sqlite/connection.rb +0 -25
  270. data/test/connections/native_sqlite3/connection.rb +0 -25
  271. data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
  272. data/test/connections/native_sybase/connection.rb +0 -23
  273. data/test/fixtures/accounts.yml +0 -29
  274. data/test/fixtures/all/developers.yml +0 -0
  275. data/test/fixtures/all/people.csv +0 -0
  276. data/test/fixtures/all/tasks.yml +0 -0
  277. data/test/fixtures/author_addresses.yml +0 -5
  278. data/test/fixtures/author_favorites.yml +0 -4
  279. data/test/fixtures/authors.yml +0 -9
  280. data/test/fixtures/binaries.yml +0 -132
  281. data/test/fixtures/books.yml +0 -7
  282. data/test/fixtures/categories/special_categories.yml +0 -9
  283. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
  284. data/test/fixtures/categories.yml +0 -14
  285. data/test/fixtures/categories_ordered.yml +0 -7
  286. data/test/fixtures/categories_posts.yml +0 -23
  287. data/test/fixtures/categorizations.yml +0 -17
  288. data/test/fixtures/clubs.yml +0 -6
  289. data/test/fixtures/comments.yml +0 -59
  290. data/test/fixtures/companies.yml +0 -56
  291. data/test/fixtures/computers.yml +0 -4
  292. data/test/fixtures/courses.yml +0 -7
  293. data/test/fixtures/customers.yml +0 -26
  294. data/test/fixtures/developers.yml +0 -21
  295. data/test/fixtures/developers_projects.yml +0 -17
  296. data/test/fixtures/edges.yml +0 -6
  297. data/test/fixtures/entrants.yml +0 -14
  298. data/test/fixtures/faces.yml +0 -11
  299. data/test/fixtures/fk_test_has_fk.yml +0 -3
  300. data/test/fixtures/fk_test_has_pk.yml +0 -2
  301. data/test/fixtures/funny_jokes.yml +0 -10
  302. data/test/fixtures/interests.yml +0 -33
  303. data/test/fixtures/items.yml +0 -4
  304. data/test/fixtures/jobs.yml +0 -7
  305. data/test/fixtures/legacy_things.yml +0 -3
  306. data/test/fixtures/mateys.yml +0 -4
  307. data/test/fixtures/member_types.yml +0 -6
  308. data/test/fixtures/members.yml +0 -6
  309. data/test/fixtures/memberships.yml +0 -20
  310. data/test/fixtures/men.yml +0 -5
  311. data/test/fixtures/minimalistics.yml +0 -2
  312. data/test/fixtures/mixed_case_monkeys.yml +0 -6
  313. data/test/fixtures/mixins.yml +0 -29
  314. data/test/fixtures/movies.yml +0 -7
  315. data/test/fixtures/naked/csv/accounts.csv +0 -1
  316. data/test/fixtures/naked/yml/accounts.yml +0 -1
  317. data/test/fixtures/naked/yml/companies.yml +0 -1
  318. data/test/fixtures/naked/yml/courses.yml +0 -1
  319. data/test/fixtures/organizations.yml +0 -5
  320. data/test/fixtures/owners.yml +0 -7
  321. data/test/fixtures/parrots.yml +0 -27
  322. data/test/fixtures/parrots_pirates.yml +0 -7
  323. data/test/fixtures/people.yml +0 -15
  324. data/test/fixtures/pets.yml +0 -14
  325. data/test/fixtures/pirates.yml +0 -9
  326. data/test/fixtures/polymorphic_designs.yml +0 -19
  327. data/test/fixtures/polymorphic_prices.yml +0 -19
  328. data/test/fixtures/posts.yml +0 -52
  329. data/test/fixtures/price_estimates.yml +0 -7
  330. data/test/fixtures/projects.yml +0 -7
  331. data/test/fixtures/readers.yml +0 -9
  332. data/test/fixtures/references.yml +0 -17
  333. data/test/fixtures/reserved_words/distinct.yml +0 -5
  334. data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
  335. data/test/fixtures/reserved_words/group.yml +0 -14
  336. data/test/fixtures/reserved_words/select.yml +0 -8
  337. data/test/fixtures/reserved_words/values.yml +0 -7
  338. data/test/fixtures/ships.yml +0 -5
  339. data/test/fixtures/sponsors.yml +0 -9
  340. data/test/fixtures/subscribers.yml +0 -7
  341. data/test/fixtures/subscriptions.yml +0 -12
  342. data/test/fixtures/taggings.yml +0 -28
  343. data/test/fixtures/tags.yml +0 -7
  344. data/test/fixtures/tasks.yml +0 -7
  345. data/test/fixtures/tees.yml +0 -4
  346. data/test/fixtures/ties.yml +0 -4
  347. data/test/fixtures/topics.yml +0 -42
  348. data/test/fixtures/toys.yml +0 -4
  349. data/test/fixtures/treasures.yml +0 -10
  350. data/test/fixtures/vertices.yml +0 -4
  351. data/test/fixtures/warehouse-things.yml +0 -3
  352. data/test/fixtures/zines.yml +0 -5
  353. data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
  354. data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
  355. data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
  356. data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
  357. data/test/migrations/duplicate/3_foo.rb +0 -7
  358. data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
  359. data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
  360. data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
  361. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
  362. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
  363. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
  364. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
  365. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
  366. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
  367. data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
  368. data/test/migrations/missing/1_people_have_last_names.rb +0 -9
  369. data/test/migrations/missing/3_we_need_reminders.rb +0 -12
  370. data/test/migrations/missing/4_innocent_jointable.rb +0 -12
  371. data/test/migrations/valid/1_people_have_last_names.rb +0 -9
  372. data/test/migrations/valid/2_we_need_reminders.rb +0 -12
  373. data/test/migrations/valid/3_innocent_jointable.rb +0 -12
  374. data/test/models/author.rb +0 -151
  375. data/test/models/auto_id.rb +0 -4
  376. data/test/models/binary.rb +0 -2
  377. data/test/models/bird.rb +0 -9
  378. data/test/models/book.rb +0 -4
  379. data/test/models/categorization.rb +0 -5
  380. data/test/models/category.rb +0 -34
  381. data/test/models/citation.rb +0 -6
  382. data/test/models/club.rb +0 -13
  383. data/test/models/column_name.rb +0 -3
  384. data/test/models/comment.rb +0 -29
  385. data/test/models/company.rb +0 -173
  386. data/test/models/company_in_module.rb +0 -78
  387. data/test/models/computer.rb +0 -3
  388. data/test/models/contact.rb +0 -16
  389. data/test/models/contract.rb +0 -5
  390. data/test/models/course.rb +0 -3
  391. data/test/models/customer.rb +0 -73
  392. data/test/models/default.rb +0 -2
  393. data/test/models/developer.rb +0 -101
  394. data/test/models/edge.rb +0 -5
  395. data/test/models/entrant.rb +0 -3
  396. data/test/models/essay.rb +0 -3
  397. data/test/models/event.rb +0 -3
  398. data/test/models/event_author.rb +0 -8
  399. data/test/models/face.rb +0 -7
  400. data/test/models/guid.rb +0 -2
  401. data/test/models/interest.rb +0 -5
  402. data/test/models/invoice.rb +0 -4
  403. data/test/models/item.rb +0 -7
  404. data/test/models/job.rb +0 -5
  405. data/test/models/joke.rb +0 -3
  406. data/test/models/keyboard.rb +0 -3
  407. data/test/models/legacy_thing.rb +0 -3
  408. data/test/models/line_item.rb +0 -3
  409. data/test/models/man.rb +0 -9
  410. data/test/models/matey.rb +0 -4
  411. data/test/models/member.rb +0 -12
  412. data/test/models/member_detail.rb +0 -5
  413. data/test/models/member_type.rb +0 -3
  414. data/test/models/membership.rb +0 -9
  415. data/test/models/minimalistic.rb +0 -2
  416. data/test/models/mixed_case_monkey.rb +0 -3
  417. data/test/models/movie.rb +0 -5
  418. data/test/models/order.rb +0 -4
  419. data/test/models/organization.rb +0 -6
  420. data/test/models/owner.rb +0 -5
  421. data/test/models/parrot.rb +0 -22
  422. data/test/models/person.rb +0 -16
  423. data/test/models/pet.rb +0 -5
  424. data/test/models/pirate.rb +0 -80
  425. data/test/models/polymorphic_design.rb +0 -3
  426. data/test/models/polymorphic_price.rb +0 -3
  427. data/test/models/post.rb +0 -102
  428. data/test/models/price_estimate.rb +0 -3
  429. data/test/models/project.rb +0 -30
  430. data/test/models/reader.rb +0 -4
  431. data/test/models/reference.rb +0 -4
  432. data/test/models/reply.rb +0 -46
  433. data/test/models/ship.rb +0 -19
  434. data/test/models/ship_part.rb +0 -7
  435. data/test/models/sponsor.rb +0 -4
  436. data/test/models/subject.rb +0 -4
  437. data/test/models/subscriber.rb +0 -8
  438. data/test/models/subscription.rb +0 -4
  439. data/test/models/tag.rb +0 -7
  440. data/test/models/tagging.rb +0 -10
  441. data/test/models/task.rb +0 -3
  442. data/test/models/tee.rb +0 -4
  443. data/test/models/tie.rb +0 -4
  444. data/test/models/topic.rb +0 -80
  445. data/test/models/toy.rb +0 -6
  446. data/test/models/treasure.rb +0 -8
  447. data/test/models/vertex.rb +0 -9
  448. data/test/models/warehouse_thing.rb +0 -5
  449. data/test/models/zine.rb +0 -3
  450. data/test/schema/mysql_specific_schema.rb +0 -31
  451. data/test/schema/postgresql_specific_schema.rb +0 -114
  452. data/test/schema/schema.rb +0 -550
  453. data/test/schema/schema2.rb +0 -6
  454. data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,46 +1,20 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'set'
3
-
4
- module MysqlCompat #:nodoc:
5
- # add all_hashes method to standard mysql-c bindings or pure ruby version
6
- def self.define_all_hashes_method!
7
- raise 'Mysql not loaded' unless defined?(::Mysql)
8
-
9
- target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes
10
- return if target.instance_methods.include?('all_hashes') ||
11
- target.instance_methods.include?(:all_hashes)
12
-
13
- # Ruby driver has a version string and returns null values in each_hash
14
- # C driver >= 2.7 returns null values in each_hash
15
- if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
16
- target.class_eval <<-'end_eval'
17
- def all_hashes # def all_hashes
18
- rows = [] # rows = []
19
- each_hash { |row| rows << row } # each_hash { |row| rows << row }
20
- rows # rows
21
- end # end
22
- end_eval
23
-
24
- # adapters before 2.7 don't have a version constant
25
- # and don't return null values in each_hash
26
- else
27
- target.class_eval <<-'end_eval'
28
- def all_hashes # def all_hashes
29
- rows = [] # rows = []
30
- all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
31
- fields[f.name] = nil; fields # fields[f.name] = nil; fields
32
- } # }
33
- each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
34
- rows # rows
35
- end # end
36
- end_eval
37
- end
38
-
39
- unless target.instance_methods.include?('all_hashes') ||
40
- target.instance_methods.include?(:all_hashes)
41
- raise "Failed to defined #{target.name}#all_hashes method. Mysql::VERSION = #{Mysql::VERSION.inspect}"
1
+ require 'active_record/connection_adapters/abstract_mysql_adapter'
2
+ require 'active_record/connection_adapters/statement_pool'
3
+ require 'active_support/core_ext/hash/keys'
4
+
5
+ gem 'mysql', '~> 2.8'
6
+ require 'mysql'
7
+
8
+ class Mysql
9
+ class Time
10
+ ###
11
+ # This monkey patch is for test_additional_columns_from_join_table
12
+ def to_date
13
+ Date.new(year, month, day)
42
14
  end
43
15
  end
16
+ class Stmt; include Enumerable end
17
+ class Result; include Enumerable end
44
18
  end
45
19
 
46
20
  module ActiveRecord
@@ -55,90 +29,17 @@ module ActiveRecord
55
29
  password = config[:password].to_s
56
30
  database = config[:database]
57
31
 
58
- # Require the MySQL driver and define Mysql::Result.all_hashes
59
- unless defined? Mysql
60
- begin
61
- require_library_or_gem('mysql')
62
- rescue LoadError
63
- $stderr.puts '!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.'
64
- raise
65
- end
66
- end
67
-
68
- MysqlCompat.define_all_hashes_method!
69
-
70
32
  mysql = Mysql.init
71
33
  mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
72
34
 
73
35
  default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0
36
+ default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
74
37
  options = [host, username, password, database, port, socket, default_flags]
75
38
  ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
76
39
  end
77
40
  end
78
41
 
79
42
  module ConnectionAdapters
80
- class MysqlColumn < Column #:nodoc:
81
- def extract_default(default)
82
- if sql_type =~ /blob/i || type == :text
83
- if default.blank?
84
- return null ? nil : ''
85
- else
86
- raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
87
- end
88
- elsif missing_default_forged_as_empty_string?(default)
89
- nil
90
- else
91
- super
92
- end
93
- end
94
-
95
- def has_default?
96
- return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
97
- super
98
- end
99
-
100
- private
101
- def simplified_type(field_type)
102
- return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
103
- return :string if field_type =~ /enum/i
104
- super
105
- end
106
-
107
- def extract_limit(sql_type)
108
- case sql_type
109
- when /blob|text/i
110
- case sql_type
111
- when /tiny/i
112
- 255
113
- when /medium/i
114
- 16777215
115
- when /long/i
116
- 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
117
- else
118
- super # we could return 65535 here, but we leave it undecorated by default
119
- end
120
- when /^bigint/i; 8
121
- when /^int/i; 4
122
- when /^mediumint/i; 3
123
- when /^smallint/i; 2
124
- when /^tinyint/i; 1
125
- else
126
- super
127
- end
128
- end
129
-
130
- # MySQL misreports NOT NULL column default when none is given.
131
- # We can't detect this for columns which may have a legitimate ''
132
- # default (string) but we can for others (integer, datetime, boolean,
133
- # and the rest).
134
- #
135
- # Test whether the column has default '', is not null, and is not
136
- # a type allowing default ''.
137
- def missing_default_forged_as_empty_string?(default)
138
- type != :string && !null && default == ''
139
- end
140
- end
141
-
142
43
  # The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
143
44
  # the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
144
45
  #
@@ -158,116 +59,115 @@ module ActiveRecord
158
59
  # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
159
60
  # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
160
61
  #
161
- class MysqlAdapter < AbstractAdapter
162
-
163
- ##
164
- # :singleton-method:
165
- # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
166
- # as boolean. If you wish to disable this emulation (which was the default
167
- # behavior in versions 0.13.1 and earlier) you can add the following line
168
- # to your environment.rb file:
169
- #
170
- # ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
171
- cattr_accessor :emulate_booleans
172
- self.emulate_booleans = true
173
-
174
- ADAPTER_NAME = 'MySQL'.freeze
175
-
176
- LOST_CONNECTION_ERROR_MESSAGES = [
177
- "Server shutdown in progress",
178
- "Broken pipe",
179
- "Lost connection to MySQL server during query",
180
- "MySQL server has gone away" ]
181
-
182
- QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze
183
-
184
- NATIVE_DATABASE_TYPES = {
185
- :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
186
- :string => { :name => "varchar", :limit => 255 },
187
- :text => { :name => "text" },
188
- :integer => { :name => "int", :limit => 4 },
189
- :float => { :name => "float" },
190
- :decimal => { :name => "decimal" },
191
- :datetime => { :name => "datetime" },
192
- :timestamp => { :name => "datetime" },
193
- :time => { :name => "time" },
194
- :date => { :name => "date" },
195
- :binary => { :name => "blob" },
196
- :boolean => { :name => "tinyint", :limit => 1 }
197
- }
62
+ 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
198
76
 
199
- def initialize(connection, logger, connection_options, config)
200
- super(connection, logger)
201
- @connection_options, @config = connection_options, config
202
- @quoted_column_names, @quoted_table_names = {}, {}
203
- connect
204
- end
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
205
81
 
206
- def adapter_name #:nodoc:
207
- ADAPTER_NAME
208
- end
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
209
86
 
210
- def supports_migrations? #:nodoc:
211
- true
212
- end
213
-
214
- def supports_primary_key? #:nodoc:
215
- true
87
+ def adapter
88
+ MysqlAdapter
89
+ end
216
90
  end
217
91
 
218
- def supports_savepoints? #:nodoc:
219
- true
220
- end
92
+ ADAPTER_NAME = 'MySQL'
221
93
 
222
- def native_database_types #:nodoc:
223
- NATIVE_DATABASE_TYPES
224
- end
94
+ class StatementPool < ConnectionAdapters::StatementPool
95
+ def initialize(connection, max = 1000)
96
+ super
97
+ @cache = Hash.new { |h,pid| h[pid] = {} }
98
+ end
225
99
 
100
+ def each(&block); cache.each(&block); end
101
+ def key?(key); cache.key?(key); end
102
+ def [](key); cache[key]; end
103
+ def length; cache.length; end
104
+ def delete(key); cache.delete(key); end
226
105
 
227
- # QUOTING ==================================================
106
+ def []=(sql, key)
107
+ while @max <= cache.size
108
+ cache.shift.last[:stmt].close
109
+ end
110
+ cache[sql] = key
111
+ end
228
112
 
229
- def quote(value, column = nil)
230
- if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
231
- s = column.class.string_to_binary(value).unpack("H*")[0]
232
- "x'#{s}'"
233
- elsif value.kind_of?(BigDecimal)
234
- value.to_s("F")
235
- else
236
- super
113
+ def clear
114
+ cache.values.each do |hash|
115
+ hash[:stmt].close
116
+ end
117
+ cache.clear
118
+ end
119
+
120
+ private
121
+ def cache
122
+ @cache[$$]
237
123
  end
238
124
  end
239
125
 
240
- def quote_column_name(name) #:nodoc:
241
- @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
126
+ def initialize(connection, logger, connection_options, config)
127
+ super
128
+ @statements = StatementPool.new(@connection,
129
+ config.fetch(:statement_limit) { 1000 })
130
+ @client_encoding = nil
131
+ connect
242
132
  end
243
133
 
244
- def quote_table_name(name) #:nodoc:
245
- @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
134
+ # Returns true, since this connection adapter supports prepared statement
135
+ # caching.
136
+ def supports_statement_cache?
137
+ true
246
138
  end
247
139
 
248
- def quote_string(string) #:nodoc:
249
- @connection.quote(string)
140
+ # HELPER METHODS ===========================================
141
+
142
+ def each_hash(result) # :nodoc:
143
+ if block_given?
144
+ result.each_hash do |row|
145
+ row.symbolize_keys!
146
+ yield row
147
+ end
148
+ else
149
+ to_enum(:each_hash, result)
150
+ end
250
151
  end
251
152
 
252
- def quoted_true
253
- QUOTED_TRUE
153
+ def new_column(field, default, type, null, collation) # :nodoc:
154
+ Column.new(field, default, type, null, collation)
254
155
  end
255
156
 
256
- def quoted_false
257
- QUOTED_FALSE
157
+ def error_number(exception) # :nodoc:
158
+ exception.errno if exception.respond_to?(:errno)
258
159
  end
259
160
 
260
- # REFERENTIAL INTEGRITY ====================================
161
+ # QUOTING ==================================================
261
162
 
262
- def disable_referential_integrity(&block) #:nodoc:
263
- old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
163
+ def type_cast(value, column)
164
+ return super unless value == true || value == false
264
165
 
265
- begin
266
- update("SET FOREIGN_KEY_CHECKS = 0")
267
- yield
268
- ensure
269
- update("SET FOREIGN_KEY_CHECKS = #{old}")
270
- end
166
+ value ? 1 : 0
167
+ end
168
+
169
+ def quote_string(string) #:nodoc:
170
+ @connection.quote(string)
271
171
  end
272
172
 
273
173
  # CONNECTION MANAGEMENT ====================================
@@ -291,9 +191,12 @@ module ActiveRecord
291
191
 
292
192
  def reconnect!
293
193
  disconnect!
194
+ clear_cache!
294
195
  connect
295
196
  end
296
197
 
198
+ # Disconnects from the database if already connected. Otherwise, this
199
+ # method does nothing.
297
200
  def disconnect!
298
201
  @connection.close rescue nil
299
202
  end
@@ -311,352 +214,228 @@ module ActiveRecord
311
214
 
312
215
  def select_rows(sql, name = nil)
313
216
  @connection.query_with_result = true
314
- result = execute(sql, name)
315
- rows = []
316
- result.each { |row| rows << row }
317
- result.free
217
+ rows = exec_query(sql, name).rows
318
218
  @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
319
219
  rows
320
220
  end
321
221
 
322
- # Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it.
323
- def execute(sql, name = nil) #:nodoc:
324
- log(sql, name) { @connection.query(sql) }
325
- rescue ActiveRecord::StatementInvalid => exception
326
- if exception.message.split(":").first =~ /Packets out of order/
327
- raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
222
+ # Clears the prepared statements cache.
223
+ def clear_cache!
224
+ @statements.clear
225
+ end
226
+
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
273
+
274
+ # Get the client encoding for this database
275
+ def client_encoding
276
+ return @client_encoding if @client_encoding
277
+
278
+ result = exec_query(
279
+ "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
280
+ 'SCHEMA')
281
+ @client_encoding = ENCODINGS[result.rows.last.last]
282
+ end
283
+
284
+ 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?
290
+ result_set, affected_rows = exec_without_stmt(sql, name)
328
291
  else
329
- raise
292
+ result_set, affected_rows = exec_stmt(sql, name, binds)
330
293
  end
331
- end
332
294
 
333
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
334
- super sql, name
335
- id_value || @connection.insert_id
336
- end
337
-
338
- def update_sql(sql, name = nil) #:nodoc:
339
- super
340
- @connection.affected_rows
341
- end
342
-
343
- def begin_db_transaction #:nodoc:
344
- execute "BEGIN"
345
- rescue Exception
346
- # Transactions aren't supported
347
- end
348
-
349
- def commit_db_transaction #:nodoc:
350
- execute "COMMIT"
351
- rescue Exception
352
- # Transactions aren't supported
353
- end
354
-
355
- def rollback_db_transaction #:nodoc:
356
- execute "ROLLBACK"
357
- rescue Exception
358
- # Transactions aren't supported
359
- end
295
+ yield affected_rows if block_given?
360
296
 
361
- def create_savepoint
362
- execute("SAVEPOINT #{current_savepoint_name}")
297
+ result_set
363
298
  end
364
299
 
365
- def rollback_to_savepoint
366
- execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
300
+ def last_inserted_id(result)
301
+ @connection.insert_id
367
302
  end
368
303
 
369
- def release_savepoint
370
- execute("RELEASE SAVEPOINT #{current_savepoint_name}")
371
- end
304
+ def exec_without_stmt(sql, name = 'SQL') # :nodoc:
305
+ # Some queries, like SHOW CREATE TABLE don't work through the prepared
306
+ # statement API. For those queries, we need to use this method. :'(
307
+ log(sql, name) do
308
+ result = @connection.query(sql)
309
+ affected_rows = @connection.affected_rows
372
310
 
373
- def add_limit_offset!(sql, options) #:nodoc:
374
- if limit = options[:limit]
375
- limit = sanitize_limit(limit)
376
- unless offset = options[:offset]
377
- sql << " LIMIT #{limit}"
311
+ if result
312
+ cols = result.fetch_fields.map { |field| field.name }
313
+ result_set = ActiveRecord::Result.new(cols, result.to_a)
314
+ result.free
378
315
  else
379
- sql << " LIMIT #{offset.to_i}, #{limit}"
316
+ result_set = ActiveRecord::Result.new([], [])
380
317
  end
381
- end
382
- end
383
318
 
384
-
385
- # SCHEMA STATEMENTS ========================================
386
-
387
- def structure_dump #:nodoc:
388
- if supports_views?
389
- sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
390
- else
391
- sql = "SHOW TABLES"
392
- end
393
-
394
- select_all(sql).inject("") do |structure, table|
395
- table.delete('Table_type')
396
- structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
319
+ [result_set, affected_rows]
397
320
  end
398
321
  end
399
322
 
400
- def recreate_database(name, options = {}) #:nodoc:
401
- drop_database(name)
402
- create_database(name, options)
403
- end
404
-
405
- # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
406
- # Charset defaults to utf8.
407
- #
408
- # Example:
409
- # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
410
- # create_database 'matt_development'
411
- # create_database 'matt_development', :charset => :big5
412
- def create_database(name, options = {})
413
- if options[:collation]
414
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
415
- else
416
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
417
- end
323
+ def execute_and_free(sql, name = nil)
324
+ result = execute(sql, name)
325
+ ret = yield result
326
+ result.free
327
+ ret
418
328
  end
419
329
 
420
- def drop_database(name) #:nodoc:
421
- execute "DROP DATABASE IF EXISTS `#{name}`"
330
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
331
+ super sql, name
332
+ id_value || @connection.insert_id
422
333
  end
334
+ alias :create :insert_sql
423
335
 
424
- def current_database
425
- select_value 'SELECT DATABASE() as db'
426
- end
336
+ def exec_delete(sql, name, binds)
337
+ affected_rows = 0
427
338
 
428
- # Returns the database character set.
429
- def charset
430
- show_variable 'character_set_database'
431
- end
339
+ exec_query(sql, name, binds) do |n|
340
+ affected_rows = n
341
+ end
432
342
 
433
- # Returns the database collation strategy.
434
- def collation
435
- show_variable 'collation_database'
343
+ affected_rows
436
344
  end
345
+ alias :exec_update :exec_delete
437
346
 
438
- def tables(name = nil) #:nodoc:
439
- tables = []
440
- result = execute("SHOW TABLES", name)
441
- result.each { |field| tables << field[0] }
442
- result.free
443
- tables
347
+ def begin_db_transaction #:nodoc:
348
+ exec_query "BEGIN"
349
+ rescue Mysql::Error
350
+ # Transactions aren't supported
444
351
  end
445
352
 
446
- def drop_table(table_name, options = {})
447
- super(table_name, options)
448
- end
353
+ private
449
354
 
450
- def indexes(table_name, name = nil)#:nodoc:
451
- indexes = []
452
- current_index = nil
453
- result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
454
- result.each do |row|
455
- if current_index != row[2]
456
- next if row[2] == "PRIMARY" # skip the primary key
457
- current_index = row[2]
458
- indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], [])
355
+ def exec_stmt(sql, name, binds)
356
+ cache = {}
357
+ log(sql, name, binds) do
358
+ if binds.empty?
359
+ stmt = @connection.prepare(sql)
360
+ else
361
+ cache = @statements[sql] ||= {
362
+ :stmt => @connection.prepare(sql)
363
+ }
364
+ stmt = cache[:stmt]
459
365
  end
460
366
 
461
- indexes.last.columns << row[4]
462
- indexes.last.lengths << row[7]
463
- end
464
- result.free
465
- indexes
466
- end
467
-
468
- def columns(table_name, name = nil)#:nodoc:
469
- sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
470
- columns = []
471
- result = execute(sql, name)
472
- result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
473
- result.free
474
- columns
475
- end
476
-
477
- def create_table(table_name, options = {}) #:nodoc:
478
- super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
479
- end
480
-
481
- def rename_table(table_name, new_name)
482
- execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
483
- end
367
+ begin
368
+ stmt.execute(*binds.map { |col, val| type_cast(val, col) })
369
+ rescue Mysql::Error => e
370
+ # Older versions of MySQL leave the prepared statement in a bad
371
+ # place when an error occurs. To support older mysql versions, we
372
+ # need to close the statement and delete the statement from the
373
+ # cache.
374
+ stmt.close
375
+ @statements.delete sql
376
+ raise e
377
+ end
484
378
 
485
- def add_column(table_name, column_name, type, options = {})
486
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
487
- add_column_options!(add_column_sql, options)
488
- add_column_position!(add_column_sql, options)
489
- execute(add_column_sql)
490
- end
379
+ cols = nil
380
+ if metadata = stmt.result_metadata
381
+ cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
382
+ field.name
383
+ }
384
+ end
491
385
 
492
- def change_column_default(table_name, column_name, default) #:nodoc:
493
- column = column_for(table_name, column_name)
494
- change_column table_name, column_name, column.sql_type, :default => default
495
- end
386
+ result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
387
+ affected_rows = stmt.affected_rows
496
388
 
497
- def change_column_null(table_name, column_name, null, default = nil)
498
- column = column_for(table_name, column_name)
389
+ stmt.result_metadata.free if cols
390
+ stmt.free_result
391
+ stmt.close if binds.empty?
499
392
 
500
- unless null || default.nil?
501
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
393
+ [result_set, affected_rows]
502
394
  end
503
-
504
- change_column table_name, column_name, column.sql_type, :null => null
505
395
  end
506
396
 
507
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
508
- column = column_for(table_name, column_name)
509
-
510
- unless options_include_default?(options)
511
- options[:default] = column.default
397
+ def connect
398
+ encoding = @config[:encoding]
399
+ if encoding
400
+ @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
512
401
  end
513
402
 
514
- unless options.has_key?(:null)
515
- options[:null] = column.null
403
+ if @config[:sslca] || @config[:sslkey]
404
+ @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
516
405
  end
517
406
 
518
- change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
519
- add_column_options!(change_column_sql, options)
520
- add_column_position!(change_column_sql, options)
521
- execute(change_column_sql)
522
- end
407
+ @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
408
+ @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
409
+ @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
523
410
 
524
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
525
- options = {}
526
- if column = columns(table_name).find { |c| c.name == column_name.to_s }
527
- options[:default] = column.default
528
- options[:null] = column.null
529
- else
530
- raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
531
- end
532
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
533
- rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
534
- add_column_options!(rename_column_sql, options)
535
- execute(rename_column_sql)
536
- end
411
+ @connection.real_connect(*@connection_options)
537
412
 
538
- # Maps logical Rails types to MySQL-specific data types.
539
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
540
- return super unless type.to_s == 'integer'
541
-
542
- case limit
543
- when 1; 'tinyint'
544
- when 2; 'smallint'
545
- when 3; 'mediumint'
546
- when nil, 4, 11; 'int(11)' # compatibility with MySQL default
547
- when 5..8; 'bigint'
548
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
549
- end
550
- end
413
+ # reconnect must be set after real_connect is called, because real_connect sets it to false internally
414
+ @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
551
415
 
552
- def add_column_position!(sql, options)
553
- if options[:first]
554
- sql << " FIRST"
555
- elsif options[:after]
556
- sql << " AFTER #{quote_column_name(options[:after])}"
557
- end
558
- end
559
-
560
- # SHOW VARIABLES LIKE 'name'
561
- def show_variable(name)
562
- variables = select_all("SHOW VARIABLES LIKE '#{name}'")
563
- variables.first['Value'] unless variables.empty?
416
+ configure_connection
564
417
  end
565
418
 
566
- # Returns a table's primary key and belonging sequence.
567
- def pk_and_sequence_for(table) #:nodoc:
568
- keys = []
569
- result = execute("describe #{quote_table_name(table)}")
570
- result.each_hash do |h|
571
- keys << h["Field"]if h["Key"] == "PRI"
572
- end
573
- result.free
574
- keys.length == 1 ? [keys.first, nil] : nil
575
- end
419
+ def configure_connection
420
+ encoding = @config[:encoding]
421
+ execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
576
422
 
577
- # Returns just a table's primary key
578
- def primary_key(table)
579
- pk_and_sequence = pk_and_sequence_for(table)
580
- pk_and_sequence && pk_and_sequence.first
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)
581
426
  end
582
427
 
583
- def case_sensitive_equality_operator
584
- "= BINARY"
428
+ def select(sql, name = nil, binds = [])
429
+ @connection.query_with_result = true
430
+ rows = exec_query(sql, name, binds).to_a
431
+ @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
432
+ rows
585
433
  end
586
434
 
587
- def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
588
- where_sql
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 }
589
438
  end
590
-
591
- protected
592
- def quoted_columns_for_index(column_names, options = {})
593
- length = options[:length] if options.is_a?(Hash)
594
-
595
- quoted_column_names = case length
596
- when Hash
597
- column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
598
- when Fixnum
599
- column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
600
- else
601
- column_names.map {|name| quote_column_name(name) }
602
- end
603
- end
604
-
605
- private
606
- def connect
607
- encoding = @config[:encoding]
608
- if encoding
609
- @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
610
- end
611
-
612
- if @config[:sslca] || @config[:sslkey]
613
- @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
614
- end
615
-
616
- @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
617
- @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
618
- @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
619
-
620
- @connection.real_connect(*@connection_options)
621
-
622
- # reconnect must be set after real_connect is called, because real_connect sets it to false internally
623
- @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
624
-
625
- configure_connection
626
- end
627
-
628
- def configure_connection
629
- encoding = @config[:encoding]
630
- execute("SET NAMES '#{encoding}'") if encoding
631
-
632
- # By default, MySQL 'where id is null' selects the last inserted id.
633
- # Turn this off. http://dev.rubyonrails.org/ticket/6778
634
- execute("SET SQL_AUTO_IS_NULL=0")
635
- end
636
-
637
- def select(sql, name = nil)
638
- @connection.query_with_result = true
639
- result = execute(sql, name)
640
- rows = result.all_hashes
641
- result.free
642
- @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
643
- rows
644
- end
645
-
646
- def supports_views?
647
- version[0] >= 5
648
- end
649
-
650
- def version
651
- @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
652
- end
653
-
654
- def column_for(table_name, column_name)
655
- unless column = columns(table_name).find { |c| c.name == column_name.to_s }
656
- raise "No such column: #{table_name}.#{column_name}"
657
- end
658
- column
659
- end
660
439
  end
661
440
  end
662
441
  end