diff options
31 files changed, 2094 insertions, 25 deletions
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue index 102afaf308f..d5a7fc36ace 100644 --- a/app/assets/javascripts/notes/components/discussion_filter.vue +++ b/app/assets/javascripts/notes/components/discussion_filter.vue @@ -116,7 +116,7 @@ export default { <gl-dropdown v-if="displayFilters" id="discussion-filter-dropdown" - class="gl-mr-3 full-width-mobile discussion-filter-container js-discussion-filter-container" + class="full-width-mobile discussion-filter-container js-discussion-filter-container" data-qa-selector="discussion_filter_dropdown" :text="currentFilter.title" :disabled="isLoading" diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 4841225de08..63ff1390d92 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -117,6 +117,10 @@ module IssuableCollections options[:attempt_group_search_optimizations] = true end + if collection_type == 'Issue' && Feature.enabled?(:issues_full_text_search, @project || @group, default_enabled: :yaml) + options[:attempt_full_text_search] = true + end + params.permit(finder_type.valid_params).merge(options) end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 3e436f30971..416db1c9c7d 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -37,6 +37,7 @@ # attempt_project_search_optimizations: boolean # crm_contact_id: integer # crm_organization_id: integer +# attempt_full_text_search: boolean # class IssuableFinder prepend FinderWithCrossProjectAccess @@ -46,6 +47,7 @@ class IssuableFinder requires_cross_project_access unless: -> { params.project? } + FULL_TEXT_SEARCH_TERM_REGEX = /\A[\p{ASCII}|\p{Latin}]+\z/.freeze NEGATABLE_PARAMS_HELPER_KEYS = %i[project_id scope status include_subgroups].freeze attr_accessor :current_user, :params @@ -331,6 +333,8 @@ class IssuableFinder return items if items.is_a?(ActiveRecord::NullRelation) return items if Feature.enabled?(:disable_anonymous_search, type: :ops) && current_user.nil? + return items.pg_full_text_search(search) if use_full_text_search? + if use_cte_for_search? cte = Gitlab::SQL::CTE.new(klass.table_name, items) @@ -341,6 +345,10 @@ class IssuableFinder end # rubocop: enable CodeReuse/ActiveRecord + def use_full_text_search? + params[:attempt_full_text_search] && params[:search] =~ FULL_TEXT_SEARCH_TERM_REGEX + end + # rubocop: disable CodeReuse/ActiveRecord def by_iids(items) params[:iids].present? ? items.where(iid: params[:iids]) : items diff --git a/app/graphql/resolvers/work_item_resolver.rb b/app/graphql/resolvers/work_item_resolver.rb index b1733d657e4..6eb23916d83 100644 --- a/app/graphql/resolvers/work_item_resolver.rb +++ b/app/graphql/resolvers/work_item_resolver.rb @@ -4,7 +4,7 @@ module Resolvers class WorkItemResolver < BaseResolver include Gitlab::Graphql::Authorize::AuthorizeResource - authorize :read_issue + authorize :read_work_item type Types::WorkItemType, null: true diff --git a/app/graphql/types/work_item_type.rb b/app/graphql/types/work_item_type.rb index 78cef150b11..512b9ef64d2 100644 --- a/app/graphql/types/work_item_type.rb +++ b/app/graphql/types/work_item_type.rb @@ -4,7 +4,7 @@ module Types class WorkItemType < BaseObject graphql_name 'WorkItem' - authorize :read_issue + authorize :read_work_item field :description, GraphQL::Types::String, null: true, description: 'Description of the work item.' diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb new file mode 100644 index 00000000000..612a9c5908b --- /dev/null +++ b/app/models/concerns/pg_full_text_searchable.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +# This module adds PG full-text search capabilities to a model. +# A `search_data` association with a `search_vector` column is required. +# +# Declare the fields that will be part of the search vector with their +# corresponding weights. Possible values for weight are A, B, C, or D. +# For example: +# +# include PgFullTextSearchable +# pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }, { name: 'description', weight: 'B' }] +# +# This module sets up an after_commit hook that updates the search data +# when the searchable columns are changed. You will need to implement the +# `#persist_pg_full_text_search_vector` method that does the actual insert or update. +# +# This also adds a `pg_full_text_search` scope so you can do: +# +# Model.pg_full_text_search("some search term") + +module PgFullTextSearchable + extend ActiveSupport::Concern + + LONG_WORDS_REGEX = %r([A-Za-z0-9+/]{50,}).freeze + TSVECTOR_MAX_LENGTH = 1.megabyte.freeze + TEXT_SEARCH_DICTIONARY = 'english' + + def update_search_data! + tsvector_sql_nodes = self.class.pg_full_text_searchable_columns.map do |column, weight| + tsvector_arel_node(column, weight)&.to_sql + end + + persist_pg_full_text_search_vector(Arel.sql(tsvector_sql_nodes.compact.join(' || '))) + rescue ActiveRecord::StatementInvalid => e + raise unless e.cause.is_a?(PG::ProgramLimitExceeded) && e.message.include?('string is too long for tsvector') + + Gitlab::AppJsonLogger.error( + message: 'Error updating search data: string is too long for tsvector', + class: self.class.name, + model_id: self.id + ) + end + + private + + def persist_pg_full_text_search_vector(search_vector) + raise NotImplementedError + end + + def tsvector_arel_node(column, weight) + return if self[column].blank? + + column_text = self[column].gsub(LONG_WORDS_REGEX, ' ') + column_text = column_text[0..(TSVECTOR_MAX_LENGTH - 1)] + column_text = ActiveSupport::Inflector.transliterate(column_text) + + Arel::Nodes::NamedFunction.new( + 'setweight', + [ + Arel::Nodes::NamedFunction.new( + 'to_tsvector', + [Arel::Nodes.build_quoted(TEXT_SEARCH_DICTIONARY), Arel::Nodes.build_quoted(column_text)] + ), + Arel::Nodes.build_quoted(weight) + ] + ) + end + + included do + cattr_reader :pg_full_text_searchable_columns do + {} + end + end + + class_methods do + def pg_full_text_searchable(columns:) + raise 'Full text search columns already defined!' if pg_full_text_searchable_columns.present? + + columns.each do |column| + pg_full_text_searchable_columns[column[:name]] = column[:weight] + end + + # We update this outside the transaction because this could raise an error if the resulting tsvector + # is too long. When that happens, we still persist the create / update but the model will not have a + # search data record. This is fine in most cases because this is a very rare occurrence and only happens + # with strings that are most likely unsearchable anyway. + # + # We also do not want to use a subtransaction here due to: https://gitlab.com/groups/gitlab-org/-/epics/6540 + after_save_commit do + next unless pg_full_text_searchable_columns.keys.any? { |f| saved_changes.has_key?(f) } + + update_search_data! + end + end + + def pg_full_text_search(search_term) + search_data_table = reflect_on_association(:search_data).klass.arel_table + + joins(:search_data).where( + Arel::Nodes::InfixOperation.new( + '@@', + search_data_table[:search_vector], + Arel::Nodes::NamedFunction.new( + 'websearch_to_tsquery', + [Arel::Nodes.build_quoted(TEXT_SEARCH_DICTIONARY), Arel::Nodes.build_quoted(search_term)] + ) + ) + ) + end + end +end diff --git a/app/models/issue.rb b/app/models/issue.rb index 68ea6cb3abc..75727fff2cd 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -24,6 +24,7 @@ class Issue < ApplicationRecord include Todoable include FromUnion include EachBatch + include PgFullTextSearchable extend ::Gitlab::Utils::Override @@ -77,6 +78,7 @@ class Issue < ApplicationRecord end end + has_one :search_data, class_name: 'Issues::SearchData' has_one :issuable_severity has_one :sentry_issue has_one :alert_management_alert, class_name: 'AlertManagement::Alert' @@ -102,6 +104,8 @@ class Issue < ApplicationRecord alias_attribute :external_author, :service_desk_reply_to + pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }, { name: 'description', weight: 'B' }] + scope :in_projects, ->(project_ids) { where(project_id: project_ids) } scope :not_in_projects, ->(project_ids) { where.not(project_id: project_ids) } @@ -233,6 +237,11 @@ class Issue < ApplicationRecord def order_upvotes_asc reorder(upvotes_count: :asc) end + + override :pg_full_text_search + def pg_full_text_search(search_term) + super.where('issue_search_data.project_id = issues.project_id') + end end def next_object_by_relative_position(ignoring: nil, order: :asc) @@ -611,6 +620,11 @@ class Issue < ApplicationRecord private + override :persist_pg_full_text_search_vector + def persist_pg_full_text_search_vector(search_vector) + Issues::SearchData.upsert({ project_id: project_id, issue_id: id, search_vector: search_vector }, unique_by: %i(project_id issue_id)) + end + def spammable_attribute_changed? title_changed? || description_changed? || diff --git a/app/models/issues/search_data.rb b/app/models/issues/search_data.rb new file mode 100644 index 00000000000..0eda292796d --- /dev/null +++ b/app/models/issues/search_data.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Issues + class SearchData < ApplicationRecord + extend SuppressCompositePrimaryKeyWarning + + self.table_name = 'issue_search_data' + + belongs_to :issue + end +end diff --git a/app/models/note.rb b/app/models/note.rb index 3f3fa968393..1d661367599 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -609,7 +609,6 @@ class Note < ApplicationRecord def show_outdated_changes? return false unless for_merge_request? - return false unless Feature.enabled?(:display_outdated_line_diff, noteable.source_project, default_enabled: :yaml) return false unless system? return false unless change_position&.line_range diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 4cc5ed06d61..147ca9c9881 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -264,8 +264,6 @@ class ProjectPolicy < BasePolicy enable :create_work_item end - rule { can?(:update_issue) }.enable :update_work_item - # These abilities are not allowed to admins that are not members of the project, # that's why they are defined separately. rule { guest & can?(:download_code) }.enable :build_download_code diff --git a/app/policies/work_item_policy.rb b/app/policies/work_item_policy.rb index 7ba5102a406..b4723bc7ed8 100644 --- a/app/policies/work_item_policy.rb +++ b/app/policies/work_item_policy.rb @@ -1,12 +1,9 @@ # frozen_string_literal: true -class WorkItemPolicy < BasePolicy - delegate { @subject.project } +class WorkItemPolicy < IssuePolicy + rule { can?(:owner_access) | is_author }.enable :delete_work_item - desc 'User is author of the work item' - condition(:author) do - @user && @user == @subject.author - end + rule { can?(:update_issue) }.enable :update_work_item - rule { can?(:owner_access) | author }.enable :delete_work_item + rule { can?(:read_issue) }.enable :read_work_item end diff --git a/config/feature_flags/development/display_outdated_line_diff.yml b/config/feature_flags/development/display_outdated_line_diff.yml deleted file mode 100644 index 979575da381..00000000000 --- a/config/feature_flags/development/display_outdated_line_diff.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: display_outdated_line_diff -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72597 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345491 -milestone: '14.5' -type: development -group: group::code review -default_enabled: true diff --git a/config/feature_flags/development/issues_full_text_search.yml b/config/feature_flags/development/issues_full_text_search.yml new file mode 100644 index 00000000000..35af801e3ab --- /dev/null +++ b/config/feature_flags/development/issues_full_text_search.yml @@ -0,0 +1,8 @@ +--- +name: issues_full_text_search +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71913 +rollout_issue_url: +milestone: '14.5' +type: development +group: group::project management +default_enabled: false diff --git a/db/migrate/20211007090229_create_issue_search_table.rb b/db/migrate/20211007090229_create_issue_search_table.rb new file mode 100644 index 00000000000..1fc15d20bd0 --- /dev/null +++ b/db/migrate/20211007090229_create_issue_search_table.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class CreateIssueSearchTable < Gitlab::Database::Migration[1.0] + include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers + + def up + execute <<~SQL + CREATE TABLE issue_search_data ( + project_id bigint NOT NULL REFERENCES projects(id) ON DELETE CASCADE, + issue_id bigint NOT NULL REFERENCES issues(id) ON DELETE CASCADE, + created_at timestamp with time zone DEFAULT NOW() NOT NULL, + updated_at timestamp with time zone DEFAULT NOW() NOT NULL, + search_vector tsvector, + PRIMARY KEY (project_id, issue_id) + ) PARTITION BY HASH (project_id) + SQL + + # rubocop: disable Migration/AddIndex + add_index :issue_search_data, :issue_id + add_index :issue_search_data, :search_vector, using: :gin, name: 'index_issue_search_data_on_search_vector' + # rubocop: enable Migration/AddIndex + + create_hash_partitions :issue_search_data, 64 + end + + def down + drop_table :issue_search_data + end +end diff --git a/db/post_migrate/20211026070408_backfill_issue_search_data.rb b/db/post_migrate/20211026070408_backfill_issue_search_data.rb new file mode 100644 index 00000000000..a840adcb991 --- /dev/null +++ b/db/post_migrate/20211026070408_backfill_issue_search_data.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class BackfillIssueSearchData < Gitlab::Database::Migration[1.0] + MIGRATION = 'BackfillIssueSearchData' + + def up + queue_batched_background_migration( + MIGRATION, + :issues, + :id, + batch_size: 100_000, + sub_batch_size: 1_000, + job_interval: 5.minutes + ) + end + + def down + Gitlab::Database::BackgroundMigration::BatchedMigration + .for_configuration(MIGRATION, :issues, :id, []) + .delete_all + end +end diff --git a/db/schema_migrations/20211007090229 b/db/schema_migrations/20211007090229 new file mode 100644 index 00000000000..9302c958709 --- /dev/null +++ b/db/schema_migrations/20211007090229 @@ -0,0 +1 @@ +9d87052305a552ce380e81a33c690496c44e332eb86869ea6882f5cd4856ab93
\ No newline at end of file diff --git a/db/schema_migrations/20211026070408 b/db/schema_migrations/20211026070408 new file mode 100644 index 00000000000..e48db972388 --- /dev/null +++ b/db/schema_migrations/20211026070408 @@ -0,0 +1 @@ +630899d5a7f833ce0533ae553de89e70bd03fad9b438fd367e3a568261b08b00
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index cebe5aef9a5..111cb4ca521 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1169,6 +1169,591 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_merge_request_st ); ALTER TABLE ONLY analytics_cycle_analytics_merge_request_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_31 FOR VALUES WITH (modulus 32, remainder 31); +CREATE TABLE issue_search_data ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +) +PARTITION BY HASH (project_id); + +CREATE TABLE gitlab_partitions_static.issue_search_data_00 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_00 FOR VALUES WITH (modulus 64, remainder 0); + +CREATE TABLE gitlab_partitions_static.issue_search_data_01 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_01 FOR VALUES WITH (modulus 64, remainder 1); + +CREATE TABLE gitlab_partitions_static.issue_search_data_02 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_02 FOR VALUES WITH (modulus 64, remainder 2); + +CREATE TABLE gitlab_partitions_static.issue_search_data_03 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_03 FOR VALUES WITH (modulus 64, remainder 3); + +CREATE TABLE gitlab_partitions_static.issue_search_data_04 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_04 FOR VALUES WITH (modulus 64, remainder 4); + +CREATE TABLE gitlab_partitions_static.issue_search_data_05 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_05 FOR VALUES WITH (modulus 64, remainder 5); + +CREATE TABLE gitlab_partitions_static.issue_search_data_06 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_06 FOR VALUES WITH (modulus 64, remainder 6); + +CREATE TABLE gitlab_partitions_static.issue_search_data_07 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_07 FOR VALUES WITH (modulus 64, remainder 7); + +CREATE TABLE gitlab_partitions_static.issue_search_data_08 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_08 FOR VALUES WITH (modulus 64, remainder 8); + +CREATE TABLE gitlab_partitions_static.issue_search_data_09 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_09 FOR VALUES WITH (modulus 64, remainder 9); + +CREATE TABLE gitlab_partitions_static.issue_search_data_10 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_10 FOR VALUES WITH (modulus 64, remainder 10); + +CREATE TABLE gitlab_partitions_static.issue_search_data_11 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_11 FOR VALUES WITH (modulus 64, remainder 11); + +CREATE TABLE gitlab_partitions_static.issue_search_data_12 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_12 FOR VALUES WITH (modulus 64, remainder 12); + +CREATE TABLE gitlab_partitions_static.issue_search_data_13 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_13 FOR VALUES WITH (modulus 64, remainder 13); + +CREATE TABLE gitlab_partitions_static.issue_search_data_14 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_14 FOR VALUES WITH (modulus 64, remainder 14); + +CREATE TABLE gitlab_partitions_static.issue_search_data_15 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_15 FOR VALUES WITH (modulus 64, remainder 15); + +CREATE TABLE gitlab_partitions_static.issue_search_data_16 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_16 FOR VALUES WITH (modulus 64, remainder 16); + +CREATE TABLE gitlab_partitions_static.issue_search_data_17 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_17 FOR VALUES WITH (modulus 64, remainder 17); + +CREATE TABLE gitlab_partitions_static.issue_search_data_18 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_18 FOR VALUES WITH (modulus 64, remainder 18); + +CREATE TABLE gitlab_partitions_static.issue_search_data_19 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_19 FOR VALUES WITH (modulus 64, remainder 19); + +CREATE TABLE gitlab_partitions_static.issue_search_data_20 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_20 FOR VALUES WITH (modulus 64, remainder 20); + +CREATE TABLE gitlab_partitions_static.issue_search_data_21 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_21 FOR VALUES WITH (modulus 64, remainder 21); + +CREATE TABLE gitlab_partitions_static.issue_search_data_22 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_22 FOR VALUES WITH (modulus 64, remainder 22); + +CREATE TABLE gitlab_partitions_static.issue_search_data_23 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_23 FOR VALUES WITH (modulus 64, remainder 23); + +CREATE TABLE gitlab_partitions_static.issue_search_data_24 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_24 FOR VALUES WITH (modulus 64, remainder 24); + +CREATE TABLE gitlab_partitions_static.issue_search_data_25 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_25 FOR VALUES WITH (modulus 64, remainder 25); + +CREATE TABLE gitlab_partitions_static.issue_search_data_26 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_26 FOR VALUES WITH (modulus 64, remainder 26); + +CREATE TABLE gitlab_partitions_static.issue_search_data_27 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_27 FOR VALUES WITH (modulus 64, remainder 27); + +CREATE TABLE gitlab_partitions_static.issue_search_data_28 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_28 FOR VALUES WITH (modulus 64, remainder 28); + +CREATE TABLE gitlab_partitions_static.issue_search_data_29 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_29 FOR VALUES WITH (modulus 64, remainder 29); + +CREATE TABLE gitlab_partitions_static.issue_search_data_30 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_30 FOR VALUES WITH (modulus 64, remainder 30); + +CREATE TABLE gitlab_partitions_static.issue_search_data_31 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_31 FOR VALUES WITH (modulus 64, remainder 31); + +CREATE TABLE gitlab_partitions_static.issue_search_data_32 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_32 FOR VALUES WITH (modulus 64, remainder 32); + +CREATE TABLE gitlab_partitions_static.issue_search_data_33 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_33 FOR VALUES WITH (modulus 64, remainder 33); + +CREATE TABLE gitlab_partitions_static.issue_search_data_34 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_34 FOR VALUES WITH (modulus 64, remainder 34); + +CREATE TABLE gitlab_partitions_static.issue_search_data_35 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_35 FOR VALUES WITH (modulus 64, remainder 35); + +CREATE TABLE gitlab_partitions_static.issue_search_data_36 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_36 FOR VALUES WITH (modulus 64, remainder 36); + +CREATE TABLE gitlab_partitions_static.issue_search_data_37 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_37 FOR VALUES WITH (modulus 64, remainder 37); + +CREATE TABLE gitlab_partitions_static.issue_search_data_38 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_38 FOR VALUES WITH (modulus 64, remainder 38); + +CREATE TABLE gitlab_partitions_static.issue_search_data_39 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_39 FOR VALUES WITH (modulus 64, remainder 39); + +CREATE TABLE gitlab_partitions_static.issue_search_data_40 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_40 FOR VALUES WITH (modulus 64, remainder 40); + +CREATE TABLE gitlab_partitions_static.issue_search_data_41 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_41 FOR VALUES WITH (modulus 64, remainder 41); + +CREATE TABLE gitlab_partitions_static.issue_search_data_42 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_42 FOR VALUES WITH (modulus 64, remainder 42); + +CREATE TABLE gitlab_partitions_static.issue_search_data_43 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_43 FOR VALUES WITH (modulus 64, remainder 43); + +CREATE TABLE gitlab_partitions_static.issue_search_data_44 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_44 FOR VALUES WITH (modulus 64, remainder 44); + +CREATE TABLE gitlab_partitions_static.issue_search_data_45 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_45 FOR VALUES WITH (modulus 64, remainder 45); + +CREATE TABLE gitlab_partitions_static.issue_search_data_46 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_46 FOR VALUES WITH (modulus 64, remainder 46); + +CREATE TABLE gitlab_partitions_static.issue_search_data_47 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_47 FOR VALUES WITH (modulus 64, remainder 47); + +CREATE TABLE gitlab_partitions_static.issue_search_data_48 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_48 FOR VALUES WITH (modulus 64, remainder 48); + +CREATE TABLE gitlab_partitions_static.issue_search_data_49 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_49 FOR VALUES WITH (modulus 64, remainder 49); + +CREATE TABLE gitlab_partitions_static.issue_search_data_50 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_50 FOR VALUES WITH (modulus 64, remainder 50); + +CREATE TABLE gitlab_partitions_static.issue_search_data_51 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_51 FOR VALUES WITH (modulus 64, remainder 51); + +CREATE TABLE gitlab_partitions_static.issue_search_data_52 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_52 FOR VALUES WITH (modulus 64, remainder 52); + +CREATE TABLE gitlab_partitions_static.issue_search_data_53 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_53 FOR VALUES WITH (modulus 64, remainder 53); + +CREATE TABLE gitlab_partitions_static.issue_search_data_54 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_54 FOR VALUES WITH (modulus 64, remainder 54); + +CREATE TABLE gitlab_partitions_static.issue_search_data_55 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_55 FOR VALUES WITH (modulus 64, remainder 55); + +CREATE TABLE gitlab_partitions_static.issue_search_data_56 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_56 FOR VALUES WITH (modulus 64, remainder 56); + +CREATE TABLE gitlab_partitions_static.issue_search_data_57 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_57 FOR VALUES WITH (modulus 64, remainder 57); + +CREATE TABLE gitlab_partitions_static.issue_search_data_58 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_58 FOR VALUES WITH (modulus 64, remainder 58); + +CREATE TABLE gitlab_partitions_static.issue_search_data_59 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_59 FOR VALUES WITH (modulus 64, remainder 59); + +CREATE TABLE gitlab_partitions_static.issue_search_data_60 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_60 FOR VALUES WITH (modulus 64, remainder 60); + +CREATE TABLE gitlab_partitions_static.issue_search_data_61 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_61 FOR VALUES WITH (modulus 64, remainder 61); + +CREATE TABLE gitlab_partitions_static.issue_search_data_62 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_62 FOR VALUES WITH (modulus 64, remainder 62); + +CREATE TABLE gitlab_partitions_static.issue_search_data_63 ( + project_id bigint NOT NULL, + issue_id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + search_vector tsvector +); +ALTER TABLE ONLY issue_search_data ATTACH PARTITION gitlab_partitions_static.issue_search_data_63 FOR VALUES WITH (modulus 64, remainder 63); + CREATE TABLE product_analytics_events_experimental ( id bigint NOT NULL, project_id integer NOT NULL, @@ -22749,6 +23334,201 @@ ALTER TABLE ONLY gitlab_partitions_static.analytics_cycle_analytics_merge_reques ALTER TABLE ONLY gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_31 ADD CONSTRAINT analytics_cycle_analytics_merge_request_stage_events_31_pkey PRIMARY KEY (stage_event_hash_id, merge_request_id); +ALTER TABLE ONLY issue_search_data + ADD CONSTRAINT issue_search_data_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_00 + ADD CONSTRAINT issue_search_data_00_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_01 + ADD CONSTRAINT issue_search_data_01_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_02 + ADD CONSTRAINT issue_search_data_02_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_03 + ADD CONSTRAINT issue_search_data_03_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_04 + ADD CONSTRAINT issue_search_data_04_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_05 + ADD CONSTRAINT issue_search_data_05_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_06 + ADD CONSTRAINT issue_search_data_06_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_07 + ADD CONSTRAINT issue_search_data_07_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_08 + ADD CONSTRAINT issue_search_data_08_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_09 + ADD CONSTRAINT issue_search_data_09_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_10 + ADD CONSTRAINT issue_search_data_10_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_11 + ADD CONSTRAINT issue_search_data_11_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_12 + ADD CONSTRAINT issue_search_data_12_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_13 + ADD CONSTRAINT issue_search_data_13_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_14 + ADD CONSTRAINT issue_search_data_14_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_15 + ADD CONSTRAINT issue_search_data_15_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_16 + ADD CONSTRAINT issue_search_data_16_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_17 + ADD CONSTRAINT issue_search_data_17_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_18 + ADD CONSTRAINT issue_search_data_18_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_19 + ADD CONSTRAINT issue_search_data_19_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_20 + ADD CONSTRAINT issue_search_data_20_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_21 + ADD CONSTRAINT issue_search_data_21_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_22 + ADD CONSTRAINT issue_search_data_22_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_23 + ADD CONSTRAINT issue_search_data_23_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_24 + ADD CONSTRAINT issue_search_data_24_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_25 + ADD CONSTRAINT issue_search_data_25_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_26 + ADD CONSTRAINT issue_search_data_26_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_27 + ADD CONSTRAINT issue_search_data_27_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_28 + ADD CONSTRAINT issue_search_data_28_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_29 + ADD CONSTRAINT issue_search_data_29_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_30 + ADD CONSTRAINT issue_search_data_30_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_31 + ADD CONSTRAINT issue_search_data_31_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_32 + ADD CONSTRAINT issue_search_data_32_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_33 + ADD CONSTRAINT issue_search_data_33_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_34 + ADD CONSTRAINT issue_search_data_34_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_35 + ADD CONSTRAINT issue_search_data_35_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_36 + ADD CONSTRAINT issue_search_data_36_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_37 + ADD CONSTRAINT issue_search_data_37_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_38 + ADD CONSTRAINT issue_search_data_38_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_39 + ADD CONSTRAINT issue_search_data_39_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_40 + ADD CONSTRAINT issue_search_data_40_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_41 + ADD CONSTRAINT issue_search_data_41_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_42 + ADD CONSTRAINT issue_search_data_42_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_43 + ADD CONSTRAINT issue_search_data_43_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_44 + ADD CONSTRAINT issue_search_data_44_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_45 + ADD CONSTRAINT issue_search_data_45_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_46 + ADD CONSTRAINT issue_search_data_46_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_47 + ADD CONSTRAINT issue_search_data_47_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_48 + ADD CONSTRAINT issue_search_data_48_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_49 + ADD CONSTRAINT issue_search_data_49_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_50 + ADD CONSTRAINT issue_search_data_50_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_51 + ADD CONSTRAINT issue_search_data_51_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_52 + ADD CONSTRAINT issue_search_data_52_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_53 + ADD CONSTRAINT issue_search_data_53_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_54 + ADD CONSTRAINT issue_search_data_54_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_55 + ADD CONSTRAINT issue_search_data_55_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_56 + ADD CONSTRAINT issue_search_data_56_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_57 + ADD CONSTRAINT issue_search_data_57_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_58 + ADD CONSTRAINT issue_search_data_58_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_59 + ADD CONSTRAINT issue_search_data_59_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_60 + ADD CONSTRAINT issue_search_data_60_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_61 + ADD CONSTRAINT issue_search_data_61_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_62 + ADD CONSTRAINT issue_search_data_62_pkey PRIMARY KEY (project_id, issue_id); + +ALTER TABLE ONLY gitlab_partitions_static.issue_search_data_63 + ADD CONSTRAINT issue_search_data_63_pkey PRIMARY KEY (project_id, issue_id); + ALTER TABLE ONLY product_analytics_events_experimental ADD CONSTRAINT product_analytics_events_experimental_pkey PRIMARY KEY (id, project_id); @@ -25149,6 +25929,266 @@ CREATE INDEX index_ff39be5400 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_ff8741d8d7 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_28 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); +CREATE INDEX index_issue_search_data_on_issue_id ON ONLY issue_search_data USING btree (issue_id); + +CREATE INDEX issue_search_data_00_issue_id_idx ON gitlab_partitions_static.issue_search_data_00 USING btree (issue_id); + +CREATE INDEX index_issue_search_data_on_search_vector ON ONLY issue_search_data USING gin (search_vector); + +CREATE INDEX issue_search_data_00_search_vector_idx ON gitlab_partitions_static.issue_search_data_00 USING gin (search_vector); + +CREATE INDEX issue_search_data_01_issue_id_idx ON gitlab_partitions_static.issue_search_data_01 USING btree (issue_id); + +CREATE INDEX issue_search_data_01_search_vector_idx ON gitlab_partitions_static.issue_search_data_01 USING gin (search_vector); + +CREATE INDEX issue_search_data_02_issue_id_idx ON gitlab_partitions_static.issue_search_data_02 USING btree (issue_id); + +CREATE INDEX issue_search_data_02_search_vector_idx ON gitlab_partitions_static.issue_search_data_02 USING gin (search_vector); + +CREATE INDEX issue_search_data_03_issue_id_idx ON gitlab_partitions_static.issue_search_data_03 USING btree (issue_id); + +CREATE INDEX issue_search_data_03_search_vector_idx ON gitlab_partitions_static.issue_search_data_03 USING gin (search_vector); + +CREATE INDEX issue_search_data_04_issue_id_idx ON gitlab_partitions_static.issue_search_data_04 USING btree (issue_id); + +CREATE INDEX issue_search_data_04_search_vector_idx ON gitlab_partitions_static.issue_search_data_04 USING gin (search_vector); + +CREATE INDEX issue_search_data_05_issue_id_idx ON gitlab_partitions_static.issue_search_data_05 USING btree (issue_id); + +CREATE INDEX issue_search_data_05_search_vector_idx ON gitlab_partitions_static.issue_search_data_05 USING gin (search_vector); + +CREATE INDEX issue_search_data_06_issue_id_idx ON gitlab_partitions_static.issue_search_data_06 USING btree (issue_id); + +CREATE INDEX issue_search_data_06_search_vector_idx ON gitlab_partitions_static.issue_search_data_06 USING gin (search_vector); + +CREATE INDEX issue_search_data_07_issue_id_idx ON gitlab_partitions_static.issue_search_data_07 USING btree (issue_id); + +CREATE INDEX issue_search_data_07_search_vector_idx ON gitlab_partitions_static.issue_search_data_07 USING gin (search_vector); + +CREATE INDEX issue_search_data_08_issue_id_idx ON gitlab_partitions_static.issue_search_data_08 USING btree (issue_id); + +CREATE INDEX issue_search_data_08_search_vector_idx ON gitlab_partitions_static.issue_search_data_08 USING gin (search_vector); + +CREATE INDEX issue_search_data_09_issue_id_idx ON gitlab_partitions_static.issue_search_data_09 USING btree (issue_id); + +CREATE INDEX issue_search_data_09_search_vector_idx ON gitlab_partitions_static.issue_search_data_09 USING gin (search_vector); + +CREATE INDEX issue_search_data_10_issue_id_idx ON gitlab_partitions_static.issue_search_data_10 USING btree (issue_id); + +CREATE INDEX issue_search_data_10_search_vector_idx ON gitlab_partitions_static.issue_search_data_10 USING gin (search_vector); + +CREATE INDEX issue_search_data_11_issue_id_idx ON gitlab_partitions_static.issue_search_data_11 USING btree (issue_id); + +CREATE INDEX issue_search_data_11_search_vector_idx ON gitlab_partitions_static.issue_search_data_11 USING gin (search_vector); + +CREATE INDEX issue_search_data_12_issue_id_idx ON gitlab_partitions_static.issue_search_data_12 USING btree (issue_id); + +CREATE INDEX issue_search_data_12_search_vector_idx ON gitlab_partitions_static.issue_search_data_12 USING gin (search_vector); + +CREATE INDEX issue_search_data_13_issue_id_idx ON gitlab_partitions_static.issue_search_data_13 USING btree (issue_id); + +CREATE INDEX issue_search_data_13_search_vector_idx ON gitlab_partitions_static.issue_search_data_13 USING gin (search_vector); + +CREATE INDEX issue_search_data_14_issue_id_idx ON gitlab_partitions_static.issue_search_data_14 USING btree (issue_id); + +CREATE INDEX issue_search_data_14_search_vector_idx ON gitlab_partitions_static.issue_search_data_14 USING gin (search_vector); + +CREATE INDEX issue_search_data_15_issue_id_idx ON gitlab_partitions_static.issue_search_data_15 USING btree (issue_id); + +CREATE INDEX issue_search_data_15_search_vector_idx ON gitlab_partitions_static.issue_search_data_15 USING gin (search_vector); + +CREATE INDEX issue_search_data_16_issue_id_idx ON gitlab_partitions_static.issue_search_data_16 USING btree (issue_id); + +CREATE INDEX issue_search_data_16_search_vector_idx ON gitlab_partitions_static.issue_search_data_16 USING gin (search_vector); + +CREATE INDEX issue_search_data_17_issue_id_idx ON gitlab_partitions_static.issue_search_data_17 USING btree (issue_id); + +CREATE INDEX issue_search_data_17_search_vector_idx ON gitlab_partitions_static.issue_search_data_17 USING gin (search_vector); + +CREATE INDEX issue_search_data_18_issue_id_idx ON gitlab_partitions_static.issue_search_data_18 USING btree (issue_id); + +CREATE INDEX issue_search_data_18_search_vector_idx ON gitlab_partitions_static.issue_search_data_18 USING gin (search_vector); + +CREATE INDEX issue_search_data_19_issue_id_idx ON gitlab_partitions_static.issue_search_data_19 USING btree (issue_id); + +CREATE INDEX issue_search_data_19_search_vector_idx ON gitlab_partitions_static.issue_search_data_19 USING gin (search_vector); + +CREATE INDEX issue_search_data_20_issue_id_idx ON gitlab_partitions_static.issue_search_data_20 USING btree (issue_id); + +CREATE INDEX issue_search_data_20_search_vector_idx ON gitlab_partitions_static.issue_search_data_20 USING gin (search_vector); + +CREATE INDEX issue_search_data_21_issue_id_idx ON gitlab_partitions_static.issue_search_data_21 USING btree (issue_id); + +CREATE INDEX issue_search_data_21_search_vector_idx ON gitlab_partitions_static.issue_search_data_21 USING gin (search_vector); + +CREATE INDEX issue_search_data_22_issue_id_idx ON gitlab_partitions_static.issue_search_data_22 USING btree (issue_id); + +CREATE INDEX issue_search_data_22_search_vector_idx ON gitlab_partitions_static.issue_search_data_22 USING gin (search_vector); + +CREATE INDEX issue_search_data_23_issue_id_idx ON gitlab_partitions_static.issue_search_data_23 USING btree (issue_id); + +CREATE INDEX issue_search_data_23_search_vector_idx ON gitlab_partitions_static.issue_search_data_23 USING gin (search_vector); + +CREATE INDEX issue_search_data_24_issue_id_idx ON gitlab_partitions_static.issue_search_data_24 USING btree (issue_id); + +CREATE INDEX issue_search_data_24_search_vector_idx ON gitlab_partitions_static.issue_search_data_24 USING gin (search_vector); + +CREATE INDEX issue_search_data_25_issue_id_idx ON gitlab_partitions_static.issue_search_data_25 USING btree (issue_id); + +CREATE INDEX issue_search_data_25_search_vector_idx ON gitlab_partitions_static.issue_search_data_25 USING gin (search_vector); + +CREATE INDEX issue_search_data_26_issue_id_idx ON gitlab_partitions_static.issue_search_data_26 USING btree (issue_id); + +CREATE INDEX issue_search_data_26_search_vector_idx ON gitlab_partitions_static.issue_search_data_26 USING gin (search_vector); + +CREATE INDEX issue_search_data_27_issue_id_idx ON gitlab_partitions_static.issue_search_data_27 USING btree (issue_id); + +CREATE INDEX issue_search_data_27_search_vector_idx ON gitlab_partitions_static.issue_search_data_27 USING gin (search_vector); + +CREATE INDEX issue_search_data_28_issue_id_idx ON gitlab_partitions_static.issue_search_data_28 USING btree (issue_id); + +CREATE INDEX issue_search_data_28_search_vector_idx ON gitlab_partitions_static.issue_search_data_28 USING gin (search_vector); + +CREATE INDEX issue_search_data_29_issue_id_idx ON gitlab_partitions_static.issue_search_data_29 USING btree (issue_id); + +CREATE INDEX issue_search_data_29_search_vector_idx ON gitlab_partitions_static.issue_search_data_29 USING gin (search_vector); + +CREATE INDEX issue_search_data_30_issue_id_idx ON gitlab_partitions_static.issue_search_data_30 USING btree (issue_id); + +CREATE INDEX issue_search_data_30_search_vector_idx ON gitlab_partitions_static.issue_search_data_30 USING gin (search_vector); + +CREATE INDEX issue_search_data_31_issue_id_idx ON gitlab_partitions_static.issue_search_data_31 USING btree (issue_id); + +CREATE INDEX issue_search_data_31_search_vector_idx ON gitlab_partitions_static.issue_search_data_31 USING gin (search_vector); + +CREATE INDEX issue_search_data_32_issue_id_idx ON gitlab_partitions_static.issue_search_data_32 USING btree (issue_id); + +CREATE INDEX issue_search_data_32_search_vector_idx ON gitlab_partitions_static.issue_search_data_32 USING gin (search_vector); + +CREATE INDEX issue_search_data_33_issue_id_idx ON gitlab_partitions_static.issue_search_data_33 USING btree (issue_id); + +CREATE INDEX issue_search_data_33_search_vector_idx ON gitlab_partitions_static.issue_search_data_33 USING gin (search_vector); + +CREATE INDEX issue_search_data_34_issue_id_idx ON gitlab_partitions_static.issue_search_data_34 USING btree (issue_id); + +CREATE INDEX issue_search_data_34_search_vector_idx ON gitlab_partitions_static.issue_search_data_34 USING gin (search_vector); + +CREATE INDEX issue_search_data_35_issue_id_idx ON gitlab_partitions_static.issue_search_data_35 USING btree (issue_id); + +CREATE INDEX issue_search_data_35_search_vector_idx ON gitlab_partitions_static.issue_search_data_35 USING gin (search_vector); + +CREATE INDEX issue_search_data_36_issue_id_idx ON gitlab_partitions_static.issue_search_data_36 USING btree (issue_id); + +CREATE INDEX issue_search_data_36_search_vector_idx ON gitlab_partitions_static.issue_search_data_36 USING gin (search_vector); + +CREATE INDEX issue_search_data_37_issue_id_idx ON gitlab_partitions_static.issue_search_data_37 USING btree (issue_id); + +CREATE INDEX issue_search_data_37_search_vector_idx ON gitlab_partitions_static.issue_search_data_37 USING gin (search_vector); + +CREATE INDEX issue_search_data_38_issue_id_idx ON gitlab_partitions_static.issue_search_data_38 USING btree (issue_id); + +CREATE INDEX issue_search_data_38_search_vector_idx ON gitlab_partitions_static.issue_search_data_38 USING gin (search_vector); + +CREATE INDEX issue_search_data_39_issue_id_idx ON gitlab_partitions_static.issue_search_data_39 USING btree (issue_id); + +CREATE INDEX issue_search_data_39_search_vector_idx ON gitlab_partitions_static.issue_search_data_39 USING gin (search_vector); + +CREATE INDEX issue_search_data_40_issue_id_idx ON gitlab_partitions_static.issue_search_data_40 USING btree (issue_id); + +CREATE INDEX issue_search_data_40_search_vector_idx ON gitlab_partitions_static.issue_search_data_40 USING gin (search_vector); + +CREATE INDEX issue_search_data_41_issue_id_idx ON gitlab_partitions_static.issue_search_data_41 USING btree (issue_id); + +CREATE INDEX issue_search_data_41_search_vector_idx ON gitlab_partitions_static.issue_search_data_41 USING gin (search_vector); + +CREATE INDEX issue_search_data_42_issue_id_idx ON gitlab_partitions_static.issue_search_data_42 USING btree (issue_id); + +CREATE INDEX issue_search_data_42_search_vector_idx ON gitlab_partitions_static.issue_search_data_42 USING gin (search_vector); + +CREATE INDEX issue_search_data_43_issue_id_idx ON gitlab_partitions_static.issue_search_data_43 USING btree (issue_id); + +CREATE INDEX issue_search_data_43_search_vector_idx ON gitlab_partitions_static.issue_search_data_43 USING gin (search_vector); + +CREATE INDEX issue_search_data_44_issue_id_idx ON gitlab_partitions_static.issue_search_data_44 USING btree (issue_id); + +CREATE INDEX issue_search_data_44_search_vector_idx ON gitlab_partitions_static.issue_search_data_44 USING gin (search_vector); + +CREATE INDEX issue_search_data_45_issue_id_idx ON gitlab_partitions_static.issue_search_data_45 USING btree (issue_id); + +CREATE INDEX issue_search_data_45_search_vector_idx ON gitlab_partitions_static.issue_search_data_45 USING gin (search_vector); + +CREATE INDEX issue_search_data_46_issue_id_idx ON gitlab_partitions_static.issue_search_data_46 USING btree (issue_id); + +CREATE INDEX issue_search_data_46_search_vector_idx ON gitlab_partitions_static.issue_search_data_46 USING gin (search_vector); + +CREATE INDEX issue_search_data_47_issue_id_idx ON gitlab_partitions_static.issue_search_data_47 USING btree (issue_id); + +CREATE INDEX issue_search_data_47_search_vector_idx ON gitlab_partitions_static.issue_search_data_47 USING gin (search_vector); + +CREATE INDEX issue_search_data_48_issue_id_idx ON gitlab_partitions_static.issue_search_data_48 USING btree (issue_id); + +CREATE INDEX issue_search_data_48_search_vector_idx ON gitlab_partitions_static.issue_search_data_48 USING gin (search_vector); + +CREATE INDEX issue_search_data_49_issue_id_idx ON gitlab_partitions_static.issue_search_data_49 USING btree (issue_id); + +CREATE INDEX issue_search_data_49_search_vector_idx ON gitlab_partitions_static.issue_search_data_49 USING gin (search_vector); + +CREATE INDEX issue_search_data_50_issue_id_idx ON gitlab_partitions_static.issue_search_data_50 USING btree (issue_id); + +CREATE INDEX issue_search_data_50_search_vector_idx ON gitlab_partitions_static.issue_search_data_50 USING gin (search_vector); + +CREATE INDEX issue_search_data_51_issue_id_idx ON gitlab_partitions_static.issue_search_data_51 USING btree (issue_id); + +CREATE INDEX issue_search_data_51_search_vector_idx ON gitlab_partitions_static.issue_search_data_51 USING gin (search_vector); + +CREATE INDEX issue_search_data_52_issue_id_idx ON gitlab_partitions_static.issue_search_data_52 USING btree (issue_id); + +CREATE INDEX issue_search_data_52_search_vector_idx ON gitlab_partitions_static.issue_search_data_52 USING gin (search_vector); + +CREATE INDEX issue_search_data_53_issue_id_idx ON gitlab_partitions_static.issue_search_data_53 USING btree (issue_id); + +CREATE INDEX issue_search_data_53_search_vector_idx ON gitlab_partitions_static.issue_search_data_53 USING gin (search_vector); + +CREATE INDEX issue_search_data_54_issue_id_idx ON gitlab_partitions_static.issue_search_data_54 USING btree (issue_id); + +CREATE INDEX issue_search_data_54_search_vector_idx ON gitlab_partitions_static.issue_search_data_54 USING gin (search_vector); + +CREATE INDEX issue_search_data_55_issue_id_idx ON gitlab_partitions_static.issue_search_data_55 USING btree (issue_id); + +CREATE INDEX issue_search_data_55_search_vector_idx ON gitlab_partitions_static.issue_search_data_55 USING gin (search_vector); + +CREATE INDEX issue_search_data_56_issue_id_idx ON gitlab_partitions_static.issue_search_data_56 USING btree (issue_id); + +CREATE INDEX issue_search_data_56_search_vector_idx ON gitlab_partitions_static.issue_search_data_56 USING gin (search_vector); + +CREATE INDEX issue_search_data_57_issue_id_idx ON gitlab_partitions_static.issue_search_data_57 USING btree (issue_id); + +CREATE INDEX issue_search_data_57_search_vector_idx ON gitlab_partitions_static.issue_search_data_57 USING gin (search_vector); + +CREATE INDEX issue_search_data_58_issue_id_idx ON gitlab_partitions_static.issue_search_data_58 USING btree (issue_id); + +CREATE INDEX issue_search_data_58_search_vector_idx ON gitlab_partitions_static.issue_search_data_58 USING gin (search_vector); + +CREATE INDEX issue_search_data_59_issue_id_idx ON gitlab_partitions_static.issue_search_data_59 USING btree (issue_id); + +CREATE INDEX issue_search_data_59_search_vector_idx ON gitlab_partitions_static.issue_search_data_59 USING gin (search_vector); + +CREATE INDEX issue_search_data_60_issue_id_idx ON gitlab_partitions_static.issue_search_data_60 USING btree (issue_id); + +CREATE INDEX issue_search_data_60_search_vector_idx ON gitlab_partitions_static.issue_search_data_60 USING gin (search_vector); + +CREATE INDEX issue_search_data_61_issue_id_idx ON gitlab_partitions_static.issue_search_data_61 USING btree (issue_id); + +CREATE INDEX issue_search_data_61_search_vector_idx ON gitlab_partitions_static.issue_search_data_61 USING gin (search_vector); + +CREATE INDEX issue_search_data_62_issue_id_idx ON gitlab_partitions_static.issue_search_data_62 USING btree (issue_id); + +CREATE INDEX issue_search_data_62_search_vector_idx ON gitlab_partitions_static.issue_search_data_62 USING gin (search_vector); + +CREATE INDEX issue_search_data_63_issue_id_idx ON gitlab_partitions_static.issue_search_data_63 USING btree (issue_id); + +CREATE INDEX issue_search_data_63_search_vector_idx ON gitlab_partitions_static.issue_search_data_63 USING gin (search_vector); + CREATE INDEX index_product_analytics_events_experimental_project_and_time ON ONLY product_analytics_events_experimental USING btree (project_id, collector_tstamp); CREATE INDEX product_analytics_events_expe_project_id_collector_tstamp_idx10 ON gitlab_partitions_static.product_analytics_events_experimental_10 USING btree (project_id, collector_tstamp); @@ -29157,6 +30197,390 @@ ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_pa ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_ff8741d8d7; +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_00_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_00_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_00_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_01_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_01_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_01_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_02_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_02_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_02_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_03_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_03_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_03_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_04_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_04_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_04_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_05_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_05_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_05_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_06_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_06_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_06_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_07_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_07_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_07_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_08_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_08_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_08_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_09_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_09_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_09_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_10_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_10_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_10_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_11_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_11_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_11_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_12_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_12_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_12_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_13_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_13_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_13_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_14_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_14_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_14_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_15_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_15_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_15_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_16_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_16_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_16_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_17_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_17_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_17_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_18_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_18_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_18_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_19_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_19_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_19_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_20_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_20_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_20_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_21_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_21_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_21_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_22_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_22_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_22_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_23_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_23_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_23_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_24_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_24_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_24_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_25_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_25_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_25_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_26_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_26_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_26_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_27_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_27_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_27_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_28_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_28_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_28_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_29_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_29_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_29_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_30_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_30_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_30_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_31_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_31_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_31_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_32_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_32_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_32_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_33_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_33_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_33_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_34_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_34_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_34_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_35_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_35_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_35_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_36_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_36_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_36_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_37_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_37_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_37_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_38_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_38_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_38_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_39_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_39_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_39_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_40_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_40_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_40_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_41_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_41_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_41_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_42_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_42_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_42_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_43_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_43_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_43_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_44_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_44_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_44_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_45_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_45_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_45_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_46_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_46_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_46_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_47_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_47_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_47_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_48_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_48_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_48_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_49_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_49_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_49_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_50_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_50_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_50_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_51_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_51_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_51_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_52_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_52_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_52_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_53_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_53_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_53_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_54_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_54_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_54_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_55_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_55_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_55_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_56_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_56_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_56_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_57_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_57_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_57_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_58_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_58_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_58_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_59_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_59_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_59_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_60_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_60_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_60_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_61_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_61_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_61_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_62_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_62_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_62_search_vector_idx; + +ALTER INDEX index_issue_search_data_on_issue_id ATTACH PARTITION gitlab_partitions_static.issue_search_data_63_issue_id_idx; + +ALTER INDEX issue_search_data_pkey ATTACH PARTITION gitlab_partitions_static.issue_search_data_63_pkey; + +ALTER INDEX index_issue_search_data_on_search_vector ATTACH PARTITION gitlab_partitions_static.issue_search_data_63_search_vector_idx; + ALTER INDEX index_product_analytics_events_experimental_project_and_time ATTACH PARTITION gitlab_partitions_static.product_analytics_events_expe_project_id_collector_tstamp_idx10; ALTER INDEX index_product_analytics_events_experimental_project_and_time ATTACH PARTITION gitlab_partitions_static.product_analytics_events_expe_project_id_collector_tstamp_idx11; @@ -31884,6 +33308,12 @@ ALTER TABLE ONLY timelogs ALTER TABLE ONLY u2f_registrations ADD CONSTRAINT fk_u2f_registrations_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; +ALTER TABLE issue_search_data + ADD CONSTRAINT issue_search_data_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE; + +ALTER TABLE issue_search_data + ADD CONSTRAINT issue_search_data_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE product_analytics_events_experimental ADD CONSTRAINT product_analytics_events_experimental_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; diff --git a/lib/gitlab/background_migration/backfill_issue_search_data.rb b/lib/gitlab/background_migration/backfill_issue_search_data.rb new file mode 100644 index 00000000000..82722fe1dc3 --- /dev/null +++ b/lib/gitlab/background_migration/backfill_issue_search_data.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true +# rubocop:disable Style/Documentation + +module Gitlab + module BackgroundMigration + # Backfills the new `issue_search_data` table, which contains + # the tsvector from the issue title and description. + class BackfillIssueSearchData + include Gitlab::Database::DynamicModelHelpers + + def perform(start_id, stop_id, batch_table, batch_column, sub_batch_size, pause_ms) + define_batchable_model(batch_table, connection: ActiveRecord::Base.connection).where(batch_column => start_id..stop_id).each_batch(of: sub_batch_size) do |sub_batch| + update_search_data(sub_batch) + + sleep(pause_ms * 0.001) + rescue ActiveRecord::StatementInvalid => e + raise unless e.cause.is_a?(PG::ProgramLimitExceeded) && e.message.include?('string is too long for tsvector') + + update_search_data_individually(sub_batch, pause_ms) + end + end + + private + + def update_search_data(relation) + relation.klass.connection.execute( + <<~SQL + INSERT INTO issue_search_data (project_id, issue_id, search_vector, created_at, updated_at) + SELECT + project_id, + id, + setweight(to_tsvector('english', LEFT(title, 255)), 'A') || setweight(to_tsvector('english', LEFT(REGEXP_REPLACE(description, '[A-Za-z0-9+/]{50,}', ' ', 'g'), 1048576)), 'B'), + NOW(), + NOW() + FROM issues + WHERE issues.id IN (#{relation.select(:id).to_sql}) + ON CONFLICT DO NOTHING + SQL + ) + end + + def update_search_data_individually(relation, pause_ms) + relation.pluck(:id).each do |issue_id| + update_search_data(relation.klass.where(id: issue_id)) + + sleep(pause_ms * 0.001) + rescue ActiveRecord::StatementInvalid => e + raise unless e.cause.is_a?(PG::ProgramLimitExceeded) && e.message.include?('string is too long for tsvector') + + logger.error( + message: 'Error updating search data: string is too long for tsvector', + class: relation.klass.name, + model_id: issue_id + ) + end + end + + def logger + @logger ||= Gitlab::BackgroundMigration::Logger.build + end + end + end +end diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml index 04b35a32b9d..95005871194 100644 --- a/lib/gitlab/database/gitlab_schemas.yml +++ b/lib/gitlab/database/gitlab_schemas.yml @@ -274,6 +274,7 @@ issue_emails: :gitlab_main issue_email_participants: :gitlab_main issue_links: :gitlab_main issue_metrics: :gitlab_main +issue_search_data: :gitlab_main issues: :gitlab_main issues_prometheus_alert_events: :gitlab_main issues_self_managed_prometheus_alert_events: :gitlab_main diff --git a/lib/gitlab/issuables_count_for_state.rb b/lib/gitlab/issuables_count_for_state.rb index f11dd520d2d..42b549cf127 100644 --- a/lib/gitlab/issuables_count_for_state.rb +++ b/lib/gitlab/issuables_count_for_state.rb @@ -144,7 +144,7 @@ module Gitlab def params_include_filters? non_filtering_params = %i[ scope state sort group_id include_subgroups - attempt_group_search_optimizations non_archived issue_types + attempt_group_search_optimizations attempt_full_text_search non_archived issue_types ] finder.params.except(*non_filtering_params).values.any? diff --git a/qa/qa/resource/protected_branch.rb b/qa/qa/resource/protected_branch.rb index 062d4e9f3d8..55ad6edb3c1 100644 --- a/qa/qa/resource/protected_branch.rb +++ b/qa/qa/resource/protected_branch.rb @@ -72,6 +72,16 @@ module QA self.remove_via_api!(&block) end + # Remove the branch protection after confirming that it exists + def remove_via_api! + Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1, message: "Waiting for branch #{branch_name} to be protected") do + # We confirm it exists before removal because there's no creation event when the default branch is automatically protected by GitLab itself, and there's a slight delay between creating the repo and protecting the default branch + exists? + end + + super + end + def api_get_path "/projects/#{project.id}/protected_branches/#{branch_name}" end diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index 8fae617ea65..dbeac9fd240 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -13,7 +13,22 @@ RSpec.describe DashboardController do end describe 'GET issues' do - it_behaves_like 'issuables list meta-data', :issue, :issues + context 'when issues_full_text_search is disabled' do + before do + stub_feature_flags(issues_full_text_search: false) + end + + it_behaves_like 'issuables list meta-data', :issue, :issues + end + + context 'when issues_full_text_search is enabled' do + before do + stub_feature_flags(issues_full_text_search: true) + end + + it_behaves_like 'issuables list meta-data', :issue, :issues + end + it_behaves_like 'issuables requiring filter', :issues end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index bf0b833b311..c159768690e 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -72,7 +72,21 @@ RSpec.describe Projects::IssuesController do project.add_developer(user) end - it_behaves_like "issuables list meta-data", :issue + context 'when issues_full_text_search is disabled' do + before do + stub_feature_flags(issues_full_text_search: false) + end + + it_behaves_like 'issuables list meta-data', :issue + end + + context 'when issues_full_text_search is enabled' do + before do + stub_feature_flags(issues_full_text_search: true) + end + + it_behaves_like 'issuables list meta-data', :issue + end it_behaves_like 'set sort order from user preference' do let(:sorting_param) { 'updated_asc' } diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index edf3df7c16e..2883030e9ac 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -497,6 +497,8 @@ RSpec.describe 'Filter issues', :js do end it 'filters issues by searched text containing special characters' do + stub_feature_flags(issues_full_text_search: false) + issue = create(:issue, project: project, author: user, title: "issue with !@\#{$%^&*()-+") search = '!@#{$%^&*()-+' diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index c22e56c3b9e..b00cdca3423 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -632,6 +632,29 @@ RSpec.describe IssuesFinder do end end + context 'filtering by issue term using full-text search' do + let(:params) { { search: search_term, attempt_full_text_search: true } } + + let_it_be(:english) { create(:issue, project: project1, title: 'title', description: 'something english') } + let_it_be(:japanese) { create(:issue, project: project1, title: '日本語 title', description: 'another english description') } + + context 'with latin search term' do + let(:search_term) { 'title english' } + + it 'returns matching issues' do + expect(issues).to contain_exactly(english, japanese) + end + end + + context 'with non-latin search term' do + let(:search_term) { '日本語' } + + it 'returns matching issues' do + expect(issues).to contain_exactly(japanese) + end + end + end + context 'filtering by issues iids' do let(:params) { { iids: [issue3.iid] } } diff --git a/spec/graphql/types/work_item_type_spec.rb b/spec/graphql/types/work_item_type_spec.rb index 4e44123d475..6a5b4a0882e 100644 --- a/spec/graphql/types/work_item_type_spec.rb +++ b/spec/graphql/types/work_item_type_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['WorkItem'] do specify { expect(described_class.graphql_name).to eq('WorkItem') } - specify { expect(described_class).to require_graphql_authorizations(:read_issue) } + specify { expect(described_class).to require_graphql_authorizations(:read_work_item) } it 'has specific fields' do fields = %i[description description_html id iid lock_version state title title_html work_item_type] diff --git a/spec/lib/gitlab/background_migration/backfill_issue_search_data_spec.rb b/spec/lib/gitlab/background_migration/backfill_issue_search_data_spec.rb new file mode 100644 index 00000000000..b29d4c3583b --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_issue_search_data_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::BackfillIssueSearchData do + let(:namespaces_table) { table(:namespaces) } + let(:projects_table) { table(:projects) } + let(:issue_search_data_table) { table(:issue_search_data) } + + let!(:namespace) { namespaces_table.create!(name: 'gitlab-org', path: 'gitlab-org') } + let!(:project) { projects_table.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: namespace.id) } + let!(:issues) { Array.new(10) { table(:issues).create!(project_id: project.id, title: 'test title', description: 'test description') } } + + let(:migration) { described_class.new } + + before do + allow(migration).to receive(:sleep) + end + + it 'backfills search data for the specified records' do + # sleeps for every sub-batch + expect(migration).to receive(:sleep).with(0.05).exactly(3).times + + migration.perform(issues[0].id, issues[5].id, :issues, :id, 2, 50) + + expect(issue_search_data_table.count).to eq(6) + end + + it 'skips issues that already have search data' do + old_time = Time.new(2019, 1, 1).in_time_zone + issue_search_data_table.create!(project_id: project.id, issue_id: issues[0].id, updated_at: old_time) + + migration.perform(issues[0].id, issues[5].id, :issues, :id, 2, 50) + + expect(issue_search_data_table.count).to eq(6) + expect(issue_search_data_table.find_by_issue_id(issues[0].id).updated_at).to be_like_time(old_time) + end + + it 'rescues batch with bad data and inserts other rows' do + issues[1].update!(description: Array.new(30_000) { SecureRandom.hex }.join(' ')) + + expect_next_instance_of(Gitlab::BackgroundMigration::Logger) do |logger| + expect(logger).to receive(:error).with(a_hash_including(message: /string is too long for tsvector/, model_id: issues[1].id)) + end + + expect { migration.perform(issues[0].id, issues[5].id, :issues, :id, 2, 50) }.not_to raise_error + + expect(issue_search_data_table.count).to eq(5) + expect(issue_search_data_table.find_by_issue_id(issues[1].id)).to eq(nil) + end + + it 're-raises other errors' do + allow(migration).to receive(:update_search_data).and_raise(ActiveRecord::StatementTimeout) + + expect { migration.perform(issues[0].id, issues[5].id, :issues, :id, 2, 50) }.to raise_error(ActiveRecord::StatementTimeout) + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index ce13f405459..ae3aafffe56 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -34,6 +34,7 @@ issues: - issuable_severity - issuable_sla - issue_assignees +- search_data - closed_by - epic_issue - epic @@ -627,6 +628,8 @@ issuable_severity: issue_assignees: - issue - assignee +search_data: +- issue merge_request_assignees: - merge_request - assignee diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb new file mode 100644 index 00000000000..50a97e873a5 --- /dev/null +++ b/spec/models/concerns/pg_full_text_searchable_spec.rb @@ -0,0 +1,162 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe PgFullTextSearchable do + let(:project) { create(:project) } + + let(:model_class) do + Class.new(ActiveRecord::Base) do + include PgFullTextSearchable + + self.table_name = 'issues' + + belongs_to :project + has_one :search_data, class_name: 'Issues::SearchData' + + def persist_pg_full_text_search_vector(search_vector) + Issues::SearchData.upsert({ project_id: project_id, issue_id: id, search_vector: search_vector }, unique_by: %i(project_id issue_id)) + end + + def self.name + 'Issue' + end + end + end + + describe '.pg_full_text_searchable' do + it 'sets pg_full_text_searchable_columns' do + model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }] + + expect(model_class.pg_full_text_searchable_columns).to eq({ 'title' => 'A' }) + end + + it 'raises an error when called twice' do + model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }] + + expect { model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }] }.to raise_error('Full text search columns already defined!') + end + end + + describe 'after commit hook' do + let(:model) { model_class.create!(project: project) } + + before do + model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }] + end + + context 'when specified columns are changed' do + it 'calls update_search_data!' do + expect(model).to receive(:update_search_data!) + + model.update!(title: 'A new title') + end + end + + context 'when specified columns are not changed' do + it 'does not enqueue worker' do + expect(model).not_to receive(:update_search_data!) + + model.update!(description: 'A new description') + end + end + end + + describe '.pg_full_text_search' do + let(:english) { model_class.create!(project: project, title: 'title', description: 'something english') } + let(:with_accent) { model_class.create!(project: project, title: 'Jürgen', description: 'Ærøskøbing') } + let(:japanese) { model_class.create!(project: project, title: '日本語 title', description: 'another english description') } + + before do + model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }, { name: 'description', weight: 'B' }] + + [english, with_accent, japanese].each(&:update_search_data!) + end + + it 'searches across all fields' do + expect(model_class.pg_full_text_search('title english')).to contain_exactly(english, japanese) + end + + it 'searches for exact term with quotes' do + expect(model_class.pg_full_text_search('"something english"')).to contain_exactly(english) + end + + it 'ignores accents' do + expect(model_class.pg_full_text_search('jurgen')).to contain_exactly(with_accent) + end + + it 'does not support searching by non-Latin characters' do + expect(model_class.pg_full_text_search('日本')).to be_empty + end + end + + describe '#update_search_data!' do + let(:model) { model_class.create!(project: project, title: 'title', description: 'description') } + + before do + model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }, { name: 'description', weight: 'B' }] + end + + it 'sets the correct weights' do + model.update_search_data! + + expect(model.search_data.search_vector).to match(/'titl':1A/) + expect(model.search_data.search_vector).to match(/'descript':2B/) + end + + context 'with accented and non-Latin characters' do + let(:model) { model_class.create!(project: project, title: '日本語', description: 'Jürgen') } + + it 'transliterates accented characters and removes non-Latin ones' do + model.update_search_data! + + expect(model.search_data.search_vector).not_to match(/日本語/) + expect(model.search_data.search_vector).to match(/jurgen/) + end + end + + context 'when upsert times out' do + it 're-raises the exception' do + expect(Issues::SearchData).to receive(:upsert).once.and_raise(ActiveRecord::StatementTimeout) + + expect { model.update_search_data! }.to raise_error(ActiveRecord::StatementTimeout) + end + end + + context 'with strings that go over tsvector limit', :delete do + let(:long_string) { Array.new(30_000) { SecureRandom.hex }.join(' ') } + let(:model) { model_class.create!(project: project, title: 'title', description: long_string) } + + it 'does not raise an exception' do + expect(Gitlab::AppJsonLogger).to receive(:error).with( + a_hash_including(class: model_class.name, model_id: model.id) + ) + + expect { model.update_search_data! }.not_to raise_error + + expect(model.search_data).to eq(nil) + end + end + + context 'when model class does not implement persist_pg_full_text_search_vector' do + let(:model_class) do + Class.new(ActiveRecord::Base) do + include PgFullTextSearchable + + self.table_name = 'issues' + + belongs_to :project + has_one :search_data, class_name: 'Issues::SearchData' + + def self.name + 'Issue' + end + end + end + + it 'raises an error' do + expect { model.update_search_data! }.to raise_error(NotImplementedError) + end + end + end +end diff --git a/spec/policies/work_item_policy_spec.rb b/spec/policies/work_item_policy_spec.rb new file mode 100644 index 00000000000..08a22a95540 --- /dev/null +++ b/spec/policies/work_item_policy_spec.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WorkItemPolicy do + let_it_be(:project) { create(:project) } + let_it_be(:public_project) { create(:project, :public) } + let_it_be(:guest) { create(:user).tap { |user| project.add_guest(user) } } + let_it_be(:guest_author) { create(:user).tap { |user| project.add_guest(user) } } + let_it_be(:reporter) { create(:user).tap { |user| project.add_reporter(user) } } + let_it_be(:non_member_user) { create(:user) } + let_it_be(:work_item) { create(:work_item, project: project) } + let_it_be(:authored_work_item) { create(:work_item, project: project, author: guest_author) } + let_it_be(:public_work_item) { create(:work_item, project: public_project) } + + let(:work_item_subject) { work_item } + + subject { described_class.new(current_user, work_item_subject) } + + before_all do + public_project.add_developer(guest_author) + end + + describe 'read_work_item' do + context 'when project is public' do + let(:work_item_subject) { public_work_item } + + context 'when user is not a member of the project' do + let(:current_user) { non_member_user } + + it { is_expected.to be_allowed(:read_work_item) } + end + + context 'when user is a member of the project' do + let(:current_user) { guest_author } + + it { is_expected.to be_allowed(:read_work_item) } + end + end + + context 'when project is private' do + let(:work_item_subject) { work_item } + + context 'when user is not a member of the project' do + let(:current_user) { non_member_user } + + it { is_expected.to be_disallowed(:read_work_item) } + end + + context 'when user is a member of the project' do + let(:current_user) { guest_author } + + it { is_expected.to be_allowed(:read_work_item) } + end + end + end + + describe 'update_work_item' do + context 'when user is reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_allowed(:update_work_item) } + end + + context 'when user is guest' do + let(:current_user) { guest } + + it { is_expected.to be_disallowed(:update_work_item) } + + context 'when guest authored the work item' do + let(:work_item_subject) { authored_work_item } + let(:current_user) { guest_author } + + it { is_expected.to be_allowed(:update_work_item) } + end + end + end + + describe 'delete_work_item' do + context 'when user is a member of the project' do + let(:work_item_subject) { work_item } + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:delete_work_item) } + + context 'when guest authored the work item' do + let(:work_item_subject) { authored_work_item } + let(:current_user) { guest_author } + + it { is_expected.to be_allowed(:delete_work_item) } + end + end + end +end |