activerecord 1.0.0 → 2.0.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 (311) hide show
  1. data/CHANGELOG +4928 -3
  2. data/README +45 -46
  3. data/RUNNING_UNIT_TESTS +8 -11
  4. data/Rakefile +247 -0
  5. data/install.rb +8 -38
  6. data/lib/active_record/aggregations.rb +64 -49
  7. data/lib/active_record/associations/association_collection.rb +217 -47
  8. data/lib/active_record/associations/association_proxy.rb +159 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
  12. data/lib/active_record/associations/has_many_association.rb +145 -75
  13. data/lib/active_record/associations/has_many_through_association.rb +283 -0
  14. data/lib/active_record/associations/has_one_association.rb +96 -0
  15. data/lib/active_record/associations.rb +1537 -304
  16. data/lib/active_record/attribute_methods.rb +328 -0
  17. data/lib/active_record/base.rb +2001 -588
  18. data/lib/active_record/calculations.rb +269 -0
  19. data/lib/active_record/callbacks.rb +169 -165
  20. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
  22. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  23. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
  31. data/lib/active_record/fixtures.rb +946 -100
  32. data/lib/active_record/locking/optimistic.rb +144 -0
  33. data/lib/active_record/locking/pessimistic.rb +77 -0
  34. data/lib/active_record/migration.rb +417 -0
  35. data/lib/active_record/observer.rb +142 -32
  36. data/lib/active_record/query_cache.rb +23 -0
  37. data/lib/active_record/reflection.rb +163 -70
  38. data/lib/active_record/schema.rb +58 -0
  39. data/lib/active_record/schema_dumper.rb +171 -0
  40. data/lib/active_record/serialization.rb +98 -0
  41. data/lib/active_record/serializers/json_serializer.rb +71 -0
  42. data/lib/active_record/serializers/xml_serializer.rb +315 -0
  43. data/lib/active_record/timestamp.rb +41 -0
  44. data/lib/active_record/transactions.rb +87 -57
  45. data/lib/active_record/validations.rb +909 -122
  46. data/lib/active_record/vendor/db2.rb +362 -0
  47. data/lib/active_record/vendor/mysql.rb +126 -29
  48. data/lib/active_record/version.rb +9 -0
  49. data/lib/active_record.rb +35 -7
  50. data/lib/activerecord.rb +1 -0
  51. data/test/aaa_create_tables_test.rb +72 -0
  52. data/test/abstract_unit.rb +73 -5
  53. data/test/active_schema_test_mysql.rb +43 -0
  54. data/test/adapter_test.rb +105 -0
  55. data/test/adapter_test_sqlserver.rb +95 -0
  56. data/test/aggregations_test.rb +110 -16
  57. data/test/all.sh +2 -2
  58. data/test/ar_schema_test.rb +33 -0
  59. data/test/association_inheritance_reload.rb +14 -0
  60. data/test/associations/ar_joins_test.rb +0 -0
  61. data/test/associations/callbacks_test.rb +147 -0
  62. data/test/associations/cascaded_eager_loading_test.rb +110 -0
  63. data/test/associations/eager_singularization_test.rb +145 -0
  64. data/test/associations/eager_test.rb +442 -0
  65. data/test/associations/extension_test.rb +47 -0
  66. data/test/associations/inner_join_association_test.rb +88 -0
  67. data/test/associations/join_model_test.rb +553 -0
  68. data/test/associations_test.rb +1930 -267
  69. data/test/attribute_methods_test.rb +146 -0
  70. data/test/base_test.rb +1316 -84
  71. data/test/binary_test.rb +32 -0
  72. data/test/calculations_test.rb +251 -0
  73. data/test/callbacks_test.rb +400 -0
  74. data/test/class_inheritable_attributes_test.rb +3 -4
  75. data/test/column_alias_test.rb +17 -0
  76. data/test/connection_test_firebird.rb +8 -0
  77. data/test/connection_test_mysql.rb +30 -0
  78. data/test/connections/native_db2/connection.rb +25 -0
  79. data/test/connections/native_firebird/connection.rb +26 -0
  80. data/test/connections/native_frontbase/connection.rb +27 -0
  81. data/test/connections/native_mysql/connection.rb +21 -18
  82. data/test/connections/native_openbase/connection.rb +21 -0
  83. data/test/connections/native_oracle/connection.rb +27 -0
  84. data/test/connections/native_postgresql/connection.rb +17 -18
  85. data/test/connections/native_sqlite/connection.rb +17 -16
  86. data/test/connections/native_sqlite3/connection.rb +25 -0
  87. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  88. data/test/connections/native_sybase/connection.rb +23 -0
  89. data/test/copy_table_test_sqlite.rb +69 -0
  90. data/test/datatype_test_postgresql.rb +203 -0
  91. data/test/date_time_test.rb +37 -0
  92. data/test/default_test_firebird.rb +16 -0
  93. data/test/defaults_test.rb +67 -0
  94. data/test/deprecated_finder_test.rb +30 -0
  95. data/test/finder_test.rb +607 -32
  96. data/test/fixtures/accounts.yml +28 -0
  97. data/test/fixtures/all/developers.yml +0 -0
  98. data/test/fixtures/all/people.csv +0 -0
  99. data/test/fixtures/all/tasks.yml +0 -0
  100. data/test/fixtures/author.rb +107 -0
  101. data/test/fixtures/author_favorites.yml +4 -0
  102. data/test/fixtures/authors.yml +7 -0
  103. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  104. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  105. data/test/fixtures/bad_fixtures/blank_line +3 -0
  106. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  107. data/test/fixtures/bad_fixtures/missing_value +1 -0
  108. data/test/fixtures/binaries.yml +132 -0
  109. data/test/fixtures/binary.rb +2 -0
  110. data/test/fixtures/book.rb +4 -0
  111. data/test/fixtures/books.yml +7 -0
  112. data/test/fixtures/categories/special_categories.yml +9 -0
  113. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  114. data/test/fixtures/categories.yml +14 -0
  115. data/test/fixtures/categories_ordered.yml +7 -0
  116. data/test/fixtures/categories_posts.yml +23 -0
  117. data/test/fixtures/categorization.rb +5 -0
  118. data/test/fixtures/categorizations.yml +17 -0
  119. data/test/fixtures/category.rb +26 -0
  120. data/test/fixtures/citation.rb +6 -0
  121. data/test/fixtures/comment.rb +23 -0
  122. data/test/fixtures/comments.yml +59 -0
  123. data/test/fixtures/companies.yml +55 -0
  124. data/test/fixtures/company.rb +81 -4
  125. data/test/fixtures/company_in_module.rb +32 -6
  126. data/test/fixtures/computer.rb +4 -0
  127. data/test/fixtures/computers.yml +4 -0
  128. data/test/fixtures/contact.rb +16 -0
  129. data/test/fixtures/courses.yml +7 -0
  130. data/test/fixtures/customer.rb +28 -3
  131. data/test/fixtures/customers.yml +17 -0
  132. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  133. data/test/fixtures/db_definitions/db2.sql +235 -0
  134. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  135. data/test/fixtures/db_definitions/db22.sql +5 -0
  136. data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
  137. data/test/fixtures/db_definitions/firebird.sql +310 -0
  138. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  139. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  140. data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
  141. data/test/fixtures/db_definitions/frontbase.sql +273 -0
  142. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  143. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  144. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  145. data/test/fixtures/db_definitions/openbase.sql +318 -0
  146. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  148. data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
  149. data/test/fixtures/db_definitions/oracle.sql +330 -0
  150. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  151. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  152. data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
  153. data/test/fixtures/db_definitions/postgresql.sql +217 -38
  154. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/postgresql2.sql +2 -2
  156. data/test/fixtures/db_definitions/schema.rb +354 -0
  157. data/test/fixtures/db_definitions/schema2.rb +11 -0
  158. data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
  159. data/test/fixtures/db_definitions/sqlite.sql +139 -5
  160. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  161. data/test/fixtures/db_definitions/sqlite2.sql +1 -0
  162. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  163. data/test/fixtures/db_definitions/sybase.sql +222 -0
  164. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  165. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  166. data/test/fixtures/developer.rb +70 -6
  167. data/test/fixtures/developers.yml +21 -0
  168. data/test/fixtures/developers_projects/david_action_controller +2 -1
  169. data/test/fixtures/developers_projects/david_active_record +2 -1
  170. data/test/fixtures/developers_projects.yml +17 -0
  171. data/test/fixtures/edge.rb +5 -0
  172. data/test/fixtures/edges.yml +6 -0
  173. data/test/fixtures/entrants.yml +14 -0
  174. data/test/fixtures/example.log +1 -0
  175. data/test/fixtures/fk_test_has_fk.yml +3 -0
  176. data/test/fixtures/fk_test_has_pk.yml +2 -0
  177. data/test/fixtures/flowers.jpg +0 -0
  178. data/test/fixtures/funny_jokes.yml +10 -0
  179. data/test/fixtures/item.rb +7 -0
  180. data/test/fixtures/items.yml +4 -0
  181. data/test/fixtures/joke.rb +3 -0
  182. data/test/fixtures/keyboard.rb +3 -0
  183. data/test/fixtures/legacy_thing.rb +3 -0
  184. data/test/fixtures/legacy_things.yml +3 -0
  185. data/test/fixtures/matey.rb +4 -0
  186. data/test/fixtures/mateys.yml +4 -0
  187. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  188. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  189. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  190. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  191. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  192. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  193. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  194. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  195. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  196. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  197. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  198. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  199. data/test/fixtures/minimalistic.rb +2 -0
  200. data/test/fixtures/minimalistics.yml +2 -0
  201. data/test/fixtures/mixed_case_monkey.rb +3 -0
  202. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  203. data/test/fixtures/mixins.yml +29 -0
  204. data/test/fixtures/movies.yml +7 -0
  205. data/test/fixtures/naked/csv/accounts.csv +1 -0
  206. data/test/fixtures/naked/yml/accounts.yml +1 -0
  207. data/test/fixtures/naked/yml/companies.yml +1 -0
  208. data/test/fixtures/naked/yml/courses.yml +1 -0
  209. data/test/fixtures/order.rb +4 -0
  210. data/test/fixtures/parrot.rb +13 -0
  211. data/test/fixtures/parrots.yml +27 -0
  212. data/test/fixtures/parrots_pirates.yml +7 -0
  213. data/test/fixtures/people.yml +3 -0
  214. data/test/fixtures/person.rb +4 -0
  215. data/test/fixtures/pirate.rb +5 -0
  216. data/test/fixtures/pirates.yml +9 -0
  217. data/test/fixtures/post.rb +59 -0
  218. data/test/fixtures/posts.yml +48 -0
  219. data/test/fixtures/project.rb +27 -2
  220. data/test/fixtures/projects.yml +7 -0
  221. data/test/fixtures/reader.rb +4 -0
  222. data/test/fixtures/readers.yml +4 -0
  223. data/test/fixtures/reply.rb +18 -2
  224. data/test/fixtures/reserved_words/distinct.yml +5 -0
  225. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  226. data/test/fixtures/reserved_words/group.yml +14 -0
  227. data/test/fixtures/reserved_words/select.yml +8 -0
  228. data/test/fixtures/reserved_words/values.yml +7 -0
  229. data/test/fixtures/ship.rb +3 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/subject.rb +4 -0
  232. data/test/fixtures/subscriber.rb +4 -3
  233. data/test/fixtures/tag.rb +7 -0
  234. data/test/fixtures/tagging.rb +10 -0
  235. data/test/fixtures/taggings.yml +25 -0
  236. data/test/fixtures/tags.yml +7 -0
  237. data/test/fixtures/task.rb +3 -0
  238. data/test/fixtures/tasks.yml +7 -0
  239. data/test/fixtures/topic.rb +20 -3
  240. data/test/fixtures/topics.yml +22 -0
  241. data/test/fixtures/treasure.rb +4 -0
  242. data/test/fixtures/treasures.yml +10 -0
  243. data/test/fixtures/vertex.rb +9 -0
  244. data/test/fixtures/vertices.yml +4 -0
  245. data/test/fixtures_test.rb +574 -8
  246. data/test/inheritance_test.rb +113 -27
  247. data/test/json_serialization_test.rb +180 -0
  248. data/test/lifecycle_test.rb +56 -29
  249. data/test/locking_test.rb +273 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +933 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_test.rb +95 -0
  254. data/test/modules_test.rb +23 -10
  255. data/test/multiple_db_test.rb +17 -3
  256. data/test/pk_test.rb +59 -15
  257. data/test/query_cache_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +124 -27
  260. data/test/reserved_word_test_mysql.rb +177 -0
  261. data/test/schema_authorization_test_postgresql.rb +75 -0
  262. data/test/schema_dumper_test.rb +131 -0
  263. data/test/schema_test_postgresql.rb +64 -0
  264. data/test/serialization_test.rb +47 -0
  265. data/test/synonym_test_oracle.rb +17 -0
  266. data/test/table_name_test_sqlserver.rb +23 -0
  267. data/test/threaded_connections_test.rb +48 -0
  268. data/test/transactions_test.rb +227 -29
  269. data/test/unconnected_test.rb +14 -6
  270. data/test/validations_test.rb +1293 -32
  271. data/test/xml_serialization_test.rb +202 -0
  272. metadata +347 -143
  273. data/dev-utils/eval_debugger.rb +0 -9
  274. data/examples/associations.rb +0 -87
  275. data/examples/shared_setup.rb +0 -15
  276. data/examples/validation.rb +0 -88
  277. data/lib/active_record/deprecated_associations.rb +0 -70
  278. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  279. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  280. data/lib/active_record/support/clean_logger.rb +0 -10
  281. data/lib/active_record/support/inflector.rb +0 -70
  282. data/lib/active_record/vendor/simple.rb +0 -702
  283. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  284. data/lib/active_record/wrappings.rb +0 -59
  285. data/rakefile +0 -122
  286. data/test/deprecated_associations_test.rb +0 -336
  287. data/test/fixtures/accounts/signals37 +0 -3
  288. data/test/fixtures/accounts/unknown +0 -2
  289. data/test/fixtures/companies/first_client +0 -6
  290. data/test/fixtures/companies/first_firm +0 -4
  291. data/test/fixtures/companies/second_client +0 -6
  292. data/test/fixtures/courses/java +0 -2
  293. data/test/fixtures/courses/ruby +0 -2
  294. data/test/fixtures/customers/david +0 -6
  295. data/test/fixtures/db_definitions/mysql.sql +0 -96
  296. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  297. data/test/fixtures/developers/david +0 -2
  298. data/test/fixtures/developers/jamis +0 -2
  299. data/test/fixtures/entrants/first +0 -3
  300. data/test/fixtures/entrants/second +0 -3
  301. data/test/fixtures/entrants/third +0 -3
  302. data/test/fixtures/fixture_database.sqlite +0 -0
  303. data/test/fixtures/fixture_database_2.sqlite +0 -0
  304. data/test/fixtures/movies/first +0 -2
  305. data/test/fixtures/movies/second +0 -2
  306. data/test/fixtures/projects/action_controller +0 -2
  307. data/test/fixtures/projects/active_record +0 -2
  308. data/test/fixtures/topics/first +0 -9
  309. data/test/fixtures/topics/second +0 -8
  310. data/test/inflector_test.rb +0 -104
  311. data/test/thread_safety_test.rb +0 -33
