activerecord 2.0.5 → 2.1.0

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 (289) hide show
  1. data/CHANGELOG +168 -6
  2. data/README +27 -22
  3. data/RUNNING_UNIT_TESTS +7 -4
  4. data/Rakefile +22 -25
  5. data/lib/active_record.rb +8 -2
  6. data/lib/active_record/aggregations.rb +21 -12
  7. data/lib/active_record/association_preload.rb +277 -0
  8. data/lib/active_record/associations.rb +481 -295
  9. data/lib/active_record/associations/association_collection.rb +162 -37
  10. data/lib/active_record/associations/association_proxy.rb +71 -7
  11. data/lib/active_record/associations/belongs_to_association.rb +5 -3
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
  14. data/lib/active_record/associations/has_many_association.rb +8 -73
  15. data/lib/active_record/associations/has_many_through_association.rb +68 -117
  16. data/lib/active_record/associations/has_one_association.rb +7 -5
  17. data/lib/active_record/associations/has_one_through_association.rb +28 -0
  18. data/lib/active_record/attribute_methods.rb +69 -19
  19. data/lib/active_record/base.rb +496 -275
  20. data/lib/active_record/calculations.rb +28 -21
  21. data/lib/active_record/callbacks.rb +9 -38
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  31. data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
  32. data/lib/active_record/dirty.rb +158 -0
  33. data/lib/active_record/fixtures.rb +121 -156
  34. data/lib/active_record/locking/optimistic.rb +14 -11
  35. data/lib/active_record/locking/pessimistic.rb +2 -2
  36. data/lib/active_record/migration.rb +157 -77
  37. data/lib/active_record/named_scope.rb +163 -0
  38. data/lib/active_record/observer.rb +19 -5
  39. data/lib/active_record/reflection.rb +34 -14
  40. data/lib/active_record/schema.rb +7 -14
  41. data/lib/active_record/schema_dumper.rb +4 -4
  42. data/lib/active_record/serialization.rb +5 -5
  43. data/lib/active_record/serializers/json_serializer.rb +37 -28
  44. data/lib/active_record/serializers/xml_serializer.rb +52 -29
  45. data/lib/active_record/test_case.rb +36 -0
  46. data/lib/active_record/timestamp.rb +4 -4
  47. data/lib/active_record/transactions.rb +3 -3
  48. data/lib/active_record/validations.rb +182 -248
  49. data/lib/active_record/version.rb +2 -2
  50. data/test/{fixtures → assets}/example.log +0 -0
  51. data/test/{fixtures → assets}/flowers.jpg +0 -0
  52. data/test/cases/aaa_create_tables_test.rb +24 -0
  53. data/test/cases/active_schema_test_mysql.rb +95 -0
  54. data/test/cases/active_schema_test_postgresql.rb +24 -0
  55. data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
  56. data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
  57. data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
  58. data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
  59. data/test/cases/associations/belongs_to_associations_test.rb +412 -0
  60. data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
  61. data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
  62. data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
  63. data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
  64. data/test/{associations → cases/associations}/eager_test.rb +216 -51
  65. data/test/{associations → cases/associations}/extension_test.rb +8 -8
  66. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
  67. data/test/cases/associations/has_many_associations_test.rb +932 -0
  68. data/test/cases/associations/has_many_through_associations_test.rb +190 -0
  69. data/test/cases/associations/has_one_associations_test.rb +323 -0
  70. data/test/cases/associations/has_one_through_associations_test.rb +74 -0
  71. data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
  72. data/test/{associations → cases/associations}/join_model_test.rb +175 -35
  73. data/test/cases/associations_test.rb +262 -0
  74. data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
  75. data/test/{base_test.rb → cases/base_test.rb} +338 -191
  76. data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
  77. data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
  78. data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
  79. data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
  80. data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
  81. data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
  82. data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
  83. data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
  84. data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
  85. data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
  86. data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
  87. data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
  88. data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
  89. data/test/cases/dirty_test.rb +163 -0
  90. data/test/cases/finder_respond_to_test.rb +76 -0
  91. data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
  92. data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
  93. data/test/cases/helper.rb +47 -0
  94. data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
  95. data/test/cases/invalid_date_test.rb +24 -0
  96. data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
  97. data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
  98. data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
  99. data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
  100. data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
  101. data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
  102. data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
  103. data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
  104. data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
  105. data/test/cases/named_scope_test.rb +157 -0
  106. data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
  107. data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
  108. data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
  109. data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
  110. data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
  111. data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
  112. data/test/cases/schema_dumper_test.rb +138 -0
  113. data/test/cases/schema_test_postgresql.rb +102 -0
  114. data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
  115. data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
  116. data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
  117. data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
  118. data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
  119. data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
  120. data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
  121. data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
  122. data/test/config.rb +5 -0
  123. data/test/connections/native_db2/connection.rb +1 -1
  124. data/test/connections/native_firebird/connection.rb +1 -1
  125. data/test/connections/native_frontbase/connection.rb +1 -1
  126. data/test/connections/native_mysql/connection.rb +1 -1
  127. data/test/connections/native_openbase/connection.rb +1 -1
  128. data/test/connections/native_oracle/connection.rb +1 -1
  129. data/test/connections/native_postgresql/connection.rb +1 -3
  130. data/test/connections/native_sqlite/connection.rb +2 -2
  131. data/test/connections/native_sqlite3/connection.rb +2 -2
  132. data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
  133. data/test/connections/native_sybase/connection.rb +1 -1
  134. data/test/fixtures/author_addresses.yml +5 -0
  135. data/test/fixtures/authors.yml +2 -0
  136. data/test/fixtures/clubs.yml +6 -0
  137. data/test/fixtures/jobs.yml +7 -0
  138. data/test/fixtures/members.yml +4 -0
  139. data/test/fixtures/memberships.yml +20 -0
  140. data/test/fixtures/owners.yml +7 -0
  141. data/test/fixtures/people.yml +4 -1
  142. data/test/fixtures/pets.yml +14 -0
  143. data/test/fixtures/posts.yml +1 -0
  144. data/test/fixtures/price_estimates.yml +7 -0
  145. data/test/fixtures/readers.yml +5 -0
  146. data/test/fixtures/references.yml +17 -0
  147. data/test/fixtures/sponsors.yml +9 -0
  148. data/test/fixtures/subscribers.yml +7 -0
  149. data/test/fixtures/subscriptions.yml +12 -0
  150. data/test/fixtures/taggings.yml +4 -1
  151. data/test/fixtures/topics.yml +22 -2
  152. data/test/fixtures/warehouse-things.yml +3 -0
  153. data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
  154. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
  155. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
  156. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
  157. data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
  158. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  159. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  160. data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
  161. data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
  162. data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
  163. data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
  164. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  165. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  166. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
  167. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  168. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
  169. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  170. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  171. data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
  172. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  173. data/test/{fixtures → models}/author.rb +28 -4
  174. data/test/{fixtures → models}/auto_id.rb +0 -0
  175. data/test/{fixtures → models}/binary.rb +0 -0
  176. data/test/{fixtures → models}/book.rb +0 -0
  177. data/test/{fixtures → models}/categorization.rb +0 -0
  178. data/test/{fixtures → models}/category.rb +8 -5
  179. data/test/{fixtures → models}/citation.rb +0 -0
  180. data/test/models/club.rb +7 -0
  181. data/test/{fixtures → models}/column_name.rb +0 -0
  182. data/test/{fixtures → models}/comment.rb +5 -3
  183. data/test/{fixtures → models}/company.rb +15 -6
  184. data/test/{fixtures → models}/company_in_module.rb +5 -3
  185. data/test/{fixtures → models}/computer.rb +0 -1
  186. data/test/{fixtures → models}/contact.rb +1 -1
  187. data/test/{fixtures → models}/course.rb +0 -0
  188. data/test/{fixtures → models}/customer.rb +8 -8
  189. data/test/{fixtures → models}/default.rb +0 -0
  190. data/test/{fixtures → models}/developer.rb +14 -10
  191. data/test/{fixtures → models}/edge.rb +0 -0
  192. data/test/{fixtures → models}/entrant.rb +0 -0
  193. data/test/models/guid.rb +2 -0
  194. data/test/{fixtures → models}/item.rb +0 -0
  195. data/test/models/job.rb +5 -0
  196. data/test/{fixtures → models}/joke.rb +0 -0
  197. data/test/{fixtures → models}/keyboard.rb +0 -0
  198. data/test/{fixtures → models}/legacy_thing.rb +0 -0
  199. data/test/{fixtures → models}/matey.rb +0 -0
  200. data/test/models/member.rb +9 -0
  201. data/test/models/membership.rb +9 -0
  202. data/test/{fixtures → models}/minimalistic.rb +0 -0
  203. data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
  204. data/test/{fixtures → models}/movie.rb +0 -0
  205. data/test/{fixtures → models}/order.rb +2 -2
  206. data/test/models/owner.rb +4 -0
  207. data/test/{fixtures → models}/parrot.rb +0 -0
  208. data/test/models/person.rb +10 -0
  209. data/test/models/pet.rb +4 -0
  210. data/test/models/pirate.rb +9 -0
  211. data/test/{fixtures → models}/post.rb +23 -2
  212. data/test/models/price_estimate.rb +3 -0
  213. data/test/{fixtures → models}/project.rb +1 -0
  214. data/test/{fixtures → models}/reader.rb +0 -0
  215. data/test/models/reference.rb +4 -0
  216. data/test/{fixtures → models}/reply.rb +7 -5
  217. data/test/{fixtures → models}/ship.rb +0 -0
  218. data/test/models/sponsor.rb +4 -0
  219. data/test/{fixtures → models}/subject.rb +0 -0
  220. data/test/{fixtures → models}/subscriber.rb +2 -0
  221. data/test/models/subscription.rb +4 -0
  222. data/test/{fixtures → models}/tag.rb +0 -0
  223. data/test/{fixtures → models}/tagging.rb +0 -0
  224. data/test/{fixtures → models}/task.rb +0 -0
  225. data/test/{fixtures → models}/topic.rb +32 -4
  226. data/test/{fixtures → models}/treasure.rb +2 -0
  227. data/test/{fixtures → models}/vertex.rb +0 -0
  228. data/test/models/warehouse_thing.rb +5 -0
  229. data/test/schema/mysql_specific_schema.rb +12 -0
  230. data/test/schema/postgresql_specific_schema.rb +103 -0
  231. data/test/schema/schema.rb +421 -0
  232. data/test/schema/schema2.rb +6 -0
  233. data/test/schema/sqlite_specific_schema.rb +25 -0
  234. data/test/schema/sqlserver_specific_schema.rb +5 -0
  235. metadata +192 -176
  236. data/test/aaa_create_tables_test.rb +0 -72
  237. data/test/abstract_unit.rb +0 -84
  238. data/test/active_schema_test_mysql.rb +0 -46
  239. data/test/all.sh +0 -8
  240. data/test/association_inheritance_reload.rb +0 -14
  241. data/test/associations_test.rb +0 -2177
  242. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
  243. data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
  244. data/test/fixtures/bad_fixtures/blank_line +0 -3
  245. data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
  246. data/test/fixtures/bad_fixtures/missing_value +0 -1
  247. data/test/fixtures/db_definitions/db2.drop.sql +0 -33
  248. data/test/fixtures/db_definitions/db2.sql +0 -235
  249. data/test/fixtures/db_definitions/db22.drop.sql +0 -2
  250. data/test/fixtures/db_definitions/db22.sql +0 -5
  251. data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
  252. data/test/fixtures/db_definitions/firebird.sql +0 -310
  253. data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
  254. data/test/fixtures/db_definitions/firebird2.sql +0 -6
  255. data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
  256. data/test/fixtures/db_definitions/frontbase.sql +0 -273
  257. data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
  258. data/test/fixtures/db_definitions/frontbase2.sql +0 -4
  259. data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
  260. data/test/fixtures/db_definitions/openbase.sql +0 -318
  261. data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
  262. data/test/fixtures/db_definitions/openbase2.sql +0 -7
  263. data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
  264. data/test/fixtures/db_definitions/oracle.sql +0 -330
  265. data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
  266. data/test/fixtures/db_definitions/oracle2.sql +0 -6
  267. data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
  268. data/test/fixtures/db_definitions/postgresql.sql +0 -292
  269. data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
  270. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  271. data/test/fixtures/db_definitions/schema.rb +0 -354
  272. data/test/fixtures/db_definitions/schema2.rb +0 -11
  273. data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
  274. data/test/fixtures/db_definitions/sqlite.sql +0 -219
  275. data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
  276. data/test/fixtures/db_definitions/sqlite2.sql +0 -5
  277. data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
  278. data/test/fixtures/db_definitions/sybase.sql +0 -222
  279. data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
  280. data/test/fixtures/db_definitions/sybase2.sql +0 -5
  281. data/test/fixtures/developers_projects/david_action_controller +0 -3
  282. data/test/fixtures/developers_projects/david_active_record +0 -3
  283. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  284. data/test/fixtures/person.rb +0 -4
  285. data/test/fixtures/pirate.rb +0 -5
  286. data/test/fixtures/subscribers/first +0 -2
  287. data/test/fixtures/subscribers/second +0 -2
  288. data/test/schema_dumper_test.rb +0 -131
  289. data/test/schema_test_postgresql.rb +0 -64
