~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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