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/quoting.rb81
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb79
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb134
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb271
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/type.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/binary.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/boolean.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/date.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/date_time.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/decimal.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/float.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/integer.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/string.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/text.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/time.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/value.rb17
25 files changed, 354 insertions, 367 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 75501852ed..f836e60988 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -9,26 +9,22 @@ module ActiveRecord
# records are quoted as their primary key
return value.quoted_id if value.respond_to?(:quoted_id)
+ # FIXME: The only case we get an object other than nil or a real column
+ # is `SchemaStatements#add_column` with a PG array that has a non-empty default
+ # value. Is this really the only case? Are we missing tests for other types?
+ # We should have a real column object passed (or nil) here, and check for that
+ # instead
+ if column.respond_to?(:type_cast_for_database)
+ value = column.type_cast_for_database(value)
+ end
+
case value
when String, ActiveSupport::Multibyte::Chars
- value = value.to_s
- return "'#{quote_string(value)}'" unless column
-
- case column.type
- when :integer then value.to_i.to_s
- when :float then value.to_f.to_s
- else
- "'#{quote_string(value)}'"
- end
-
- when true, false
- if column && column.type == :integer
- value ? '1' : '0'
- else
- value ? quoted_true : quoted_false
- end
- # BigDecimals need to be put in a non-normalized form and quoted.
+ "'#{quote_string(value.to_s)}'"
+ when true then quoted_true
+ when false then quoted_false
when nil then "NULL"
+ # BigDecimals need to be put in a non-normalized form and quoted.
when BigDecimal then value.to_s('F')
when Numeric, ActiveSupport::Duration then value.to_s
when Date, Time then "'#{quoted_date(value)}'"
@@ -47,30 +43,25 @@ module ActiveRecord
return value.id
end
- case value
- when String, ActiveSupport::Multibyte::Chars
- value = value.to_s
- return value unless column
-
- case column.type
- when :integer then value.to_i
- when :float then value.to_f
- else
- value
- end
+ # FIXME: The only case we get an object other than nil or a real column
+ # is `SchemaStatements#add_column` with a PG array that has a non-empty default
+ # value. Is this really the only case? Are we missing tests for other types?
+ # We should have a real column object passed (or nil) here, and check for that
+ # instead
+ if column.respond_to?(:type_cast_for_database)
+ value = column.type_cast_for_database(value)
+ end
- when true, false
- if column && column.type == :integer
- value ? 1 : 0
- else
- value ? 't' : 'f'
- end
- # BigDecimals need to be put in a non-normalized form and quoted.
- when nil then nil
+ case value
+ when Symbol, ActiveSupport::Multibyte::Chars
+ value.to_s
+ when true then unquoted_true
+ when false then unquoted_false
+ # BigDecimals need to be put in a non-normalized form and quoted.
when BigDecimal then value.to_s('F')
- when Numeric then value
when Date, Time then quoted_date(value)
- when Symbol then value.to_s
+ when *types_which_need_no_typecasting
+ value
else
to_type = column ? " to #{column.type}" : ""
raise TypeError, "can't cast #{value.class}#{to_type}"
@@ -109,10 +100,18 @@ module ActiveRecord
"'t'"
end
+ def unquoted_true
+ 't'
+ end
+
def quoted_false
"'f'"
end
+ def unquoted_false
+ 'f'
+ end
+
def quoted_date(value)
if value.acts_like?(:time)
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -124,6 +123,12 @@ module ActiveRecord
value.to_s(:db)
end
+
+ private
+
+ def types_which_need_no_typecasting
+ [nil, Numeric, String]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index cdf0cbe218..ac14740cfe 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -20,15 +20,8 @@ module ActiveRecord
def prepare_column_options(column, types)
spec = {}
spec[:name] = column.name.inspect
-
- # AR has an optimization which handles zero-scale decimals as integers. This
- # code ensures that the dumper still dumps the column as a decimal.
- spec[:type] = if column.type == :integer && /^(numeric|decimal)/ =~ column.sql_type
- 'decimal'
- else
- column.type.to_s
- end
- spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit] && spec[:type] != 'decimal'
+ spec[:type] = column.type.to_s
+ spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit]
spec[:precision] = column.precision.inspect if column.precision
spec[:scale] = column.scale.inspect if column.scale
spec[:null] = 'false' unless column.null
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index ea7703c82c..6ecd4efdc8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -396,7 +396,8 @@ module ActiveRecord
precision = extract_precision(sql_type)
if scale == 0
- Type::Integer.new(precision: precision)
+ # FIXME: Remove this class as well
+ Type::DecimalWithoutScale.new(precision: precision)
else
Type::Decimal.new(precision: precision, scale: scale)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index d3f8470c30..82e62786ca 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -178,17 +178,6 @@ module ActiveRecord
true
end
- def type_cast(value, column)
- case value
- when TrueClass
- 1
- when FalseClass
- 0
- else
- super
- end
- end
-
# MySQL 4 technically support transaction isolation, but it is affected by a bug
# where the transaction level gets persisted for the whole session:
#
@@ -234,8 +223,6 @@ module ActiveRecord
if value.kind_of?(String) && column && column.type == :binary
s = value.unpack("H*")[0]
"x'#{s}'"
- elsif value.kind_of?(BigDecimal)
- value.to_s("F")
else
super
end
@@ -253,10 +240,18 @@ module ActiveRecord
QUOTED_TRUE
end
+ def unquoted_true
+ 1
+ end
+
def quoted_false
QUOTED_FALSE
end
+ def unquoted_false
+ 0
+ end
+
# REFERENTIAL INTEGRITY ====================================
def disable_referential_integrity #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 704868c058..86232f9d3f 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -14,11 +14,12 @@ module ActiveRecord
end
attr_reader :name, :default, :cast_type, :null, :sql_type, :default_function
- attr_accessor :primary, :coder
+ attr_accessor :coder
alias :encoded? :coder
- delegate :type, :precision, :scale, :limit, :klass, :text?, :number?, :binary?, :type_cast_for_write, to: :cast_type
+ delegate :type, :precision, :scale, :limit, :klass, :text?, :number?, :binary?,
+ :type_cast_for_write, :type_cast_for_database, to: :cast_type
# Instantiates a new column in the table.
#
@@ -36,7 +37,6 @@ module ActiveRecord
@null = null
@default = extract_default(default)
@default_function = nil
- @primary = nil
@coder = nil
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index 0cbedb0987..f7bad20f00 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -6,13 +6,6 @@ module ActiveRecord
"(#{point[0]},#{point[1]})"
end
- def string_to_point(string) # :nodoc:
- if string[0] == '(' && string[-1] == ')'
- string = string[1...-1]
- end
- string.split(',').map{ |v| Float(v) }
- end
-
def string_to_bit(value) # :nodoc:
case value
when /^0x/i
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index b55766bde0..9a5e2d05ef 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -6,18 +6,16 @@ module ActiveRecord
class PostgreSQLColumn < Column #:nodoc:
attr_accessor :array
- def initialize(name, default, cast_type, sql_type = nil, null = true)
- default_value = self.class.extract_value_from_default(default)
-
+ def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
if sql_type =~ /\[\]$/
@array = true
- super(name, default_value, cast_type, sql_type[0..sql_type.length - 3], null)
+ super(name, default, cast_type, sql_type[0..sql_type.length - 3], null)
else
@array = false
- super(name, default_value, cast_type, sql_type, null)
+ super(name, default, cast_type, sql_type, null)
end
- @default_function = default if has_default_function?(default_value, default)
+ @default_function = default_function
end
# :stopdoc:
@@ -38,78 +36,9 @@ module ActiveRecord
end
# :startdoc:
- # Extracts the value from a PostgreSQL column default definition.
- def self.extract_value_from_default(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.
- return default unless default
-
- case default
- when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
- $1
- # Numeric types
- when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
- $1
- # Character types
- when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
- $1.gsub(/''/, "'")
- # Binary data types
- when /\A'(.*)'::bytea\z/m
- $1
- # Date/time types
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
- $1
- when /\A'(.*)'::interval\z/
- $1
- # Boolean type
- when 'true'
- true
- when 'false'
- false
- # Geometric types
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
- $1
- # Network address types
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
- $1
- # Bit string types
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
- $1
- # XML type
- when /\A'(.*)'::xml\z/m
- $1
- # Arrays
- when /\A'(.*)'::"?\D+"?\[\]\z/
- $1
- # Hstore
- when /\A'(.*)'::hstore\z/
- $1
- # JSON
- when /\A'(.*)'::json\z/
- $1
- # Object identifier types
- when /\A-?\d+\z/
- $1
- else
- # Anything else is blank, some user type, or some function
- # and we can't know the value of that, so return nil.
- nil
- end
- end
-
def accessor
cast_type.accessor
end
-
- private
-
- def has_default_function?(default_value, default)
- !default_value && (%r{\w+\(.*\)} === default)
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
index 2769a8d3b4..f9531ddee3 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
@@ -5,7 +5,10 @@ module ActiveRecord
class Point < Type::String
def type_cast(value)
if ::String === value
- ConnectionAdapters::PostgreSQLColumn.string_to_point value
+ if value[0] == '(' && value[-1] == ')'
+ value = value[1...-1]
+ end
+ value.split(',').map{ |v| Float(v) }
else
value
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
new file mode 100644
index 0000000000..bcfd605165
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -0,0 +1,134 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module PostgreSQL
+ module ColumnMethods
+ def xml(*args)
+ options = args.extract_options!
+ column(args[0], 'xml', options)
+ end
+
+ def tsvector(*args)
+ options = args.extract_options!
+ column(args[0], 'tsvector', options)
+ end
+
+ def int4range(name, options = {})
+ column(name, 'int4range', options)
+ end
+
+ def int8range(name, options = {})
+ column(name, 'int8range', options)
+ end
+
+ def tsrange(name, options = {})
+ column(name, 'tsrange', options)
+ end
+
+ def tstzrange(name, options = {})
+ column(name, 'tstzrange', options)
+ end
+
+ def numrange(name, options = {})
+ column(name, 'numrange', options)
+ end
+
+ def daterange(name, options = {})
+ column(name, 'daterange', options)
+ end
+
+ def hstore(name, options = {})
+ column(name, 'hstore', options)
+ end
+
+ def ltree(name, options = {})
+ column(name, 'ltree', options)
+ end
+
+ def inet(name, options = {})
+ column(name, 'inet', options)
+ end
+
+ def cidr(name, options = {})
+ column(name, 'cidr', options)
+ end
+
+ def macaddr(name, options = {})
+ column(name, 'macaddr', options)
+ end
+
+ def uuid(name, options = {})
+ column(name, 'uuid', options)
+ end
+
+ def json(name, options = {})
+ column(name, 'json', options)
+ end
+
+ def citext(name, options = {})
+ column(name, 'citext', options)
+ end
+ end
+
+ class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
+ attr_accessor :array
+ end
+
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+ include ColumnMethods
+
+ # Defines the primary key field.
+ # Use of the native PostgreSQL UUID type is supported, and can be used
+ # by defining your tables as such:
+ #
+ # create_table :stuffs, id: :uuid do |t|
+ # t.string :content
+ # t.timestamps
+ # end
+ #
+ # By default, this will use the +uuid_generate_v4()+ function from the
+ # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
+ # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
+ # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
+ # set the +:default+ option to +nil+:
+ #
+ # create_table :stuffs, id: false do |t|
+ # t.primary_key :id, :uuid, default: nil
+ # t.uuid :foo_id
+ # t.timestamps
+ # end
+ #
+ # You may also pass a different UUID generation function from +uuid-ossp+
+ # or another library.
+ #
+ # Note that setting the UUID primary key default value to +nil+ will
+ # require you to assure that you always provide a UUID value before saving
+ # a record (as primary keys cannot be +nil+). This might be done via the
+ # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
+ def primary_key(name, type = :primary_key, options = {})
+ return super unless type == :uuid
+ options[:default] = options.fetch(:default, 'uuid_generate_v4()')
+ options[:primary_key] = true
+ column name, type, options
+ end
+
+ def column(name, type = nil, options = {})
+ super
+ column = self[name]
+ column.array = options[:array]
+
+ self
+ end
+
+ private
+
+ def create_column_definition(name, type)
+ PostgreSQL::ColumnDefinition.new name, type
+ end
+ end
+
+ class Table < ActiveRecord::ConnectionAdapters::Table
+ include ColumnMethods
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index c04a1d7178..484c44dc8d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -5,7 +5,7 @@ module ActiveRecord
private
def visit_AddColumn(o)
- sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
+ sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
add_column_options!(sql, column_options(o))
end
@@ -179,7 +179,9 @@ module ActiveRecord
# Limit, precision, and scale are all handled by the superclass.
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
- PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
+ default_value = extract_value_from_default(default)
+ default_function = extract_default_function(default_value, default)
+ PostgreSQLColumn.new(column_name, default_value, oid, type, notnull == 'f', default_function)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index e3a2422160..027169ae3c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -6,6 +6,7 @@ require 'active_record/connection_adapters/postgresql/column'
require 'active_record/connection_adapters/postgresql/oid'
require 'active_record/connection_adapters/postgresql/quoting'
require 'active_record/connection_adapters/postgresql/referential_integrity'
+require 'active_record/connection_adapters/postgresql/schema_definitions'
require 'active_record/connection_adapters/postgresql/schema_statements'
require 'active_record/connection_adapters/postgresql/database_statements'
@@ -73,139 +74,6 @@ module ActiveRecord
# In addition, default connection parameters of libpq can be set per environment variables.
# See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
class PostgreSQLAdapter < AbstractAdapter
- class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
- attr_accessor :array
- end
-
- module ColumnMethods
- def xml(*args)
- options = args.extract_options!
- column(args[0], 'xml', options)
- end
-
- def tsvector(*args)
- options = args.extract_options!
- column(args[0], 'tsvector', options)
- end
-
- def int4range(name, options = {})
- column(name, 'int4range', options)
- end
-
- def int8range(name, options = {})
- column(name, 'int8range', options)
- end
-
- def tsrange(name, options = {})
- column(name, 'tsrange', options)
- end
-
- def tstzrange(name, options = {})
- column(name, 'tstzrange', options)
- end
-
- def numrange(name, options = {})
- column(name, 'numrange', options)
- end
-
- def daterange(name, options = {})
- column(name, 'daterange', options)
- end
-
- def hstore(name, options = {})
- column(name, 'hstore', options)
- end
-
- def ltree(name, options = {})
- column(name, 'ltree', options)
- end
-
- def inet(name, options = {})
- column(name, 'inet', options)
- end
-
- def cidr(name, options = {})
- column(name, 'cidr', options)
- end
-
- def macaddr(name, options = {})
- column(name, 'macaddr', options)
- end
-
- def uuid(name, options = {})
- column(name, 'uuid', options)
- end
-
- def json(name, options = {})
- column(name, 'json', options)
- end
-
- def citext(name, options = {})
- column(name, 'citext', options)
- end
- end
-
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
- include ColumnMethods
-
- # Defines the primary key field.
- # Use of the native PostgreSQL UUID type is supported, and can be used
- # by defining your tables as such:
- #
- # create_table :stuffs, id: :uuid do |t|
- # t.string :content
- # t.timestamps
- # end
- #
- # By default, this will use the +uuid_generate_v4()+ function from the
- # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
- # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
- # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
- # set the +:default+ option to +nil+:
- #
- # create_table :stuffs, id: false do |t|
- # t.primary_key :id, :uuid, default: nil
- # t.uuid :foo_id
- # t.timestamps
- # end
- #
- # You may also pass a different UUID generation function from +uuid-ossp+
- # or another library.
- #
- # Note that setting the UUID primary key default value to +nil+ will
- # require you to assure that you always provide a UUID value before saving
- # a record (as primary keys cannot be +nil+). This might be done via the
- # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
- def primary_key(name, type = :primary_key, options = {})
- return super unless type == :uuid
- options[:default] = options.fetch(:default, 'uuid_generate_v4()')
- options[:primary_key] = true
- column name, type, options
- end
-
- def citext(name, options = {})
- column(name, 'citext', options)
- end
-
- def column(name, type = nil, options = {})
- super
- column = self[name]
- column.array = options[:array]
-
- self
- end
-
- private
-
- def create_column_definition(name, type)
- ColumnDefinition.new name, type
- end
- end
-
- class Table < ActiveRecord::ConnectionAdapters::Table
- include ColumnMethods
- end
-
ADAPTER_NAME = 'PostgreSQL'
NATIVE_DATABASE_TYPES = {
@@ -251,13 +119,13 @@ module ActiveRecord
ADAPTER_NAME
end
- def schema_creation
+ def schema_creation # :nodoc:
PostgreSQL::SchemaCreation.new self
end
# Adds `:array` option to the default set provided by the
# AbstractAdapter
- def prepare_column_options(column, types)
+ def prepare_column_options(column, types) # :nodoc:
spec = super
spec[:array] = 'true' if column.respond_to?(:array) && column.array
spec[:default] = "\"#{column.default_function}\"" if column.default_function
@@ -509,7 +377,7 @@ module ActiveRecord
end
def update_table_definition(table_name, base) #:nodoc:
- Table.new(table_name, base)
+ PostgreSQL::Table.new(table_name, base)
end
protected
@@ -538,12 +406,12 @@ module ActiveRecord
private
- def get_oid_type(oid, fmod, column_name, sql_type = '')
+ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
if !type_map.key?(oid)
load_additional_types(type_map, [oid])
end
- type_map.fetch(normalize_oid_type(oid, fmod), sql_type) {
+ type_map.fetch(oid, fmod, sql_type) {
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
Type::Value.new.tap do |cast_type|
type_map.register_type(oid, cast_type)
@@ -551,24 +419,7 @@ module ActiveRecord
}
end
- OID_FOR_DECIMAL_TREATED_AS_INT = 23 # :nodoc:
-
- def normalize_oid_type(ftype, fmod)
- # The type for the numeric depends on the width of the field,
- # so we'll do something special here.
- #
- # When dealing with decimal columns:
- #
- # places after decimal = fmod - 4 & 0xffff
- # places before decimal = (fmod - 4) >> 16 & 0xffff
- if ftype == 1700 && (fmod - 4 & 0xffff).zero?
- OID_FOR_DECIMAL_TREATED_AS_INT
- else
- ftype
- end
- end
-
- def initialize_type_map(m)
+ def initialize_type_map(m) # :nodoc:
register_class_with_limit m, 'int2', OID::Integer
m.alias_type 'int4', 'int2'
m.alias_type 'int8', 'int2'
@@ -610,20 +461,29 @@ module ActiveRecord
m.alias_type 'lseg', 'varchar'
m.alias_type 'box', 'varchar'
- m.register_type 'timestamp' do |_, sql_type|
+ m.register_type 'timestamp' do |_, _, sql_type|
precision = extract_precision(sql_type)
OID::DateTime.new(precision: precision)
end
- m.register_type 'numeric' do |_, sql_type|
+ m.register_type 'numeric' do |_, fmod, sql_type|
precision = extract_precision(sql_type)
scale = extract_scale(sql_type)
- OID::Decimal.new(precision: precision, scale: scale)
- end
- m.register_type OID_FOR_DECIMAL_TREATED_AS_INT do |_, sql_type|
- precision = extract_precision(sql_type)
- OID::Integer.new(precision: precision)
+ # The type for the numeric depends on the width of the field,
+ # so we'll do something special here.
+ #
+ # When dealing with decimal columns:
+ #
+ # places after decimal = fmod - 4 & 0xffff
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
+ if fmod && (fmod - 4 & 0xffff).zero?
+ # FIXME: Remove this class, and the second argument to
+ # lookups on PG
+ Type::DecimalWithoutScale.new(precision: precision)
+ else
+ OID::Decimal.new(precision: precision, scale: scale)
+ end
end
load_additional_types(m)
@@ -637,7 +497,78 @@ module ActiveRecord
end
end
- def load_additional_types(type_map, oids = nil)
+ # Extracts the value from a PostgreSQL column default definition.
+ def extract_value_from_default(default) # :nodoc:
+ # 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.
+ return default unless default
+
+ case default
+ when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
+ $1
+ # Numeric types
+ when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
+ $1
+ # Character types
+ when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
+ $1.gsub(/''/, "'")
+ # Binary data types
+ when /\A'(.*)'::bytea\z/m
+ $1
+ # Date/time types
+ when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
+ $1
+ when /\A'(.*)'::interval\z/
+ $1
+ # Boolean type
+ when 'true'
+ true
+ when 'false'
+ false
+ # Geometric types
+ when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
+ $1
+ # Network address types
+ when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
+ $1
+ # Bit string types
+ when /\AB'(.*)'::"?bit(?: varying)?"?\z/
+ $1
+ # XML type
+ when /\A'(.*)'::xml\z/m
+ $1
+ # Arrays
+ when /\A'(.*)'::"?\D+"?\[\]\z/
+ $1
+ # Hstore
+ when /\A'(.*)'::hstore\z/
+ $1
+ # JSON
+ when /\A'(.*)'::json\z/
+ $1
+ # Object identifier types
+ when /\A-?\d+\z/
+ $1
+ else
+ # Anything else is blank, some user type, or some function
+ # and we can't know the value of that, so return nil.
+ nil
+ end
+ end
+
+ def extract_default_function(default_value, default) # :nodoc:
+ default if has_default_function?(default_value, default)
+ end
+
+ def has_default_function?(default_value, default) # :nodoc:
+ !default_value && (%r{\w+\(.*\)} === default)
+ end
+
+ def load_additional_types(type_map, oids = nil) # :nodoc:
if supports_ranges?
query = <<-SQL
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
@@ -831,23 +762,13 @@ module ActiveRecord
end_sql
end
- def extract_pg_identifier_from_name(name) # :nodoc:
- match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
-
- if match_data
- rest = name[match_data[0].length, name.length]
- rest = rest[1, rest.length] if rest.start_with? "."
- [match_data[1], (rest.length > 0 ? rest : nil)]
- end
- end
-
def extract_table_ref_from_insert_sql(sql) # :nodoc:
sql[/into\s+([^\(]*).*values\s*\(/im]
$1.strip if $1
end
def create_table_definition(name, temporary, options, as = nil) # :nodoc:
- TableDefinition.new native_database_types, name, temporary, options, as
+ PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index e5c9f6f54a..4d8afcf16a 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -12,11 +12,10 @@ module ActiveRecord
@columns_hash = {}
@primary_keys = {}
@tables = {}
- prepare_default_proc
end
def primary_keys(table_name)
- @primary_keys[table_name]
+ @primary_keys[table_name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil
end
# A cached lookup for table existence.
@@ -29,9 +28,9 @@ module ActiveRecord
# Add internal cache for table with +table_name+.
def add(table_name)
if table_exists?(table_name)
- @primary_keys[table_name]
- @columns[table_name]
- @columns_hash[table_name]
+ primary_keys(table_name)
+ columns(table_name)
+ columns_hash(table_name)
end
end
@@ -40,14 +39,16 @@ module ActiveRecord
end
# Get the columns for a table
- def columns(table)
- @columns[table]
+ def columns(table_name)
+ @columns[table_name] ||= connection.columns(table_name)
end
# Get the columns for a table as a hash, key is the column name
# value is the column object.
- def columns_hash(table)
- @columns_hash[table]
+ def columns_hash(table_name)
+ @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
+ [col.name, col]
+ }]
end
# Clears out internal caches
@@ -76,32 +77,11 @@ module ActiveRecord
def marshal_dump
# if we get current version during initialization, it happens stack over flow.
@version = ActiveRecord::Migrator.current_version
- [@version] + [@columns, @columns_hash, @primary_keys, @tables].map { |val|
- Hash[val]
- }
+ [@version, @columns, @columns_hash, @primary_keys, @tables]
end
def marshal_load(array)
@version, @columns, @columns_hash, @primary_keys, @tables = array
- prepare_default_proc
- end
-
- private
-
- def prepare_default_proc
- @columns.default_proc = Proc.new do |h, table_name|
- h[table_name] = connection.columns(table_name)
- end
-
- @columns_hash.default_proc = Proc.new do |h, table_name|
- h[table_name] = Hash[columns(table_name).map { |col|
- [col.name, col]
- }]
- end
-
- @primary_keys.default_proc = Proc.new do |h, table_name|
- h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/type.rb b/activerecord/lib/active_record/connection_adapters/type.rb
index 395a4160a8..bab7a3ff7e 100644
--- a/activerecord/lib/active_record/connection_adapters/type.rb
+++ b/activerecord/lib/active_record/connection_adapters/type.rb
@@ -7,6 +7,7 @@ require 'active_record/connection_adapters/type/boolean'
require 'active_record/connection_adapters/type/date'
require 'active_record/connection_adapters/type/date_time'
require 'active_record/connection_adapters/type/decimal'
+require 'active_record/connection_adapters/type/decimal_without_scale'
require 'active_record/connection_adapters/type/float'
require 'active_record/connection_adapters/type/integer'
require 'active_record/connection_adapters/type/string'
diff --git a/activerecord/lib/active_record/connection_adapters/type/binary.rb b/activerecord/lib/active_record/connection_adapters/type/binary.rb
index 4b2d1a66e0..60afe44de1 100644
--- a/activerecord/lib/active_record/connection_adapters/type/binary.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/binary.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Binary < Value # :nodoc:
+ class Binary < Value
def type
:binary
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/boolean.rb b/activerecord/lib/active_record/connection_adapters/type/boolean.rb
index 2337bdd563..0d97379189 100644
--- a/activerecord/lib/active_record/connection_adapters/type/boolean.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/boolean.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Boolean < Value # :nodoc:
+ class Boolean < Value
def type
:boolean
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/date.rb b/activerecord/lib/active_record/connection_adapters/type/date.rb
index 1e7205fd0b..e8becbe1f4 100644
--- a/activerecord/lib/active_record/connection_adapters/type/date.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/date.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Date < Value # :nodoc:
+ class Date < Value
def type
:date
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/date_time.rb b/activerecord/lib/active_record/connection_adapters/type/date_time.rb
index c34f4c5a53..64f5d05301 100644
--- a/activerecord/lib/active_record/connection_adapters/type/date_time.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/date_time.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class DateTime < Value # :nodoc:
+ class DateTime < Value
include TimeValue
def type
diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal.rb b/activerecord/lib/active_record/connection_adapters/type/decimal.rb
index ac5af4b963..e93906ba19 100644
--- a/activerecord/lib/active_record/connection_adapters/type/decimal.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/decimal.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Decimal < Value # :nodoc:
+ class Decimal < Value
include Numeric
def type
diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb b/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb
new file mode 100644
index 0000000000..e58c6e198d
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb
@@ -0,0 +1,13 @@
+require 'active_record/connection_adapters/type/integer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class DecimalWithoutScale < Integer # :nodoc:
+ def type
+ :decimal
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/float.rb b/activerecord/lib/active_record/connection_adapters/type/float.rb
index 51cfa5d86a..f2427d2dfa 100644
--- a/activerecord/lib/active_record/connection_adapters/type/float.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/float.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Float < Value # :nodoc:
+ class Float < Value
include Numeric
def type
@@ -12,6 +12,8 @@ module ActiveRecord
::Float
end
+ alias type_cast_for_database type_cast
+
private
def cast_value(value)
diff --git a/activerecord/lib/active_record/connection_adapters/type/integer.rb b/activerecord/lib/active_record/connection_adapters/type/integer.rb
index 8f3469434c..596f4de2a8 100644
--- a/activerecord/lib/active_record/connection_adapters/type/integer.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/integer.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Integer < Value # :nodoc:
+ class Integer < Value
include Numeric
def type
@@ -12,6 +12,8 @@ module ActiveRecord
::Fixnum
end
+ alias type_cast_for_database type_cast
+
private
def cast_value(value)
diff --git a/activerecord/lib/active_record/connection_adapters/type/string.rb b/activerecord/lib/active_record/connection_adapters/type/string.rb
index 55f0e1ee1c..471f949e09 100644
--- a/activerecord/lib/active_record/connection_adapters/type/string.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/string.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class String < Value # :nodoc:
+ class String < Value
def type
:string
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/text.rb b/activerecord/lib/active_record/connection_adapters/type/text.rb
index ee5842a3fc..61095ebb38 100644
--- a/activerecord/lib/active_record/connection_adapters/type/text.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/text.rb
@@ -3,7 +3,7 @@ require 'active_record/connection_adapters/type/string'
module ActiveRecord
module ConnectionAdapters
module Type
- class Text < String # :nodoc:
+ class Text < String
def type
:text
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/time.rb b/activerecord/lib/active_record/connection_adapters/type/time.rb
index 4dd201e3fe..bc331b0fa7 100644
--- a/activerecord/lib/active_record/connection_adapters/type/time.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/time.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Time < Value # :nodoc:
+ class Time < Value
include TimeValue
def type
diff --git a/activerecord/lib/active_record/connection_adapters/type/value.rb b/activerecord/lib/active_record/connection_adapters/type/value.rb
index 54a3e9dd7a..60b443004c 100644
--- a/activerecord/lib/active_record/connection_adapters/type/value.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/value.rb
@@ -1,9 +1,11 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Value # :nodoc:
+ class Value
attr_reader :precision, :scale, :limit
+ # Valid options are +precision+, +scale+, and +limit+.
+ # They are only used when dumping schema.
def initialize(options = {})
options.assert_valid_keys(:precision, :scale, :limit)
@precision = options[:precision]
@@ -11,8 +13,13 @@ module ActiveRecord
@limit = options[:limit]
end
+ # The simplified that this object represents. Subclasses
+ # should override this method.
def type; end
+ # Takes an input from the database, or from attribute setters,
+ # and casts it to a type appropriate for this object. This method
+ # should not be overriden by subclasses. Instead, override `cast_value`.
def type_cast(value)
cast_value(value) unless value.nil?
end
@@ -21,6 +28,10 @@ module ActiveRecord
value
end
+ def type_cast_for_database(value)
+ type_cast_for_write(value)
+ end
+
def text?
false
end
@@ -39,7 +50,9 @@ module ActiveRecord
private
- def cast_value(value)
+ # Responsible for casting values from external sources to the appropriate
+ # type. Called by `type_cast` for all values except `nil`.
+ def cast_value(value) # :api: public
value
end
end