Commit 9e01240f authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'sh-remove-opclasses-patch' into 'master'

Remove PostgreSQL opclasses monkey patch

Closes #64529 and #59673

See merge request gitlab-org/gitlab-ce!30709
parents ce57b4a0 c25ef8ee
# rubocop:disable all
# These changes add support for PostgreSQL operator classes when creating
# indexes and dumping/loading schemas. Taken from Rails pull request
# https://github.com/rails/rails/pull/19090.
#
# License:
#
# Copyright (c) 2004-2016 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require 'date'
require 'set'
require 'bigdecimal'
require 'bigdecimal/util'
# As the Struct definition is changed in this PR/patch we have to first remove
# the existing one.
ActiveRecord::ConnectionAdapters.send(:remove_const, :IndexDefinition)
module ActiveRecord
module ConnectionAdapters #:nodoc:
# Abstract representation of an index definition on a table. Instances of
# this type are typically created and returned by methods in database
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
attrs = [:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment, :opclasses]
class IndexDefinition < Struct.new(*attrs) #:nodoc:
end
end
end
module ActiveRecord
module ConnectionAdapters # :nodoc:
module SchemaStatements
def add_index_options(table_name, column_name, options = {}) #:nodoc:
column_names = Array(column_name)
index_name = index_name(table_name, column: column_names)
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclasses)
index_type = options[:unique] ? "UNIQUE" : ""
index_type = options[:type].to_s if options.key?(:type)
index_name = options[:name].to_s if options.key?(:name)
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
if options.key?(:algorithm)
algorithm = index_algorithms.fetch(options[:algorithm]) {
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
}
end
using = "USING #{options[:using]}" if options[:using].present?
if supports_partial_index?
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
end
if index_name.length > max_index_length
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
end
if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
end
index_columns = quoted_columns_for_index(column_names, options).join(", ")
[index_name, index_type, index_columns, index_options, algorithm, using]
end
end
end
end
module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module SchemaStatements
# 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)) )
ORDER BY i.relname
SQL
result.map do |row|
index_name = row[0]
unique = row[1]
indkey = row[2].split(" ").map(&:to_i)
inddef = row[3]
oid = row[4]
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
column_names = columns.values_at(*indkey).compact
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
opclasses = Hash[inddef.scan(/\((.+?)\)(?:$| WHERE )/).flatten[0].split(',').map do |column_and_opclass|
column, opclass = column_and_opclass.split(' ').map(&:strip)
end.reject do |column, opclass|
['desc', 'asc'].include?(opclass&.downcase)
end.map do |column, opclass|
[column, opclass] if opclass
end.compact]
index_attrs = [table_name, index_name, unique, column_names, [], orders, where, nil, using, nil, opclasses]
IndexDefinition.new(*index_attrs)
end
end.compact
end
def add_index(table_name, column_name, options = {}) #:nodoc:
index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns_and_opclasses})#{index_options}"
end
protected
def quoted_columns_for_index(column_names, options = {})
column_opclasses = options[:opclasses] || {}
column_names.map {|name| "#{quote_column_name(name)} #{column_opclasses[name]}"}
quoted_columns = Hash[column_names.map { |name| [name.to_sym, "#{quote_column_name(name)} #{column_opclasses[name]}"] }]
add_options_for_index_columns(quoted_columns, options).values
end
end
end
end
end
module ActiveRecord
class SchemaDumper
private
def indexes(table, stream)
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
table_name = remove_prefix_and_suffix(index.table).inspect
" add_index #{([table_name]+index_parts(index)).join(', ')}"
end
stream.puts add_index_statements.sort.join("\n")
stream.puts
end
end
def indexes_in_create(table, stream)
if (indexes = @connection.indexes(table)).any?
index_statements = indexes.map do |index|
" t.index #{index_parts(index).join(', ')}"
end
stream.puts index_statements.sort.join("\n")
end
end
def index_parts(index)
index_parts = [
index.columns.inspect,
"name: #{index.name.inspect}",
]
index_parts << "unique: true" if index.unique
index_parts << "length: { #{format_options(index.lengths)} }" if index.lengths.present?
index_parts << "order: { #{format_options(index.orders)} }" if index.orders.present?
index_parts << "where: #{index.where.inspect}" if index.where
index_parts << "using: #{index.using.inspect}" if index.using
index_parts << "type: #{index.type.inspect}" if index.type
index_parts << "opclasses: #{index.opclasses.inspect}" if index.opclasses.present?
index_parts << "comment: #{index.comment.inspect}" if index.comment
index_parts
end
def format_options(options)
options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
end
end
end
......@@ -788,7 +788,7 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.datetime_with_timezone "closed_at"
t.index ["author_id"], name: "index_issues_on_author_id", using: :btree
t.index ["confidential"], name: "index_issues_on_confidential", using: :btree
t.index ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
t.index ["description"], name: "index_issues_on_description_trigram", using: :gin, opclass: {"description"=>"gin_trgm_ops"}
t.index ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
t.index ["moved_to_id"], name: "index_issues_on_moved_to_id", where: "(moved_to_id IS NOT NULL)", using: :btree
t.index ["project_id", "created_at", "id", "state"], name: "index_issues_on_project_id_and_created_at_and_id_and_state", using: :btree
......@@ -797,7 +797,7 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.index ["project_id", "updated_at", "id", "state"], name: "index_issues_on_project_id_and_updated_at_and_id_and_state", using: :btree
t.index ["relative_position"], name: "index_issues_on_relative_position", using: :btree
t.index ["state"], name: "index_issues_on_state", using: :btree
t.index ["title"], name: "index_issues_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
t.index ["title"], name: "index_issues_on_title_trigram", using: :gin, opclass: {"title"=>"gin_trgm_ops"}
t.index ["updated_by_id"], name: "index_issues_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
end
create_table "keys", id: :serial do |t|
......@@ -989,7 +989,7 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.index ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
t.index ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
t.index ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
t.index ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
t.index ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclass: {"description"=>"gin_trgm_ops"}
t.index ["head_pipeline_id"], name: "index_merge_requests_on_head_pipeline_id", using: :btree
t.index ["latest_merge_request_diff_id"], name: "index_merge_requests_on_latest_merge_request_diff_id", using: :btree
t.index ["merge_user_id"], name: "index_merge_requests_on_merge_user_id", where: "(merge_user_id IS NOT NULL)", using: :btree
......@@ -1001,7 +1001,7 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
t.index ["target_project_id", "merge_commit_sha", "id"], name: "index_merge_requests_on_tp_id_and_merge_commit_sha_and_id", using: :btree
t.index ["title"], name: "index_merge_requests_on_title", using: :btree
t.index ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
t.index ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclass: {"title"=>"gin_trgm_ops"}
t.index ["updated_by_id"], name: "index_merge_requests_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
end
create_table "merge_requests_closing_issues", id: :serial do |t|
......@@ -1026,12 +1026,12 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.date "start_date"
t.integer "cached_markdown_version"
t.integer "group_id"
t.index ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
t.index ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclass: {"description"=>"gin_trgm_ops"}
t.index ["due_date"], name: "index_milestones_on_due_date", using: :btree
t.index ["group_id"], name: "index_milestones_on_group_id", using: :btree
t.index ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
t.index ["title"], name: "index_milestones_on_title", using: :btree
t.index ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
t.index ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclass: {"title"=>"gin_trgm_ops"}
end
create_table "namespaces", id: :serial do |t|
t.string "name", null: false
......@@ -1054,11 +1054,11 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.string "runners_token"
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
t.index ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
t.index ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
t.index ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclass: {"name"=>"gin_trgm_ops"}
t.index ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
t.index ["parent_id", "id"], name: "index_namespaces_on_parent_id_and_id", unique: true, using: :btree
t.index ["path"], name: "index_namespaces_on_path", using: :btree
t.index ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
t.index ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclass: {"path"=>"gin_trgm_ops"}
t.index ["require_two_factor_authentication"], name: "index_namespaces_on_require_two_factor_authentication", using: :btree
t.index ["type"], name: "index_namespaces_on_type", using: :btree
end
......@@ -1091,7 +1091,7 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.index ["created_at"], name: "index_notes_on_created_at", using: :btree
t.index ["discussion_id"], name: "index_notes_on_discussion_id", using: :btree
t.index ["line_code"], name: "index_notes_on_line_code", using: :btree
t.index ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"}
t.index ["note"], name: "index_notes_on_note_trigram", using: :gin, opclass: {"note"=>"gin_trgm_ops"}
t.index ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
t.index ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
t.index ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
......@@ -1304,14 +1304,14 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.index ["ci_id"], name: "index_projects_on_ci_id", using: :btree
t.index ["created_at"], name: "index_projects_on_created_at", using: :btree
t.index ["creator_id"], name: "index_projects_on_creator_id", using: :btree
t.index ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
t.index ["description"], name: "index_projects_on_description_trigram", using: :gin, opclass: {"description"=>"gin_trgm_ops"}
t.index ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
t.index ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree
t.index ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree
t.index ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
t.index ["name"], name: "index_projects_on_name_trigram", using: :gin, opclass: {"name"=>"gin_trgm_ops"}
t.index ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
t.index ["path"], name: "index_projects_on_path", using: :btree
t.index ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
t.index ["path"], name: "index_projects_on_path_trigram", using: :gin, opclass: {"path"=>"gin_trgm_ops"}
t.index ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree
t.index ["repository_storage"], name: "index_projects_on_repository_storage", using: :btree
t.index ["runners_token"], name: "index_projects_on_runners_token", using: :btree
......@@ -1375,7 +1375,7 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.datetime "updated_at", null: false
t.boolean "permanent"
t.index ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree
t.index ["path"], name: "index_redirect_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
t.index ["path"], name: "index_redirect_routes_on_path_text_pattern_ops", using: :btree, opclass: {"path"=>"varchar_pattern_ops"}
t.index ["permanent"], name: "index_redirect_routes_on_permanent", using: :btree
t.index ["source_type", "source_id"], name: "index_redirect_routes_on_source_type_and_source_id", using: :btree
end
......@@ -1398,7 +1398,7 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.datetime "updated_at"
t.string "name"
t.index ["path"], name: "index_routes_on_path", unique: true, using: :btree
t.index ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
t.index ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclass: {"path"=>"varchar_pattern_ops"}
t.index ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree
end
create_table "sent_notifications", id: :serial do |t|
......@@ -1454,9 +1454,9 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.text "description"
t.text "description_html"
t.index ["author_id"], name: "index_snippets_on_author_id", using: :btree
t.index ["file_name"], name: "index_snippets_on_file_name_trigram", using: :gin, opclasses: {"file_name"=>"gin_trgm_ops"}
t.index ["file_name"], name: "index_snippets_on_file_name_trigram", using: :gin, opclass: {"file_name"=>"gin_trgm_ops"}
t.index ["project_id"], name: "index_snippets_on_project_id", using: :btree
t.index ["title"], name: "index_snippets_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
t.index ["title"], name: "index_snippets_on_title_trigram", using: :gin, opclass: {"title"=>"gin_trgm_ops"}
t.index ["updated_at"], name: "index_snippets_on_updated_at", using: :btree
t.index ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
end
......@@ -1663,16 +1663,16 @@ class InitSchema < ActiveRecord::Migration[4.2]
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
t.index ["created_at"], name: "index_users_on_created_at", using: :btree
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
t.index ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"}
t.index ["email"], name: "index_users_on_email_trigram", using: :gin, opclass: {"email"=>"gin_trgm_ops"}
t.index ["ghost"], name: "index_users_on_ghost", using: :btree
t.index ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree
t.index ["name"], name: "index_users_on_name", using: :btree
t.index ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
t.index ["name"], name: "index_users_on_name_trigram", using: :gin, opclass: {"name"=>"gin_trgm_ops"}
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
t.index ["rss_token"], name: "index_users_on_rss_token", using: :btree
t.index ["state"], name: "index_users_on_state", using: :btree
t.index ["username"], name: "index_users_on_username", using: :btree
t.index ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
t.index ["username"], name: "index_users_on_username_trigram", using: :gin, opclass: {"username"=>"gin_trgm_ops"}
end
create_table "users_star_projects", id: :serial do |t|
t.integer "project_id", null: false
......
......@@ -9,7 +9,7 @@ class AddIndexToTags < ActiveRecord::Migration[5.0]
disable_ddl_transaction!
def up
add_concurrent_index :tags, :name, name: INDEX_NAME, using: :gin, opclasses: { name: :gin_trgm_ops }
add_concurrent_index :tags, :name, name: INDEX_NAME, using: :gin, opclass: { name: :gin_trgm_ops }
end
def down
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment