48 lines
1.8 KiB
Ruby
48 lines
1.8 KiB
Ruby
|
require 'active_support/core_ext'
|
||
|
require_relative 'db/sanitize.rb'
|
||
|
|
||
|
module Carto
|
||
|
class ValidTableNameProposer
|
||
|
include ::LoggerHelper
|
||
|
|
||
|
DEFAULT_SEPARATOR = '_'.freeze
|
||
|
DEFAULT_TABLE_NAME = 'untitled_table'.freeze
|
||
|
MAX_RENAME_RETRIES = 10000
|
||
|
NON_COLLISIONABLE_STRING_LENGTH = 61
|
||
|
|
||
|
def propose_valid_table_name(contendent = DEFAULT_TABLE_NAME.dup, taken_names:)
|
||
|
contendent = DEFAULT_TABLE_NAME.dup unless contendent.present?
|
||
|
|
||
|
sanitized_contendent = Carto::DB::Sanitize.sanitize_identifier(contendent)
|
||
|
used_table_names = taken_names +
|
||
|
Carto::DB::Sanitize::SYSTEM_TABLE_NAMES +
|
||
|
Carto::DB::Sanitize::RESERVED_TABLE_NAMES
|
||
|
|
||
|
find_unused_name_with_prefix(used_table_names, sanitized_contendent)
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def find_unused_name_with_prefix(names, prefix, separator: DEFAULT_SEPARATOR)
|
||
|
proposal = prefix
|
||
|
|
||
|
(1..MAX_RENAME_RETRIES).each do |appendix|
|
||
|
# We exclude the proposal either if the taken names array already have the proposal, or if the taken names
|
||
|
# array contains a string with the first 62 chars equal to the proposal. With this we avoid typnames collision
|
||
|
# when moving schemas
|
||
|
return proposal unless names.any? { |name| name_can_have_typname_collision(name, proposal) }
|
||
|
|
||
|
proposal = Carto::DB::Sanitize.append_with_truncate_and_sanitize(prefix, "#{separator}#{appendix}")
|
||
|
end
|
||
|
|
||
|
log_error(message: 'Physical tables: Out of rename retries', table: { name: prefix })
|
||
|
|
||
|
raise "Out of retries (#{MAX_RENAME_RETRIES}) renaming #{proposal}"
|
||
|
end
|
||
|
|
||
|
def name_can_have_typname_collision(name, proposal)
|
||
|
name == proposal || name[0..NON_COLLISIONABLE_STRING_LENGTH] == proposal[0..NON_COLLISIONABLE_STRING_LENGTH]
|
||
|
end
|
||
|
end
|
||
|
end
|