diff options
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
5 files changed, 136 insertions, 126 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 5ff81aa023..6d52cc344d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -62,6 +62,13 @@ module ActiveRecord exec_query(sql, name, binds) end + # Executes delete +sql+ statement in the context of this connection using + # +binds+ as the bind substitutes. +name+ is the logged along with + # the executed +sql+ statement. + def exec_delete(sql, name, binds) + exec_query(sql, name, binds) + end + # Returns the last auto-generated ID from the affected table. # # +id_value+ will be returned unless the value is nil, in @@ -82,8 +89,8 @@ module ActiveRecord end # Executes the delete statement and returns the number of rows affected. - def delete(sql, name = nil) - delete_sql(sql, name) + def delete(sql, name = nil, binds = []) + exec_delete(sql, name, binds) end # Checks whether there is currently no transaction active. This is done @@ -287,10 +294,6 @@ module ActiveRecord execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert' end - def null_insert_value - Arel.sql 'DEFAULT' - end - def empty_insert_statement_value "VALUES(DEFAULT)" end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index fc91814275..dee5c93b24 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -1,5 +1,6 @@ # encoding: utf-8 +gem 'mysql2', '~> 0.3.0' require 'mysql2' module ActiveRecord @@ -291,6 +292,14 @@ module ActiveRecord execute sql.gsub('?') { quote(*binds.shift.reverse) }, name end + def exec_delete(sql, name, binds) + binds = binds.dup + + # Pretend to support bind parameters + execute sql.gsub('?') { quote(*binds.shift.reverse) }, name + @connection.affected_rows + end + def last_inserted_id(result) @connection.last_id end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index eaadbc179b..6bc3b1548f 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -3,15 +3,8 @@ require 'active_support/core_ext/kernel/requires' require 'active_support/core_ext/object/blank' require 'set' -begin - require 'mysql' -rescue LoadError - raise "!!! Missing the mysql gem. Add it to your Gemfile: gem 'mysql'" -end - -unless defined?(Mysql::Result) && Mysql::Result.method_defined?(:each_hash) - raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'. Or use gem 'mysql2'" -end +gem 'mysql', '~> 2.8.1' +require 'mysql' class Mysql class Time @@ -402,34 +395,9 @@ module ActiveRecord def exec_query(sql, name = 'SQL', binds = []) log(sql, name, binds) do - result = nil - - cache = {} - if binds.empty? - stmt = @connection.prepare(sql) - else - cache = @statements[sql] ||= { - :stmt => @connection.prepare(sql) - } - stmt = cache[:stmt] - end - - stmt.execute(*binds.map { |col, val| - type_cast(val, col) - }) - if metadata = stmt.result_metadata - cols = cache[:cols] ||= metadata.fetch_fields.map { |field| - field.name - } - - metadata.free - result = ActiveRecord::Result.new(cols, stmt.to_a) + exec_stmt(sql, name, binds) do |cols, stmt| + ActiveRecord::Result.new(cols, stmt.to_a) if cols end - - stmt.free_result - stmt.close if binds.empty? - - result end end @@ -481,6 +449,14 @@ module ActiveRecord @connection.affected_rows end + def exec_delete(sql, name, binds) + log(sql, name, binds) do + exec_stmt(sql, name, binds) do |cols, stmt| + stmt.affected_rows + end + end + end + def begin_db_transaction #:nodoc: exec_without_stmt "BEGIN" rescue Mysql::Error @@ -840,6 +816,46 @@ module ActiveRecord end private + def exec_stmt(sql, name, binds) + cache = {} + if binds.empty? + stmt = @connection.prepare(sql) + else + cache = @statements[sql] ||= { + :stmt => @connection.prepare(sql) + } + stmt = cache[:stmt] + end + + + begin + stmt.execute(*binds.map { |col, val| type_cast(val, col) }) + rescue Mysql::Error => e + # Older versions of MySQL leave the prepared statement in a bad + # place when an error occurs. To support older mysql versions, we + # need to close the statement and delete the statement from the + # cache. + stmt.close + @statements.delete sql + raise e + end + + cols = nil + if metadata = stmt.result_metadata + cols = cache[:cols] ||= metadata.fetch_fields.map { |field| + field.name + } + end + + result = yield [cols, stmt] + + stmt.result_metadata.free if cols + stmt.free_result + stmt.close if binds.empty? + + result + end + def connect encoding = @config[:encoding] if encoding diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index aa9c9f5889..70d3d58c8f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -98,6 +98,9 @@ module ActiveRecord # XML type when 'xml' :xml + # tsvector type + when 'tsvector' + :tsvector # Arrays when /^\D+\[\]$/ :string @@ -119,6 +122,14 @@ module ActiveRecord # Extracts the value from a PostgreSQL column default definition. def self.extract_value_from_default(default) case default + # This is a performance optimization for Ruby 1.9.2 in development. + # If the value is nil, we return nil straight away without checking + # the regular expressions. If we check each regular expression, + # Regexp#=== will call NilClass#to_str, which will trigger + # method_missing (defined by whiny nil in ActiveSupport) which + # makes this method very very slow. + when NilClass + nil # Numeric types when /\A\(?(-?\d+(\.\d*)?\)?)\z/ $1 @@ -189,6 +200,11 @@ module ActiveRecord options = args.extract_options! column(args[0], 'xml', options) end + + def tsvector(*args) + options = args.extract_options! + column(args[0], 'tsvector', options) + end end ADAPTER_NAME = 'PostgreSQL' @@ -206,7 +222,8 @@ module ActiveRecord :date => { :name => "date" }, :binary => { :name => "bytea" }, :boolean => { :name => "boolean" }, - :xml => { :name => "xml" } + :xml => { :name => "xml" }, + :tsvector => { :name => "tsvector" } } # Returns 'PostgreSQL' as adapter name for identification purposes. @@ -228,7 +245,6 @@ module ActiveRecord # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil @table_alias_length = nil - @postgresql_version = nil @statements = {} connect @@ -250,28 +266,16 @@ module ActiveRecord # Is this connection alive and ready for queries? def active? - if @connection.respond_to?(:status) - @connection.status == PGconn::CONNECTION_OK - else - # We're asking the driver, not Active Record, so use @connection.query instead of #query - @connection.query 'SELECT 1' - true - end - # postgres-pr raises a NoMethodError when querying if no connection is available. - rescue PGError, NoMethodError + @connection.status == PGconn::CONNECTION_OK + rescue PGError false end # Close then reopen the connection. def reconnect! - if @connection.respond_to?(:reset) - clear_cache! - @connection.reset - configure_connection - else - disconnect! - connect - end + clear_cache! + @connection.reset + configure_connection end def reset! @@ -425,17 +429,17 @@ module ActiveRecord # REFERENTIAL INTEGRITY ==================================== - def supports_disable_referential_integrity?() #:nodoc: + def supports_disable_referential_integrity? #:nodoc: true end def disable_referential_integrity #:nodoc: - if supports_disable_referential_integrity?() then + if supports_disable_referential_integrity? then execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";")) end yield ensure - if supports_disable_referential_integrity?() then + if supports_disable_referential_integrity? then execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";")) end end @@ -509,12 +513,7 @@ module ActiveRecord # Queries the database and returns the results in an Array-like object def query(sql, name = nil) #:nodoc: log(sql, name) do - if @async - res = @connection.async_exec(sql) - else - res = @connection.exec(sql) - end - return result_as_array(res) + result_as_array @connection.async_exec(sql) end end @@ -522,11 +521,7 @@ module ActiveRecord # or raising a PGError exception otherwise. def execute(sql, name = nil) log(sql, name) do - if @async - @connection.async_exec(sql) - else - @connection.exec(sql) - end + @connection.async_exec(sql) end end @@ -535,30 +530,26 @@ module ActiveRecord end def exec_query(sql, name = 'SQL', binds = []) - return exec_no_cache(sql, name) if binds.empty? - log(sql, name, binds) do - unless @statements.key? sql - nextkey = "a#{@statements.length + 1}" - @connection.prepare nextkey, sql - @statements[sql] = nextkey - end - - key = @statements[sql] + result = binds.empty? ? exec_no_cache(sql, binds) : + exec_cache(sql, binds) - # Clear the queue - @connection.get_last_result - @connection.send_query_prepared(key, binds.map { |col, val| - type_cast(val, col) - }) - @connection.block - result = @connection.get_last_result ret = ActiveRecord::Result.new(result.fields, result_as_array(result)) result.clear return ret end end + def exec_delete(sql, name = 'SQL', binds = []) + log(sql, name, binds) do + result = binds.empty? ? exec_no_cache(sql, binds) : + exec_cache(sql, binds) + affected = result.cmd_tuples + result.clear + affected + end + end + def sql_for_insert(sql, pk, id_value, sequence_name, binds) unless pk _, table = extract_schema_and_table(sql.split(" ", 4)[2]) @@ -945,22 +936,7 @@ module ActiveRecord protected # Returns the version of the connected PostgreSQL server. def postgresql_version - @postgresql_version ||= - if @connection.respond_to?(:server_version) - @connection.server_version - else - # Mimic PGconn.server_version behavior - begin - if query('SELECT version()')[0][0] =~ /PostgreSQL ([0-9.]+)/ - major, minor, tiny = $1.split(".") - (major.to_i * 10000) + (minor.to_i * 100) + tiny.to_i - else - 0 - end - rescue - 0 - end - end + @connection.server_version end def translate_exception(exception, message) @@ -975,13 +951,26 @@ module ActiveRecord end private - def exec_no_cache(sql, name) - log(sql, name) do - result = @connection.async_exec(sql) - ret = ActiveRecord::Result.new(result.fields, result_as_array(result)) - result.clear - ret + def exec_no_cache(sql, binds) + @connection.async_exec(sql) + end + + def exec_cache(sql, binds) + unless @statements.key? sql + nextkey = "a#{@statements.length + 1}" + @connection.prepare nextkey, sql + @statements[sql] = nextkey end + + key = @statements[sql] + + # Clear the queue + @connection.get_last_result + @connection.send_query_prepared(key, binds.map { |col, val| + type_cast(val, col) + }) + @connection.block + @connection.get_last_result end # The internal PostgreSQL identifier of the money data type. @@ -993,10 +982,6 @@ module ActiveRecord # connected server's characteristics. def connect @connection = PGconn.connect(*@connection_parameters) - PGconn.translate_results = false if PGconn.respond_to?(:translate_results=) - - # Ignore async_exec and async_query when using postgres-pr. - @async = @connection.respond_to?(:async_exec) # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision @@ -1010,11 +995,7 @@ module ActiveRecord # This is called by #connect and should not be called manually. def configure_connection if @config[:encoding] - if @connection.respond_to?(:set_client_encoding) - @connection.set_client_encoding(@config[:encoding]) - else - execute("SET client_encoding TO '#{@config[:encoding]}'") - end + @connection.set_client_encoding(@config[:encoding]) end self.client_min_messages = @config[:min_messages] if @config[:min_messages] self.schema_search_path = @config[:schema_search_path] || @config[:schema_order] diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 4540cf015b..fe2868f367 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -183,6 +183,11 @@ module ActiveRecord end end + def exec_delete(sql, name = 'SQL', binds = []) + exec_query(sql, name, binds) + @connection.changes + end + def last_inserted_id(result) @connection.last_insert_row_id end @@ -357,10 +362,6 @@ module ActiveRecord alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s}) end - def null_insert_value - Arel.sql 'NULL' - end - def empty_insert_statement_value "VALUES(NULL)" end |