@@ -13,13 +13,17 @@ module ActiveRecord
13
13
  255
14
14
  end
15
15
 
16
- # Truncates a table alias according to the limits of the current adapter.
16
+ # Truncates a table alias according to the limits of the current adapter.
17
17
  def table_alias_for(table_name)
18
18
  table_name[0..table_alias_length-1].gsub(/\./, '_')
19
19
  end
20
20
 
21
21
  # def tables(name = nil) end
22
22
 
23
+ def table_exists?(table_name)
24
+ tables.include?(table_name.to_s)
25
+ end
26
+
23
27
  # Returns an array of indexes for the given table.
24
28
  # def indexes(table_name, name = nil) end
25
29
 
@@ -28,7 +32,7 @@ module ActiveRecord
28
32
  def columns(table_name, name = nil) end
29
33
 
30
34
  # Creates a new table
31
- # There are two ways to work with #create_table. You can use the block
35
+ # There are two ways to work with +create_table+. You can use the block
32
36
  # form or the regular form, like this:
33
37
  #
34
38
  # === Block form
@@ -45,7 +49,7 @@ module ActiveRecord
45
49
  # The +options+ hash can include the following keys:
46
50
  # [<tt>:id</tt>]
47
51
  # Whether to automatically add a primary key column. Defaults to true.
