aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/relation/merger.rb22
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb28
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb13
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb2
-rw-r--r--activerecord/test/cases/defaults_test.rb40
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb9
11 files changed, 107 insertions, 37 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 7b80598171..f3362f81a3 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,9 @@
## Rails 4.0.0 (unreleased) ##
+* PostgreSQL adapter correctly fetches default values when using multiple schemas and domains in a db. Fixes #7914
+
+ *Arturo Pie*
+
* Learn ActiveRecord::QueryMethods#order work with hash arguments
When symbol or hash passed we convert it to Arel::Nodes::Ordering.
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 37d43d891d..9d3fa18e3a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -90,7 +90,7 @@ module ActiveRecord
else super(value, column)
end
when IPAddr
- return super(value, column) unless ['inet','cidr'].includes? column.sql_type
+ return super(value, column) unless ['inet','cidr'].include? column.sql_type
PostgreSQLColumn.cidr_to_string(value)
else
super(value, column)
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 2264595751..7cad8f94cf 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -280,16 +280,13 @@ module ActiveRecord
end_sql
if result.nil? or result.empty?
- # If that fails, try parsing the primary key's default value.
- # Support the 7.x and 8.0 nextval('foo'::text) as well as
- # the 8.1+ nextval('foo'::regclass).
result = query(<<-end_sql, 'SCHEMA')[0]
SELECT attr.attname,
CASE
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
- substr(split_part(def.adsrc, '''', 2),
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
- ELSE split_part(def.adsrc, '''', 2)
+ WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
+ substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
+ strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
+ ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
END
FROM pg_class t
JOIN pg_attribute attr ON (t.oid = attrelid)
@@ -297,7 +294,7 @@ module ActiveRecord
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
WHERE t.oid = '#{quote_table_name(table)}'::regclass
AND cons.contype = 'p'
- AND def.adsrc ~* 'nextval'
+ AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval'
end_sql
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index bd375ad15a..e18464fa35 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -78,11 +78,8 @@ module ActiveRecord
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
$1
# Character types
- when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
+ when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
$1
- # Character types (8.1 formatting)
- when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
- $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
# Binary data types
when /\A'(.*)'::bytea\z/m
$1
@@ -763,7 +760,8 @@ module ActiveRecord
# - ::regclass is a function that gives the id for a table name
def column_definitions(table_name) #:nodoc:
exec_query(<<-end_sql, 'SCHEMA').rows
- SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull, a.atttypid, a.atttypmod
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index e5b50673da..59226d316e 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -22,7 +22,17 @@ module ActiveRecord
# the values.
def other
other = Relation.new(relation.klass, relation.table)
- hash.each { |k, v| other.send("#{k}!", v) }
+ hash.each { |k, v|
+ if k == :joins
+ if Hash === v
+ other.joins!(v)
+ else
+ other.joins!(*v)
+ end
+ else
+ other.send("#{k}!", v)
+ end
+ }
other
end
end
@@ -39,16 +49,18 @@ module ActiveRecord
@values = other.values
end
+ NORMAL_VALUES = Relation::SINGLE_VALUE_METHODS +
+ Relation::MULTI_VALUE_METHODS -
+ [:where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
+
def normal_values
- Relation::SINGLE_VALUE_METHODS +
- Relation::MULTI_VALUE_METHODS -
- [:where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from]
+ NORMAL_VALUES
end
def merge
normal_values.each do |name|
value = values[name]
- relation.send("#{name}!", value) unless value.blank?
+ relation.send("#{name}!", *value) unless value.blank?
end
merge_multi_values
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 2b12e66ed0..a9ace10093 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -202,13 +202,13 @@ module ActiveRecord
#
# User.order('name DESC, email')
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
- #
+ #
# User.order(:name)
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
- #
+ #
# User.order(email: :desc)
# => SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
- #
+ #
# User.order(:name, email: :desc)
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
def order(*args)
@@ -218,7 +218,7 @@ module ActiveRecord
# Like #order, but modifies relation in place.
def order!(*args)
args.flatten!
-
+
validate_order_args args
references = args.reject { |arg| Arel::Node === arg }
@@ -245,7 +245,7 @@ module ActiveRecord
# Like #reorder, but modifies relation in place.
def reorder!(*args)
args.flatten!
-
+
validate_order_args args
self.reordering_value = true
@@ -258,13 +258,11 @@ module ActiveRecord
# User.joins(:posts)
# => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
def joins(*args)
- args.compact.blank? ? self : spawn.joins!(*args)
+ args.compact.blank? ? self : spawn.joins!(*args.flatten)
end
# Like #joins, but modifies relation in place.
def joins!(*args)
- args.flatten!
-
self.joins_values += args
self
end
@@ -803,9 +801,9 @@ module ActiveRecord
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
end
when Symbol
- { o => :desc }
+ { o => :desc }
when Hash
- o.each_with_object({}) do |(field, dir), memo|
+ o.each_with_object({}) do |(field, dir), memo|
memo[field] = (dir == :asc ? :desc : :asc )
end
else
@@ -817,25 +815,25 @@ module ActiveRecord
def array_of_strings?(o)
o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
end
-
+
def build_order(arel)
orders = order_values
orders = reverse_sql_order(orders) if reverse_order_value
-
+
orders = orders.uniq.reject(&:blank?).map do |order|
case order
when Symbol
table[order].asc
when Hash
order.map { |field, dir| table[field].send(dir) }
- else
+ else
order
end
end.flatten
-
+
arel.order(*orders) unless orders.empty?
end
-
+
def validate_order_args(args)
args.select { |a| Hash === a }.each do |h|
unless (h.values - [:asc, :desc]).empty?
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 5dece1cb36..5fa6a0b892 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -26,11 +26,12 @@ module ActiveRecord
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
Array(options[:scope]).each do |scope_item|
- scope_value = record.read_attribute(scope_item)
reflection = record.class.reflect_on_association(scope_item)
if reflection
scope_value = record.send(reflection.foreign_key)
scope_item = reflection.foreign_key
+ else
+ scope_value = record.read_attribute(scope_item)
end
relation = relation.and(table[scope_item].eq(scope_value))
end
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index f8a605b67c..685f0ea74f 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'ipaddr'
module ActiveRecord
module ConnectionAdapters
@@ -20,6 +21,18 @@ module ActiveRecord
assert_equal 'f', @conn.type_cast(false, c)
end
+ def test_type_cast_cidr
+ ip = IPAddr.new('255.0.0.0/8')
+ c = Column.new(nil, ip, 'cidr')
+ assert_equal ip, @conn.type_cast(ip, c)
+ end
+
+ def test_type_cast_inet
+ ip = IPAddr.new('255.1.0.0/8')
+ c = Column.new(nil, ip, 'inet')
+ assert_equal ip, @conn.type_cast(ip, c)
+ end
+
def test_quote_float_nan
nan = 0.0/0
c = Column.new(nil, 1, 'float')
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index ffbf14b528..cd31900d4e 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -72,7 +72,7 @@ class SchemaTest < ActiveRecord::TestCase
end
def test_schema_names
- assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names
+ assert_equal ["public", "schema_1", "test_schema", "test_schema2"], @connection.schema_names
end
def test_create_schema
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index deaf5252db..72f1c99ca0 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -109,3 +109,43 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
end
end
end
+
+if current_adapter?(:PostgreSQLAdapter)
+ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+
+ @old_search_path = @connection.schema_search_path
+ @connection.schema_search_path = "schema_1, pg_catalog"
+ @connection.create_table "defaults" do |t|
+ t.text "text_col", :default => "some value"
+ t.string "string_col", :default => "some value"
+ end
+ Default.reset_column_information
+ end
+
+ def test_text_defaults_in_new_schema_when_overriding_domain
+ assert_equal "some value", Default.new.text_col, "Default of text column was not correctly parse"
+ end
+
+ def test_string_defaults_in_new_schema_when_overriding_domain
+ assert_equal "some value", Default.new.string_col, "Default of string column was not correctly parse"
+ end
+
+ def test_bpchar_defaults_in_new_schema_when_overriding_domain
+ @connection.execute "ALTER TABLE defaults ADD bpchar_col bpchar DEFAULT 'some value'"
+ Default.reset_column_information
+ assert_equal "some value", Default.new.bpchar_col, "Default of bpchar column was not correctly parse"
+ end
+
+ def test_text_defaults_after_updating_column_default
+ @connection.execute "ALTER TABLE defaults ALTER COLUMN text_col SET DEFAULT 'some text'::schema_1.text"
+ assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parse after updating default using '::text' since postgreSQL will add parens to the default in db"
+ end
+
+ def teardown
+ @connection.schema_search_path = @old_search_path
+ Default.reset_column_information
+ end
+ end
+end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 2cd9f30b59..d0e7338f15 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -12,6 +12,8 @@ ActiveRecord::Schema.define do
execute 'DROP FUNCTION IF EXISTS partitioned_insert_trigger()'
+ execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
+
%w(accounts_id_seq developers_id_seq projects_id_seq topics_id_seq customers_id_seq orders_id_seq).each do |seq_name|
execute "SELECT setval('#{seq_name}', 100)"
end
@@ -37,7 +39,12 @@ ActiveRecord::Schema.define do
);
_SQL
- execute <<_SQL
+ execute "CREATE SCHEMA schema_1"
+ execute "CREATE DOMAIN schema_1.text AS text"
+ execute "CREATE DOMAIN schema_1.varchar AS varchar"
+ execute "CREATE DOMAIN schema_1.bpchar AS bpchar"
+
+ execute <<_SQL
CREATE TABLE geometrics (
id serial primary key,
a_point point,