aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb88
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb141
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb9
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