48
- # Join tables for has_and_belongs_to_many should set :id => false.
52
+ # Join tables for +has_and_belongs_to_many+ should set <tt>:id => false</tt>.
49
53
  # [<tt>:primary_key</tt>]
50
54
  # The name of the primary key, if one is to be added automatically.
51
55
  # Defaults to +id+.
@@ -93,8 +97,8 @@ module ActiveRecord
93
97
 
94
98
  yield table_definition
95
99
 
96
- if options[:force]
97
- drop_table(table_name, options) rescue nil
100
+ if options[:force] && table_exists?(table_name)
101
+ drop_table(table_name, options)
98
102
  end
99
103
 
100
104
  create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
@@ -104,6 +108,67 @@ module ActiveRecord
104
108
  execute create_sql
105
109
  end
106
110
 
111
+ # A block for changing columns in +table+.
112
+ #
113
+ # === Example
114
+ # # change_table() yields a Table instance
115
+ # change_table(:suppliers) do |t|
116
+ # t.column :name, :string, :limit => 60
117
+ # # Other column alterations here
118
+ # end
119
+ #
120
+ # ===== Examples
121
+ # ====== Add a column
122
+ # change_table(:suppliers) do |t|
123
+ # t.column :name, :string, :limit => 60
124
+ # end
125
+ #
126
+ # ====== Add 2 integer columns
127
+ # change_table(:suppliers) do |t|
128
+ # t.integer :width, :height, :null => false, :default => 0
129
+ # end
130
+ #
131
+ # ====== Add created_at/updated_at columns
132
+ # change_table(:suppliers) do |t|
133
+ # t.timestamps
134
+ # end
135
+ #
136
+ # ====== Add a foreign key column
137
+ # change_table(:suppliers) do |t|
138
+ # t.references :company
139
+ # end
140
+ #
141
+ # Creates a <tt>company_id(integer)</tt> column
142
+ #
143
+ # ====== Add a polymorphic foreign key column
144
+ # change_table(:suppliers) do |t|
145
+ # t.belongs_to :company, :polymorphic => true
146
+ # end
147
+ #
148
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
149
+ #
150
+ # ====== Remove a column
151
+ # change_table(:suppliers) do |t|
152
+ # t.remove :company
153
+ # end
154
+ #
155
+ # ====== Remove several columns
156
+ # change_table(:suppliers) do |t|
157
+ # t.remove :company_id
158
+ # t.remove :width, :height
159
+ # end
160
+ #
161
+ # ====== Remove an index
162
+ # change_table(:suppliers) do |t|
163
+ # t.remove_index :company_id
164
+ # end
165
+ #
166
+ # See also Table for details on
167
+ # all of the various column transformation
168
+ def change_table(table_name)
169
+ yield Table.new(table_name, self)
170
+ end
171
+
107
172
  # Renames a table.
