Mercurial > gemma
view pkg/pgxutils/errors.go @ 5520:05db984d3db1
Improve performance of bottleneck area calculation
Avoid buffer calculations by replacing them with simple distance comparisons
and calculate the boundary of the result geometry only once per iteration.
In some edge cases with very large numbers of iterations, this reduced
the runtime of a bottleneck import by a factor of more than twenty.
author | Tom Gottfried <tom@intevation.de> |
---|---|
date | Thu, 21 Oct 2021 19:50:39 +0200 |
parents | 73563c4bba5b |
children | 2dd155cc95ec |
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> // * Sascha L. Teichmann <sascha.teichmann@intevation.de> package pgxutils import ( "net/http" "strings" "github.com/jackc/pgx" ) const ( // camel cased condition name = error code // from appendix A of PostgreSQL documentation notNullViolation = "23502" foreignKeyViolation = "23503" uniqueViolation = "23505" checkViolation = "23514" exclusionViolation = "23P01" insufficientPrivilege = "42501" duplicateObject = "42710" noDataFound = "P0002" ) // ReadableError wraps a given error Err and // permits extraction of more user-friendly // error messages from it in case it is an error // from the PostgreSQL backend. type ReadableError struct { Err error } func (re ReadableError) Error() string { m, _ := re.MessageAndCode() return m } // MessageAndCode returns a user-readable message // and a matching HTTP status code. // If its not a pgx.PgError it defaults to // calling the parent Error method and returns its // result together with http.StatusInternalServerError. func (re ReadableError) MessageAndCode() (string, int) { if e, ok := re.Err.(pgx.PgError); ok { return messageAndCode(e) } return re.Err.Error(), http.StatusInternalServerError } func messageAndCode(err pgx.PgError) (m string, c int) { c = http.StatusInternalServerError // Most recent line from stacktrace contains failed statement recent := strings.SplitN(err.Where, "\n", 1)[0] switch err.Code { case notNullViolation: switch err.SchemaName { case "waterway": switch err.TableName { case "gauges": switch err.ColumnName { case "objname": m = "Missing objname" return case "geom": m = "Missing lat/lon" return case "zero_point": m = "Missing zeropoint" return } } } case foreignKeyViolation: switch err.SchemaName { case "waterway": switch err.TableName { case "gauge_measurements", "gauge_predictions", "bottlenecks": switch err.ConstraintName { case "waterway_bottlenecks_reference_gauge", "waterway_gauge_measurements_reference_gauge", "waterway_gauge_predictions_reference_gauge": m = "Referenced gauge with matching temporal validity not available" return } } switch err.TableName { case "fairway_marks_bcnlat_dirimps", "fairway_marks_daymar_dirimps", "fairway_marks_notmrk_dirimps": switch err.ConstraintName { case "fairway_marks_bcnlat_dirimps_dirimp_fkey", "fairway_marks_daymar_dirimps_dirimp_fkey", "fairway_marks_notmrk_dirimps_dirimp_fkey": m = "Invalid value for dirimp" return } } } case uniqueViolation: switch err.SchemaName { case "users": switch err.TableName { case "stretches": switch err.ConstraintName { case "stretches_name_staging_done_key": m = "A stretch with that name already exists" c = http.StatusConflict return } } case "waterway": switch err.TableName { case "sections": switch err.ConstraintName { case "sections_name_staging_done_key": m = "A section with that name already exists" c = http.StatusConflict return } case "fairway_dimensions": switch err.ConstraintName { case "fairway_dimensions_area_unique": m = "Duplicate fairway dimension area" c = http.StatusConflict return } } } case exclusionViolation: switch err.SchemaName { case "waterway": switch err.TableName { case "sections": switch err.ConstraintName { case "sections_name_country_excl": m = "A section with that name already exists for another country" c = http.StatusConflict return } } } case checkViolation: switch err.SchemaName { case "waterway": switch err.TableName { case "sounding_results": switch err.ConstraintName { case "b_sounding_results_in_bn_area": m = "Dataset does not intersect with given bottleneck" c = http.StatusConflict return } case "fairway_dimensions": switch err.ConstraintName { case "fairway_dimensions_area_check": m = "Geometry could not be stored as valid, non-empty polygon" return } } case "internal": switch err.TableName { case "user_profiles": switch err.ConstraintName { case "user_profiles_username_check": m = "User name too long" c = http.StatusBadRequest return } } } case duplicateObject: switch { case strings.Contains(recent, "CREATE ROLE"): m = "A user with that name already exists" c = http.StatusConflict return } case noDataFound: switch { case strings.Contains(recent, "isrsrange_points"): m = "No distance mark found for at least one given ISRS Location Code" return case strings.Contains(recent, "isrsrange_axis"): m = "No contiguous axis found between given ISRS Location Codes" return case strings.Contains(recent, "isrsrange_area"): m = "No area around axis between given ISRS Location Codes" return } case insufficientPrivilege: m = "Could not save: Data outside the area of responsibility." return } m = "Unexpected database error: " + err.Message return }