activerecord_authorails 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +3043 -0
- data/README +360 -0
- data/RUNNING_UNIT_TESTS +64 -0
- data/Rakefile +226 -0
- data/examples/associations.png +0 -0
- data/examples/associations.rb +87 -0
- data/examples/shared_setup.rb +15 -0
- data/examples/validation.rb +85 -0
- data/install.rb +30 -0
- data/lib/active_record.rb +85 -0
- data/lib/active_record/acts/list.rb +244 -0
- data/lib/active_record/acts/nested_set.rb +211 -0
- data/lib/active_record/acts/tree.rb +89 -0
- data/lib/active_record/aggregations.rb +191 -0
- data/lib/active_record/associations.rb +1637 -0
- data/lib/active_record/associations/association_collection.rb +190 -0
- data/lib/active_record/associations/association_proxy.rb +158 -0
- data/lib/active_record/associations/belongs_to_association.rb +56 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
- data/lib/active_record/associations/has_many_association.rb +210 -0
- data/lib/active_record/associations/has_many_through_association.rb +247 -0
- data/lib/active_record/associations/has_one_association.rb +80 -0
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +2164 -0
- data/lib/active_record/calculations.rb +270 -0
- data/lib/active_record/callbacks.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
- data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
- data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
- data/lib/active_record/deprecated_associations.rb +104 -0
- data/lib/active_record/deprecated_finders.rb +44 -0
- data/lib/active_record/fixtures.rb +628 -0
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +394 -0
- data/lib/active_record/observer.rb +178 -0
- data/lib/active_record/query_cache.rb +64 -0
- data/lib/active_record/reflection.rb +222 -0
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +149 -0
- data/lib/active_record/timestamp.rb +51 -0
- data/lib/active_record/transactions.rb +136 -0
- data/lib/active_record/validations.rb +843 -0
- data/lib/active_record/vendor/db2.rb +362 -0
- data/lib/active_record/vendor/mysql.rb +1214 -0
- data/lib/active_record/vendor/simple.rb +693 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
- data/lib/active_record/wrappings.rb +58 -0
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +59 -0
- data/test/abstract_unit.rb +77 -0
- data/test/active_schema_test_mysql.rb +31 -0
- data/test/adapter_test.rb +87 -0
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +95 -0
- data/test/all.sh +8 -0
- data/test/ar_schema_test.rb +33 -0
- data/test/association_inheritance_reload.rb +14 -0
- data/test/associations/callbacks_test.rb +126 -0
- data/test/associations/cascaded_eager_loading_test.rb +138 -0
- data/test/associations/eager_test.rb +393 -0
- data/test/associations/extension_test.rb +42 -0
- data/test/associations/join_model_test.rb +497 -0
- data/test/associations_test.rb +1809 -0
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +1586 -0
- data/test/binary_test.rb +37 -0
- data/test/calculations_test.rb +219 -0
- data/test/callbacks_test.rb +377 -0
- data/test/class_inheritable_attributes_test.rb +32 -0
- data/test/column_alias_test.rb +17 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +25 -0
- data/test/connections/native_firebird/connection.rb +26 -0
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +24 -0
- data/test/connections/native_openbase/connection.rb +21 -0
- data/test/connections/native_oracle/connection.rb +27 -0
- data/test/connections/native_postgresql/connection.rb +23 -0
- data/test/connections/native_sqlite/connection.rb +34 -0
- data/test/connections/native_sqlite3/connection.rb +34 -0
- data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
- data/test/connections/native_sqlserver/connection.rb +23 -0
- data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
- data/test/connections/native_sybase/connection.rb +23 -0
- data/test/copy_table_sqlite.rb +64 -0
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/default_test_firebird.rb +16 -0
- data/test/defaults_test.rb +60 -0
- data/test/deprecated_associations_test.rb +396 -0
- data/test/deprecated_finder_test.rb +151 -0
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +504 -0
- data/test/fixtures/accounts.yml +28 -0
- data/test/fixtures/author.rb +99 -0
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/authors.yml +7 -0
- data/test/fixtures/auto_id.rb +4 -0
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
- data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
- data/test/fixtures/bad_fixtures/blank_line +3 -0
- data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
- data/test/fixtures/bad_fixtures/missing_value +1 -0
- data/test/fixtures/binary.rb +2 -0
- data/test/fixtures/categories.yml +14 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/categories_posts.yml +23 -0
- data/test/fixtures/categorization.rb +5 -0
- data/test/fixtures/categorizations.yml +17 -0
- data/test/fixtures/category.rb +20 -0
- data/test/fixtures/column_name.rb +3 -0
- data/test/fixtures/comment.rb +23 -0
- data/test/fixtures/comments.yml +59 -0
- data/test/fixtures/companies.yml +55 -0
- data/test/fixtures/company.rb +107 -0
- data/test/fixtures/company_in_module.rb +59 -0
- data/test/fixtures/computer.rb +3 -0
- data/test/fixtures/computers.yml +4 -0
- data/test/fixtures/course.rb +3 -0
- data/test/fixtures/courses.yml +7 -0
- data/test/fixtures/customer.rb +55 -0
- data/test/fixtures/customers.yml +17 -0
- data/test/fixtures/db_definitions/db2.drop.sql +32 -0
- data/test/fixtures/db_definitions/db2.sql +231 -0
- data/test/fixtures/db_definitions/db22.drop.sql +2 -0
- data/test/fixtures/db_definitions/db22.sql +5 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
- data/test/fixtures/db_definitions/firebird.sql +304 -0
- data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
- data/test/fixtures/db_definitions/firebird2.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
- data/test/fixtures/db_definitions/frontbase.sql +268 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
- data/test/fixtures/db_definitions/mysql.sql +234 -0
- data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/mysql2.sql +5 -0
- data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase.sql +302 -0
- data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase2.sql +7 -0
- data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
- data/test/fixtures/db_definitions/oracle.sql +325 -0
- data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle2.sql +6 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
- data/test/fixtures/db_definitions/postgresql.sql +263 -0
- data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/postgresql2.sql +5 -0
- data/test/fixtures/db_definitions/schema.rb +60 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
- data/test/fixtures/db_definitions/sqlite.sql +215 -0
- data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlite2.sql +5 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
- data/test/fixtures/db_definitions/sqlserver.sql +243 -0
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
- data/test/fixtures/db_definitions/sybase.sql +218 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/default.rb +2 -0
- data/test/fixtures/developer.rb +52 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +17 -0
- data/test/fixtures/developers_projects/david_action_controller +3 -0
- data/test/fixtures/developers_projects/david_active_record +3 -0
- data/test/fixtures/developers_projects/jamis_active_record +2 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/entrant.rb +3 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/funny_jokes.yml +10 -0
- data/test/fixtures/joke.rb +6 -0
- data/test/fixtures/keyboard.rb +3 -0
- data/test/fixtures/legacy_thing.rb +3 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
- data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixed_case_monkey.rb +3 -0
- data/test/fixtures/mixed_case_monkeys.yml +6 -0
- data/test/fixtures/mixin.rb +63 -0
- data/test/fixtures/mixins.yml +127 -0
- data/test/fixtures/movie.rb +5 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/order.rb +4 -0
- data/test/fixtures/people.yml +3 -0
- data/test/fixtures/person.rb +4 -0
- data/test/fixtures/post.rb +58 -0
- data/test/fixtures/posts.yml +48 -0
- data/test/fixtures/project.rb +27 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/reader.rb +4 -0
- data/test/fixtures/readers.yml +4 -0
- data/test/fixtures/reply.rb +37 -0
- data/test/fixtures/subject.rb +4 -0
- data/test/fixtures/subscriber.rb +6 -0
- data/test/fixtures/subscribers/first +2 -0
- data/test/fixtures/subscribers/second +2 -0
- data/test/fixtures/tag.rb +7 -0
- data/test/fixtures/tagging.rb +6 -0
- data/test/fixtures/taggings.yml +18 -0
- data/test/fixtures/tags.yml +7 -0
- data/test/fixtures/task.rb +3 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures/topic.rb +25 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +401 -0
- data/test/inheritance_test.rb +205 -0
- data/test/lifecycle_test.rb +137 -0
- data/test/locking_test.rb +190 -0
- data/test/method_scoping_test.rb +416 -0
- data/test/migration_test.rb +768 -0
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +196 -0
- data/test/mixin_test.rb +550 -0
- data/test/modules_test.rb +34 -0
- data/test/multiple_db_test.rb +60 -0
- data/test/pk_test.rb +104 -0
- data/test/readonly_test.rb +107 -0
- data/test/reflection_test.rb +159 -0
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +96 -0
- data/test/schema_test_postgresql.rb +64 -0
- data/test/synonym_test_oracle.rb +17 -0
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +48 -0
- data/test/transactions_test.rb +230 -0
- data/test/unconnected_test.rb +32 -0
- data/test/validations_test.rb +1097 -0
- data/test/xml_serialization_test.rb +125 -0
- metadata +365 -0
|
@@ -0,0 +1,279 @@
|
|
|
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
|
+
@@active_connections.each do |name, conn|
|
|
93
|
+
if conn.requires_reloading?
|
|
94
|
+
conn.disconnect!
|
|
95
|
+
@@active_connections.delete(name)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Verify active connections.
|
|
101
|
+
def verify_active_connections! #:nodoc:
|
|
102
|
+
if @@allow_concurrency
|
|
103
|
+
remove_stale_cached_threads!(@@active_connections) do |name, conn|
|
|
104
|
+
conn.disconnect!
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
active_connections.each_value do |connection|
|
|
109
|
+
connection.verify!(@@verification_timeout)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
def clear_cache!(cache, thread_id = nil, &block)
|
|
115
|
+
if cache
|
|
116
|
+
if @@allow_concurrency
|
|
117
|
+
thread_id ||= Thread.current.object_id
|
|
118
|
+
thread_cache, cache = cache, cache[thread_id]
|
|
119
|
+
return unless cache
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
cache.each(&block) if block_given?
|
|
123
|
+
cache.clear
|
|
124
|
+
end
|
|
125
|
+
ensure
|
|
126
|
+
if thread_cache && @@allow_concurrency
|
|
127
|
+
thread_cache.delete(thread_id)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Remove stale threads from the cache.
|
|
132
|
+
def remove_stale_cached_threads!(cache, &block)
|
|
133
|
+
stale = Set.new(cache.keys)
|
|
134
|
+
|
|
135
|
+
Thread.list.each do |thread|
|
|
136
|
+
stale.delete(thread.object_id) if thread.alive?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
stale.each do |thread_id|
|
|
140
|
+
clear_cache!(cache, thread_id, &block)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def clear_all_cached_connections!
|
|
145
|
+
if @@allow_concurrency
|
|
146
|
+
@@active_connections.each_value do |connection_hash_for_thread|
|
|
147
|
+
connection_hash_for_thread.each_value {|conn| conn.disconnect! }
|
|
148
|
+
connection_hash_for_thread.clear
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
@@active_connections.each_value {|conn| conn.disconnect! }
|
|
152
|
+
end
|
|
153
|
+
@@active_connections.clear
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns the connection currently associated with the class. This can
|
|
158
|
+
# also be used to "borrow" the connection to do database work that isn't
|
|
159
|
+
# easily done without going straight to SQL.
|
|
160
|
+
def connection
|
|
161
|
+
self.class.connection
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Establishes the connection to the database. Accepts a hash as input where
|
|
165
|
+
# the :adapter key must be specified with the name of a database adapter (in lower-case)
|
|
166
|
+
# example for regular databases (MySQL, Postgresql, etc):
|
|
167
|
+
#
|
|
168
|
+
# ActiveRecord::Base.establish_connection(
|
|
169
|
+
# :adapter => "mysql",
|
|
170
|
+
# :host => "localhost",
|
|
171
|
+
# :username => "myuser",
|
|
172
|
+
# :password => "mypass",
|
|
173
|
+
# :database => "somedatabase"
|
|
174
|
+
# )
|
|
175
|
+
#
|
|
176
|
+
# Example for SQLite database:
|
|
177
|
+
#
|
|
178
|
+
# ActiveRecord::Base.establish_connection(
|
|
179
|
+
# :adapter => "sqlite",
|
|
180
|
+
# :database => "path/to/dbfile"
|
|
181
|
+
# )
|
|
182
|
+
#
|
|
183
|
+
# Also accepts keys as strings (for parsing from yaml for example):
|
|
184
|
+
# ActiveRecord::Base.establish_connection(
|
|
185
|
+
# "adapter" => "sqlite",
|
|
186
|
+
# "database" => "path/to/dbfile"
|
|
187
|
+
# )
|
|
188
|
+
#
|
|
189
|
+
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
|
|
190
|
+
# may be returned on an error.
|
|
191
|
+
def self.establish_connection(spec = nil)
|
|
192
|
+
case spec
|
|
193
|
+
when nil
|
|
194
|
+
raise AdapterNotSpecified unless defined? RAILS_ENV
|
|
195
|
+
establish_connection(RAILS_ENV)
|
|
196
|
+
when ConnectionSpecification
|
|
197
|
+
clear_active_connection_name
|
|
198
|
+
@active_connection_name = name
|
|
199
|
+
@@defined_connections[name] = spec
|
|
200
|
+
when Symbol, String
|
|
201
|
+
if configuration = configurations[spec.to_s]
|
|
202
|
+
establish_connection(configuration)
|
|
203
|
+
else
|
|
204
|
+
raise AdapterNotSpecified, "#{spec} database is not configured"
|
|
205
|
+
end
|
|
206
|
+
else
|
|
207
|
+
spec = spec.symbolize_keys
|
|
208
|
+
unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
|
|
209
|
+
adapter_method = "#{spec[:adapter]}_connection"
|
|
210
|
+
unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
|
|
211
|
+
remove_connection
|
|
212
|
+
establish_connection(ConnectionSpecification.new(spec, adapter_method))
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Locate the connection of the nearest super class. This can be an
|
|
217
|
+
# active or defined connections: if it is the latter, it will be
|
|
218
|
+
# opened and set as the active connection for the class it was defined
|
|
219
|
+
# for (not necessarily the current class).
|
|
220
|
+
def self.retrieve_connection #:nodoc:
|
|
221
|
+
# Name is nil if establish_connection hasn't been called for
|
|
222
|
+
# some class along the inheritance chain up to AR::Base yet.
|
|
223
|
+
if name = active_connection_name
|
|
224
|
+
if conn = active_connections[name]
|
|
225
|
+
# Verify the connection.
|
|
226
|
+
conn.verify!(@@verification_timeout)
|
|
227
|
+
elsif spec = @@defined_connections[name]
|
|
228
|
+
# Activate this connection specification.
|
|
229
|
+
klass = name.constantize
|
|
230
|
+
klass.connection = spec
|
|
231
|
+
conn = active_connections[name]
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
conn or raise ConnectionNotEstablished
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Returns true if a connection that's accessible to this class have already been opened.
|
|
239
|
+
def self.connected?
|
|
240
|
+
active_connections[active_connection_name] ? true : false
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Remove the connection for this class. This will close the active
|
|
244
|
+
# connection and the defined connection (if they exist). The result
|
|
245
|
+
# can be used as argument for establish_connection, for easy
|
|
246
|
+
# re-establishing of the connection.
|
|
247
|
+
def self.remove_connection(klass=self)
|
|
248
|
+
spec = @@defined_connections[klass.name]
|
|
249
|
+
konn = active_connections[klass.name]
|
|
250
|
+
@@defined_connections.delete_if { |key, value| value == spec }
|
|
251
|
+
active_connections.delete_if { |key, value| value == konn }
|
|
252
|
+
konn.disconnect! if konn
|
|
253
|
+
spec.config if spec
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Set the connection for the class.
|
|
257
|
+
def self.connection=(spec) #:nodoc:
|
|
258
|
+
if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
|
259
|
+
active_connections[name] = spec
|
|
260
|
+
elsif spec.kind_of?(ConnectionSpecification)
|
|
261
|
+
config = spec.config.reverse_merge(:allow_concurrency => @@allow_concurrency)
|
|
262
|
+
self.connection = self.send(spec.adapter_method, config)
|
|
263
|
+
elsif spec.nil?
|
|
264
|
+
raise ConnectionNotEstablished
|
|
265
|
+
else
|
|
266
|
+
establish_connection spec
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# connection state logging
|
|
271
|
+
def self.log_connections #:nodoc:
|
|
272
|
+
if logger
|
|
273
|
+
logger.info "Defined connections: #{@@defined_connections.inspect}"
|
|
274
|
+
logger.info "Active connections: #{active_connections.inspect}"
|
|
275
|
+
logger.info "Active connection name: #{@active_connection_name}"
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
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(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
|
+
result = select_one(sql, name)
|
|
20
|
+
result.nil? ? nil : result.values.first
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns an array of the values of the first column in a select:
|
|
24
|
+
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
|
25
|
+
def select_values(sql, name = nil)
|
|
26
|
+
result = select_all(sql, name)
|
|
27
|
+
result.map{ |v| v.values.first }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Executes the SQL statement in the context of this connection.
|
|
31
|
+
def execute(sql, name = nil)
|
|
32
|
+
raise NotImplementedError, "execute is an abstract method"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns the last auto-generated ID from the affected table.
|
|
36
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
|
37
|
+
raise NotImplementedError, "insert is an abstract method"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Executes the update statement and returns the number of rows affected.
|
|
41
|
+
def update(sql, name = nil)
|
|
42
|
+
execute(sql, name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Executes the delete statement and returns the number of rows affected.
|
|
46
|
+
def delete(sql, name = nil)
|
|
47
|
+
update(sql, name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Wrap a block in a transaction. Returns result of block.
|
|
51
|
+
def transaction(start_db_transaction = true)
|
|
52
|
+
transaction_open = false
|
|
53
|
+
begin
|
|
54
|
+
if block_given?
|
|
55
|
+
if start_db_transaction
|
|
56
|
+
begin_db_transaction
|
|
57
|
+
transaction_open = true
|
|
58
|
+
end
|
|
59
|
+
yield
|
|
60
|
+
end
|
|
61
|
+
rescue Exception => database_transaction_rollback
|
|
62
|
+
if transaction_open
|
|
63
|
+
transaction_open = false
|
|
64
|
+
rollback_db_transaction
|
|
65
|
+
end
|
|
66
|
+
raise
|
|
67
|
+
end
|
|
68
|
+
ensure
|
|
69
|
+
commit_db_transaction if transaction_open
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Begins the transaction (and turns off auto-committing).
|
|
73
|
+
def begin_db_transaction() end
|
|
74
|
+
|
|
75
|
+
# Commits the transaction (and turns on auto-committing).
|
|
76
|
+
def commit_db_transaction() end
|
|
77
|
+
|
|
78
|
+
# Rolls back the transaction (and turns on auto-committing). Must be
|
|
79
|
+
# done if the transaction block raises an exception or returns false.
|
|
80
|
+
def rollback_db_transaction() end
|
|
81
|
+
|
|
82
|
+
# Alias for #add_limit_offset!.
|
|
83
|
+
def add_limit!(sql, options)
|
|
84
|
+
add_limit_offset!(sql, options) if options
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Appends +LIMIT+ and +OFFSET+ options to a SQL statement.
|
|
88
|
+
# This method *modifies* the +sql+ parameter.
|
|
89
|
+
# ===== Examples
|
|
90
|
+
# add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
|
|
91
|
+
# generates
|
|
92
|
+
# SELECT * FROM suppliers LIMIT 10 OFFSET 50
|
|
93
|
+
def add_limit_offset!(sql, options)
|
|
94
|
+
if limit = options[:limit]
|
|
95
|
+
sql << " LIMIT #{limit}"
|
|
96
|
+
if offset = options[:offset]
|
|
97
|
+
sql << " OFFSET #{offset}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Appends a locking clause to a SQL statement. *Modifies the +sql+ parameter*.
|
|
103
|
+
# # SELECT * FROM suppliers FOR UPDATE
|
|
104
|
+
# add_lock! 'SELECT * FROM suppliers', :lock => true
|
|
105
|
+
# add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
|
|
106
|
+
def add_lock!(sql, options)
|
|
107
|
+
case lock = options[:lock]
|
|
108
|
+
when true: sql << ' FOR UPDATE'
|
|
109
|
+
when String: sql << " #{lock}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def default_sequence_name(table, column)
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Set the sequence to the max value of the table's column.
|
|
118
|
+
def reset_sequence!(table, column, sequence = nil)
|
|
119
|
+
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
protected
|
|
123
|
+
# Returns an array of record hashes with the column names as keys and
|
|
124
|
+
# column values as values.
|
|
125
|
+
def select(sql, name = nil)
|
|
126
|
+
raise NotImplementedError, "select is an abstract method"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
"'#{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
|
+
"'#{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
|
+
when Date then "'#{value.to_s}'"
|
|
28
|
+
when Time, DateTime then "'#{quoted_date(value)}'"
|
|
29
|
+
else "'#{quote_string(value.to_yaml)}'"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Quotes a string, escaping any ' (single quote) and \ (backslash)
|
|
34
|
+
# characters.
|
|
35
|
+
def quote_string(s)
|
|
36
|
+
s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns a quoted form of the column name. This is highly adapter
|
|
40
|
+
# specific.
|
|
41
|
+
def quote_column_name(name)
|
|
42
|
+
name
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def quoted_true
|
|
46
|
+
"'t'"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def quoted_false
|
|
50
|
+
"'f'"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def quoted_date(value)
|
|
54
|
+
value.strftime("%Y-%m-%d %H:%M:%S")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|