108
173
  # ===== Example
109
174
  # rename_table('octopuses', 'octopi')
@@ -124,12 +189,16 @@ module ActiveRecord
124
189
  execute(add_column_sql)
125
190
  end
126
191
 
127
- # Removes the column from the table definition.
192
+ # Removes the column(s) from the table definition.
128
193
  # ===== Examples
129
194
  # remove_column(:suppliers, :qualification)
130
- def remove_column(table_name, column_name)
131
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
195
+ # remove_columns(:suppliers, :qualification, :experience)
196
+ def remove_column(table_name, *column_names)
197
+ column_names.flatten.each do |column_name|
198
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
199
+ end
132
200
  end
201
+ alias :remove_columns :remove_column
133
202
 
134
203
  # Changes the column's definition according to the new options.
135
204
  # See TableDefinition#column for details of the options you can use.
@@ -161,12 +230,12 @@ module ActiveRecord
161
230
  # an Array of Symbols.
162
231
  #
163
232
  # The index will be named after the table and the first column name,
164
- # unless you pass +:name+ as an option.
233
+ # unless you pass <tt>:name</tt> as an option.
165
234
  #
166
235
  # When creating an index on multiple columns, the first column is used as a name
167
236
  # for the index. For example, when you specify an index on two columns
168
- # [+:first+, +:last+], the DBMS creates an index for both columns as well as an
169
- # index for the first column +:first+. Using just the first name for this index
237
+ # [<tt>:first</tt>, <tt>:last</tt>], the DBMS creates an index for both columns as well as an
238
+ # index for the first column <tt>:first</tt>. Using just the first name for this index
170
239
  # makes sense, because you will never have to create a singular index with this
