+++ /dev/null
-module ActiveRecord
- module ConnectionAdapters # :nodoc:
- module DatabaseStatements
- # Returns an array of record hashes with the column names as keys and
- # column values as values.
- def select_all(sql, name = nil)
- select(sql, name)
- end
-
- # Returns a record hash with the column names as keys and column values
- # as values.
- def select_one(sql, name = nil)
- result = select_all(sql, name)
- result.first if result
- end
-
- # Returns a single value from a record
- def select_value(sql, name = nil)
- if result = select_one(sql, name)
- result.values.first
- end
- end
-
- # Returns an array of the values of the first column in a select:
- # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
- def select_values(sql, name = nil)
- result = select_rows(sql, name)
- result.map { |v| v[0] }
- end
-
- # Returns an array of arrays containing the field values.
- # Order is the same as that returned by +columns+.
- def select_rows(sql, name = nil)
- end
- undef_method :select_rows
-
- # Executes the SQL statement in the context of this connection.
- def execute(sql, name = nil, skip_logging = false)
- end
- undef_method :execute
-
- # Returns the last auto-generated ID from the affected table.
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- insert_sql(sql, name, pk, id_value, sequence_name)
- end
-
- # Executes the update statement and returns the number of rows affected.
- def update(sql, name = nil)
- update_sql(sql, name)
- end
-
- # Executes the delete statement and returns the number of rows affected.
- def delete(sql, name = nil)
- delete_sql(sql, name)
- end
-
- # Checks whether there is currently no transaction active. This is done
- # by querying the database driver, and does not use the transaction
- # house-keeping information recorded by #increment_open_transactions and
- # friends.
- #
- # Returns true if there is no transaction active, false if there is a
- # transaction active, and nil if this information is unknown.
- #
- # Not all adapters supports transaction state introspection. Currently,
- # only the PostgreSQL adapter supports this.
- def outside_transaction?
- nil
- end
-
- # Runs the given block in a database transaction, and returns the result
- # of the block.
- #
- # == Nested transactions support
- #
- # Most databases don't support true nested transactions. At the time of
- # writing, the only database that supports true nested transactions that
- # we're aware of, is MS-SQL.
- #
- # In order to get around this problem, #transaction will emulate the effect
- # of nested transactions, by using savepoints:
- # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
- # Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
- #
- # It is safe to call this method if a database transaction is already open,
- # i.e. if #transaction is called within another #transaction block. In case
- # of a nested call, #transaction will behave as follows:
- #
- # - The block will be run without doing anything. All database statements
- # that happen within the block are effectively appended to the already
- # open database transaction.
- # - However, if +:requires_new+ is set, the block will be wrapped in a
- # database savepoint acting as a sub-transaction.
- #
- # === Caveats
- #
- # MySQL doesn't support DDL transactions. If you perform a DDL operation,
- # then any created savepoints will be automatically released. For example,
- # if you've created a savepoint, then you execute a CREATE TABLE statement,
- # then the savepoint that was created will be automatically released.
- #
- # This means that, on MySQL, you shouldn't execute DDL operations inside
- # a #transaction call that you know might create a savepoint. Otherwise,
- # #transaction will raise exceptions when it tries to release the
- # already-automatically-released savepoints:
- #
- # Model.connection.transaction do # BEGIN
- # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
- # Model.connection.create_table(...)
- # # active_record_1 now automatically released
- # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
- # end
- def transaction(options = {})
- options.assert_valid_keys :requires_new, :joinable
-
- last_transaction_joinable = @transaction_joinable
- if options.has_key?(:joinable)
- @transaction_joinable = options[:joinable]
- else
- @transaction_joinable = true
- end
- requires_new = options[:requires_new] || !last_transaction_joinable
-
- transaction_open = false
- begin
- if block_given?
- if requires_new || open_transactions == 0
- if open_transactions == 0
- begin_db_transaction
- elsif requires_new
- create_savepoint
- end
- increment_open_transactions
- transaction_open = true
- end
- yield
- end
- rescue Exception => database_transaction_rollback
- if transaction_open && !outside_transaction?
- transaction_open = false
- decrement_open_transactions
- if open_transactions == 0
- rollback_db_transaction
- else
- rollback_to_savepoint
- end
- end
- raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
- end
- ensure
- @transaction_joinable = last_transaction_joinable
-
- if outside_transaction?
- @open_transactions = 0
- elsif transaction_open
- decrement_open_transactions
- begin
- if open_transactions == 0
- commit_db_transaction
- else
- release_savepoint
- end
- rescue Exception => database_transaction_rollback
- if open_transactions == 0
- rollback_db_transaction
- else
- rollback_to_savepoint
- end
- raise
- end
- end
- end
-
- # Begins the transaction (and turns off auto-committing).
- def begin_db_transaction() end
-
- # Commits the transaction (and turns on auto-committing).
- def commit_db_transaction() end
-
- # Rolls back the transaction (and turns on auto-committing). Must be
- # done if the transaction block raises an exception or returns false.
- def rollback_db_transaction() end
-
- # Alias for <tt>add_limit_offset!</tt>.
- def add_limit!(sql, options)
- add_limit_offset!(sql, options) if options
- end
-
- # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
- # fragment that has the same semantics as LIMIT and OFFSET.
- #
- # +options+ must be a Hash which contains a +:limit+ option (required)
- # and an +:offset+ option (optional).
- #
- # This method *modifies* the +sql+ parameter.
- #
- # ===== Examples
- # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
- # generates
- # SELECT * FROM suppliers LIMIT 10 OFFSET 50
- def add_limit_offset!(sql, options)
- if limit = options[:limit]
- sql << " LIMIT #{sanitize_limit(limit)}"
- if offset = options[:offset]
- sql << " OFFSET #{offset.to_i}"
- end
- end
- sql
- end
-
- # Appends a locking clause to an SQL statement.
- # This method *modifies* the +sql+ parameter.
- # # SELECT * FROM suppliers FOR UPDATE
- # add_lock! 'SELECT * FROM suppliers', :lock => true
- # add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
- def add_lock!(sql, options)
- case lock = options[:lock]
- when true; sql << ' FOR UPDATE'
- when String; sql << " #{lock}"
- end
- end
-
- def default_sequence_name(table, column)
- nil
- end
-
- # Set the sequence to the max value of the table's column.
- def reset_sequence!(table, column, sequence = nil)
- # Do nothing by default. Implement for PostgreSQL, Oracle, ...
- end
-
- # Inserts the given fixture into the table. Overridden in adapters that require
- # something beyond a simple insert (eg. Oracle).
- def insert_fixture(fixture, table_name)
- execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
- end
-
- def empty_insert_statement(table_name)
- "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
- end
-
- def case_sensitive_equality_operator
- "="
- end
-
- def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
- "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
- end
-
- protected
- # Returns an array of record hashes with the column names as keys and
- # column values as values.
- def select(sql, name = nil)
- end
- undef_method :select
-
- # Returns the last auto-generated ID from the affected table.
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- execute(sql, name)
- id_value
- end
-
- # Executes the update statement and returns the number of rows affected.
- def update_sql(sql, name = nil)
- execute(sql, name)
- end
-
- # Executes the delete statement and returns the number of rows affected.
- def delete_sql(sql, name = nil)
- update_sql(sql, name)
- end
-
- # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
- #
- # +limit+ may be anything that can evaluate to a string via #to_s. It
- # should look like an integer, or a comma-delimited list of integers.
- #
- # Returns the sanitized limit parameter, either as an integer, or as a
- # string which contains a comma-delimited list of integers.
- def sanitize_limit(limit)
- if limit.to_s =~ /,/
- limit.to_s.split(',').map{ |i| i.to_i }.join(',')
- else
- limit.to_i
- end
- end
- end
- end
-end