Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / activerecord / lib / active_record / connection_adapters / abstract / database_statements.rb
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 end
35 undef_method :select_rows
36
37 # Executes the SQL statement in the context of this connection.
38 def execute(sql, name = nil, skip_logging = false)
39 end
40 undef_method :execute
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 # Checks whether there is currently no transaction active. This is done
58 # by querying the database driver, and does not use the transaction
59 # house-keeping information recorded by #increment_open_transactions and
60 # friends.
61 #
62 # Returns true if there is no transaction active, false if there is a
63 # transaction active, and nil if this information is unknown.
64 #
65 # Not all adapters supports transaction state introspection. Currently,
66 # only the PostgreSQL adapter supports this.
67 def outside_transaction?
68 nil
69 end
70
71 # Runs the given block in a database transaction, and returns the result
72 # of the block.
73 #
74 # == Nested transactions support
75 #
76 # Most databases don't support true nested transactions. At the time of
77 # writing, the only database that supports true nested transactions that
78 # we're aware of, is MS-SQL.
79 #
80 # In order to get around this problem, #transaction will emulate the effect
81 # of nested transactions, by using savepoints:
82 # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
83 # Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
84 #
85 # It is safe to call this method if a database transaction is already open,
86 # i.e. if #transaction is called within another #transaction block. In case
87 # of a nested call, #transaction will behave as follows:
88 #
89 # - The block will be run without doing anything. All database statements
90 # that happen within the block are effectively appended to the already
91 # open database transaction.
92 # - However, if +:requires_new+ is set, the block will be wrapped in a
93 # database savepoint acting as a sub-transaction.
94 #
95 # === Caveats
96 #
97 # MySQL doesn't support DDL transactions. If you perform a DDL operation,
98 # then any created savepoints will be automatically released. For example,
99 # if you've created a savepoint, then you execute a CREATE TABLE statement,
100 # then the savepoint that was created will be automatically released.
101 #
102 # This means that, on MySQL, you shouldn't execute DDL operations inside
103 # a #transaction call that you know might create a savepoint. Otherwise,
104 # #transaction will raise exceptions when it tries to release the
105 # already-automatically-released savepoints:
106 #
107 # Model.connection.transaction do # BEGIN
108 # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
109 # Model.connection.create_table(...)
110 # # active_record_1 now automatically released
111 # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
112 # end
113 def transaction(options = {})
114 options.assert_valid_keys :requires_new, :joinable
115
116 last_transaction_joinable = @transaction_joinable
117 if options.has_key?(:joinable)
118 @transaction_joinable = options[:joinable]
119 else
120 @transaction_joinable = true
121 end
122 requires_new = options[:requires_new] || !last_transaction_joinable
123
124 transaction_open = false
125 begin
126 if block_given?
127 if requires_new || open_transactions == 0
128 if open_transactions == 0
129 begin_db_transaction
130 elsif requires_new
131 create_savepoint
132 end
133 increment_open_transactions
134 transaction_open = true
135 end
136 yield
137 end
138 rescue Exception => database_transaction_rollback
139 if transaction_open && !outside_transaction?
140 transaction_open = false
141 decrement_open_transactions
142 if open_transactions == 0
143 rollback_db_transaction
144 else
145 rollback_to_savepoint
146 end
147 end
148 raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
149 end
150 ensure
151 @transaction_joinable = last_transaction_joinable
152
153 if outside_transaction?
154 @open_transactions = 0
155 elsif transaction_open
156 decrement_open_transactions
157 begin
158 if open_transactions == 0
159 commit_db_transaction
160 else
161 release_savepoint
162 end
163 rescue Exception => database_transaction_rollback
164 if open_transactions == 0
165 rollback_db_transaction
166 else
167 rollback_to_savepoint
168 end
169 raise
170 end
171 end
172 end
173
174 # Begins the transaction (and turns off auto-committing).
175 def begin_db_transaction() end
176
177 # Commits the transaction (and turns on auto-committing).
178 def commit_db_transaction() end
179
180 # Rolls back the transaction (and turns on auto-committing). Must be
181 # done if the transaction block raises an exception or returns false.
182 def rollback_db_transaction() end
183
184 # Alias for <tt>add_limit_offset!</tt>.
185 def add_limit!(sql, options)
186 add_limit_offset!(sql, options) if options
187 end
188
189 # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
190 # fragment that has the same semantics as LIMIT and OFFSET.
191 #
192 # +options+ must be a Hash which contains a +:limit+ option (required)
193 # and an +:offset+ option (optional).
194 #
195 # This method *modifies* the +sql+ parameter.
196 #
197 # ===== Examples
198 # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
199 # generates
200 # SELECT * FROM suppliers LIMIT 10 OFFSET 50
201 def add_limit_offset!(sql, options)
202 if limit = options[:limit]
203 sql << " LIMIT #{sanitize_limit(limit)}"
204 if offset = options[:offset]
205 sql << " OFFSET #{offset.to_i}"
206 end
207 end
208 sql
209 end
210
211 # Appends a locking clause to an SQL statement.
212 # This method *modifies* the +sql+ parameter.
213 # # SELECT * FROM suppliers FOR UPDATE
214 # add_lock! 'SELECT * FROM suppliers', :lock => true
215 # add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
216 def add_lock!(sql, options)
217 case lock = options[:lock]
218 when true; sql << ' FOR UPDATE'
219 when String; sql << " #{lock}"
220 end
221 end
222
223 def default_sequence_name(table, column)
224 nil
225 end
226
227 # Set the sequence to the max value of the table's column.
228 def reset_sequence!(table, column, sequence = nil)
229 # Do nothing by default. Implement for PostgreSQL, Oracle, ...
230 end
231
232 # Inserts the given fixture into the table. Overridden in adapters that require
233 # something beyond a simple insert (eg. Oracle).
234 def insert_fixture(fixture, table_name)
235 execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
236 end
237
238 def empty_insert_statement(table_name)
239 "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
240 end
241
242 def case_sensitive_equality_operator
243 "="
244 end
245
246 def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
247 "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
248 end
249
250 protected
251 # Returns an array of record hashes with the column names as keys and
252 # column values as values.
253 def select(sql, name = nil)
254 end
255 undef_method :select
256
257 # Returns the last auto-generated ID from the affected table.
258 def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
259 execute(sql, name)
260 id_value
261 end
262
263 # Executes the update statement and returns the number of rows affected.
264 def update_sql(sql, name = nil)
265 execute(sql, name)
266 end
267
268 # Executes the delete statement and returns the number of rows affected.
269 def delete_sql(sql, name = nil)
270 update_sql(sql, name)
271 end
272
273 # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
274 #
275 # +limit+ may be anything that can evaluate to a string via #to_s. It
276 # should look like an integer, or a comma-delimited list of integers.
277 #
278 # Returns the sanitized limit parameter, either as an integer, or as a
279 # string which contains a comma-delimited list of integers.
280 def sanitize_limit(limit)
281 if limit.to_s =~ /,/
282 limit.to_s.split(',').map{ |i| i.to_i }.join(',')
283 else
284 limit.to_i
285 end
286 end
287 end
288 end
289 end