Skip to content

Commit db6c969

Browse files
committed
ErrIntegrityConstraintViolation
1 parent 03b7ac1 commit db6c969

File tree

3 files changed

+139
-7
lines changed

3 files changed

+139
-7
lines changed

errors.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,107 @@ const (
5555
ErrNotWithinTransaction sentinelError = "not within a transaction"
5656
)
5757

58+
type ErrIntegrityConstraintViolation struct {
59+
Constraint string
60+
}
61+
62+
func (e ErrIntegrityConstraintViolation) Error() string {
63+
if e.Constraint == "" {
64+
return "integrity constraint violation"
65+
}
66+
return "integrity constraint violation of constraint: " + e.Constraint
67+
}
68+
69+
type ErrRestrictViolation struct {
70+
Constraint string
71+
}
72+
73+
func (e ErrRestrictViolation) Error() string {
74+
if e.Constraint == "" {
75+
return "restrict violation"
76+
}
77+
return "restrict violation of constraint: " + e.Constraint
78+
}
79+
80+
func (e ErrRestrictViolation) Unwrap() error {
81+
return ErrIntegrityConstraintViolation{Constraint: e.Constraint}
82+
}
83+
84+
type ErrNotNullViolation struct {
85+
Constraint string
86+
}
87+
88+
func (e ErrNotNullViolation) Error() string {
89+
if e.Constraint == "" {
90+
return "not null violation"
91+
}
92+
return "not null violation of constraint: " + e.Constraint
93+
}
94+
95+
func (e ErrNotNullViolation) Unwrap() error {
96+
return ErrIntegrityConstraintViolation{Constraint: e.Constraint}
97+
}
98+
99+
type ErrForeignKeyViolation struct {
100+
Constraint string
101+
}
102+
103+
func (e ErrForeignKeyViolation) Error() string {
104+
if e.Constraint == "" {
105+
return "foreign key violation"
106+
}
107+
return "foreign key violation of constraint: " + e.Constraint
108+
}
109+
110+
func (e ErrForeignKeyViolation) Unwrap() error {
111+
return ErrIntegrityConstraintViolation{Constraint: e.Constraint}
112+
}
113+
114+
type ErrUniqueViolation struct {
115+
Constraint string
116+
}
117+
118+
func (e ErrUniqueViolation) Error() string {
119+
if e.Constraint == "" {
120+
return "unique violation"
121+
}
122+
return "unique violation of constraint: " + e.Constraint
123+
}
124+
125+
func (e ErrUniqueViolation) Unwrap() error {
126+
return ErrIntegrityConstraintViolation{Constraint: e.Constraint}
127+
}
128+
129+
type ErrCheckViolation struct {
130+
Constraint string
131+
}
132+
133+
func (e ErrCheckViolation) Error() string {
134+
if e.Constraint == "" {
135+
return "check violation"
136+
}
137+
return "check violation of constraint: " + e.Constraint
138+
}
139+
140+
func (e ErrCheckViolation) Unwrap() error {
141+
return ErrIntegrityConstraintViolation{Constraint: e.Constraint}
142+
}
143+
144+
type ErrExclusionViolation struct {
145+
Constraint string
146+
}
147+
148+
func (e ErrExclusionViolation) Error() string {
149+
if e.Constraint == "" {
150+
return "exclusion violation"
151+
}
152+
return "exclusion violation of constraint: " + e.Constraint
153+
}
154+
155+
func (e ErrExclusionViolation) Unwrap() error {
156+
return ErrIntegrityConstraintViolation{Constraint: e.Constraint}
157+
}
158+
58159
// ConnectionWithError
59160

60161
// ConnectionWithError returns a dummy Connection

