sskirby-activerecord 3.2.1

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.
Files changed (150) hide show
  1. data/CHANGELOG.md +6749 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +222 -0
  4. data/examples/associations.png +0 -0
  5. data/examples/performance.rb +177 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +147 -0
  8. data/lib/active_record/aggregations.rb +255 -0
  9. data/lib/active_record/associations.rb +1604 -0
  10. data/lib/active_record/associations/alias_tracker.rb +79 -0
  11. data/lib/active_record/associations/association.rb +239 -0
  12. data/lib/active_record/associations/association_scope.rb +119 -0
  13. data/lib/active_record/associations/belongs_to_association.rb +79 -0
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -0
  15. data/lib/active_record/associations/builder/association.rb +55 -0
  16. data/lib/active_record/associations/builder/belongs_to.rb +85 -0
  17. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
  19. data/lib/active_record/associations/builder/has_many.rb +71 -0
  20. data/lib/active_record/associations/builder/has_one.rb +62 -0
  21. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  22. data/lib/active_record/associations/collection_association.rb +574 -0
  23. data/lib/active_record/associations/collection_proxy.rb +132 -0
  24. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +62 -0
  25. data/lib/active_record/associations/has_many_association.rb +108 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +180 -0
  27. data/lib/active_record/associations/has_one_association.rb +73 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +214 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +154 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  33. data/lib/active_record/associations/join_helper.rb +55 -0
  34. data/lib/active_record/associations/preloader.rb +177 -0
  35. data/lib/active_record/associations/preloader/association.rb +127 -0
  36. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  37. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  38. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  39. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  40. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  41. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  42. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  43. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  44. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  45. data/lib/active_record/associations/singular_association.rb +64 -0
  46. data/lib/active_record/associations/through_association.rb +83 -0
  47. data/lib/active_record/attribute_assignment.rb +221 -0
  48. data/lib/active_record/attribute_methods.rb +272 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
  50. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  51. data/lib/active_record/attribute_methods/dirty.rb +101 -0
  52. data/lib/active_record/attribute_methods/primary_key.rb +114 -0
  53. data/lib/active_record/attribute_methods/query.rb +39 -0
  54. data/lib/active_record/attribute_methods/read.rb +135 -0
  55. data/lib/active_record/attribute_methods/serialization.rb +93 -0
  56. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -0
  57. data/lib/active_record/attribute_methods/write.rb +69 -0
  58. data/lib/active_record/autosave_association.rb +422 -0
  59. data/lib/active_record/base.rb +716 -0
  60. data/lib/active_record/callbacks.rb +275 -0
  61. data/lib/active_record/coders/yaml_column.rb +41 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +188 -0
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +58 -0
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +388 -0
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -0
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +115 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +492 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +598 -0
  70. data/lib/active_record/connection_adapters/abstract_adapter.rb +296 -0
  71. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  72. data/lib/active_record/connection_adapters/column.rb +270 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +288 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +426 -0
  75. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1261 -0
  76. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -0
  78. data/lib/active_record/connection_adapters/sqlite_adapter.rb +577 -0
  79. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  80. data/lib/active_record/counter_cache.rb +119 -0
  81. data/lib/active_record/dynamic_finder_match.rb +56 -0
  82. data/lib/active_record/dynamic_matchers.rb +79 -0
  83. data/lib/active_record/dynamic_scope_match.rb +23 -0
  84. data/lib/active_record/errors.rb +195 -0
  85. data/lib/active_record/explain.rb +85 -0
  86. data/lib/active_record/explain_subscriber.rb +21 -0
  87. data/lib/active_record/fixtures.rb +906 -0
  88. data/lib/active_record/fixtures/file.rb +65 -0
  89. data/lib/active_record/identity_map.rb +156 -0
  90. data/lib/active_record/inheritance.rb +167 -0
  91. data/lib/active_record/integration.rb +49 -0
  92. data/lib/active_record/locale/en.yml +40 -0
  93. data/lib/active_record/locking/optimistic.rb +183 -0
  94. data/lib/active_record/locking/pessimistic.rb +77 -0
  95. data/lib/active_record/log_subscriber.rb +68 -0
  96. data/lib/active_record/migration.rb +765 -0
  97. data/lib/active_record/migration/command_recorder.rb +105 -0
  98. data/lib/active_record/model_schema.rb +366 -0
  99. data/lib/active_record/nested_attributes.rb +469 -0
  100. data/lib/active_record/observer.rb +121 -0
  101. data/lib/active_record/persistence.rb +372 -0
  102. data/lib/active_record/query_cache.rb +74 -0
  103. data/lib/active_record/querying.rb +58 -0
  104. data/lib/active_record/railtie.rb +119 -0
  105. data/lib/active_record/railties/console_sandbox.rb +6 -0
  106. data/lib/active_record/railties/controller_runtime.rb +49 -0
  107. data/lib/active_record/railties/databases.rake +620 -0
  108. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  109. data/lib/active_record/readonly_attributes.rb +26 -0
  110. data/lib/active_record/reflection.rb +534 -0
  111. data/lib/active_record/relation.rb +534 -0
  112. data/lib/active_record/relation/batches.rb +90 -0
  113. data/lib/active_record/relation/calculations.rb +354 -0
  114. data/lib/active_record/relation/delegation.rb +49 -0
  115. data/lib/active_record/relation/finder_methods.rb +398 -0
  116. data/lib/active_record/relation/predicate_builder.rb +58 -0
  117. data/lib/active_record/relation/query_methods.rb +417 -0
  118. data/lib/active_record/relation/spawn_methods.rb +148 -0
  119. data/lib/active_record/result.rb +34 -0
  120. data/lib/active_record/sanitization.rb +194 -0
  121. data/lib/active_record/schema.rb +58 -0
  122. data/lib/active_record/schema_dumper.rb +204 -0
  123. data/lib/active_record/scoping.rb +152 -0
  124. data/lib/active_record/scoping/default.rb +142 -0
  125. data/lib/active_record/scoping/named.rb +202 -0
  126. data/lib/active_record/serialization.rb +18 -0
  127. data/lib/active_record/serializers/xml_serializer.rb +202 -0
  128. data/lib/active_record/session_store.rb +358 -0
  129. data/lib/active_record/store.rb +50 -0
  130. data/lib/active_record/test_case.rb +73 -0
  131. data/lib/active_record/timestamp.rb +113 -0
  132. data/lib/active_record/transactions.rb +360 -0
  133. data/lib/active_record/translation.rb +22 -0
  134. data/lib/active_record/validations.rb +83 -0
  135. data/lib/active_record/validations/associated.rb +43 -0
  136. data/lib/active_record/validations/uniqueness.rb +180 -0
  137. data/lib/active_record/version.rb +10 -0
  138. data/lib/rails/generators/active_record.rb +25 -0
  139. data/lib/rails/generators/active_record/migration.rb +15 -0
  140. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  141. data/lib/rails/generators/active_record/migration/templates/migration.rb +31 -0
  142. data/lib/rails/generators/active_record/model/model_generator.rb +43 -0
  143. data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
  144. data/lib/rails/generators/active_record/model/templates/model.rb +7 -0
  145. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  146. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  147. data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
  148. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
  149. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
  150. metadata +242 -0
