2018-10-22 07:00:50 +00:00
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
2015-10-07 10:30:10 +00:00
|
|
|
|
module Gitlab
|
|
|
|
|
module Database
|
2021-06-28 21:10:13 +00:00
|
|
|
|
CI_DATABASE_NAME = 'ci'
|
|
|
|
|
|
2021-05-07 15:10:39 +00:00
|
|
|
|
# This constant is used when renaming tables concurrently.
|
|
|
|
|
# If you plan to rename a table using the `rename_table_safely` method, add your table here one milestone before the rename.
|
|
|
|
|
# Example:
|
|
|
|
|
# TABLES_TO_BE_RENAMED = {
|
|
|
|
|
# 'old_name' => 'new_name'
|
|
|
|
|
# }.freeze
|
2021-07-07 15:07:24 +00:00
|
|
|
|
TABLES_TO_BE_RENAMED = {}.freeze
|
2021-05-07 15:10:39 +00:00
|
|
|
|
|
2020-07-14 21:09:03 +00:00
|
|
|
|
# Minimum PostgreSQL version requirement per documentation:
|
|
|
|
|
# https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements
|
2021-06-14 18:10:28 +00:00
|
|
|
|
MINIMUM_POSTGRES_VERSION = 12
|
2020-07-07 03:09:32 +00:00
|
|
|
|
|
2016-06-18 17:55:45 +00:00
|
|
|
|
# https://www.postgresql.org/docs/9.2/static/datatype-numeric.html
|
|
|
|
|
MAX_INT_VALUE = 2147483647
|
2020-08-19 12:10:17 +00:00
|
|
|
|
MIN_INT_VALUE = -2147483648
|
2019-06-13 13:12:28 +00:00
|
|
|
|
|
2017-10-30 23:21:56 +00:00
|
|
|
|
# The max value between MySQL's TIMESTAMP and PostgreSQL's timestampz:
|
|
|
|
|
# https://www.postgresql.org/docs/9.1/static/datatype-datetime.html
|
|
|
|
|
# https://dev.mysql.com/doc/refman/5.7/en/datetime.html
|
2019-06-13 13:12:28 +00:00
|
|
|
|
# FIXME: this should just be the max value of timestampz
|
2017-10-30 23:21:56 +00:00
|
|
|
|
MAX_TIMESTAMP_VALUE = Time.at((1 << 31) - 1).freeze
|
2016-06-18 17:55:45 +00:00
|
|
|
|
|
2019-07-17 09:54:40 +00:00
|
|
|
|
# The maximum number of characters for text fields, to avoid DoS attacks via parsing huge text fields
|
2019-09-18 14:02:45 +00:00
|
|
|
|
# https://gitlab.com/gitlab-org/gitlab-foss/issues/61974
|
2019-07-17 09:54:40 +00:00
|
|
|
|
MAX_TEXT_SIZE_LIMIT = 1_000_000
|
|
|
|
|
|
2019-06-25 17:28:18 +00:00
|
|
|
|
# Minimum schema version from which migrations are supported
|
2019-06-20 06:35:56 +00:00
|
|
|
|
# Migrations before this version may have been removed
|
|
|
|
|
MIN_SCHEMA_VERSION = 20190506135400
|
2019-06-20 14:56:46 +00:00
|
|
|
|
MIN_SCHEMA_GITLAB_VERSION = '11.11.0'
|
2019-06-20 06:35:56 +00:00
|
|
|
|
|
2020-06-26 15:08:45 +00:00
|
|
|
|
# Schema we store dynamically managed partitions in (e.g. for time partitioning)
|
2020-06-25 00:09:26 +00:00
|
|
|
|
DYNAMIC_PARTITIONS_SCHEMA = :gitlab_partitions_dynamic
|
|
|
|
|
|
2020-06-26 15:08:45 +00:00
|
|
|
|
# Schema we store static partitions in (e.g. for hash partitioning)
|
|
|
|
|
STATIC_PARTITIONS_SCHEMA = :gitlab_partitions_static
|
|
|
|
|
|
2020-06-25 00:09:26 +00:00
|
|
|
|
# This is an extensive list of postgres schemas owned by GitLab
|
|
|
|
|
# It does not include the default public schema
|
2020-06-26 15:08:45 +00:00
|
|
|
|
EXTRA_SCHEMAS = [DYNAMIC_PARTITIONS_SCHEMA, STATIC_PARTITIONS_SCHEMA].freeze
|
2020-06-25 00:09:26 +00:00
|
|
|
|
|
2021-07-23 15:09:21 +00:00
|
|
|
|
DATABASES = ActiveRecord::Base
|
|
|
|
|
.connection_handler
|
|
|
|
|
.connection_pools
|
|
|
|
|
.each_with_object({}) do |pool, hash|
|
|
|
|
|
hash[pool.db_config.name.to_sym] = Connection.new(pool.connection_klass)
|
|
|
|
|
end
|
|
|
|
|
.freeze
|
2021-05-11 06:10:29 +00:00
|
|
|
|
|
2021-07-23 15:09:21 +00:00
|
|
|
|
PRIMARY_DATABASE_NAME = ActiveRecord::Base.connection_db_config.name.to_sym
|
2021-05-11 06:10:29 +00:00
|
|
|
|
|
2021-07-23 15:09:21 +00:00
|
|
|
|
def self.main
|
|
|
|
|
DATABASES[PRIMARY_DATABASE_NAME]
|
2017-03-17 13:16:47 +00:00
|
|
|
|
end
|
|
|
|
|
|
2021-06-28 21:10:13 +00:00
|
|
|
|
def self.has_config?(database_name)
|
|
|
|
|
Gitlab::Application.config.database_configuration[Rails.env].include?(database_name.to_s)
|
|
|
|
|
end
|
|
|
|
|
|
2021-07-09 09:09:53 +00:00
|
|
|
|
def self.main_database?(name)
|
|
|
|
|
# The database is `main` if it is a first entry in `database.yml`
|
|
|
|
|
# Rails internally names them `primary` to avoid confusion
|
|
|
|
|
# with broad `primary` usage we use `main` instead
|
|
|
|
|
#
|
|
|
|
|
# TODO: The explicit `== 'main'` is needed in a transition period till
|
|
|
|
|
# the `database.yml` is not migrated into `main:` syntax
|
|
|
|
|
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65243
|
|
|
|
|
ActiveRecord::Base.configurations.primary?(name.to_s) || name.to_s == 'main'
|
|
|
|
|
end
|
|
|
|
|
|
2021-06-28 21:10:13 +00:00
|
|
|
|
def self.ci_database?(name)
|
2021-07-09 09:09:53 +00:00
|
|
|
|
name.to_s == CI_DATABASE_NAME
|
2021-06-28 21:10:13 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-07-07 03:09:32 +00:00
|
|
|
|
def self.check_postgres_version_and_print_warning
|
|
|
|
|
return if Gitlab::Runtime.rails_runner?
|
|
|
|
|
|
2021-07-23 15:09:21 +00:00
|
|
|
|
DATABASES.each do |name, connection|
|
|
|
|
|
next if connection.postgresql_minimum_supported_version?
|
|
|
|
|
|
|
|
|
|
Kernel.warn ERB.new(Rainbow.new.wrap(<<~EOS).red).result
|
|
|
|
|
|
|
|
|
|
██ ██ █████ ██████ ███ ██ ██ ███ ██ ██████
|
|
|
|
|
██ ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ██
|
|
|
|
|
██ █ ██ ███████ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ███
|
|
|
|
|
██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
|
|
|
███ ███ ██ ██ ██ ██ ██ ████ ██ ██ ████ ██████
|
|
|
|
|
|
|
|
|
|
******************************************************************************
|
2021-07-29 15:09:48 +00:00
|
|
|
|
You are using PostgreSQL <%= Gitlab::Database.main.version %> for the #{name} database, but PostgreSQL >= <%= Gitlab::Database::MINIMUM_POSTGRES_VERSION %>
|
2021-07-23 15:09:21 +00:00
|
|
|
|
is required for this version of GitLab.
|
|
|
|
|
<% if Rails.env.development? || Rails.env.test? %>
|
|
|
|
|
If using gitlab-development-kit, please find the relevant steps here:
|
|
|
|
|
https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/postgresql.md#upgrade-postgresql
|
|
|
|
|
<% end %>
|
|
|
|
|
Please upgrade your environment to a supported PostgreSQL version, see
|
|
|
|
|
https://docs.gitlab.com/ee/install/requirements.html#database for details.
|
|
|
|
|
******************************************************************************
|
|
|
|
|
EOS
|
|
|
|
|
rescue ActiveRecord::ActiveRecordError, PG::Error
|
|
|
|
|
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing
|
|
|
|
|
end
|
2020-07-07 03:09:32 +00:00
|
|
|
|
end
|
|
|
|
|
|
2021-04-27 21:10:09 +00:00
|
|
|
|
def self.nulls_order(field, direction = :asc, nulls_order = :nulls_last)
|
|
|
|
|
raise ArgumentError unless [:nulls_last, :nulls_first].include?(nulls_order)
|
|
|
|
|
raise ArgumentError unless [:asc, :desc].include?(direction)
|
|
|
|
|
|
|
|
|
|
case nulls_order
|
|
|
|
|
when :nulls_last then nulls_last_order(field, direction)
|
|
|
|
|
when :nulls_first then nulls_first_order(field, direction)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-05-13 15:26:18 +00:00
|
|
|
|
def self.nulls_last_order(field, direction = 'ASC')
|
2019-06-13 13:12:28 +00:00
|
|
|
|
Arel.sql("#{field} #{direction} NULLS LAST")
|
2016-05-13 15:26:18 +00:00
|
|
|
|
end
|
|
|
|
|
|
2017-01-31 00:26:40 +00:00
|
|
|
|
def self.nulls_first_order(field, direction = 'ASC')
|
2019-06-13 13:12:28 +00:00
|
|
|
|
Arel.sql("#{field} #{direction} NULLS FIRST")
|
2017-01-31 00:26:40 +00:00
|
|
|
|
end
|
|
|
|
|
|
2016-06-15 10:10:41 +00:00
|
|
|
|
def self.random
|
2019-06-13 13:12:28 +00:00
|
|
|
|
"RANDOM()"
|
2016-06-15 10:10:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
2017-04-10 10:23:28 +00:00
|
|
|
|
def self.true_value
|
2019-06-13 13:12:28 +00:00
|
|
|
|
"'t'"
|
2015-12-09 15:31:42 +00:00
|
|
|
|
end
|
|
|
|
|
|
2017-04-10 10:23:28 +00:00
|
|
|
|
def self.false_value
|
2019-06-13 13:12:28 +00:00
|
|
|
|
"'f'"
|
2015-12-09 15:31:42 +00:00
|
|
|
|
end
|
2016-02-02 02:29:37 +00:00
|
|
|
|
|
2017-10-30 23:21:56 +00:00
|
|
|
|
def self.sanitize_timestamp(timestamp)
|
|
|
|
|
MAX_TIMESTAMP_VALUE > timestamp ? timestamp : MAX_TIMESTAMP_VALUE.dup
|
|
|
|
|
end
|
|
|
|
|
|
2021-08-05 12:09:57 +00:00
|
|
|
|
def self.allow_cross_joins_across_databases(url:)
|
|
|
|
|
# this method is implemented in:
|
|
|
|
|
# spec/support/database/prevent_cross_joins.rb
|
|
|
|
|
end
|
|
|
|
|
|
2018-09-12 01:41:14 +00:00
|
|
|
|
def self.add_post_migrate_path_to_rails(force: false)
|
|
|
|
|
return if ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS'] && !force
|
|
|
|
|
|
|
|
|
|
Rails.application.config.paths['db'].each do |db_path|
|
|
|
|
|
path = Rails.root.join(db_path, 'post_migrate').to_s
|
|
|
|
|
|
|
|
|
|
unless Rails.application.config.paths['db/migrate'].include? path
|
|
|
|
|
Rails.application.config.paths['db/migrate'] << path
|
|
|
|
|
|
|
|
|
|
# Rails memoizes migrations at certain points where it won't read the above
|
|
|
|
|
# path just yet. As such we must also update the following list of paths.
|
|
|
|
|
ActiveRecord::Migrator.migrations_paths << path
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-06-25 09:15:35 +00:00
|
|
|
|
|
2021-08-06 15:10:05 +00:00
|
|
|
|
def self.db_config_names
|
|
|
|
|
::ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).map(&:name)
|
|
|
|
|
end
|
|
|
|
|
|
2021-07-22 09:08:22 +00:00
|
|
|
|
def self.db_config_name(ar_connection)
|
2021-06-22 00:07:53 +00:00
|
|
|
|
if ar_connection.respond_to?(:pool) &&
|
|
|
|
|
ar_connection.pool.respond_to?(:db_config) &&
|
2021-07-22 09:08:22 +00:00
|
|
|
|
ar_connection.pool.db_config.respond_to?(:name)
|
|
|
|
|
return ar_connection.pool.db_config.name
|
2021-06-22 00:07:53 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
'unknown'
|
|
|
|
|
end
|
|
|
|
|
|
2019-06-21 09:18:19 +00:00
|
|
|
|
# Monkeypatch rails with upgraded database observability
|
|
|
|
|
def self.install_monkey_patches
|
|
|
|
|
ActiveRecord::Base.prepend(ActiveRecordBaseTransactionMetrics)
|
|
|
|
|
end
|
|
|
|
|
|
2021-08-02 09:10:09 +00:00
|
|
|
|
def self.read_only?
|
|
|
|
|
false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.read_write?
|
|
|
|
|
!read_only?
|
|
|
|
|
end
|
|
|
|
|
|
2019-06-21 09:18:19 +00:00
|
|
|
|
# MonkeyPatch for ActiveRecord::Base for adding observability
|
|
|
|
|
module ActiveRecordBaseTransactionMetrics
|
2021-03-10 15:09:11 +00:00
|
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
|
|
|
|
|
class_methods do
|
|
|
|
|
# A monkeypatch over ActiveRecord::Base.transaction.
|
|
|
|
|
# It provides observability into transactional methods.
|
|
|
|
|
def transaction(**options, &block)
|
|
|
|
|
ActiveSupport::Notifications.instrument('transaction.active_record', { connection: connection }) do
|
|
|
|
|
super(**options, &block)
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-06-21 09:18:19 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2015-10-07 10:30:10 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2021-08-02 09:10:09 +00:00
|
|
|
|
|
|
|
|
|
Gitlab::Database.prepend_mod_with('Gitlab::Database')
|