171
240
  # name.
172
241
  #
@@ -230,27 +299,49 @@ module ActiveRecord
230
299
  def structure_dump
231
300
  end
232
301
 
302
+ def dump_schema_information #:nodoc:
303
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
304
+ migrated = select_values("SELECT version FROM #{sm_table}")
305
+ migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
306
+ end
307
+
233
308
  # Should not be called normally, but this operation is non-destructive.
234
309
  # The migrations module handles this automatically.
235
- def initialize_schema_information
236
- begin
237
- execute "CREATE TABLE #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version #{type_to_sql(:integer)})"
238
- execute "INSERT INTO #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version) VALUES(0)"
239
- rescue ActiveRecord::StatementInvalid
240
- # Schema has been initialized
241
- end
242
- end
310
+ def initialize_schema_migrations_table
311
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
243
312
 
244
- def dump_schema_information #:nodoc:
245
- begin
246
- if (current_schema = ActiveRecord::Migrator.current_version) > 0
247
- return "INSERT INTO #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version) VALUES (#{current_schema})"
313
+ unless tables.detect { |t| t == sm_table }
314
+ create_table(sm_table, :id => false) do |schema_migrations_table|
315
+ schema_migrations_table.column :version, :string, :null => false
316
+ end
317
+ add_index sm_table, :version, :unique => true,
318
+ :name => 'unique_schema_migrations'
319
+
320
+ # Backwards-compatibility: if we find schema_info, assume we've
321
+ # migrated up to that point:
322
+ si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
323
+
324
+ if tables.detect { |t| t == si_table }
325
+
326
+ old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
327
+ assume_migrated_upto_version(old_version)
328
+ drop_table(si_table)
248
329
  end
