aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
authorRyuta Kamizono <kamipo@gmail.com>2016-02-01 00:23:36 +0900
committerJeremy Daer <jeremydaer@gmail.com>2016-04-24 14:56:29 -0700
commitedc2b7718725016e988089b5fb6d6fb9d6e16882 (patch)
tree2dc5eb1b68ff7c0f71c5de73578e48cb5edb6d68 /activerecord/lib/active_record
parentc41ef01aec40bfaa9af707551d1e8a1f9f7380d1 (diff)
downloadrails-edc2b7718725016e988089b5fb6d6fb9d6e16882.tar.gz
rails-edc2b7718725016e988089b5fb6d6fb9d6e16882.tar.bz2
rails-edc2b7718725016e988089b5fb6d6fb9d6e16882.zip
Add Expression Indexes and Operator Classes support for PostgreSQL
Example: create_table :users do |t| t.string :name t.index 'lower(name) varchar_pattern_ops' end Fixes #19090. Fixes #21765. Fixes #21819. Fixes #24359. Signed-off-by: Jeremy Daer <jeremydaer@gmail.com>
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb35
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
4 files changed, 46 insertions, 18 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index f65dc4ed5a..99a3e99bdc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1110,15 +1110,19 @@ module ActiveRecord
Table.new(table_name, base)
end
- def add_index_options(table_name, column_name, comment: nil, **options) #:nodoc:
- column_names = Array(column_name)
+ def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
+ if column_name.is_a?(String) && /\W/ === column_name
+ column_names = column_name
+ else
+ column_names = Array(column_name)
+ end
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
index_type = options[:type].to_s if options.key?(:type)
index_type ||= options[:unique] ? "UNIQUE" : ""
index_name = options[:name].to_s if options.key?(:name)
- index_name ||= index_name(table_name, column: column_names)
+ index_name ||= index_name(table_name, index_name_options(column_names))
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
if options.key?(:algorithm)
@@ -1174,6 +1178,8 @@ module ActiveRecord
# Overridden by the MySQL adapter for supporting index lengths
def quoted_columns_for_index(column_names, options = {})
+ return [column_names] if column_names.is_a?(String)
+
option_strings = Hash[column_names.map {|name| [name, '']}]
# add index sort order if supported
@@ -1249,6 +1255,14 @@ module ActiveRecord
AlterTable.new create_table_definition(name)
end
+ def index_name_options(column_names) # :nodoc:
+ if column_names.is_a?(String)
+ column_names = column_names.scan(/\w+/).join('_')
+ end
+
+ { column: column_names }
+ end
+
def foreign_key_name(table_name, options) # :nodoc:
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 130c03dd1d..86de8f1ca0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -248,6 +248,11 @@ module ActiveRecord
false
end
+ # Does this adapter support expression indices?
+ def supports_expression_index?
+ false
+ end
+
# Does this adapter support explain?
def supports_explain?
false
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 272e6293ab..074ebb90a5 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -175,7 +175,10 @@ module ActiveRecord
result = query(<<-SQL, 'SCHEMA')
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
- pg_catalog.obj_description(i.oid, 'pg_class') AS comment
+ pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
+ (SELECT COUNT(*) FROM pg_opclass o
+ JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
+ ON o.oid = c.oid WHERE o.opcdefault = 'f')
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
INNER JOIN pg_class i ON d.indexrelid = i.oid
@@ -194,25 +197,27 @@ module ActiveRecord
inddef = row[3]
oid = row[4]
comment = row[5]
+ opclass = row[6]
- columns = Hash[query(<<-SQL, "SCHEMA")]
- SELECT a.attnum, a.attname
- FROM pg_attribute a
- WHERE a.attrelid = #{oid}
- AND a.attnum IN (#{indkey.join(",")})
- SQL
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
- column_names = columns.values_at(*indkey).compact
+ if indkey.include?(0) || opclass > 0
+ columns = expressions
+ else
+ columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
+ SELECT a.attnum, a.attname
+ FROM pg_attribute a
+ WHERE a.attrelid = #{oid}
+ AND a.attnum IN (#{indkey.join(",")})
+ SQL
- unless column_names.empty?
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
- where = inddef.scan(/WHERE (.+)$/).flatten[0]
- using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
-
- IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using, comment)
+ orders = Hash[
+ expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
+ ]
end
+
+ IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment)
end.compact
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index e397a28cac..39a2cbbda7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -140,6 +140,10 @@ module ActiveRecord
true
end
+ def supports_expression_index?
+ true
+ end
+
def supports_transaction_isolation?
true
end