1
+ use std:: borrow:: Cow ;
1
2
use std:: error:: Error as StdError ;
2
3
use std:: fmt:: { self , Debug , Display , Formatter } ;
3
4
4
5
use atoi:: atoi;
5
- use smallvec:: alloc:: borrow:: Cow ;
6
6
7
7
pub ( crate ) use sqlx_core:: error:: * ;
8
8
9
9
use crate :: message:: { Notice , PgSeverity } ;
10
10
11
11
/// An error returned from the PostgreSQL database.
12
- pub struct PgDatabaseError ( pub ( crate ) Notice ) ;
12
+ pub struct PgDatabaseError {
13
+ pub ( crate ) notice : Notice ,
14
+ pub ( crate ) error_pos : Option < ErrorPosition > ,
15
+ }
13
16
14
17
// Error message fields are documented:
15
18
// https://www.postgresql.org/docs/current/protocol-error-fields.html
16
19
17
20
impl PgDatabaseError {
21
+ pub ( crate ) fn new ( notice : Notice ) -> Self {
22
+ PgDatabaseError {
23
+ notice,
24
+ error_pos : None ,
25
+ }
26
+ }
27
+
28
+ pub ( crate ) fn apply_error_pos ( & mut self , query : & str ) {
29
+ let error_pos = self
30
+ . position_raw ( )
31
+ . and_then ( |pos_raw| pos_raw. original ( ) )
32
+ . and_then ( |pos| ErrorPosition :: from_char_pos ( query, CharBasis :: One ( pos) ) ) ;
33
+
34
+ self . error_pos = error_pos;
35
+ }
36
+
18
37
#[ inline]
19
38
pub fn severity ( & self ) -> PgSeverity {
20
- self . 0 . severity ( )
39
+ self . notice . severity ( )
21
40
}
22
41
23
42
/// The [SQLSTATE](https://www.postgresql.org/docs/current/errcodes-appendix.html) code for
24
43
/// this error.
25
44
#[ inline]
26
45
pub fn code ( & self ) -> & str {
27
- self . 0 . code ( )
46
+ self . notice . code ( )
28
47
}
29
48
30
49
/// The primary human-readable error message. This should be accurate but
31
50
/// terse (typically one line).
32
51
#[ inline]
33
52
pub fn message ( & self ) -> & str {
34
- self . 0 . message ( )
53
+ self . notice . message ( )
35
54
}
36
55
37
56
/// An optional secondary error message carrying more detail about the problem.
38
57
/// Might run to multiple lines.
39
58
#[ inline]
40
59
pub fn detail ( & self ) -> Option < & str > {
41
- self . 0 . get ( b'D' )
60
+ self . notice . get ( b'D' )
42
61
}
43
62
44
63
/// An optional suggestion what to do about the problem. This is intended to differ from
45
64
/// `detail` in that it offers advice (potentially inappropriate) rather than hard facts.
46
65
/// Might run to multiple lines.
47
66
#[ inline]
48
67
pub fn hint ( & self ) -> Option < & str > {
49
- self . 0 . get ( b'H' )
68
+ self . notice . get ( b'H' )
50
69
}
51
70
52
71
/// Indicates an error cursor position as an index into the original query string; or,
53
72
/// a position into an internally generated query.
54
73
#[ inline]
55
- pub fn position ( & self ) -> Option < PgErrorPosition < ' _ > > {
56
- self . 0
74
+ pub fn position_raw ( & self ) -> Option < PgErrorPosition < ' _ > > {
75
+ self . notice
57
76
. get_raw ( b'P' )
58
77
. and_then ( atoi)
59
78
. map ( PgErrorPosition :: Original )
60
79
. or_else ( || {
61
- let position = self . 0 . get_raw ( b'p' ) . and_then ( atoi) ?;
62
- let query = self . 0 . get ( b'q' ) ?;
80
+ let position = self . notice . get_raw ( b'p' ) . and_then ( atoi) ?;
81
+ let query = self . notice . get ( b'q' ) ?;
63
82
64
83
Some ( PgErrorPosition :: Internal { position, query } )
65
84
} )
@@ -69,50 +88,50 @@ impl PgDatabaseError {
69
88
/// stack traceback of active procedural language functions and internally-generated queries.
70
89
/// The trace is one entry per line, most recent first.
71
90
pub fn r#where ( & self ) -> Option < & str > {
72
- self . 0 . get ( b'W' )
91
+ self . notice . get ( b'W' )
73
92
}
74
93
75
94
/// If this error is with a specific database object, the
76
95
/// name of the schema containing that object, if any.
77
96
pub fn schema ( & self ) -> Option < & str > {
78
- self . 0 . get ( b's' )
97
+ self . notice . get ( b's' )
79
98
}
80
99
81
100
/// If this error is with a specific table, the name of the table.
82
101
pub fn table ( & self ) -> Option < & str > {
83
- self . 0 . get ( b't' )
102
+ self . notice . get ( b't' )
84
103
}
85
104
86
105
/// If the error is with a specific table column, the name of the column.
87
106
pub fn column ( & self ) -> Option < & str > {
88
- self . 0 . get ( b'c' )
107
+ self . notice . get ( b'c' )
89
108
}
90
109
91
110
/// If the error is with a specific data type, the name of the data type.
92
111
pub fn data_type ( & self ) -> Option < & str > {
93
- self . 0 . get ( b'd' )
112
+ self . notice . get ( b'd' )
94
113
}
95
114
96
115
/// If the error is with a specific constraint, the name of the constraint.
97
116
/// For this purpose, indexes are constraints, even if they weren't created
98
117
/// with constraint syntax.
99
118
pub fn constraint ( & self ) -> Option < & str > {
100
- self . 0 . get ( b'n' )
119
+ self . notice . get ( b'n' )
101
120
}
102
121
103
122
/// The file name of the source-code location where this error was reported.
104
123
pub fn file ( & self ) -> Option < & str > {
105
- self . 0 . get ( b'F' )
124
+ self . notice . get ( b'F' )
106
125
}
107
126
108
127
/// The line number of the source-code location where this error was reported.
109
128
pub fn line ( & self ) -> Option < usize > {
110
- self . 0 . get_raw ( b'L' ) . and_then ( atoi)
129
+ self . notice . get_raw ( b'L' ) . and_then ( atoi)
111
130
}
112
131
113
132
/// The name of the source-code routine reporting this error.
114
133
pub fn routine ( & self ) -> Option < & str > {
115
- self . 0 . get ( b'R' )
134
+ self . notice . get ( b'R' )
116
135
}
117
136
}
118
137
@@ -135,12 +154,13 @@ pub enum PgErrorPosition<'a> {
135
154
impl Debug for PgDatabaseError {
136
155
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
137
156
f. debug_struct ( "PgDatabaseError" )
157
+ . field ( "position" , & self . error_pos )
138
158
. field ( "severity" , & self . severity ( ) )
139
159
. field ( "code" , & self . code ( ) )
140
160
. field ( "message" , & self . message ( ) )
141
161
. field ( "detail" , & self . detail ( ) )
142
162
. field ( "hint" , & self . hint ( ) )
143
- . field ( "position " , & self . position ( ) )
163
+ . field ( "position_raw " , & self . position ( ) )
144
164
. field ( "where" , & self . r#where ( ) )
145
165
. field ( "schema" , & self . schema ( ) )
146
166
. field ( "table" , & self . table ( ) )
@@ -156,7 +176,12 @@ impl Debug for PgDatabaseError {
156
176
157
177
impl Display for PgDatabaseError {
158
178
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
159
- f. write_str ( self . message ( ) )
179
+ write ! ( f, "(code {}" , self . code( ) ) ?;
180
+ if let Some ( error_pos) = self . error_pos {
181
+ write ! ( f, ", line {}, column {}" , error_pos. line, error_pos. column) ?;
182
+ }
183
+
184
+ write ! ( f, ") {}" , self . message( ) )
160
185
}
161
186
}
162
187
@@ -171,6 +196,10 @@ impl DatabaseError for PgDatabaseError {
171
196
Some ( Cow :: Borrowed ( self . code ( ) ) )
172
197
}
173
198
199
+ fn position ( & self ) -> Option < ErrorPosition > {
200
+ self . error_pos
201
+ }
202
+
174
203
#[ doc( hidden) ]
175
204
fn as_error ( & self ) -> & ( dyn StdError + Send + Sync + ' static ) {
176
205
self
@@ -219,6 +248,43 @@ impl DatabaseError for PgDatabaseError {
219
248
}
220
249
}
221
250
251
+ impl PgErrorPosition < ' _ > {
252
+ fn original ( & self ) -> Option < usize > {
253
+ match * self {
254
+ Self :: Original ( original) => Some ( original) ,
255
+ _ => None ,
256
+ }
257
+ }
258
+ }
259
+
260
+ pub ( crate ) trait PgResultExt {
261
+ fn pg_apply_error_pos ( self , query : & str ) -> Self ;
262
+ }
263
+
264
+ impl < T > PgResultExt for Result < T , Error > {
265
+ fn pg_apply_error_pos ( self , query : & str ) -> Self {
266
+ self . map_err ( |e| {
267
+ match e {
268
+ Error :: Database ( e) => {
269
+ Error :: Database (
270
+ // Don't panic in case this gets called in the wrong context;
271
+ // it'd be a bug, for sure, but far from a fatal one.
272
+ // I gave the method a distinct name to call this out if it happens.
273
+ e. try_downcast :: < PgDatabaseError > ( ) . map_or_else (
274
+ |e| e,
275
+ |mut e| {
276
+ e. apply_error_pos ( query) ;
277
+ e
278
+ } ,
279
+ ) ,
280
+ )
281
+ }
282
+ other => other,
283
+ }
284
+ } )
285
+ }
286
+ }
287
+
222
288
/// For reference: <https://www.postgresql.org/docs/current/errcodes-appendix.html>
223
289
pub ( crate ) mod error_codes {
224
290
/// Caused when a unique or primary key is violated.
0 commit comments