249
- rescue ActiveRecord::StatementInvalid
250
- # No Schema Info
251
330
  end
252
331
  end
253
332
 
333
+ def assume_migrated_upto_version(version)
334
+ sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
335
+ migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
336
+ versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename|
337
+ filename.split('/').last.split('_').first.to_i
338
+ end
339
+
340
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" unless migrated.include?(version.to_i)
341
+ (versions - migrated).select { |v| v < version.to_i }.each do |v|
342
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
343
+ end
344
+ end
254
345
 
255
346
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
256
347
  if native = native_database_types[type]
@@ -281,7 +372,14 @@ module ActiveRecord
281
372
 
282
373
  def add_column_options!(sql, options) #:nodoc:
283
374
  sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
284
- sql << " NOT NULL" if options[:null] == false
375
+ # must explcitly check for :null to allow change_column to work on migrations
376
+ if options.has_key? :null
377
+ if options[:null] == false
378
+ sql << " NOT NULL"
379
+ else
380
+ sql << " NULL"
381
+ end
382
+ end
285
383
  end
286
384
 
287
385
  # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
@@ -291,13 +389,29 @@ module ActiveRecord
291
389
  def distinct(columns, order_by)
292
390
  "DISTINCT #{columns}"
293
391
  end
294
-
392
+
295
393
  # ORDER BY clause for the passed order option.
296
394
  # PostgreSQL overrides this due to its stricter standards compliance.
297
395
  def add_order_by_for_association_limiting!(sql, options)
298
396
  sql << " ORDER BY #{options[:order]}"
299
397
  end
300
398
 
399
+ # Adds timestamps (created_at and updated_at) columns to the named table.
400
+ # ===== Examples
401
+ # add_timestamps(:suppliers)
402
+ def add_timestamps(table_name)
403
+ add_column table_name, :created_at, :datetime
404
+ add_column table_name, :updated_at, :datetime
405
+ end
406
+
407
+ # Removes the timestamp columns (created_at and updated_at) from the table definition.
408
+ # ===== Examples
409
+ # remove_timestamps(:suppliers)
410
+ def remove_timestamps(table_name)
411
+ remove_column table_name, :updated_at
412
+ remove_column table_name, :created_at
413
+ end
414
+
301
415
  protected
302
416
  def options_include_default?(options)
303
417
  options.include?(:default) && !(options[:null] == false && options[:default].nil?)
@@ -66,14 +66,14 @@ module ActiveRecord
66
66
 
67
67
  # QUOTING ==================================================
68
68
 
