aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-09-25 17:56:03 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-09-25 17:56:03 +0000
commitb3df95985a449fd155868b4ec04a556530a03e6c (patch)
treed5ebffef075e2dc6680c4a81adc6ad237835fbfa /activerecord
parentea654654226924f9b900e7981fdbdbd452ca15d8 (diff)
downloadrails-b3df95985a449fd155868b4ec04a556530a03e6c.tar.gz
rails-b3df95985a449fd155868b4ec04a556530a03e6c.tar.bz2
rails-b3df95985a449fd155868b4ec04a556530a03e6c.zip
Refactored the AbstractAdapter to be a lot less scary. Cleaned up the docs and style for the OSS adapters
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2339 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb126
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb73
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb168
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb137
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb536
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/mysql_adapter.rb171
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb153
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb91
-rw-r--r--activerecord/lib/active_record/schema.rb3
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
11 files changed, 792 insertions, 710 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
new file mode 100644
index 0000000000..8b94d68717
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -0,0 +1,126 @@
+module ActiveRecord
+ class Base
+ class ConnectionSpecification #:nodoc:
+ attr_reader :config, :adapter_method
+ def initialize (config, adapter_method)
+ @config, @adapter_method = config, adapter_method
+ end
+ end
+
+ # The class -> [adapter_method, config] map
+ @@defined_connections = {}
+
+ # Establishes the connection to the database. Accepts a hash as input where
+ # the :adapter key must be specified with the name of a database adapter (in lower-case)
+ # example for regular databases (MySQL, Postgresql, etc):
+ #
+ # ActiveRecord::Base.establish_connection(
+ # :adapter => "mysql",
+ # :host => "localhost",
+ # :username => "myuser",
+ # :password => "mypass",
+ # :database => "somedatabase"
+ # )
+ #
+ # Example for SQLite database:
+ #
+ # ActiveRecord::Base.establish_connection(
+ # :adapter => "sqlite",
+ # :dbfile => "path/to/dbfile"
+ # )
+ #
+ # Also accepts keys as strings (for parsing from yaml for example):
+ # ActiveRecord::Base.establish_connection(
+ # "adapter" => "sqlite",
+ # "dbfile" => "path/to/dbfile"
+ # )
+ #
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
+ # may be returned on an error.
+ def self.establish_connection(spec = nil)
+ case spec
+ when nil
+ raise AdapterNotSpecified unless defined? RAILS_ENV
+ establish_connection(RAILS_ENV)
+ when ConnectionSpecification
+ @@defined_connections[self] = spec
+ when Symbol, String
+ if configuration = configurations[spec.to_s]
+ establish_connection(configuration)
+ else
+ raise AdapterNotSpecified, "#{spec} database is not configured"
+ end
+ else
+ spec = spec.symbolize_keys
+ unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
+ adapter_method = "#{spec[:adapter]}_connection"
+ unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
+ remove_connection
+ establish_connection(ConnectionSpecification.new(spec, adapter_method))
+ end
+ end
+
+ def self.active_connections #:nodoc:
+ if threaded_connections
+ Thread.current['active_connections'] ||= {}
+ else
+ @@active_connections ||= {}
+ end
+ end
+
+ # Locate the connection of the nearest super class. This can be an
+ # active or defined connections: if it is the latter, it will be
+ # opened and set as the active connection for the class it was defined
+ # for (not necessarily the current class).
+ def self.retrieve_connection #:nodoc:
+ klass = self
+ ar_super = ActiveRecord::Base.superclass
+ until klass == ar_super
+ if conn = active_connections[klass]
+ return conn
+ elsif conn = @@defined_connections[klass]
+ klass.connection = conn
+ return self.connection
+ end
+ klass = klass.superclass
+ end
+ raise ConnectionNotEstablished
+ end
+
+ # Returns true if a connection that's accessible to this class have already been opened.
+ def self.connected?
+ klass = self
+ until klass == ActiveRecord::Base.superclass
+ if active_connections[klass]
+ return true
+ else
+ klass = klass.superclass
+ end
+ end
+ return false
+ end
+
+ # Remove the connection for this class. This will close the active
+ # connection and the defined connection (if they exist). The result
+ # can be used as argument for establish_connection, for easy
+ # re-establishing of the connection.
+ def self.remove_connection(klass=self)
+ conn = @@defined_connections[klass]
+ @@defined_connections.delete(klass)
+ active_connections[klass] = nil
+ conn.config if conn
+ end
+
+ # Set the connection for the class.
+ def self.connection=(spec)
+ raise ConnectionNotEstablished unless spec
+ conn = self.send(spec.adapter_method, spec.config)
+ active_connections[self] = conn
+ end
+
+ # Converts all strings in a hash to symbols.
+ def self.symbolize_strings_in_hash(hash) #:nodoc:
+ hash.symbolize_keys
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
new file mode 100644
index 0000000000..1e8dd045f6
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -0,0 +1,73 @@
+module ActiveRecord
+ module ConnectionAdapters # :nodoc:
+ # TODO: Document me!
+ module DatabaseStatements
+ # Returns an array of record hashes with the column names as a keys and fields as values.
+ def select_all(sql, name = nil) end
+
+ # Returns a record hash with the column names as a keys and fields as values.
+ def select_one(sql, name = nil) end
+
+ # Returns a single value from a record
+ def select_value(sql, name = nil)
+ result = select_one(sql, name)
+ result.nil? ? nil : result.values.first
+ 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_all(sql, name)
+ result.map{ |v| v.values.first }
+ end
+
+ # Executes the statement
+ def execute(sql, name = nil) end
+
+ # Returns the last auto-generated ID from the affected table.
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) end
+
+ # Executes the update statement and returns the number of rows affected.
+ def update(sql, name = nil) end
+
+ # Executes the delete statement and returns the number of rows affected.
+ def delete(sql, name = nil) end
+
+ # Wrap a block in a transaction. Returns result of block.
+ def transaction(start_db_transaction = true)
+ begin
+ if block_given?
+ begin_db_transaction if start_db_transaction
+ result = yield
+ commit_db_transaction if start_db_transaction
+ result
+ end
+ rescue Exception => database_transaction_rollback
+ rollback_db_transaction if start_db_transaction
+ raise
+ 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
+
+ def add_limit!(sql, options) #:nodoc:
+ return unless options
+ add_limit_offset!(sql, options)
+ end
+
+ def add_limit_offset!(sql, options) #:nodoc:
+ return if options[:limit].nil?
+ sql << " LIMIT #{options[:limit]}"
+ sql << " OFFSET #{options[:offset]}" if options.has_key?(:offset) and !options[:offset].nil?
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
new file mode 100644
index 0000000000..7f7cc03c7a
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -0,0 +1,42 @@
+module ActiveRecord
+ module ConnectionAdapters # :nodoc:
+ # TODO: Document me!
+ module Quoting
+ def quote(value, column = nil)
+ case value
+ when String
+ if column && column.type == :binary
+ "'#{quote_string(column.string_to_binary(value))}'" # ' (for ruby-mode)
+ elsif column && [:integer, :float].include?(column.type)
+ value.to_s
+ else
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
+ end
+ when NilClass then "NULL"
+ when TrueClass then (column && column.type == :boolean ? quoted_true : "1")
+ when FalseClass then (column && column.type == :boolean ? quoted_false : "0")
+ when Float, Fixnum, Bignum then value.to_s
+ when Date then "'#{value.to_s}'"
+ when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
+ else "'#{quote_string(value.to_yaml)}'"
+ end
+ end
+
+ def quote_string(s)
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
+ end
+
+ def quote_column_name(name)
+ name
+ end
+
+ def quoted_true
+ "'t'"
+ end
+
+ def quoted_false
+ "'f'"
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
new file mode 100644
index 0000000000..f90ac0266a
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -0,0 +1,168 @@
+module ActiveRecord
+ module ConnectionAdapters #:nodoc:
+ class Column #:nodoc:
+ attr_reader :name, :default, :type, :limit, :null
+ # The name should contain the name of the column, such as "name" in "name varchar(250)"
+ # The default should contain the type-casted default of the column, such as 1 in "count int(11) DEFAULT 1"
+ # The type parameter should either contain :integer, :float, :datetime, :date, :text, or :string
+ # The sql_type is just used for extracting the limit, such as 10 in "varchar(10)"
+ def initialize(name, default, sql_type = nil, null = true)
+ @name, @type, @null = name, simplified_type(sql_type), null
+ # have to do this one separately because type_cast depends on #type
+ @default = type_cast(default)
+ @limit = extract_limit(sql_type) unless sql_type.nil?
+ end
+
+ def klass
+ case type
+ when :integer then Fixnum
+ when :float then Float
+ when :datetime then Time
+ when :date then Date
+ when :timestamp then Time
+ when :time then Time
+ when :text, :string then String
+ when :binary then String
+ when :boolean then Object
+ end
+ end
+
+ def type_cast(value)
+ if value.nil? then return nil end
+ case type
+ when :string then value
+ when :text then value
+ when :integer then value.to_i rescue value ? 1 : 0
+ when :float then value.to_f
+ when :datetime then string_to_time(value)
+ when :timestamp then string_to_time(value)
+ when :time then string_to_dummy_time(value)
+ when :date then string_to_date(value)
+ when :binary then binary_to_string(value)
+ when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
+ else value
+ end
+ end
+
+ def human_name
+ Base.human_attribute_name(@name)
+ end
+
+ def string_to_binary(value)
+ value
+ end
+
+ def binary_to_string(value)
+ value
+ end
+
+ private
+ def string_to_date(string)
+ return string unless string.is_a?(String)
+ date_array = ParseDate.parsedate(string.to_s)
+ # treat 0000-00-00 as nil
+ Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
+ end
+
+ def string_to_time(string)
+ return string unless string.is_a?(String)
+ time_array = ParseDate.parsedate(string.to_s).compact
+ # treat 0000-00-00 00:00:00 as nil
+ Time.send(Base.default_timezone, *time_array) rescue nil
+ end
+
+ def string_to_dummy_time(string)
+ return string unless string.is_a?(String)
+ time_array = ParseDate.parsedate(string.to_s)
+ # pad the resulting array with dummy date information
+ time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
+ Time.send(Base.default_timezone, *time_array) rescue nil
+ end
+
+ def extract_limit(sql_type)
+ $1.to_i if sql_type =~ /\((.*)\)/
+ end
+
+ def simplified_type(field_type)
+ case field_type
+ when /int/i
+ :integer
+ when /float|double|decimal|numeric/i
+ :float
+ when /datetime/i
+ :datetime
+ when /timestamp/i
+ :timestamp
+ when /time/i
+ :time
+ when /date/i
+ :date
+ when /clob/i, /text/i
+ :text
+ when /blob/i, /binary/i
+ :binary
+ when /char/i, /string/i
+ :string
+ when /boolean/i
+ :boolean
+ end
+ end
+ end
+
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
+ end
+
+ class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :default, :null) #:nodoc:
+ def to_sql
+ column_sql = "#{name} #{type_to_sql(type.to_sym, limit)}"
+ add_column_options!(column_sql, :null => null, :default => default)
+ column_sql
+ end
+ alias to_s :to_sql
+
+ private
+ def type_to_sql(name, limit)
+ base.type_to_sql(name, limit) rescue name
+ end
+
+ def add_column_options!(sql, options)
+ base.add_column_options!(sql, options.merge(:column => self))
+ end
+ end
+
+ class TableDefinition #:nodoc:
+ attr_accessor :columns
+
+ def initialize(base)
+ @columns = []
+ @base = base
+ end
+
+ def primary_key(name)
+ column(name, native[:primary_key])
+ end
+
+ def [](name)
+ @columns.find {|column| column.name == name}
+ end
+
+ def column(name, type, options = {})
+ column = self[name] || ColumnDefinition.new(@base, name, type)
+ column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym]
+ column.default = options[:default]
+ column.null = options[:null]
+ @columns << column unless @columns.include? column
+ self
+ end
+
+ def to_sql
+ @columns * ', '
+ end
+
+ private
+ def native
+ @base.native_database_types
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
new file mode 100644
index 0000000000..2ce1380f96
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -0,0 +1,137 @@
+module ActiveRecord
+ module ConnectionAdapters # :nodoc:
+ # TODO: Document me!
+ module SchemaStatements
+ def native_database_types #:nodoc:
+ {}
+ end
+
+ # def tables(name = nil) end
+
+ # Returns an array of indexes for the given table.
+ # def indexes(table_name, name = nil) end
+
+ # Returns an array of column objects for the table specified by +table_name+.
+ def columns(table_name, name = nil) end
+
+
+ def create_table(name, options = {})
+ table_definition = TableDefinition.new(self)
+ table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
+
+ yield table_definition
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
+ create_sql << "#{name} ("
+ create_sql << table_definition.to_sql
+ create_sql << ") #{options[:options]}"
+ execute create_sql
+ end
+
+ def drop_table(name)
+ execute "DROP TABLE #{name}"
+ end
+
+ def add_column(table_name, column_name, type, options = {})
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
+ add_column_options!(add_column_sql, options)
+ execute(add_column_sql)
+ end
+
+ def remove_column(table_name, column_name)
+ execute "ALTER TABLE #{table_name} DROP #{column_name}"
+ end
+
+ def change_column(table_name, column_name, type, options = {})
+ raise NotImplementedError, "change_column is not implemented"
+ end
+
+ def change_column_default(table_name, column_name, default)
+ raise NotImplementedError, "change_column_default is not implemented"
+ end
+
+ def rename_column(table_name, column_name, new_column_name)
+ raise NotImplementedError, "rename_column is not implemented"
+ end
+
+ # Create a new index on the given table. By default, it will be named
+ # <code>"#{table_name}_#{column_name.to_a.first}_index"</code>, but you
+ # can explicitly name the index by passing <code>:name => "..."</code>
+ # as the last parameter. Unique indexes may be created by passing
+ # <code>:unique => true</code>.
+ def add_index(table_name, column_name, options = {})
+ index_name = "#{table_name}_#{column_name.to_a.first}_index"
+
+ if Hash === options # legacy support, since this param was a string
+ index_type = options[:unique] ? "UNIQUE" : ""
+ index_name = options[:name] || index_name
+ else
+ index_type = options
+ end
+
+ execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{column_name.to_a.join(", ")})"
+ end
+
+ # Remove the given index from the table.
+ #
+ # remove_index :my_table, :column => :foo
+ # remove_index :my_table, :name => :my_index_on_foo
+ #
+ # The first version will remove the index named
+ # <code>"#{my_table}_#{column}_index"</code> from the table. The
+ # second removes the named column from the table.
+ def remove_index(table_name, options = {})
+ if Hash === options # legacy support
+ if options[:column]
+ index_name = "#{table_name}_#{options[:column]}_index"
+ elsif options[:name]
+ index_name = options[:name]
+ else
+ raise ArgumentError, "You must specify the index name"
+ end
+ else
+ index_name = "#{table_name}_#{options}_index"
+ end
+
+ execute "DROP INDEX #{index_name} ON #{table_name}"
+ end
+
+
+ # Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
+ def structure_dump #:nodoc:
+ end
+
+ def initialize_schema_information #:nodoc:
+ begin
+ execute "CREATE TABLE schema_info (version #{type_to_sql(:integer)})"
+ execute "INSERT INTO schema_info (version) VALUES(0)"
+ rescue ActiveRecord::StatementInvalid
+ # Schema has been intialized
+ end
+ end
+
+ def dump_schema_information #:nodoc:
+ begin
+ if (current_schema = ActiveRecord::Migrator.current_version) > 0
+ return "INSERT INTO schema_info (version) VALUES (#{current_schema});"
+ end
+ rescue ActiveRecord::StatementInvalid
+ # No Schema Info
+ end
+ end
+
+
+ def type_to_sql(type, limit = nil) #:nodoc:
+ native = native_database_types[type]
+ limit ||= native[:limit]
+ column_type_sql = native[:name]
+ column_type_sql << "(#{limit})" if limit
+ column_type_sql
+ end
+
+ def add_column_options!(sql, options) #:nodoc:
+ sql << " NOT NULL" if options[:null] == false
+ sql << " DEFAULT #{quote(options[:default], options[:column])}" unless options[:default].nil?
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index b9c06a21cb..f6dfc4bfd4 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -1,6 +1,12 @@
require 'benchmark'
require 'date'
+require 'active_record/connection_adapters/abstract/schema_definitions'
+require 'active_record/connection_adapters/abstract/schema_statements'
+require 'active_record/connection_adapters/abstract/database_statements'
+require 'active_record/connection_adapters/abstract/quoting'
+require 'active_record/connection_adapters/abstract/connection_specification'
+
# Method that requires a library, ensuring that rubygems is loaded
# This is used in the database adaptors to require DB drivers. Reasons:
# (1) database drivers are the only third-party library that Rails depend upon
@@ -25,489 +31,33 @@ def require_library_or_gem(library_name)
end
module ActiveRecord
- class Base
- class ConnectionSpecification #:nodoc:
- attr_reader :config, :adapter_method
- def initialize (config, adapter_method)
- @config, @adapter_method = config, adapter_method
- end
- end
-
- # The class -> [adapter_method, config] map
- @@defined_connections = {}
-
- # Establishes the connection to the database. Accepts a hash as input where
- # the :adapter key must be specified with the name of a database adapter (in lower-case)
- # example for regular databases (MySQL, Postgresql, etc):
- #
- # ActiveRecord::Base.establish_connection(
- # :adapter => "mysql",
- # :host => "localhost",
- # :username => "myuser",
- # :password => "mypass",
- # :database => "somedatabase"
- # )
- #
- # Example for SQLite database:
- #
- # ActiveRecord::Base.establish_connection(
- # :adapter => "sqlite",
- # :dbfile => "path/to/dbfile"
- # )
- #
- # Also accepts keys as strings (for parsing from yaml for example):
- # ActiveRecord::Base.establish_connection(
- # "adapter" => "sqlite",
- # "dbfile" => "path/to/dbfile"
- # )
- #
- # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
- # may be returned on an error.
- def self.establish_connection(spec = nil)
- case spec
- when nil
- raise AdapterNotSpecified unless defined? RAILS_ENV
- establish_connection(RAILS_ENV)
- when ConnectionSpecification
- @@defined_connections[self] = spec
- when Symbol, String
- if configuration = configurations[spec.to_s]
- establish_connection(configuration)
- else
- raise AdapterNotSpecified, "#{spec} database is not configured"
- end
- else
- spec = spec.symbolize_keys
- unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
- adapter_method = "#{spec[:adapter]}_connection"
- unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
- remove_connection
- establish_connection(ConnectionSpecification.new(spec, adapter_method))
- end
- end
-
- def self.active_connections #:nodoc:
- if threaded_connections
- Thread.current['active_connections'] ||= {}
- else
- @@active_connections ||= {}
- end
- end
-
- # Locate the connection of the nearest super class. This can be an
- # active or defined connections: if it is the latter, it will be
- # opened and set as the active connection for the class it was defined
- # for (not necessarily the current class).
- def self.retrieve_connection #:nodoc:
- klass = self
- ar_super = ActiveRecord::Base.superclass
- until klass == ar_super
- if conn = active_connections[klass]
- return conn
- elsif conn = @@defined_connections[klass]
- klass.connection = conn
- return self.connection
- end
- klass = klass.superclass
- end
- raise ConnectionNotEstablished
- end
-
- # Returns true if a connection that's accessible to this class have already been opened.
- def self.connected?
- klass = self
- until klass == ActiveRecord::Base.superclass
- if active_connections[klass]
- return true
- else
- klass = klass.superclass
- end
- end
- return false
- end
-
- # Remove the connection for this class. This will close the active
- # connection and the defined connection (if they exist). The result
- # can be used as argument for establish_connection, for easy
- # re-establishing of the connection.
- def self.remove_connection(klass=self)
- conn = @@defined_connections[klass]
- @@defined_connections.delete(klass)
- active_connections[klass] = nil
- conn.config if conn
- end
-
- # Set the connection for the class.
- def self.connection=(spec)
- raise ConnectionNotEstablished unless spec
- conn = self.send(spec.adapter_method, spec.config)
- active_connections[self] = conn
- end
-
- # Converts all strings in a hash to symbols.
- def self.symbolize_strings_in_hash(hash) #:nodoc:
- hash.symbolize_keys
- end
- end
-
module ConnectionAdapters # :nodoc:
- class Column # :nodoc:
- attr_reader :name, :default, :type, :limit, :null
- # The name should contain the name of the column, such as "name" in "name varchar(250)"
- # The default should contain the type-casted default of the column, such as 1 in "count int(11) DEFAULT 1"
- # The type parameter should either contain :integer, :float, :datetime, :date, :text, or :string
- # The sql_type is just used for extracting the limit, such as 10 in "varchar(10)"
- def initialize(name, default, sql_type = nil, null = true)
- @name, @type, @null = name, simplified_type(sql_type), null
- # have to do this one separately because type_cast depends on #type
- @default = type_cast(default)
- @limit = extract_limit(sql_type) unless sql_type.nil?
- end
-
- def klass
- case type
- when :integer then Fixnum
- when :float then Float
- when :datetime then Time
- when :date then Date
- when :timestamp then Time
- when :time then Time
- when :text, :string then String
- when :binary then String
- when :boolean then Object
- end
- end
-
- def type_cast(value)
- if value.nil? then return nil end
- case type
- when :string then value
- when :text then value
- when :integer then value.to_i rescue value ? 1 : 0
- when :float then value.to_f
- when :datetime then string_to_time(value)
- when :timestamp then string_to_time(value)
- when :time then string_to_dummy_time(value)
- when :date then string_to_date(value)
- when :binary then binary_to_string(value)
- when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
- else value
- end
- end
-
- def human_name
- Base.human_attribute_name(@name)
- end
-
- def string_to_binary(value)
- value
- end
-
- def binary_to_string(value)
- value
- end
-
- private
- def string_to_date(string)
- return string unless string.is_a?(String)
- date_array = ParseDate.parsedate(string.to_s)
- # treat 0000-00-00 as nil
- Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
- end
-
- def string_to_time(string)
- return string unless string.is_a?(String)
- time_array = ParseDate.parsedate(string.to_s).compact
- # treat 0000-00-00 00:00:00 as nil
- Time.send(Base.default_timezone, *time_array) rescue nil
- end
-
- def string_to_dummy_time(string)
- return string unless string.is_a?(String)
- time_array = ParseDate.parsedate(string.to_s)
- # pad the resulting array with dummy date information
- time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
- Time.send(Base.default_timezone, *time_array) rescue nil
- end
-
- def extract_limit(sql_type)
- $1.to_i if sql_type =~ /\((.*)\)/
- end
-
- def simplified_type(field_type)
- case field_type
- when /int/i
- :integer
- when /float|double|decimal|numeric/i
- :float
- when /datetime/i
- :datetime
- when /timestamp/i
- :timestamp
- when /time/i
- :time
- when /date/i
- :date
- when /clob/i, /text/i
- :text
- when /blob/i, /binary/i
- :binary
- when /char/i, /string/i
- :string
- when /boolean/i
- :boolean
- end
- end
- end
-
# All the concrete database adapters follow the interface laid down in this class.
# You can use this interface directly by borrowing the database connection from the Base with
# Base.connection.
class AbstractAdapter
+ include Quoting, DatabaseStatements, SchemaStatements
@@row_even = true
- def initialize(connection, logger = nil) # :nodoc:
+ def initialize(connection, logger = nil) #:nodoc:
@connection, @logger = connection, logger
@runtime = 0
end
- # Returns an array of record hashes with the column names as a keys and fields as values.
- def select_all(sql, name = nil) end
-
- # Returns a record hash with the column names as a keys and fields as values.
- def select_one(sql, name = nil) end
-
- # Returns a single value from a record
- def select_value(sql, name = nil)
- result = select_one(sql, name)
- result.nil? ? nil : result.values.first
- 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_all(sql, name)
- result.map{ |v| v.values.first }
- end
-
- # Returns an array of table names for the current database.
- # def tables(name = nil) end
-
- # Returns an array of indexes for the given table.
- # def indexes(table_name, name = nil) end
-
- # Returns an array of column objects for the table specified by +table_name+.
- def columns(table_name, name = nil) end
-
- # Returns the last auto-generated ID from the affected table.
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) end
-
- # Executes the update statement and returns the number of rows affected.
- def update(sql, name = nil) end
-
- # Executes the delete statement and returns the number of rows affected.
- def delete(sql, name = nil) end
-
- def reset_runtime # :nodoc:
- rt = @runtime
- @runtime = 0
- return rt
- end
-
- # Wrap a block in a transaction. Returns result of block.
- def transaction(start_db_transaction = true)
- begin
- if block_given?
- begin_db_transaction if start_db_transaction
- result = yield
- commit_db_transaction if start_db_transaction
- result
- end
- rescue Exception => database_transaction_rollback
- rollback_db_transaction if start_db_transaction
- raise
- 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
-
- def quoted_true() "'t'" end
- def quoted_false() "'f'" end
-
- def quote(value, column = nil)
- case value
- when String
- if column && column.type == :binary
- "'#{quote_string(column.string_to_binary(value))}'" # ' (for ruby-mode)
- elsif column && [:integer, :float].include?(column.type)
- value.to_s
- else
- "'#{quote_string(value)}'" # ' (for ruby-mode)
- end
- when NilClass then "NULL"
- when TrueClass then (column && column.type == :boolean ? quoted_true : "1")
- when FalseClass then (column && column.type == :boolean ? quoted_false : "0")
- when Float, Fixnum, Bignum then value.to_s
- when Date then "'#{value.to_s}'"
- when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
- else "'#{quote_string(value.to_yaml)}'"
- end
- end
-
- def quote_string(s)
- s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
- end
-
- def quote_column_name(name)
- name
- end
-
# Returns the human-readable name of the adapter. Use mixed case - one can always use downcase if needed.
- def adapter_name()
+ def adapter_name
'Abstract'
end
-
- # Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
- def structure_dump() end
-
- def add_limit!(sql, options)
- return unless options
- add_limit_offset!(sql, options)
- end
-
- def add_limit_offset!(sql, options)
- return if options[:limit].nil?
- sql << " LIMIT #{options[:limit]}"
- sql << " OFFSET #{options[:offset]}" if options.has_key?(:offset) and !options[:offset].nil?
- end
-
-
- def initialize_schema_information
- begin
- execute "CREATE TABLE schema_info (version #{type_to_sql(:integer)})"
- execute "INSERT INTO schema_info (version) VALUES(0)"
- rescue ActiveRecord::StatementInvalid
- # Schema has been intialized
- end
- end
-
- def dump_schema_information
- begin
- if (current_schema = ActiveRecord::Migrator.current_version) > 0
- return "INSERT INTO schema_info (version) VALUES (#{current_schema});"
- end
- rescue ActiveRecord::StatementInvalid
- # No Schema Info
- end
- end
-
- def create_table(name, options = {})
- table_definition = TableDefinition.new(self)
- table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
-
- yield table_definition
- create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
- create_sql << "#{name} ("
- create_sql << table_definition.to_sql
- create_sql << ") #{options[:options]}"
- execute create_sql
- end
-
- def drop_table(name)
- execute "DROP TABLE #{name}"
- end
-
- def add_column(table_name, column_name, type, options = {})
- add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
- add_column_options!(add_column_sql, options)
- execute(add_column_sql)
- end
-
- def remove_column(table_name, column_name)
- execute "ALTER TABLE #{table_name} DROP #{column_name}"
- end
-
- def change_column(table_name, column_name, type, options = {})
- raise NotImplementedError, "change_column is not implemented"
- end
-
- def change_column_default(table_name, column_name, default)
- raise NotImplementedError, "change_column_default is not implemented"
- end
-
- def rename_column(table_name, column_name, new_column_name)
- raise NotImplementedError, "rename_column is not implemented"
- end
-
- # Create a new index on the given table. By default, it will be named
- # <code>"#{table_name}_#{column_name.to_a.first}_index"</code>, but you
- # can explicitly name the index by passing <code>:name => "..."</code>
- # as the last parameter. Unique indexes may be created by passing
- # <code>:unique => true</code>.
- def add_index(table_name, column_name, options = {})
- index_name = "#{table_name}_#{column_name.to_a.first}_index"
-
- if Hash === options # legacy support, since this param was a string
- index_type = options[:unique] ? "UNIQUE" : ""
- index_name = options[:name] || index_name
- else
- index_type = options
- end
-
- execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{column_name.to_a.join(", ")})"
- end
-
- # Remove the given index from the table.
- #
- # remove_index :my_table, :column => :foo
- # remove_index :my_table, :name => :my_index_on_foo
- #
- # The first version will remove the index named
- # <code>"#{my_table}_#{column}_index"</code> from the table. The
- # second removes the named column from the table.
- def remove_index(table_name, options = {})
- if Hash === options # legacy support
- if options[:column]
- index_name = "#{table_name}_#{options[:column]}_index"
- elsif options[:name]
- index_name = options[:name]
- else
- raise ArgumentError, "You must specify the index name"
- end
- else
- index_name = "#{table_name}_#{options}_index"
- end
-
- execute "DROP INDEX #{index_name} ON #{table_name}"
- end
+ # Returns true for database adapters that has implemented the schema statements.
def supports_migrations?
false
- end
-
- def native_database_types
- {}
- end
+ end
- def type_to_sql(type, limit = nil)
- native = native_database_types[type]
- limit ||= native[:limit]
- column_type_sql = native[:name]
- column_type_sql << "(#{limit})" if limit
- column_type_sql
- end
-
- def add_column_options!(sql, options)
- sql << " NOT NULL" if options[:null] == false
- sql << " DEFAULT #{quote(options[:default], options[:column])}" unless options[:default].nil?
+ def reset_runtime #:nodoc:
+ rt = @runtime
+ @runtime = 0
+ return rt
end
protected
@@ -561,61 +111,5 @@ module ActiveRecord
end
end
end
-
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns)
- end
-
- class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :default, :null)
- def to_sql
- column_sql = "#{name} #{type_to_sql(type.to_sym, limit)}"
- add_column_options!(column_sql, :null => null, :default => default)
- column_sql
- end
- alias to_s :to_sql
-
- private
- def type_to_sql(name, limit)
- base.type_to_sql(name, limit) rescue name
- end
-
- def add_column_options!(sql, options)
- base.add_column_options!(sql, options.merge(:column => self))
- end
- end
-
- class TableDefinition
- attr_accessor :columns
-
- def initialize(base)
- @columns = []
- @base = base
- end
-
- def primary_key(name)
- column(name, native[:primary_key])
- end
-
- def [](name)
- @columns.find {|column| column.name == name}
- end
-
- def column(name, type, options = {})
- column = self[name] || ColumnDefinition.new(@base, name, type)
- column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym]
- column.default = options[:default]
- column.null = options[:null]
- @columns << column unless @columns.include? column
- self
- end
-
- def to_sql
- @columns * ', '
- end
-
- private
- def native
- @base.native_database_types
- end
- end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index eea47817f6..98ac1fae67 100755
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -81,12 +81,21 @@ module ActiveRecord
"Lost connection to MySQL server during query",
"MySQL server has gone away"
]
-
- def supports_migrations?
+
+ def initialize(connection, logger, connection_options=nil)
+ super(connection, logger)
+ @connection_options = connection_options
+ end
+
+ def adapter_name #:nodoc:
+ 'MySQL'
+ end
+
+ def supports_migrations? #:nodoc:
true
end
- def native_database_types
+ def native_database_types #:nodoc
{
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
:string => { :name => "varchar", :limit => 255 },
@@ -102,58 +111,42 @@ module ActiveRecord
}
end
- def initialize(connection, logger, connection_options=nil)
- super(connection, logger)
- @connection_options = connection_options
- end
- def adapter_name
- 'MySQL'
+ # QUOTING ==================================================
+
+ def quote_column_name(name) #:nodoc:
+ "`#{name}`"
end
- def select_all(sql, name = nil)
- select(sql, name)
+ def quote_string(string) #:nodoc:
+ Mysql::quote(string)
end
- def select_one(sql, name = nil)
- result = select(sql, name)
- result.nil? ? nil : result.first
+ def quoted_true
+ "1"
+ end
+
+ def quoted_false
+ "0"
end
- def tables(name = nil)
- tables = []
- execute("SHOW TABLES", name).each { |field| tables << field[0] }
- tables
+ def quote_column_name(name)
+ "`#{name}`"
end
- def indexes(table_name, name = nil)
- indexes = []
- current_index = nil
- execute("SHOW KEYS FROM #{table_name}", name).each do |row|
- if current_index != row[2]
- next if row[2] == "PRIMARY" # skip the primary key
- current_index = row[2]
- indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
- end
- indexes.last.columns << row[4]
- end
- indexes
- end
+ # DATABASE STATEMENTS ======================================
- def columns(table_name, name = nil)
- sql = "SHOW FIELDS FROM #{table_name}"
- columns = []
- execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
- columns
+ def select_all(sql, name = nil) #:nodoc:
+ select(sql, name)
end
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- execute(sql, name = nil)
- id_value || @connection.insert_id
+ def select_one(sql, name = nil) #:nodoc:
+ result = select(sql, name)
+ result.nil? ? nil : result.first
end
- def execute(sql, name = nil, retries = 2)
+ def execute(sql, name = nil, retries = 2) #:nodoc:
unless @logger
@connection.query(sql)
else
@@ -175,52 +168,39 @@ module ActiveRecord
end
end
- def update(sql, name = nil)
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+ execute(sql, name = nil)
+ id_value || @connection.insert_id
+ end
+
+ def update(sql, name = nil) #:nodoc:
execute(sql, name)
@connection.affected_rows
end
- alias_method :delete, :update
+ alias_method :delete, :update #:nodoc:
- def begin_db_transaction
+ def begin_db_transaction #:nodoc:
execute "BEGIN"
rescue Exception
# Transactions aren't supported
end
- def commit_db_transaction
+ def commit_db_transaction #:nodoc:
execute "COMMIT"
rescue Exception
# Transactions aren't supported
end
- def rollback_db_transaction
+ def rollback_db_transaction #:nodoc:
execute "ROLLBACK"
rescue Exception
# Transactions aren't supported
end
- def quoted_true() "1" end
- def quoted_false() "0" end
-
- def quote_column_name(name)
- "`#{name}`"
- end
-
- def quote_string(string)
- Mysql::quote(string)
- end
-
-
- def structure_dump
- select_all("SHOW TABLES").inject("") do |structure, table|
- structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
- end
- end
-
- def add_limit_offset!(sql, options)
+ def add_limit_offset!(sql, options) #:nodoc
if options[:limit]
if options[:offset].blank?
sql << " LIMIT #{options[:limit]}"
@@ -230,26 +210,68 @@ module ActiveRecord
end
end
- def recreate_database(name)
+
+ # SCHEMA STATEMENTS ========================================
+
+ def structure_dump #:nodoc:
+ select_all("SHOW TABLES").inject("") do |structure, table|
+ structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
+ end
+ end
+
+ def recreate_database(name) #:nodoc:
drop_database(name)
create_database(name)
end
- def drop_database(name)
+ def create_database(name) #:nodoc:
+ execute "CREATE DATABASE #{name}"
+ end
+
+ def drop_database(name) #:nodoc:
execute "DROP DATABASE IF EXISTS #{name}"
end
- def create_database(name)
- execute "CREATE DATABASE #{name}"
+
+ def tables(name = nil) #:nodoc:
+ tables = []
+ execute("SHOW TABLES", name).each { |field| tables << field[0] }
+ tables
end
-
- def change_column_default(table_name, column_name, default)
+
+ def indexes(table_name, name = nil)#:nodoc:
+ indexes = []
+ current_index = nil
+ execute("SHOW KEYS FROM #{table_name}", name).each do |row|
+ if current_index != row[2]
+ next if row[2] == "PRIMARY" # skip the primary key
+ current_index = row[2]
+ indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
+ end
+
+ indexes.last.columns << row[4]
+ end
+ indexes
+ end
+
+ def columns(table_name, name = nil)#:nodoc:
+ sql = "SHOW FIELDS FROM #{table_name}"
+ columns = []
+ execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
+ columns
+ end
+
+ def create_table(name, options = {}) #:nodoc:
+ super(name, {:options => "ENGINE=InnoDB"}.merge(options))
+ end
+
+ def change_column_default(table_name, column_name, default) #:nodoc:
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
change_column(table_name, column_name, current_type, { :default => default })
end
- def change_column(table_name, column_name, type, options = {})
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
options[:default] ||= select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Default"]
change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
@@ -257,14 +279,11 @@ module ActiveRecord
execute(change_column_sql)
end
- def rename_column(table_name, column_name, new_column_name)
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
end
- def create_table(name, options = {})
- super(name, {:options => "ENGINE=InnoDB"}.merge(options))
- end
private
def select(sql, name = nil)
@@ -278,4 +297,4 @@ module ActiveRecord
end
end
end
-end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 0fe2517657..254b1a04a8 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,14 +1,4 @@
-
-# postgresql_adaptor.rb
-# author: Luke Holden <lholden@cablelan.net>
-# notes: Currently this adaptor does not pass the test_zero_date_fields
-# and test_zero_datetime_fields unit tests in the BasicsTest test
-# group.
-#
-# This is due to the fact that, in postgresql you can not have a
-# totally zero timestamp. Instead null/nil should be used to
-# represent no value.
-#
+# Author: Luke Holden <lholden@cablelan.net>
require 'active_record/connection_adapters/abstract_adapter'
require 'parsedate'
@@ -60,6 +50,10 @@ module ActiveRecord
# * <tt>:encoding</tt> -- An optional client encoding that is using in a SET client_encoding TO <encoding> call on connection.
# * <tt>:min_messages</tt> -- An optional client min messages that is using in a SET client_min_messages TO <min_messages> call on connection.
class PostgreSQLAdapter < AbstractAdapter
+ def adapter_name
+ 'PostgreSQL'
+ end
+
def native_database_types
{
:primary_key => "serial primary key",
@@ -80,17 +74,71 @@ module ActiveRecord
true
end
- def select_all(sql, name = nil)
+
+ # QUOTING ==================================================
+
+ def quote(value, column = nil)
+ if value.class == String && column && column.type == :binary
+ quote_bytea(value)
+ else
+ super
+ end
+ end
+
+ def quote_column_name(name)
+ %("#{name}")
+ end
+
+
+ # DATABASE STATEMENTS ======================================
+
+ def select_all(sql, name = nil) #:nodoc:
select(sql, name)
end
- def select_one(sql, name = nil)
+ def select_one(sql, name = nil) #:nodoc:
result = select(sql, name)
result.nil? ? nil : result.first
end
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+ execute(sql, name)
+ table = sql.split(" ", 4)[2]
+ return id_value || last_insert_id(table, pk)
+ end
+
+ def query(sql, name = nil) #:nodoc:
+ log(sql, name) { @connection.query(sql) }
+ end
+
+ def execute(sql, name = nil) #:nodoc:
+ log(sql, name) { @connection.exec(sql) }
+ end
+
+ def update(sql, name = nil) #:nodoc:
+ execute(sql, name).cmdtuples
+ end
+
+ alias_method :delete, :update #:nodoc:
+
+
+ def begin_db_transaction #:nodoc:
+ execute "BEGIN"
+ end
+
+ def commit_db_transaction #:nodoc:
+ execute "COMMIT"
+ end
+
+ def rollback_db_transaction #:nodoc:
+ execute "ROLLBACK"
+ end
+
+
+ # SCHEMA STATEMENTS ========================================
+
# Return the list of all tables in the schema search path.
- def tables(name = nil)
+ def tables(name = nil) #:nodoc:
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
query(<<-SQL, name).map { |row| row[0] }
SELECT tablename
@@ -99,7 +147,7 @@ module ActiveRecord
SQL
end
- def indexes(table_name, name = nil)
+ def indexes(table_name, name = nil) #:nodoc:
result = query(<<-SQL, name)
SELECT i.relname, d.indisunique, a.attname
FROM pg_class t, pg_class i, pg_index d, pg_attribute a
@@ -132,33 +180,27 @@ module ActiveRecord
indexes
end
- def columns(table_name, name = nil)
+ def columns(table_name, name = nil) #:nodoc:
column_definitions(table_name).collect do |name, type, default, notnull|
Column.new(name, default_value(default), translate_field_type(type),
notnull == "f")
end
end
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- execute(sql, name)
- table = sql.split(" ", 4)[2]
- return id_value || last_insert_id(table, pk)
- end
-
- def query(sql, name = nil)
- log(sql, name) { @connection.query(sql) }
- end
-
- def execute(sql, name = nil)
- log(sql, name) { @connection.exec(sql) }
+ # Set the schema search path to a string of comma-separated schema names.
+ # Names beginning with $ are quoted (e.g. $user => '$user')
+ # See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html
+ def schema_search_path=(schema_csv) #:nodoc:
+ if schema_csv
+ execute "SET search_path TO #{schema_csv}"
+ @schema_search_path = nil
+ end
end
- def update(sql, name = nil)
- execute(sql, name).cmdtuples
+ def schema_search_path #:nodoc:
+ @schema_search_path ||= query('SHOW search_path')[0][0]
end
-
- alias_method :delete, :update
-
+
def add_column(table_name, column_name, type, options = {})
native_type = native_database_types[type]
sql_commands = ["ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"]
@@ -170,57 +212,21 @@ module ActiveRecord
end
sql_commands.each { |cmd| execute(cmd) }
end
-
-
- def begin_db_transaction() execute "BEGIN" end
- def commit_db_transaction() execute "COMMIT" end
- def rollback_db_transaction() execute "ROLLBACK" end
- def quote(value, column = nil)
- if value.class == String && column && column.type == :binary
- quote_bytea(value)
- else
- super
- end
- end
-
- def quote_column_name(name)
- %("#{name}")
- end
-
- def adapter_name
- 'PostgreSQL'
- end
-
-
- # Set the schema search path to a string of comma-separated schema names.
- # Names beginning with $ are quoted (e.g. $user => '$user')
- # See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html
- def schema_search_path=(schema_csv)
- if schema_csv
- execute "SET search_path TO #{schema_csv}"
- @schema_search_path = nil
- end
- end
-
- def schema_search_path
- @schema_search_path ||= query('SHOW search_path')[0][0]
- end
-
- def change_column(table_name, column_name, type, options = {})
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
execute = "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type}"
change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
end
- def change_column_default(table_name, column_name, default)
+ def change_column_default(table_name, column_name, default) #:nodoc:
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT '#{default}'"
end
- def rename_column(table_name, column_name, new_column_name)
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
end
- def remove_index(table_name, options)
+ def remove_index(table_name, options) #:nodoc:
if Hash === options
index_name = options[:name]
else
@@ -229,6 +235,7 @@ module ActiveRecord
execute "DROP INDEX #{index_name}"
end
+
private
BYTEA_COLUMN_TYPE_OID = 17
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index ab42490020..d1929f74e4 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,6 +1,5 @@
-# sqlite_adapter.rb
-# author: Luke Holden <lholden@cablelan.net>
-# updated for SQLite3: Jamis Buck <jamis_buck@byu.edu>
+# Author: Luke Holden <lholden@cablelan.net>
+# Updated for SQLite3: Jamis Buck <jamis_buck@byu.edu>
require 'active_record/connection_adapters/abstract_adapter'
@@ -87,7 +86,15 @@ module ActiveRecord
#
# * <tt>:dbfile</tt> -- Path to the database file.
class SQLiteAdapter < AbstractAdapter
- def native_database_types
+ def adapter_name #:nodoc:
+ 'SQLite'
+ end
+
+ def supports_migrations? #:nodoc:
+ true
+ end
+
+ def native_database_types #:nodoc:
{
:primary_key => "INTEGER PRIMARY KEY NOT NULL",
:string => { :name => "varchar", :limit => 255 },
@@ -103,32 +110,41 @@ module ActiveRecord
}
end
- def supports_migrations?
- true
+
+ # QUOTING ==================================================
+
+ def quote_string(s) #:nodoc:
+ @connection.class.quote(s)
+ end
+
+ def quote_column_name(name) #:nodoc:
+ "'#{name}'"
end
- def execute(sql, name = nil)
- #log(sql, name, @connection) { |connection| connection.execute(sql) }
+
+ # DATABASE STATEMENTS ======================================
+
+ def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.execute(sql) }
end
- def update(sql, name = nil)
+ def update(sql, name = nil) #:nodoc:
execute(sql, name)
@connection.changes
end
- def delete(sql, name = nil)
+ def delete(sql, name = nil) #:nodoc:
sql += " WHERE 1=1" unless sql =~ /WHERE/i
execute(sql, name)
@connection.changes
end
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
execute(sql, name = nil)
id_value || @connection.last_insert_row_id
end
- def select_all(sql, name = nil)
+ def select_all(sql, name = nil) #:nodoc:
execute(sql, name).map do |row|
record = {}
row.each_key do |key|
@@ -140,29 +156,40 @@ module ActiveRecord
end
end
- def select_one(sql, name = nil)
+ def select_one(sql, name = nil) #:nodoc:
result = select_all(sql, name)
result.nil? ? nil : result.first
end
- def begin_db_transaction() @connection.transaction end
- def commit_db_transaction() @connection.commit end
- def rollback_db_transaction() @connection.rollback end
+ def begin_db_transaction #:nodoc:
+ @connection.transaction
+ end
+
+ def commit_db_transaction #:nodoc:
+ @connection.commit
+ end
+
+ def rollback_db_transaction #:nodoc:
+ @connection.rollback
+ end
- def tables(name = nil)
+
+ # SCHEMA STATEMENTS ========================================
+
+ def tables(name = nil) #:nodoc:
execute("SELECT name FROM sqlite_master WHERE type = 'table'", name).map do |row|
row[0]
end
end
- def columns(table_name, name = nil)
+ def columns(table_name, name = nil) #:nodoc:
table_structure(table_name).map { |field|
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'] == "0")
}
end
- def indexes(table_name, name = nil)
+ def indexes(table_name, name = nil) #:nodoc:
execute("PRAGMA index_list(#{table_name})", name).map do |row|
index = IndexDefinition.new(table_name, row['name'])
index.unique = row['unique'] != '0'
@@ -171,24 +198,12 @@ module ActiveRecord
end
end
- def primary_key(table_name)
+ def primary_key(table_name) #:nodoc:
column = table_structure(table_name).find {|field| field['pk'].to_i == 1}
column ? column['name'] : nil
end
- def quote_string(s)
- @connection.class.quote(s)
- end
-
- def quote_column_name(name)
- "'#{name}'"
- end
-
- def adapter_name()
- 'SQLite'
- end
-
- def remove_index(table_name, options={})
+ def remove_index(table_name, options={}) #:nodoc:
if Hash === options
index_name = options[:name]
else
@@ -198,25 +213,25 @@ module ActiveRecord
execute "DROP INDEX #{index_name}"
end
- def add_column(table_name, column_name, type, options = {})
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
alter_table(table_name) do |definition|
definition.column(column_name, type, options)
end
end
- def remove_column(table_name, column_name)
+ def remove_column(table_name, column_name) #:nodoc:
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
end
end
- def change_column_default(table_name, column_name, default)
+ def change_column_default(table_name, column_name, default) #:nodoc:
alter_table(table_name) do |definition|
definition[column_name].default = default
end
end
- def change_column(table_name, column_name, type, options = {})
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
alter_table(table_name) do |definition|
definition[column_name].instance_eval do
self.type = type
@@ -226,7 +241,7 @@ module ActiveRecord
end
end
- def rename_column(table_name, column_name, new_column_name)
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
alter_table(table_name, :rename => {column_name => new_column_name})
end
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index e25385bf06..8527d54697 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -1,5 +1,6 @@
module ActiveRecord
- class Schema < Migration #:nodoc:
+ # TODO: Document me!
+ class Schema < Migration
private_class_method :new
def self.define(info={}, &block)
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 592b2a7068..0f51de58af 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -1,7 +1,7 @@
module ActiveRecord
# This class is used to dump the database schema for some connection to some
# output format (i.e., ActiveRecord::Schema).
- class SchemaDumper
+ class SchemaDumper #:nodoc:
private_class_method :new
def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)