aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/postgresql
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/postgresql')
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb27
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb50
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb50
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb142
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb2
14 files changed, 281 insertions, 110 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 11d3f5301a..6c15facf3b 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -8,7 +8,7 @@ module ActiveRecord
end
class ExplainPrettyPrinter # :nodoc:
- # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
# PostgreSQL shell:
#
# QUERY PLAN
@@ -52,8 +52,8 @@ module ActiveRecord
end
end
- def select_values(arel, name = nil)
- arel, binds = binds_from_relation arel, []
+ def select_values(arel, name = nil, binds = [])
+ arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
execute_and_clear(sql, name, binds) do |result|
if result.nfields > 0
@@ -72,28 +72,6 @@ module ActiveRecord
end
end
- # Executes an INSERT query and returns the new record's ID
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- unless pk
- # Extract the table from the insert sql. Yuck.
- table_ref = extract_table_ref_from_insert_sql(sql)
- pk = primary_key(table_ref) if table_ref
- end
-
- if pk && use_insert_returning?
- select_value("#{sql} RETURNING #{quote_column_name(pk)}")
- elsif pk
- super
- last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
- else
- super
- end
- end
-
- def create
- super.insert
- end
-
# The internal PostgreSQL identifier of the money data type.
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
# The internal PostgreSQL identifier of the BYTEA data type.
@@ -156,8 +134,8 @@ module ActiveRecord
end
end
- def exec_query(sql, name = 'SQL', binds = [])
- execute_and_clear(sql, name, binds) do |result|
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
+ execute_and_clear(sql, name, binds, prepare: prepare) do |result|
types = {}
fields = result.fields
fields.each_with_index do |fname, i|
@@ -174,7 +152,7 @@ module ActiveRecord
end
alias :exec_update :exec_delete
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
unless pk
# Extract the table from the insert sql. Yuck.
table_ref = extract_table_ref_from_insert_sql(sql)
@@ -185,7 +163,7 @@ module ActiveRecord
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
end
- [sql, binds]
+ super
end
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
@@ -202,11 +180,6 @@ module ActiveRecord
end
end
- # Executes an UPDATE query and returns the number of affected tuples.
- def update_sql(sql, name = nil)
- super.cmd_tuples
- end
-
# Begins a transaction.
def begin_db_transaction
execute "BEGIN"
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 92349e2f9b..68752cdd80 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -12,6 +12,7 @@ require 'active_record/connection_adapters/postgresql/oid/json'
require 'active_record/connection_adapters/postgresql/oid/jsonb'
require 'active_record/connection_adapters/postgresql/oid/money'
require 'active_record/connection_adapters/postgresql/oid/point'
+require 'active_record/connection_adapters/postgresql/oid/rails_5_1_point'
require 'active_record/connection_adapters/postgresql/oid/range'
require 'active_record/connection_adapters/postgresql/oid/specialized_string'
require 'active_record/connection_adapters/postgresql/oid/uuid'
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index 3de794f797..87593ef704 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -45,6 +45,15 @@ module ActiveRecord
delimiter == other.delimiter
end
+ def type_cast_for_schema(value)
+ return super unless value.is_a?(::Array)
+ "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
+ end
+
+ def map(value, &block)
+ value.map(&block)
+ end
+
private
def type_cast_array(value, method)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
index 2c04c46131..424769f765 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
@@ -4,18 +4,14 @@ module ActiveRecord
module OID # :nodoc:
class DateTime < Type::DateTime # :nodoc:
def cast_value(value)
- if value.is_a?(::String)
- case value
- when 'infinity' then ::Float::INFINITY
- when '-infinity' then -::Float::INFINITY
- when / BC$/
- astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
- super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
- else
- super
- end
+ case value
+ when 'infinity' then ::Float::INFINITY
+ when '-infinity' then -::Float::INFINITY
+ when / BC$/
+ astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
+ super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
else
- value
+ super
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
index 8e1256baad..dbc879ffd4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
@@ -2,32 +2,7 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module OID # :nodoc:
- class Json < Type::Value # :nodoc:
- include Type::Helpers::Mutable
-
- def type
- :json
- end
-
- def deserialize(value)
- if value.is_a?(::String)
- ::ActiveSupport::JSON.decode(value) rescue nil
- else
- value
- end
- end
-
- def serialize(value)
- if value.is_a?(::Array) || value.is_a?(::Hash)
- ::ActiveSupport::JSON.encode(value)
- else
- value
- end
- end
-
- def accessor
- ActiveRecord::Store::StringKeyedHashAccessor
- end
+ class Json < Type::Internal::AbstractJson
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb
index afc9383f91..87391b5dc7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb
@@ -9,7 +9,7 @@ module ActiveRecord
def changed_in_place?(raw_old_value, new_value)
# Postgres does not preserve insignificant whitespaces when
- # roundtripping jsonb columns. This causes some false positives for
+ # round-tripping jsonb columns. This causes some false positives for
# the comparison here. Therefore, we need to parse and re-dump the
# raw value here to ensure the insignificant whitespaces are
# consistent with our encoder's output.
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
new file mode 100644
index 0000000000..7427a25ad5
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
@@ -0,0 +1,50 @@
+module ActiveRecord
+ Point = Struct.new(:x, :y)
+
+ module ConnectionAdapters
+ module PostgreSQL
+ module OID # :nodoc:
+ class Rails51Point < Type::Value # :nodoc:
+ include Type::Helpers::Mutable
+
+ def type
+ :point
+ end
+
+ def cast(value)
+ case value
+ when ::String
+ if value[0] == '(' && value[-1] == ')'
+ value = value[1...-1]
+ end
+ x, y = value.split(",")
+ build_point(x, y)
+ when ::Array
+ build_point(*value)
+ else
+ value
+ end
+ end
+
+ def serialize(value)
+ if value.is_a?(ActiveRecord::Point)
+ "(#{number_for_point(value.x)},#{number_for_point(value.y)})"
+ else
+ super
+ end
+ end
+
+ private
+
+ def number_for_point(number)
+ number.to_s.gsub(/\.0$/, '')
+ end
+
+ def build_point(x, y)
+ ActiveRecord::Point.new(Float(x), Float(y))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index fc201f8fb9..a8d2310035 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -6,6 +6,7 @@ module ActiveRecord
module OID # :nodoc:
class Range < Type::Value # :nodoc:
attr_reader :subtype, :type
+ delegate :user_input_in_time_zone, to: :subtype
def initialize(subtype, type = :range)
@subtype = subtype
@@ -18,7 +19,7 @@ module ActiveRecord
def cast_value(value)
return if value == 'empty'
- return value if value.is_a?(::Range)
+ return value unless value.is_a?(::String)
extracted = extract_bounds(value)
from = type_cast_single extracted[:from]
@@ -46,6 +47,12 @@ module ActiveRecord
other.type == type
end
+ def map(value) # :nodoc:
+ new_begin = yield(value.begin)
+ new_end = yield(value.end)
+ ::Range.new(new_begin, new_end, value.exclude_end?)
+ end
+
private
def type_cast_single(value)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
index 191c828e60..6155e53632 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
@@ -36,7 +36,7 @@ module ActiveRecord
WHERE
t.typname IN (%s)
OR t.typtype IN (%s)
- OR t.typinput::varchar = 'array_in'
+ OR t.typinput = 'array_in(cstring,oid,integer)'::regprocedure
OR t.typelem != 0
SQL
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index f175730551..c1c77a967e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -31,6 +31,11 @@ module ActiveRecord
Utils.extract_schema_qualified_name(name.to_s).quoted
end
+ # Quotes schema names for use in SQL queries.
+ def quote_schema_name(name)
+ PGconn.quote_ident(name)
+ end
+
def quote_table_name_for_assignment(table, attr)
quote_column_name(attr)
end
@@ -50,10 +55,11 @@ module ActiveRecord
end
end
- # Does not quote function default values for UUID columns
- def quote_default_expression(value, column) #:nodoc:
- if column.type == :uuid && value =~ /\(\)/
- value
+ def quote_default_expression(value, column) # :nodoc:
+ if value.is_a?(Proc)
+ value.call
+ elsif column.type == :uuid && value =~ /\(\)/
+ value # Does not quote function default values for UUID columns
elsif column.respond_to?(:array?)
value = type_cast_from_column(column, value)
quote(value)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
index 022dbdfa27..6399bddbee 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -103,6 +103,30 @@ module ActiveRecord
args.each { |name| column(name, :point, options) }
end
+ def line(*args, **options)
+ args.each { |name| column(name, :line, options) }
+ end
+
+ def lseg(*args, **options)
+ args.each { |name| column(name, :lseg, options) }
+ end
+
+ def box(*args, **options)
+ args.each { |name| column(name, :box, options) }
+ end
+
+ def path(*args, **options)
+ args.each { |name| column(name, :path, options) }
+ end
+
+ def polygon(*args, **options)
+ args.each { |name| column(name, :polygon, options) }
+ end
+
+ def circle(*args, **options)
+ args.each { |name| column(name, :circle, options) }
+ end
+
def serial(*args, **options)
args.each { |name| column(name, :serial, options) }
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
new file mode 100644
index 0000000000..cc7721ddd8
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
@@ -0,0 +1,50 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module PostgreSQL
+ module ColumnDumper
+ def column_spec_for_primary_key(column)
+ spec = {}
+ if column.serial?
+ return unless column.bigint?
+ spec[:id] = ':bigserial'
+ elsif column.type == :uuid
+ spec[:id] = ':uuid'
+ spec[:default] = schema_default(column) || 'nil'
+ else
+ spec[:id] = column.type.inspect
+ spec.merge!(prepare_column_options(column).delete_if { |key, _| [:name, :type, :null].include?(key) })
+ end
+ spec
+ end
+
+ # Adds +:array+ option to the default set
+ def prepare_column_options(column)
+ spec = super
+ spec[:array] = 'true' if column.array?
+ spec
+ end
+
+ # Adds +:array+ as a valid migration key
+ def migration_keys
+ super + [:array]
+ end
+
+ private
+
+ def schema_type(column)
+ return super unless column.serial?
+
+ if column.bigint?
+ 'bigserial'
+ else
+ 'serial'
+ end
+ end
+
+ def schema_expression(column)
+ super unless column.serial?
+ end
+ 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 595c635fc0..67e727d8ed 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -68,15 +68,41 @@ module ActiveRecord
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
end
- # Returns the list of all tables in the schema search path or a specified schema.
+ # Returns the list of all tables in the schema search path.
def tables(name = nil)
+ if name
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing arguments to #tables is deprecated without replacement.
+ MSG
+ end
+
select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
end
+ def data_sources # :nodoc
+ select_values(<<-SQL, 'SCHEMA')
+ SELECT c.relname
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
+ AND n.nspname = ANY (current_schemas(false))
+ SQL
+ end
+
# Returns true if table exists.
# If the schema is not specified as part of +name+ then it will only find tables within
# the current schema search path (regardless of permissions to access tables in other schemas)
def table_exists?(name)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ #table_exists? currently checks both tables and views.
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
+ Use #data_source_exists? instead.
+ MSG
+
+ data_source_exists?(name)
+ end
+
+ def data_source_exists?(name)
name = Utils.extract_schema_qualified_name(name.to_s)
return false unless name.identifier
@@ -90,6 +116,30 @@ module ActiveRecord
SQL
end
+ def views # :nodoc:
+ select_values(<<-SQL, 'SCHEMA')
+ SELECT c.relname
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
+ AND n.nspname = ANY (current_schemas(false))
+ SQL
+ end
+
+ def view_exists?(view_name) # :nodoc:
+ name = Utils.extract_schema_qualified_name(view_name.to_s)
+ return false unless name.identifier
+
+ select_values(<<-SQL, 'SCHEMA').any?
+ SELECT c.relname
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
+ AND c.relname = '#{name.identifier}'
+ AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ SQL
+ end
+
def drop_table(table_name, options = {}) # :nodoc:
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
@@ -101,29 +151,36 @@ module ActiveRecord
# Verifies existence of an index with a given name.
def index_name_exists?(table_name, index_name, default)
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
+ index = Utils.extract_schema_qualified_name(index_name.to_s)
+
select_value(<<-SQL, 'SCHEMA').to_i > 0
SELECT COUNT(*)
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
INNER JOIN pg_class i ON d.indexrelid = i.oid
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
WHERE i.relkind = 'i'
- AND i.relname = '#{index_name}'
- AND t.relname = '#{table_name}'
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
+ AND i.relname = '#{index.identifier}'
+ AND t.relname = '#{table.identifier}'
+ AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'}
SQL
end
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil)
- result = query(<<-SQL, 'SCHEMA')
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
- FROM pg_class t
- INNER JOIN pg_index d ON t.oid = d.indrelid
- INNER JOIN pg_class i ON d.indexrelid = i.oid
- WHERE i.relkind = 'i'
- AND d.indisprimary = 'f'
- AND t.relname = '#{table_name}'
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
+
+ result = query(<<-SQL, 'SCHEMA')
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
+ FROM pg_class t
+ INNER JOIN pg_index d ON t.oid = d.indrelid
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
+ WHERE i.relkind = 'i'
+ AND d.indisprimary = 'f'
+ AND t.relname = '#{table.identifier}'
+ AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
ORDER BY i.relname
SQL
@@ -210,12 +267,12 @@ module ActiveRecord
# Creates a schema for the given schema name.
def create_schema schema_name
- execute "CREATE SCHEMA #{schema_name}"
+ execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
end
# Drops the schema for the given schema name.
- def drop_schema schema_name
- execute "DROP SCHEMA #{schema_name} CASCADE"
+ def drop_schema(schema_name, options = {})
+ execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
end
# Sets the schema search path to a string of comma-separated schema names.
@@ -349,17 +406,19 @@ module ActiveRecord
nil
end
- # Returns just a table's primary key
- def primary_key(table)
- pks = query(<<-end_sql, 'SCHEMA')
- SELECT attr.attname
- FROM pg_attribute attr
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
- WHERE cons.contype = 'p'
- AND cons.conrelid = '#{quote_table_name(table)}'::regclass
- end_sql
- return nil unless pks.count == 1
- pks[0][0]
+ def primary_keys(table_name) # :nodoc:
+ select_values(<<-SQL.strip_heredoc, 'SCHEMA')
+ WITH pk_constraint AS (
+ SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
+ WHERE contype = 'p'
+ AND conrelid = '#{quote_table_name(table_name)}'::regclass
+ ), cons AS (
+ SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
+ )
+ SELECT attr.attname FROM pg_attribute attr
+ INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
+ ORDER BY cons.rownum
+ SQL
end
# Renames a table.
@@ -376,7 +435,7 @@ module ActiveRecord
new_seq = "#{new_name}_#{pk}_seq"
idx = "#{table_name}_pkey"
new_idx = "#{new_name}_pkey"
- execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
+ execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
end
@@ -410,11 +469,12 @@ module ActiveRecord
end
# Changes the default value of a table column.
- def change_column_default(table_name, column_name, default) # :nodoc:
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
clear_cache!
column = column_for(table_name, column_name)
return unless column
+ default = extract_new_default_value(default_or_changes)
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
if default.nil?
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
@@ -446,8 +506,28 @@ module ActiveRecord
execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
end
- def remove_index!(table_name, index_name) #:nodoc:
- execute "DROP INDEX #{quote_table_name(index_name)}"
+ def remove_index(table_name, options = {}) #:nodoc:
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
+
+ if options.is_a?(Hash) && options.key?(:name)
+ provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
+
+ options[:name] = provided_index.identifier
+ table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
+
+ if provided_index.schema.present? && table.schema != provided_index.schema
+ raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
+ end
+ end
+
+ index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
+ algorithm =
+ if options.is_a?(Hash) && options.key?(:algorithm)
+ index_algorithms.fetch(options[:algorithm]) do
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
+ end
+ end
+ execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
end
# Renames an index of a table. Raises error if length of new
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
index 58715978f7..b2c49989a4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
@@ -12,7 +12,7 @@ module ActiveRecord
end
def sql_type
- super.gsub(/\[\]$/, "")
+ super.gsub(/\[\]$/, "".freeze)
end
def ==(other)