69
- # Override to return the quoted table name if the database needs it
69
+ # Override to return the quoted table name. Defaults to column quoting.
70
70
  def quote_table_name(name)
71
- name
71
+ quote_column_name(name)
72
72
  end
73
73
 
74
74
  # REFERENTIAL INTEGRITY ====================================
75
75
 
76
- # Override to turn off referential integrity while executing +&block+
76
+ # Override to turn off referential integrity while executing <tt>&block</tt>.
77
77
  def disable_referential_integrity(&block)
78
78
  yield
79
79
  end
@@ -101,7 +101,7 @@ module ActiveRecord
101
101
  false
102
102
  end
103
103
 
104
- # Lazily verify this connection, calling +active?+ only if it hasn't
104
+ # Lazily verify this connection, calling <tt>active?</tt> only if it hasn't
105
105
  # been called for +timeout+ seconds.
106
106
  def verify!(timeout)
107
107
  now = Time.now.to_i
@@ -128,15 +128,11 @@ module ActiveRecord
128
128
  protected
129
129
  def log(sql, name)
130
130
  if block_given?
131
- if @logger and @logger.debug?
132
- result = nil
133
- seconds = Benchmark.realtime { result = yield }
134
- @runtime += seconds
135
- log_info(sql, name, seconds)
136
- result
137
- else
138
- yield
139
- end
131
+ result = nil
132
+ seconds = Benchmark.realtime { result = yield }
133
+ @runtime += seconds
134
+ log_info(sql, name, seconds)
135
+ result
140
136
  else
141
137
  log_info(sql, name, 0)
142
138
  nil
@@ -94,7 +94,7 @@ module ActiveRecord
94
94
  def extract_default(default)
95
95
  if type == :binary || type == :text
96
96
  if default.blank?
97
- default
97
+ nil
98
98
  else
99
99
  raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
100
100
  end
@@ -112,6 +112,23 @@ module ActiveRecord
112
112
  super
113
113
  end
114
114
 
115
+ def extract_limit(sql_type)
116
+ if sql_type =~ /blob|text/i
117
+ case sql_type
118
+ when /tiny/i
119
+ 255
120
+ when /medium/i
121
+ 16777215
122
+ when /long/i
123
+ 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
124
+ else
125
+ super # we could return 65535 here, but we leave it undecorated by default
126
+ end
127
+ else
128
+ super
129
+ end
130
+ end
131
+
115
132
  # MySQL misreports NOT NULL column default when none is given.
116
133
  # We can't detect this for columns which may have a legitimate ''
117
134
  # default (string) but we can for others (integer, datetime, boolean,
@@ -129,19 +146,19 @@ module ActiveRecord
129
146
  #
130
147
  # Options:
131
148
  #
132
- # * <tt>:host</tt> -- Defaults to localhost
133
- # * <tt>:port</tt> -- Defaults to 3306
134
- # * <tt>:socket</tt> -- Defaults to /tmp/mysql.sock
135
- # * <tt>:username</tt> -- Defaults to root
136
- # * <tt>:password</tt> -- Defaults to nothing
137
- # * <tt>:database</tt> -- The name of the database. No default, must be provided.
138
- # * <tt>:encoding</tt> -- (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection
139
- # * <tt>:sslkey</tt> -- Necessary to use MySQL with an SSL connection
140
- # * <tt>:sslcert</tt> -- Necessary to use MySQL with an SSL connection
141
- # * <tt>:sslcapath</tt> -- Necessary to use MySQL with an SSL connection
142
- # * <tt>:sslcipher</tt> -- Necessary to use MySQL with an SSL connection
149
+ # * <tt>:host</tt> - Defaults to "localhost".
150
+ # * <tt>:port</tt> - Defaults to 3306.
151
+ # * <tt>:socket</tt> - Defaults to "/tmp/mysql.sock".
152
+ # * <tt>:username</tt> - Defaults to "root"
153
+ # * <tt>:password</tt> - Defaults to nothing.
154
+ # * <tt>:database</tt> - The name of the database. No default, must be provided.
155
+ # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
156
+ # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
157
+ # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
158
+ # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
159
+ # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
143
160
  #