@@ -0,0 +1,188 @@
1
+ module ActiveRecord
2
+ class Base
3
+ class ConnectionSpecification #:nodoc:
4
+ attr_reader :config, :adapter_method
5
+ def initialize (config, adapter_method)
6
+ @config, @adapter_method = config, adapter_method
7
+ end
8
+
9
+ ##
10
+ # Builds a ConnectionSpecification from user input
11
+ class Resolver # :nodoc:
12
+ attr_reader :config, :klass, :configurations
13
+
14
+ def initialize(config, configurations)
15
+ @config = config
16
+ @configurations = configurations
17
+ end
18
+
19
+ def spec
20
+ case config
21
+ when nil
22
+ raise AdapterNotSpecified unless defined?(Rails.env)
23
+ resolve_string_connection Rails.env
24
+ when Symbol, String
25
+ resolve_string_connection config.to_s
26
+ when Hash
27
+ resolve_hash_connection config
28
+ end
29
+ end
30
+
31
+ private
32
+ def resolve_string_connection(spec) # :nodoc:
33
+ hash = configurations.fetch(spec) do |k|
34
+ connection_url_to_hash(k)
35
+ end
36
+
37
+ raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
38
+
39
+ resolve_hash_connection hash
40
+ end
41
+
42
+ def resolve_hash_connection(spec) # :nodoc:
43
+ spec = spec.symbolize_keys
44
+
45
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
46
+
47
+ begin
48
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
49
+ rescue LoadError => e
50
+ raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace
51
+ end
52
+
53
+ adapter_method = "#{spec[:adapter]}_connection"
54
+
55
+ ConnectionSpecification.new(spec, adapter_method)
56
+ end
57
+
58
+ def connection_url_to_hash(url) # :nodoc:
59
+ config = URI.parse url
60
+ adapter = config.scheme
61
+ adapter = "postgresql" if adapter == "postgres"
62
+ spec = { :adapter => adapter,
63
+ :username => config.user,
64
+ :password => config.password,
65
+ :port => config.port,
66
+ :database => config.path.sub(%r{^/},""),
67
+ :host => config.host }
68
+ spec.reject!{ |_,value| !value }
69
+ if config.query
70
+ options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
71
+ spec.merge!(options)
72
+ end
73
+ spec
74
+ end
75
+ end
76
+ end
77
+
78
+ ##
79
+ # :singleton-method:
80
+ # The connection handler
81
+ class_attribute :connection_handler, :instance_writer => false
82
+ self.connection_handler = ConnectionAdapters::ConnectionHandler.new
83
+
84
+ # Returns the connection currently associated with the class. This can
85
+ # also be used to "borrow" the connection to do database work that isn't
86
+ # easily done without going straight to SQL.
87
+ def connection
88
+ self.class.connection
89
+ end
90
+
91
+ # Establishes the connection to the database. Accepts a hash as input where
92
+ # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
93
+ # example for regular databases (MySQL, Postgresql, etc):
94
+ #
95
+ # ActiveRecord::Base.establish_connection(
96
+ # :adapter => "mysql",
97
+ # :host => "localhost",
98
+ # :username => "myuser",
99
+ # :password => "mypass",
100
+ # :database => "somedatabase"
101
+ # )
102
+ #
103
+ # Example for SQLite database:
104
+ #
105
+ # ActiveRecord::Base.establish_connection(
106
+ # :adapter => "sqlite",
107
+ # :database => "path/to/dbfile"
108
+ # )
109
+ #
110
+ # Also accepts keys as strings (for parsing from YAML for example):
111
+ #
112
+ # ActiveRecord::Base.establish_connection(
113
+ # "adapter" => "sqlite",
114
+ # "database" => "path/to/dbfile"
115
+ # )
116
+ #
117
+ # Or a URL:
118
+ #
119
+ # ActiveRecord::Base.establish_connection(
120
+ # "postgres://myuser:mypass@localhost/somedatabase"
121
+ # )
122
+ #
123
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
124
+ # may be returned on an error.
125
+ def self.establish_connection(spec = ENV["DATABASE_URL"])
126
+ resolver = ConnectionSpecification::Resolver.new spec, configurations
127
+ spec = resolver.spec
128
+
129
+ unless respond_to?(spec.adapter_method)
130
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
131
+ end
132
+
133
+ remove_connection
134
+ connection_handler.establish_connection name, spec
135
+ end
136
+
137
+ class << self
138
+ # Returns the connection currently associated with the class. This can
139
+ # also be used to "borrow" the connection to do database work unrelated
140
+ # to any of the specific Active Records.
141
+ def connection
142
+ retrieve_connection
143
+ end
144
+
145
+ def connection_id
146
+ Thread.current['ActiveRecord::Base.connection_id']
147
+ end
148
+
149
+ def connection_id=(connection_id)
150
+ Thread.current['ActiveRecord::Base.connection_id'] = connection_id
151
+ end
152
+
153
+ # Returns the configuration of the associated connection as a hash:
154
+ #
155
+ # ActiveRecord::Base.connection_config
156
+ # # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"}
157
+ #
158
+ # Please use only for reading.
159
+ def connection_config
160
+ connection_pool.spec.config
161
+ end
162
+
163
+ def connection_pool
164
+ connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
165
+ end
166
+
167
+ def retrieve_connection
168
+ connection_handler.retrieve_connection(self)
169
+ end
170
+
171
+ # Returns true if Active Record is connected.
172
+ def connected?
173
+ connection_handler.connected?(self)
174
+ end
175
+
176
+ def remove_connection(klass = self)
177
+ connection_handler.remove_connection(klass)
178
+ end
179
+
180
+ def clear_active_connections!
181
+ connection_handler.clear_active_connections!
182
+ end
183
+
184
+ delegate :clear_reloadable_connections!,
185
+ :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,58 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module DatabaseLimits
4
+
5
+ # Returns the maximum length of a table alias.
6
+ def table_alias_length
7
+ 255
8
+ end
9
+
10
+ # Returns the maximum length of a column name.
11
+ def column_name_length
12
+ 64
13
+ end
14
+
15
+ # Returns the maximum length of a table name.
16
+ def table_name_length
17
+ 64
18
+ end
19
+
20
+ # Returns the maximum length of an index name.
21
+ def index_name_length
22
+ 64
23
+ end
24
+
25
+ # Returns the maximum number of columns per table.
26
+ def columns_per_table
27
+ 1024
28
+ end
29
+
30
+ # Returns the maximum number of indexes per table.
31
+ def indexes_per_table
32
+ 16
33
+ end
34
+
35
+ # Returns the maximum number of columns in a multicolumn index.
36
+ def columns_per_multicolumn_index
37
+ 16
38
+ end
39
+
40
+ # Returns the maximum number of elements in an IN (x,y,z) clause.
41
+ # nil means no limit.
42
+ def in_clause_length
43
+ nil
44
+ end
45
+
46
+ # Returns the maximum length of an SQL query.
47
+ def sql_query_length
48
+ 1048575
49
+ end
50
+
51
+ # Returns maximum number of joins in a single query.
52
+ def joins_per_query
53
+ 256
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,388 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module DatabaseStatements
4
+ # Converts an arel AST to SQL
5
+ def to_sql(arel)
6
+ if arel.respond_to?(:ast)
7
+ visitor.accept(arel.ast)
8
+ else
9
+ arel
10
+ end
11
+ end
12
+
13
+ # Returns an array of record hashes with the column names as keys and
14
+ # column values as values.
15
+ def select_all(arel, name = nil, binds = [])
16
+ select(to_sql(arel), name, binds)
17
+ end
18
+
19
+ # Returns a record hash with the column names as keys and column values
20
+ # as values.
21
+ def select_one(arel, name = nil)
22
+ result = select_all(arel, name)
23
+ result.first if result
24
+ end
25
+
26
+ # Returns a single value from a record
27
+ def select_value(arel, name = nil)
28
+ if result = select_one(arel, name)
29
+ result.values.first
30
+ end
31
+ end
32
+
33
+ # Returns an array of the values of the first column in a select:
34
+ # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
35
+ def select_values(arel, name = nil)
36
+ result = select_rows(to_sql(arel), name)
37
+ result.map { |v| v[0] }
38
+ end
39
+
40
+ # Returns an array of arrays containing the field values.
41
+ # Order is the same as that returned by +columns+.
42
+ def select_rows(sql, name = nil)
43
+ end
44
+ undef_method :select_rows
45
+
46
+ # Executes the SQL statement in the context of this connection.
47
+ def execute(sql, name = nil)
48
+ end
49
+ undef_method :execute
50
+
51
+ # Executes +sql+ statement in the context of this connection using
52
+ # +binds+ as the bind substitutes. +name+ is logged along with
53
+ # the executed +sql+ statement.
54
+ def exec_query(sql, name = 'SQL', binds = [])
55
+ end
56
+
57
+ # Executes insert +sql+ statement in the context of this connection using
58
+ # +binds+ as the bind substitutes. +name+ is the logged along with
59
+ # the executed +sql+ statement.
60
+ def exec_insert(sql, name, binds)
61
+ exec_query(sql, name, binds)
62
+ end
63
+
64
+ # Executes delete +sql+ statement in the context of this connection using
65
+ # +binds+ as the bind substitutes. +name+ is the logged along with
66
+ # the executed +sql+ statement.
67
+ def exec_delete(sql, name, binds)
68
+ exec_query(sql, name, binds)
69
+ end
70
+
71
+ # Executes update +sql+ statement in the context of this connection using
72
+ # +binds+ as the bind substitutes. +name+ is the logged along with
73
+ # the executed +sql+ statement.
74
+ def exec_update(sql, name, binds)
75
+ exec_query(sql, name, binds)
76
+ end
77
+
78
+ # Returns the last auto-generated ID from the affected table.
79
+ #
80
+ # +id_value+ will be returned unless the value is nil, in
81
+ # which case the database will attempt to calculate the last inserted
82
+ # id and return that value.
83
+ #
84
+ # If the next id was calculated in advance (as in Oracle), it should be
85
+ # passed in as +id_value+.
86
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
87
+ sql, binds = sql_for_insert(to_sql(arel), pk, id_value, sequence_name, binds)
88
+ value = exec_insert(sql, name, binds)
89
+ id_value || last_inserted_id(value)
90
+ end
91
+
92
+ # Executes the update statement and returns the number of rows affected.
93
+ def update(arel, name = nil, binds = [])
94
+ exec_update(to_sql(arel), name, binds)
95
+ end
96
+
97
+ # Executes the delete statement and returns the number of rows affected.
98
+ def delete(arel, name = nil, binds = [])
99
+ exec_delete(to_sql(arel), name, binds)
100
+ end
101
+
102
+ # Checks whether there is currently no transaction active. This is done
103
+ # by querying the database driver, and does not use the transaction
104
+ # house-keeping information recorded by #increment_open_transactions and
105
+ # friends.
106
+ #
107
+ # Returns true if there is no transaction active, false if there is a
108
+ # transaction active, and nil if this information is unknown.
109
+ #
110
+ # Not all adapters supports transaction state introspection. Currently,
111
+ # only the PostgreSQL adapter supports this.
112
+ def outside_transaction?
113
+ nil
114
+ end
115
+
116
+ # Returns +true+ when the connection adapter supports prepared statement
117
+ # caching, otherwise returns +false+
118
+ def supports_statement_cache?
119
+ false
120
+ end
121
+
122
+ # Runs the given block in a database transaction, and returns the result
123
+ # of the block.
124
+ #
125
+ # == Nested transactions support
126
+ #
127
+ # Most databases don't support true nested transactions. At the time of
128
+ # writing, the only database that supports true nested transactions that
129
+ # we're aware of, is MS-SQL.
130
+ #
131
+ # In order to get around this problem, #transaction will emulate the effect
132
+ # of nested transactions, by using savepoints:
133
+ # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
134
+ # Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
135
+ #
136
+ # It is safe to call this method if a database transaction is already open,
137
+ # i.e. if #transaction is called within another #transaction block. In case
138
+ # of a nested call, #transaction will behave as follows:
139
+ #
140
+ # - The block will be run without doing anything. All database statements
141
+ # that happen within the block are effectively appended to the already
142
+ # open database transaction.
143
+ # - However, if +:requires_new+ is set, the block will be wrapped in a
144
+ # database savepoint acting as a sub-transaction.
145
+ #
146
+ # === Caveats
147
+ #
148
+ # MySQL doesn't support DDL transactions. If you perform a DDL operation,
149
+ # then any created savepoints will be automatically released. For example,
150
+ # if you've created a savepoint, then you execute a CREATE TABLE statement,
151
+ # then the savepoint that was created will be automatically released.
152
+ #
153
+ # This means that, on MySQL, you shouldn't execute DDL operations inside
154
+ # a #transaction call that you know might create a savepoint. Otherwise,
155
+ # #transaction will raise exceptions when it tries to release the
156
+ # already-automatically-released savepoints:
157
+ #
158
+ # Model.connection.transaction do # BEGIN
159
+ # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
160
+ # Model.connection.create_table(...)
161
+ # # active_record_1 now automatically released
162
+ # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
163
+ # end
164
+ def transaction(options = {})
165
+ options.assert_valid_keys :requires_new, :joinable
166
+
167
+ last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
168
+ if options.has_key?(:joinable)
169
+ @transaction_joinable = options[:joinable]
170
+ else
171
+ @transaction_joinable = true
172
+ end
173
+ requires_new = options[:requires_new] || !last_transaction_joinable
174
+
175
+ transaction_open = false
176
+ @_current_transaction_records ||= []
177
+
178
+ begin
179
+ if block_given?
180
+ if requires_new || open_transactions == 0
181
+ if open_transactions == 0
182
+ begin_db_transaction
183
+ elsif requires_new
184
+ create_savepoint
185
+ end
186
+ increment_open_transactions
187
+ transaction_open = true
188
+ @_current_transaction_records.push([])
189
+ end
190
+ yield
191
+ end
192
+ rescue Exception => database_transaction_rollback
193
+ if transaction_open && !outside_transaction?
194
+ transaction_open = false
195
+ decrement_open_transactions
196
+ if open_transactions == 0
197
+ rollback_db_transaction
198
+ rollback_transaction_records(true)
199
+ else
200
+ rollback_to_savepoint
201
+ rollback_transaction_records(false)
202
+ end
203
+ end
204
+ raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
205
+ end
206
+ ensure
207
+ @transaction_joinable = last_transaction_joinable
208
+
209
+ if outside_transaction?
210
+ @open_transactions = 0
211
+ elsif transaction_open
212
+ decrement_open_transactions
213
+ begin
214
+ if open_transactions == 0
215
+ commit_db_transaction
216
+ commit_transaction_records
217
+ else
218
+ release_savepoint
219
+ save_point_records = @_current_transaction_records.pop
220
+ unless save_point_records.blank?
221
+ @_current_transaction_records.push([]) if @_current_transaction_records.empty?
222
+ @_current_transaction_records.last.concat(save_point_records)
223
+ end
224
+ end
225
+ rescue Exception => database_transaction_rollback
226
+ if open_transactions == 0
227
+ rollback_db_transaction
228
+ rollback_transaction_records(true)
229
+ else
230
+ rollback_to_savepoint
231
+ rollback_transaction_records(false)
232
+ end
233
+ raise
234
+ end
235
+ end
236
+ end
237
+
238
+ # Register a record with the current transaction so that its after_commit and after_rollback callbacks
239
+ # can be called.
240
+ def add_transaction_record(record)
241
+ last_batch = @_current_transaction_records.last
242
+ last_batch << record if last_batch
243
+ end
244
+
245
+ # Begins the transaction (and turns off auto-committing).
246
+ def begin_db_transaction() end
247
+
248
+ # Commits the transaction (and turns on auto-committing).
249
+ def commit_db_transaction() end
250
+
251
+ # Rolls back the transaction (and turns on auto-committing). Must be
252
+ # done if the transaction block raises an exception or returns false.
253
+ def rollback_db_transaction() end
254
+
255
+ def default_sequence_name(table, column)
256
+ nil
257
+ end
258
+
259
+ # Set the sequence to the max value of the table's column.
260
+ def reset_sequence!(table, column, sequence = nil)
261
+ # Do nothing by default. Implement for PostgreSQL, Oracle, ...
262
+ end
263
+
264
+ # Inserts the given fixture into the table. Overridden in adapters that require
265
+ # something beyond a simple insert (eg. Oracle).
266
+ def insert_fixture(fixture, table_name)
267
+ columns = Hash[columns(table_name).map { |c| [c.name, c] }]
268
+
269
+ key_list = []
270
+ value_list = fixture.map do |name, value|
271
+ key_list << quote_column_name(name)
272
+ quote(value, columns[name])
273
+ end
274
+
275
+ execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
276
+ end
277
+
278
+ def empty_insert_statement_value
279
+ "VALUES(DEFAULT)"
280
+ end
281
+
282
+ def case_sensitive_equality_operator
283
+ "="
284
+ end
285
+
286
+ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
287
+ "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
288
+ end
289
+
290
+ # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
291
+ #
292
+ # The +limit+ may be anything that can evaluate to a string via #to_s. It
293
+ # should look like an integer, or a comma-delimited list of integers, or
294
+ # an Arel SQL literal.
295
+ #
296
+ # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
297
+ # Returns the sanitized limit parameter, either as an integer, or as a
298
+ # string which contains a comma-delimited list of integers.
299
+ def sanitize_limit(limit)
300
+ if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
301
+ limit
302
+ elsif limit.to_s =~ /,/
303
+ Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
304
+ else
305
+ Integer(limit)
306
+ end
307
+ end
308
+
309
+ # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
310
+ # on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
311
+ # an UPDATE statement, so in the mysql adapters we redefine this to do that.
312
+ def join_to_update(update, select) #:nodoc:
313
+ subselect = select.clone
314
+ subselect.projections = [update.key]
315
+
316
+ update.where update.key.in(subselect)
317
+ end
318
+
319
+ protected
320
+ # Returns an array of record hashes with the column names as keys and
321
+ # column values as values.
322
+ def select(sql, name = nil, binds = [])
323
+ end
324
+ undef_method :select
325
+
326
+ # Returns the last auto-generated ID from the affected table.
327
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
328
+ execute(sql, name)
329
+ id_value
330
+ end
331
+
332
+ # Executes the update statement and returns the number of rows affected.
333
+ def update_sql(sql, name = nil)
334
+ execute(sql, name)
335
+ end
336
+
337
+ # Executes the delete statement and returns the number of rows affected.
338
+ def delete_sql(sql, name = nil)
339
+ update_sql(sql, name)
340
+ end
341
+
342
+ # Send a rollback message to all records after they have been rolled back. If rollback
343
+ # is false, only rollback records since the last save point.
344
+ def rollback_transaction_records(rollback)
345
+ if rollback
346
+ records = @_current_transaction_records.flatten
347
+ @_current_transaction_records.clear
348
+ else
349
+ records = @_current_transaction_records.pop
350
+ end
351
+
352
+ unless records.blank?
353
+ records.uniq.each do |record|
354
+ begin
355
+ record.rolledback!(rollback)
356
+ rescue Exception => e
357
+ record.logger.error(e) if record.respond_to?(:logger) && record.logger
358
+ end
359
+ end
360
+ end
361
+ end
362
+
363
+ # Send a commit message to all records after they have been committed.
364
+ def commit_transaction_records
365
+ records = @_current_transaction_records.flatten
366
+ @_current_transaction_records.clear
367
+ unless records.blank?
368
+ records.uniq.each do |record|
369
+ begin
370
+ record.committed!
371
+ rescue Exception => e
372
+ record.logger.error(e) if record.respond_to?(:logger) && record.logger
373
+ end
374
+ end
375
+ end
376
+ end
377
+
378
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
379
+ [sql, binds]
380
+ end
381
+
382
+ def last_inserted_id(result)
383
+ row = result.rows.first
384
+ row && row.first
385
+ end
386
+ end
387
+ end
388
+ end