view pkg/pgxutils/errors.go @ 4062:6c760abcff0e

Move handling of PostgreSQL errors to own package
author Tom Gottfried <tom@intevation.de>
date Thu, 25 Jul 2019 11:32:22 +0200
parents
children fe3dd65c0891
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2019 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Tom Gottfried <tom.gottfried@intevation.de>

package pgxutils

import (
	"strings"

	"github.com/jackc/pgx"
)

// Handle PostgreSQL error codes
func HandlePGError(err error) error {
	switch e := err.(type) {
	case pgx.PgError:
		return dbError(e)
	}
	return err
}

const (
	notNullViolation         = "23502"
	foreignKeyViolation      = "23503"
	uniqueViolation          = "23505"
	violatesRowLevelSecurity = "42501"
	noDataFound              = "P0002"
)

type dbError pgx.PgError

func (err dbError) Error() string {
	switch err.Code {
	case notNullViolation:
		switch err.SchemaName {
		case "waterway":
			switch err.TableName {
			case "gauges":
				switch err.ColumnName {
				case "objname":
					return "Missing objname"
				case "geom":
					return "Missing lat/lon"
				case "zero_point":
					return "Missing zeropoint"
				}
			}
		}
	case foreignKeyViolation:
		switch err.SchemaName {
		case "waterway":
			switch err.TableName {
			case "gauge_measurements", "gauge_predictions", "bottlenecks":
				switch err.ConstraintName {
				case "gauge_key":
					return "Referenced gauge with matching temporal validity not available"
				}
			}
		}
	case uniqueViolation:
		switch err.SchemaName {
		case "internal":
			switch err.TableName {
			case "user_profiles":
				switch err.ConstraintName {
				case "user_profiles_pkey":
					return "A user with that name already exists"
				}
			}
		}
	case noDataFound:
		// Most recent line from stacktrace contains name of failed function
		recent := strings.SplitN(err.Where, "\n", 1)[0]
		switch {
		case strings.Contains(recent, "isrsrange_points"):
			return "No distance mark found for at least one given ISRS Location Code"
		case strings.Contains(recent, "isrsrange_axis"):
			return "No contiguous axis found between given ISRS Location Codes"
		case strings.Contains(recent, "isrsrange_area"):
			return "No area around axis between given ISRS Location Codes"
		}
	case violatesRowLevelSecurity:
		return "Could not save: Data outside the area of responsibility."
	}
	return "Unexpected database error: " + err.Message
}