class ViewModel::DeserializationError::UniqueViolation
def self.from_postgres_error(err, nodes)
def self.from_postgres_error(err, nodes) result = err.result constraint = result.error_field(PG::PG_DIAG_CONSTRAINT_NAME) message_detail = result.error_field(PG::PG_DIAG_MESSAGE_DETAIL) columns, values, conflicts = parse_message_detail(message_detail) unless columns # Couldn't parse the detail message, fall back on an unparsed error return DatabaseConstraint.new(err.message, nodes) end self.new(err.message, constraint, columns, values, conflicts, nodes) end
def initialize(detail, constraint, columns, values, conflicts, nodes = [])
def initialize(detail, constraint, columns, values, conflicts, nodes = []) @detail = detail @constraint = constraint @columns = columns @values = values @conflicts = conflicts super(nodes) end
def meta
def meta super.merge(constraint: @constraint, columns: @columns, values: @values, conflicts: @conflicts) end
def parse_identifier(stream)
def parse_identifier(stream) if (identifier = stream.slice!(UNQUOTED_IDENTIFIER)) identifier elsif (quoted_identifier = stream.slice!(QUOTED_IDENTIFIER)) quoted_identifier[1..-2].gsub('""', '"') else nil end end
def parse_identifiers(stream)
def parse_identifiers(stream) identifiers = [] identifier = parse_identifier(stream) return nil unless identifier identifiers << identifier while stream.delete_prefix!(', ') identifier = parse_identifier(stream) return nil unless identifier identifiers << identifier end identifiers end
def parse_message_detail(detail)
def parse_message_detail(detail) stream = detail.dup return nil unless stream.delete_prefix!(DETAIL_PREFIX) # The message should start with an identifier list: pop off identifier # tokens while we can. identifiers = parse_identifiers(stream) return nil if identifiers.nil? # The message should now contain ")=(" followed by the value list and # the suffix, potentially including a conflict list. We consider the # value and conflict lists to be essentially unparseable because they # are free to contain commas and no escaping is used. We make a best # effort to extract them anyway. values, conflicts = if (m = UNIQUE_SUFFIX_TEMPLATE.match(stream)) m.values_at(:values) elsif (m = EXCLUSION_SUFFIX_TEMPLATE.match(stream)) m.values_at(:values, :conflicts) else return nil end return identifiers, values, conflicts end