144
- # By default, the MysqlAdapter will consider all columns of type tinyint(1)
161
+ # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
145
162
  # as boolean. If you wish to disable this emulation (which was the default
146
163
  # behavior in versions 0.13.1 and earlier) you can add the following line
147
164
  # to your environment.rb file:
@@ -155,13 +172,14 @@ module ActiveRecord
155
172
  "Server shutdown in progress",
156
173
  "Broken pipe",
157
174
  "Lost connection to MySQL server during query",
158
- "MySQL server has gone away"
159
- ]
175
+ "MySQL server has gone away" ]
176
+
177
+ QUOTED_TRUE, QUOTED_FALSE = '1', '0'
160
178
 
161
179
  def initialize(connection, logger, connection_options, config)
162
180
  super(connection, logger)
163
181
  @connection_options, @config = connection_options, config
164
-
182
+ @quoted_column_names, @quoted_table_names = {}, {}
165
183
  connect
166
184
  end
167
185
 
@@ -178,7 +196,7 @@ module ActiveRecord
178
196
  :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
179
197
  :string => { :name => "varchar", :limit => 255 },
180
198
  :text => { :name => "text" },
181
- :integer => { :name => "int", :limit => 11 },
199
+ :integer => { :name => "int"},
182
200
  :float => { :name => "float" },
183
201
  :decimal => { :name => "decimal" },
184
202
  :datetime => { :name => "datetime" },
@@ -205,11 +223,11 @@ module ActiveRecord
205
223
  end
206
224
 
207
225
  def quote_column_name(name) #:nodoc:
208
- "`#{name}`"
226
+ @quoted_column_names[name] ||= "`#{name}`"
209
227
  end
210
228
 
211
229
  def quote_table_name(name) #:nodoc:
212
- quote_column_name(name).gsub('.', '`.`')
230
+ @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
213
231
  end
214
232
 
215
233
  def quote_string(string) #:nodoc:
@@ -217,11 +235,11 @@ module ActiveRecord
217
235
  end
218
236
 
219
237
  def quoted_true
220
- "1"
238
+ QUOTED_TRUE
221
239
  end
222
240
 
223
241
  def quoted_false
224
- "0"
242
+ QUOTED_FALSE
225
243
  end
226
244
 
227
245
  # REFERENTIAL INTEGRITY ====================================
@@ -318,11 +336,10 @@ module ActiveRecord
318
336
 
319
337
  def add_limit_offset!(sql, options) #:nodoc:
320
338
  if limit = options[:limit]
321
- limit = sanitize_limit(limit)
322
339
  unless offset = options[:offset]
323
340
  sql << " LIMIT #{limit}"
324
341
  else
325
- sql << " LIMIT #{offset.to_i}, #{limit}"
342
+ sql << " LIMIT #{offset}, #{limit}"
326
343
  end
327
344
  end
328
345
  end
@@ -348,7 +365,7 @@ module ActiveRecord
348
365
  create_database(name)
349
366
  end
350
367
 
351
- # Create a new MySQL database with optional :charset and :collation.
368
+ # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
352
369
  # Charset defaults to utf8.
353
370
  #
354
371
  # Example:
@@ -446,6 +463,22 @@ module ActiveRecord
446
463
  execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
447
464
  end
448
465
 
466
+ # Maps logical Rails types to MySQL-specific data types.
467
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
468
+ return super unless type.to_s == 'integer'
469
+
470
+ case limit
471
+ when 0..3
472
+ "smallint(#{limit})"
473
+ when 4..8
474
+ "int(#{limit})"
475
+ when 9..20
476
+ "bigint(#{limit})"
477
+ else
478
+ 'int(11)'
479
+ end
480
+ end
481
+
449
482
 
450
483
  # SHOW VARIABLES LIKE 'name'
451
484
  def show_variable(name)