# HG changeset patch # User Tom Gottfried # Date 1564047142 -7200 # Node ID 6c760abcff0effec853835d3e2962f567ae5add3 # Parent f391497287fba60dde4d67acaf804167ed02da13 Move handling of PostgreSQL errors to own package diff -r f391497287fb -r 6c760abcff0e pkg/common/errors.go --- a/pkg/common/errors.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/common/errors.go Thu Jul 25 11:32:22 2019 +0200 @@ -4,13 +4,12 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // License-Filename: LICENSES/AGPL-3.0.txt // -// Copyright (C) 2018, 2019 by via donau +// Copyright (C) 2018 by via donau // – Österreichische Wasserstraßen-Gesellschaft mbH // Software engineering by Intevation GmbH // // Author(s): // * Sascha L. Teichmann -// * Tom Gottfried package common @@ -18,8 +17,6 @@ "errors" "fmt" "strings" - - "github.com/jackc/pgx" ) // ToError concats a slice of errors to a single error. @@ -33,79 +30,3 @@ } return errors.New(b.String()) } - -// 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 -} diff -r f391497287fb -r 6c760abcff0e pkg/controllers/user.go --- a/pkg/controllers/user.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/controllers/user.go Thu Jul 25 11:32:22 2019 +0200 @@ -27,9 +27,9 @@ "github.com/gorilla/mux" "gemma.intevation.de/gemma/pkg/auth" - "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/misc" "gemma.intevation.de/gemma/pkg/models" + "gemma.intevation.de/gemma/pkg/pgxutils" "gemma.intevation.de/gemma/pkg/scheduler" ) @@ -263,7 +263,7 @@ } if err != nil { - err = common.HandlePGError(err) + err = pgxutils.HandlePGError(err) return } diff -r f391497287fb -r 6c760abcff0e pkg/imports/bn.go --- a/pkg/imports/bn.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/bn.go Thu Jul 25 11:32:22 2019 +0200 @@ -24,7 +24,7 @@ "strings" "time" - "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/pgxutils" "gemma.intevation.de/gemma/pkg/soap/ifbn" "github.com/jackc/pgx/pgtype" ) @@ -591,7 +591,7 @@ ) } if err != nil { - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) return nil } defer bns.Close() @@ -603,7 +603,7 @@ bnIds = append(bnIds, nid) } if err := bns.Err(); err != nil { - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) return nil } if len(bnIds) == 0 { @@ -620,7 +620,7 @@ &validity, &pgBnIds, ); err != nil { - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) if err2 := tx.Rollback(); err2 != nil { return err2 } @@ -633,7 +633,7 @@ bn.Bottleneck_id, validity, ); err != nil { - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) if err2 := tx.Rollback(); err2 != nil { return err2 } @@ -670,7 +670,7 @@ &pgMaterials, ); err != nil { feedback.Warn("Failed to insert riverbed materials") - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) return nil } } diff -r f391497287fb -r 6c760abcff0e pkg/imports/fd.go --- a/pkg/imports/fd.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/fd.go Thu Jul 25 11:32:22 2019 +0200 @@ -21,8 +21,8 @@ "io" "time" - "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/misc" + "gemma.intevation.de/gemma/pkg/pgxutils" "gemma.intevation.de/gemma/pkg/wfs" ) @@ -311,7 +311,7 @@ // ignore -> filtered by responsibility_areas continue features case err != nil: - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) continue features } // Store for potential later removal. diff -r f391497287fb -r 6c760abcff0e pkg/imports/gm.go --- a/pkg/imports/gm.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/gm.go Thu Jul 25 11:32:22 2019 +0200 @@ -24,8 +24,8 @@ "strings" "time" - "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/models" + "gemma.intevation.de/gemma/pkg/pgxutils" "gemma.intevation.de/gemma/pkg/soap/nts" "github.com/jackc/pgx/pgtype" ) @@ -457,7 +457,7 @@ case err == sql.ErrNoRows: // thats expected, nothing to do case err != nil: - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) default: newP++ } @@ -488,7 +488,7 @@ case err == sql.ErrNoRows: // thats expected, nothing to do case err != nil: - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) default: newM++ } diff -r f391497287fb -r 6c760abcff0e pkg/imports/sec.go --- a/pkg/imports/sec.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/sec.go Thu Jul 25 11:32:22 2019 +0200 @@ -18,8 +18,8 @@ "database/sql" "time" - "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/models" + "gemma.intevation.de/gemma/pkg/pgxutils" ) type Section struct { @@ -182,7 +182,7 @@ sec.Source, sec.Tolerance, ).Scan(&id); err != nil { - return nil, common.HandlePGError(err) + return nil, pgxutils.HandlePGError(err) } if err := track(ctx, tx, importID, "waterway.sections", id); err != nil { diff -r f391497287fb -r 6c760abcff0e pkg/imports/st.go --- a/pkg/imports/st.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/st.go Thu Jul 25 11:32:22 2019 +0200 @@ -19,8 +19,8 @@ "errors" "time" - "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/models" + "gemma.intevation.de/gemma/pkg/pgxutils" ) type Stretch struct { @@ -203,7 +203,7 @@ st.Source, st.Tolerance, ).Scan(&id); err != nil { - return nil, common.HandlePGError(err) + return nil, pgxutils.HandlePGError(err) } // store the associated countries. diff -r f391497287fb -r 6c760abcff0e pkg/imports/wa.go --- a/pkg/imports/wa.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/wa.go Thu Jul 25 11:32:22 2019 +0200 @@ -24,7 +24,7 @@ "strconv" "time" - "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/pgxutils" "gemma.intevation.de/gemma/pkg/wfs" ) @@ -247,7 +247,7 @@ outside++ // ignore -> filtered by responsibility_areas case err != nil: - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) default: features++ } diff -r f391497287fb -r 6c760abcff0e pkg/imports/wg.go --- a/pkg/imports/wg.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/wg.go Thu Jul 25 11:32:22 2019 +0200 @@ -21,8 +21,8 @@ "github.com/jackc/pgx/pgtype" - "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/models" + "gemma.intevation.de/gemma/pkg/pgxutils" "gemma.intevation.de/gemma/pkg/soap/erdms" ) @@ -327,7 +327,7 @@ ).Scan(&isNew) switch { case err != nil: - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) if err2 := tx.Rollback(); err2 != nil { return nil, err2 } @@ -352,7 +352,7 @@ source, time.Time(*dr.Lastupdate), ); err != nil { - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) if err2 := tx.Rollback(); err2 != nil { return nil, err2 } @@ -389,7 +389,7 @@ unchanged++ continue case err2 != nil: - feedback.Warn(common.HandlePGError(err2).Error()) + feedback.Warn(pgxutils.HandlePGError(err2).Error()) if err3 := tx.Rollback(); err3 != nil { return nil, err3 } @@ -436,7 +436,7 @@ code.String(), &validity, ); err != nil { - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) if err2 := tx.Rollback(); err2 != nil { return nil, err2 } @@ -484,7 +484,7 @@ string(**wl.level), int64(**wl.value), ); err != nil { - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) tx.Rollback() continue } diff -r f391497287fb -r 6c760abcff0e pkg/imports/wx.go --- a/pkg/imports/wx.go Thu Jul 25 11:11:02 2019 +0200 +++ b/pkg/imports/wx.go Thu Jul 25 11:32:22 2019 +0200 @@ -23,7 +23,7 @@ "io" "time" - "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/pgxutils" "gemma.intevation.de/gemma/pkg/wfs" ) @@ -321,7 +321,7 @@ // ignore -> filtered by responsibility_areas return nil case err != nil: - feedback.Warn(common.HandlePGError(err).Error()) + feedback.Warn(pgxutils.HandlePGError(err).Error()) default: *features++ } diff -r f391497287fb -r 6c760abcff0e pkg/pgxutils/errors.go --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/pgxutils/errors.go Thu Jul 25 11:32:22 2019 +0200 @@ -0,0 +1,95 @@ +// 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 + +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 +}