pqconn/connection.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,30 +108,32 @@ func (conn *connection) Now() (time.Time, error) {
108108

109109
func (conn *connection) Exec(query string, args ...any) error {
110110
_, err := conn.db.ExecContext(conn.ctx, query, args...)
111-
return impl.WrapNonNilErrorWithQuery(err, query, argFmt, args)
111+
return wrapError(err, query, argFmt, args)
112112
}
113113

114114
func (conn *connection) Insert(table string, columValues sqldb.Values) error {
115-
return impl.Insert(conn, table, argFmt, columValues)
115+
return WrapKnownErrors(impl.Insert(conn, table, argFmt, columValues))
116116
}
117117

118118
func (conn *connection) InsertUnique(table string, values sqldb.Values, onConflict string) (inserted bool, err error) {
119-
return impl.InsertUnique(conn, table, argFmt, values, onConflict)
119+
inserted, err = impl.InsertUnique(conn, table, argFmt, values, onConflict)
120+
return inserted, WrapKnownErrors(err)
120121
}
121122

122123
func (conn *connection) InsertReturning(table string, values sqldb.Values, returning string) sqldb.RowScanner {
123124
return impl.InsertReturning(conn, table, argFmt, values, returning)
124125
}
125126

126127
func (conn *connection) InsertStruct(table string, rowStruct any, ignoreColumns ...sqldb.ColumnFilter) error {
127-
return impl.InsertStruct(conn, table, rowStruct, conn.structFieldNamer, argFmt, ignoreColumns)
128+
return WrapKnownErrors(impl.InsertStruct(conn, table, rowStruct, conn.structFieldNamer, argFmt, ignoreColumns))
128129
}
129130

130131
func (conn *connection) InsertStructs(table string, rowStructs any, ignoreColumns ...sqldb.ColumnFilter) error {
131-
return impl.InsertStructs(conn, table, rowStructs, ignoreColumns...)
132+
return WrapKnownErrors(impl.InsertStructs(conn, table, rowStructs, ignoreColumns...))
132133
}
133134

134135
func (conn *connection) InsertUniqueStruct(table string, rowStruct any, onConflict string, ignoreColumns ...sqldb.ColumnFilter) (inserted bool, err error) {
136+
// TODO more error wrapping
135137
return impl.InsertUniqueStruct(conn, table, rowStruct, onConflict, conn.structFieldNamer, argFmt, ignoreColumns)
136138
}
137139

@@ -158,7 +160,7 @@ func (conn *connection) UpsertStruct(table string, rowStruct any, ignoreColumns
158160
func (conn *connection) QueryRow(query string, args ...any) sqldb.RowScanner {
159161
rows, err := conn.db.QueryContext(conn.ctx, query, args...)
160162
if err != nil {
161-
err = impl.WrapNonNilErrorWithQuery(err, query, argFmt, args)
163+
err = wrapError(err, query, argFmt, args)
162164
return sqldb.RowScannerWithError(err)
163165
}
164166
return impl.NewRowScanner(rows, conn.structFieldNamer, query, argFmt, args)
@@ -167,7 +169,7 @@ func (conn *connection) QueryRow(query string, args ...any) sqldb.RowScanner {
167169
func (conn *connection) QueryRows(query string, args ...any) sqldb.RowsScanner {
168170
rows, err := conn.db.QueryContext(conn.ctx, query, args...)
169171
if err != nil {
170-
err = impl.WrapNonNilErrorWithQuery(err, query, argFmt, args)
172+
err = wrapError(err, query, argFmt, args)
171173
return sqldb.RowsScannerWithError(err)
172174
}
173175
return impl.NewRowsScanner(conn.ctx, rows, conn.structFieldNamer, query, argFmt, args)

pqconn/errors.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,38 @@ import (
44
"errors"
55
"slices"
66

7+
"github.com/domonda/go-sqldb"
8+
"github.com/domonda/go-sqldb/impl"
79
"github.com/lib/pq"
810
)
911

12+
func wrapError(err error, query, argFmt string, args []any) error {
13+
return impl.WrapNonNilErrorWithQuery(WrapKnownErrors(err), query, argFmt, args)
14+
}
15+
16+
func WrapKnownErrors(err error) error {
17+
var e *pq.Error
18+
if errors.As(err, &e) {
19+
switch e.Code {
20+
case "23000":
21+
return errors.Join(sqldb.ErrIntegrityConstraintViolation{Constraint: e.Constraint}, err)
22+
case "23001":
23+
return errors.Join(sqldb.ErrRestrictViolation{Constraint: e.Constraint}, err)
24+
case "23502":
25+
return errors.Join(sqldb.ErrNotNullViolation{Constraint: e.Constraint}, err)
26+
case "23503":
27+
return errors.Join(sqldb.ErrForeignKeyViolation{Constraint: e.Constraint}, err)
28+
case "23505":
29+
return errors.Join(sqldb.ErrUniqueViolation{Constraint: e.Constraint}, err)
30+
case "23514":
31+
return errors.Join(sqldb.ErrCheckViolation{Constraint: e.Constraint}, err)
32+
case "23P01":
33+
return errors.Join(sqldb.ErrExclusionViolation{Constraint: e.Constraint}, err)
34+
}
35+
}
36+
return err
37+
}
38+
1039
// Class 23 — Integrity Constraint Violation
1140

1241
func IsIntegrityConstraintViolation(err error) bool {

0 commit comments

Comments
 (0)