@@ -0,0 +1,308 @@
1
+ require 'set'
2
+
3
+ module ActiveRecord
4
+ class Base
5
+ class ConnectionSpecification #:nodoc:
6
+ attr_reader :config, :adapter_method
7
+ def initialize (config, adapter_method)
8
+ @config, @adapter_method = config, adapter_method
9
+ end
10
+ end
11
+
12
+ # Check for activity after at least +verification_timeout+ seconds.
13
+ # Defaults to 0 (always check.)
14
+ cattr_accessor :verification_timeout, :instance_writer => false
15
+ @@verification_timeout = 0
16
+
17
+ # The class -> [adapter_method, config] map
18
+ @@defined_connections = {}
19
+
20
+ # The class -> thread id -> adapter cache. (class -> adapter if not allow_concurrency)
21
+ @@active_connections = {}
22
+
23
+ class << self
24
+ # Retrieve the connection cache.
25
+ def thread_safe_active_connections #:nodoc:
26
+ @@active_connections[Thread.current.object_id] ||= {}
27
+ end
28
+
29
+ def single_threaded_active_connections #:nodoc:
30
+ @@active_connections
31
+ end
32
+
33
+ # pick up the right active_connection method from @@allow_concurrency
34
+ if @@allow_concurrency
35
+ alias_method :active_connections, :thread_safe_active_connections
36
+ else
37
+ alias_method :active_connections, :single_threaded_active_connections
38
+ end
39
+
40
+ # set concurrency support flag (not thread safe, like most of the methods in this file)
41
+ def allow_concurrency=(threaded) #:nodoc:
42
+ logger.debug "allow_concurrency=#{threaded}" if logger
43
+ return if @@allow_concurrency == threaded
44
+ clear_all_cached_connections!
45
+ @@allow_concurrency = threaded
46
+ method_prefix = threaded ? "thread_safe" : "single_threaded"
47
+ sing = (class << self; self; end)
48
+ [:active_connections, :scoped_methods].each do |method|
49
+ sing.send(:alias_method, method, "#{method_prefix}_#{method}")
50
+ end
51
+ log_connections if logger
52
+ end
53
+
54
+ def active_connection_name #:nodoc:
55
+ @active_connection_name ||=
56
+ if active_connections[name] || @@defined_connections[name]
57
+ name
58
+ elsif self == ActiveRecord::Base
59
+ nil
60
+ else
61
+ superclass.active_connection_name
62
+ end
63
+ end
64
+
65
+ def clear_active_connection_name #:nodoc:
66
+ @active_connection_name = nil
67
+ subclasses.each { |klass| klass.clear_active_connection_name }
68
+ end
69
+
70
+ # Returns the connection currently associated with the class. This can
71
+ # also be used to "borrow" the connection to do database work unrelated
72
+ # to any of the specific Active Records.
73
+ def connection
74
+ if @active_connection_name && (conn = active_connections[@active_connection_name])
75
+ conn
76
+ else
77
+ # retrieve_connection sets the cache key.
78
+ conn = retrieve_connection
79
+ active_connections[@active_connection_name] = conn
80
+ end
81
+ end
82
+
83
+ # Clears the cache which maps classes to connections.
84
+ def clear_active_connections!
85
+ clear_cache!(@@active_connections) do |name, conn|
86
+ conn.disconnect!
87
+ end
88
+ end
89
+
90
+ # Clears the cache which maps classes
91
+ def clear_reloadable_connections!
92
+ if @@allow_concurrency
93
+ # With concurrent connections @@active_connections is
94
+ # a hash keyed by thread id.
95
+ @@active_connections.each do |thread_id, conns|
96
+ conns.each do |name, conn|
97
+ if conn.requires_reloading?
98
+ conn.disconnect!
99
+ @@active_connections[thread_id].delete(name)
100
+ end
101
+ end
102
+ end
103
+ else
104
+ @@active_connections.each do |name, conn|
105
+ if conn.requires_reloading?
106
+ conn.disconnect!
107
+ @@active_connections.delete(name)
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ # Verify active connections.
114
+ def verify_active_connections! #:nodoc:
115
+ if @@allow_concurrency
116
+ remove_stale_cached_threads!(@@active_connections) do |name, conn|
117
+ conn.disconnect!
118
+ end
119
+ end
120
+
121
+ active_connections.each_value do |connection|
122
+ connection.verify!(@@verification_timeout)
123
+ end
124
+ end
125
+
126
+ private
127
+ def clear_cache!(cache, thread_id = nil, &block)
128
+ if cache
129
+ if @@allow_concurrency
130
+ thread_id ||= Thread.current.object_id
131
+ thread_cache, cache = cache, cache[thread_id]
132
+ return unless cache
133
+ end
134
+
135
+ cache.each(&block) if block_given?
136
+ cache.clear
137
+ end
138
+ ensure
139
+ if thread_cache && @@allow_concurrency
140
+ thread_cache.delete(thread_id)
141
+ end
142
+ end
143
+
144
+ # Remove stale threads from the cache.
145
+ def remove_stale_cached_threads!(cache, &block)
146
+ stale = Set.new(cache.keys)
147
+
148
+ Thread.list.each do |thread|
149
+ stale.delete(thread.object_id) if thread.alive?
150
+ end
151
+
152
+ stale.each do |thread_id|
153
+ clear_cache!(cache, thread_id, &block)
154
+ end
155
+ end
156
+
157
+ def clear_all_cached_connections!
158
+ if @@allow_concurrency
159
+ @@active_connections.each_value do |connection_hash_for_thread|
160
+ connection_hash_for_thread.each_value {|conn| conn.disconnect! }
161
+ connection_hash_for_thread.clear
162
+ end
163
+ else
164
+ @@active_connections.each_value {|conn| conn.disconnect! }
165
+ end
166
+ @@active_connections.clear
167
+ end
168
+ end
169
+
170
+ # Returns the connection currently associated with the class. This can
171
+ # also be used to "borrow" the connection to do database work that isn't
172
+ # easily done without going straight to SQL.
173
+ def connection
174
+ self.class.connection
175
+ end
176
+
177
+ # Establishes the connection to the database. Accepts a hash as input where
178
+ # the :adapter key must be specified with the name of a database adapter (in lower-case)
179
+ # example for regular databases (MySQL, Postgresql, etc):
180
+ #
181
+ # ActiveRecord::Base.establish_connection(
182
+ # :adapter => "mysql",
183
+ # :host => "localhost",
184
+ # :username => "myuser",
185
+ # :password => "mypass",
186
+ # :database => "somedatabase"
187
+ # )
188
+ #
189
+ # Example for SQLite database:
190
+ #
191
+ # ActiveRecord::Base.establish_connection(
192
+ # :adapter => "sqlite",
193
+ # :database => "path/to/dbfile"
194
+ # )
195
+ #
196
+ # Also accepts keys as strings (for parsing from yaml for example):
197
+ # ActiveRecord::Base.establish_connection(
198
+ # "adapter" => "sqlite",
199
+ # "database" => "path/to/dbfile"
200
+ # )
201
+ #
202
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
203
+ # may be returned on an error.
204
+ def self.establish_connection(spec = nil)
205
+ case spec
206
+ when nil
207
+ raise AdapterNotSpecified unless defined? RAILS_ENV
208
+ establish_connection(RAILS_ENV)
209
+ when ConnectionSpecification
210
+ clear_active_connection_name
211
+ @active_connection_name = name
212
+ @@defined_connections[name] = spec
213
+ when Symbol, String
214
+ if configuration = configurations[spec.to_s]
215
+ establish_connection(configuration)
216
+ else
217
+ raise AdapterNotSpecified, "#{spec} database is not configured"
218
+ end
219
+ else
220
+ spec = spec.symbolize_keys
221
+ unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
222
+
223
+ begin
224
+ require 'rubygems'
225
+ gem "activerecord-#{spec[:adapter]}-adapter"
226
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
227
+ rescue LoadError
228
+ begin
229
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
230
+ rescue LoadError
231
+ raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})"
232
+ end
233
+ end
234
+
235
+ adapter_method = "#{spec[:adapter]}_connection"
236
+ if !respond_to?(adapter_method)
237
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
238
+ end
239
+
240
+ remove_connection
241
+ establish_connection(ConnectionSpecification.new(spec, adapter_method))
242
+ end
243
+ end
244
+
245
+ # Locate the connection of the nearest super class. This can be an
246
+ # active or defined connection: if it is the latter, it will be
247
+ # opened and set as the active connection for the class it was defined
248
+ # for (not necessarily the current class).
249
+ def self.retrieve_connection #:nodoc:
250
+ # Name is nil if establish_connection hasn't been called for
251
+ # some class along the inheritance chain up to AR::Base yet.
252
+ if name = active_connection_name
253
+ if conn = active_connections[name]
254
+ # Verify the connection.
255
+ conn.verify!(@@verification_timeout)
256
+ elsif spec = @@defined_connections[name]
257
+ # Activate this connection specification.
258
+ klass = name.constantize
259
+ klass.connection = spec
260
+ conn = active_connections[name]
261
+ end
262
+ end
263
+
264
+ conn or raise ConnectionNotEstablished
265
+ end
266
+
267
+ # Returns true if a connection that's accessible to this class has already been opened.
268
+ def self.connected?
269
+ active_connections[active_connection_name] ? true : false
270
+ end
271
+
272
+ # Remove the connection for this class. This will close the active
273
+ # connection and the defined connection (if they exist). The result
274
+ # can be used as an argument for establish_connection, for easily
275
+ # re-establishing the connection.
276
+ def self.remove_connection(klass=self)
277
+ spec = @@defined_connections[klass.name]
278
+ konn = active_connections[klass.name]
279
+ @@defined_connections.delete_if { |key, value| value == spec }
280
+ active_connections.delete_if { |key, value| value == konn }
281
+ konn.disconnect! if konn
282
+ spec.config if spec
283
+ end
284
+
285
+ # Set the connection for the class.
286
+ def self.connection=(spec) #:nodoc:
287
+ if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
288
+ active_connections[name] = spec
289
+ elsif spec.kind_of?(ConnectionSpecification)
290
+ config = spec.config.reverse_merge(:allow_concurrency => @@allow_concurrency)
291
+ self.connection = self.send(spec.adapter_method, config)
292
+ elsif spec.nil?
293
+ raise ConnectionNotEstablished
294
+ else
295
+ establish_connection spec
296
+ end
297
+ end
298
+
299
+ # connection state logging
300
+ def self.log_connections #:nodoc:
301
+ if logger
302
+ logger.info "Defined connections: #{@@defined_connections.inspect}"
303
+ logger.info "Active connections: #{active_connections.inspect}"
304
+ logger.info "Active connection name: #{@active_connection_name}"
305
+ end
306
+ end
307
+ end
308
+ end
@@ -0,0 +1,171 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module DatabaseStatements
4
+ # Returns an array of record hashes with the column names as keys and
5
+ # column values as values.
6
+ def select_all(sql, name = nil)
7
+ select(sql, name)
8
+ end
9
+
10
+ # Returns a record hash with the column names as keys and column values
11
+ # as values.
12
+ def select_one(sql, name = nil)
13
+ result = select_all(sql, name)
14
+ result.first if result
15
+ end
16
+
17
+ # Returns a single value from a record
18
+ def select_value(sql, name = nil)
19
+ if result = select_one(sql, name)
20
+ result.values.first
21
+ end
22
+ end
23
+
24
+ # Returns an array of the values of the first column in a select:
25
+ # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
26
+ def select_values(sql, name = nil)
27
+ result = select_rows(sql, name)
28
+ result.map { |v| v[0] }
29
+ end
30
+
31
+ # Returns an array of arrays containing the field values.
32
+ # Order is the same as that returned by #columns.
33
+ def select_rows(sql, name = nil)
34
+ raise NotImplementedError, "select_rows is an abstract method"
35
+ end
36
+
37
+ # Executes the SQL statement in the context of this connection.
38
+ def execute(sql, name = nil)
39
+ raise NotImplementedError, "execute is an abstract method"
40
+ end
41
+
42
+ # Returns the last auto-generated ID from the affected table.
43
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
44
+ insert_sql(sql, name, pk, id_value, sequence_name)
45
+ end
46
+
47
+ # Executes the update statement and returns the number of rows affected.
48
+ def update(sql, name = nil)
49
+ update_sql(sql, name)
50
+ end
51
+
52
+ # Executes the delete statement and returns the number of rows affected.
53
+ def delete(sql, name = nil)
54
+ delete_sql(sql, name)
55
+ end
56
+
57
+ # Wrap a block in a transaction. Returns result of block.
58
+ def transaction(start_db_transaction = true)
59
+ transaction_open = false
60
+ begin
61
+ if block_given?
62
+ if start_db_transaction
63
+ begin_db_transaction
64
+ transaction_open = true
65
+ end
66
+ yield
67
+ end
68
+ rescue Exception => database_transaction_rollback
69
+ if transaction_open
70
+ transaction_open = false
71
+ rollback_db_transaction
72
+ end
73
+ raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
74
+ end
75
+ ensure
76
+ if transaction_open
77
+ begin
78
+ commit_db_transaction
79
+ rescue Exception => database_transaction_rollback
80
+ rollback_db_transaction
81
+ raise
82
+ end
83
+ end
84
+ end
85
+
86
+ # Begins the transaction (and turns off auto-committing).
87
+ def begin_db_transaction() end
88
+
89
+ # Commits the transaction (and turns on auto-committing).
90
+ def commit_db_transaction() end
91
+
92
+ # Rolls back the transaction (and turns on auto-committing). Must be
93
+ # done if the transaction block raises an exception or returns false.
94
+ def rollback_db_transaction() end
95
+
96
+ # Alias for #add_limit_offset!.
97
+ def add_limit!(sql, options)
98
+ add_limit_offset!(sql, options) if options
99
+ end
100
+
101
+ # Appends +LIMIT+ and +OFFSET+ options to an SQL statement.
102
+ # This method *modifies* the +sql+ parameter.
103
+ # ===== Examples
104
+ # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
105
+ # generates
106
+ # SELECT * FROM suppliers LIMIT 10 OFFSET 50
107
+ def add_limit_offset!(sql, options)
108
+ if limit = options[:limit]
109
+ sql << " LIMIT #{limit}"
110
+ if offset = options[:offset]
111
+ sql << " OFFSET #{offset}"
112
+ end
113
+ end
114
+ end
115
+
116
+ # Appends a locking clause to an SQL statement.
117
+ # This method *modifies* the +sql+ parameter.
118
+ # # SELECT * FROM suppliers FOR UPDATE
119
+ # add_lock! 'SELECT * FROM suppliers', :lock => true
120
+ # add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
121
+ def add_lock!(sql, options)
122
+ case lock = options[:lock]
123
+ when true; sql << ' FOR UPDATE'
124
+ when String; sql << " #{lock}"
125
+ end
126
+ end
127
+
128
+ def default_sequence_name(table, column)
129
+ nil
130
+ end
131
+
132
+ # Set the sequence to the max value of the table's column.
133
+ def reset_sequence!(table, column, sequence = nil)
134
+ # Do nothing by default. Implement for PostgreSQL, Oracle, ...
135
+ end
136
+
137
+ # Inserts the given fixture into the table. Overridden in adapters that require
138
+ # something beyond a simple insert (eg. Oracle).
139
+ def insert_fixture(fixture, table_name)
140
+ execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
141
+ end
142
+
143
+ def empty_insert_statement(table_name)
144
+ "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
145
+ end
146
+
147
+ protected
148
+ # Returns an array of record hashes with the column names as keys and
149
+ # column values as values.
150
+ def select(sql, name = nil)
151
+ raise NotImplementedError, "select is an abstract method"
152
+ end
153
+
154
+ # Returns the last auto-generated ID from the affected table.
155
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
156
+ execute(sql, name)
157
+ id_value
158
+ end
159
+
160
+ # Executes the update statement and returns the number of rows affected.
161
+ def update_sql(sql, name = nil)
162
+ execute(sql, name)
163
+ end
164
+
165
+ # Executes the delete statement and returns the number of rows affected.
166
+ def delete_sql(sql, name = nil)
167
+ update_sql(sql, name)
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,87 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module QueryCache
4
+ class << self
5
+ def included(base)
6
+ base.class_eval do
7
+ attr_accessor :query_cache_enabled
8
+ alias_method_chain :columns, :query_cache
9
+ alias_method_chain :select_all, :query_cache
10
+ end
11
+
12
+ dirties_query_cache base, :insert, :update, :delete
13
+ end
14
+
15
+ def dirties_query_cache(base, *method_names)
16
+ method_names.each do |method_name|
17
+ base.class_eval <<-end_code, __FILE__, __LINE__
18
+ def #{method_name}_with_query_dirty(*args)
19
+ clear_query_cache if @query_cache_enabled
20
+ #{method_name}_without_query_dirty(*args)
21
+ end
22
+
23
+ alias_method_chain :#{method_name}, :query_dirty
24
+ end_code
25
+ end
26
+ end
27
+ end
28
+
29
+ # Enable the query cache within the block.
30
+ def cache
31
+ old, @query_cache_enabled = @query_cache_enabled, true
32
+ @query_cache ||= {}
33
+ yield
34
+ ensure
35
+ clear_query_cache
36
+ @query_cache_enabled = old
37
+ end
38
+
39
+ # Disable the query cache within the block.
40
+ def uncached
41
+ old, @query_cache_enabled = @query_cache_enabled, false
42
+ yield
43
+ ensure
44
+ @query_cache_enabled = old
45
+ end
46
+
47
+ def clear_query_cache
48
+ @query_cache.clear if @query_cache
49
+ end
50
+
51
+ def select_all_with_query_cache(*args)
52
+ if @query_cache_enabled
53
+ cache_sql(args.first) { select_all_without_query_cache(*args) }
54
+ else
55
+ select_all_without_query_cache(*args)
56
+ end
57
+ end
58
+
59
+ def columns_with_query_cache(*args)
60
+ if @query_cache_enabled
61
+ @query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
62
+ else
63
+ columns_without_query_cache(*args)
64
+ end
65
+ end
66
+
67
+ private
68
+ def cache_sql(sql)
69
+ result =
70
+ if @query_cache.has_key?(sql)
71
+ log_info(sql, "CACHE", 0.0)
72
+ @query_cache[sql]
73
+ else
74
+ @query_cache[sql] = yield
75
+ end
76
+
77
+ if Array === result
78
+ result.collect { |row| row.dup }
79
+ else
80
+ result.duplicable? ? result.dup : result
81
+ end
82
+ rescue TypeError
83
+ result
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,69 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module Quoting
4
+ # Quotes the column value to help prevent
5
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
6
+ def quote(value, column = nil)
7
+ # records are quoted as their primary key
8
+ return value.quoted_id if value.respond_to?(:quoted_id)
9
+
10
+ case value
11
+ when String, ActiveSupport::Multibyte::Chars
12
+ value = value.to_s
13
+ if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
14
+ "#{quoted_string_prefix}'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
15
+ elsif column && [:integer, :float].include?(column.type)
16
+ value = column.type == :integer ? value.to_i : value.to_f
17
+ value.to_s
18
+ else
19
+ "#{quoted_string_prefix}'#{quote_string(value)}'" # ' (for ruby-mode)
20
+ end
21
+ when NilClass then "NULL"
22
+ when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
23
+ when FalseClass then (column && column.type == :integer ? '0' : quoted_false)
24
+ when Float, Fixnum, Bignum then value.to_s
25
+ # BigDecimals need to be output in a non-normalized form and quoted.
26
+ when BigDecimal then value.to_s('F')
27
+ else
28
+ if value.acts_like?(:date) || value.acts_like?(:time)
29
+ "'#{quoted_date(value)}'"
30
+ else
31
+ "#{quoted_string_prefix}'#{quote_string(value.to_yaml)}'"
32
+ end
33
+ end
34
+ end
35
+
36
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
37
+ # characters.
38
+ def quote_string(s)
39
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
40
+ end
41
+
42
+ # Quotes the column name. Defaults to no quoting.
43
+ def quote_column_name(column_name)
44
+ column_name
45
+ end
46
+
47
+ # Quotes the table name. Defaults to column name quoting.
48
+ def quote_table_name(table_name)
49
+ quote_column_name(table_name)
50
+ end
51
+
52
+ def quoted_true
53
+ "'t'"
54
+ end
55
+
56
+ def quoted_false
57
+ "'f'"
58
+ end
59
+
60
+ def quoted_date(value)
61
+ value.to_s(:db)
62
+ end
63
+
64
+ def quoted_string_prefix
65
+ ''
66
+ end
67
+ end
68
+ end
69
+ end