diesel/pg/connection/
result.rs1#![allow(unsafe_code)] extern crate pq_sys;
3
4use self::pq_sys::*;
5use alloc::rc::Rc;
6use core::ffi as libc;
7use core::num::NonZeroU32;
8use core::str;
9
10use super::raw::{RawConnection, RawResult, ResultField};
11use super::row::PgRow;
12use crate::result::{DatabaseErrorInformation, DatabaseErrorKind, Error, QueryResult};
13use core::cell::OnceCell;
14
15#[allow(missing_debug_implementations)]
16pub struct PgResult {
17 internal_result: RawResult,
18 column_count: libc::c_int,
19 row_count: libc::c_int,
20 column_name_map: OnceCell<Vec<Option<*const str>>>,
24}
25
26impl PgResult {
27 #[allow(clippy::new_ret_no_self)]
28 pub(super) fn new(internal_result: RawResult, conn: &RawConnection) -> QueryResult<Self> {
29 match internal_result.result_status() {
30 ExecStatusType::PGRES_SINGLE_TUPLE
31 | ExecStatusType::PGRES_COMMAND_OK
32 | ExecStatusType::PGRES_COPY_IN
33 | ExecStatusType::PGRES_COPY_OUT
34 | ExecStatusType::PGRES_TUPLES_OK => {
35 let column_count = internal_result.column_count();
36 let row_count = internal_result.row_count();
37 Ok(PgResult {
38 internal_result,
39 column_count,
40 row_count,
41 column_name_map: OnceCell::new(),
42 })
43 }
44 ExecStatusType::PGRES_EMPTY_QUERY => {
45 let error_message = "Received an empty query".to_string();
46 Err(Error::DatabaseError(
47 DatabaseErrorKind::Unknown,
48 Box::new(error_message),
49 ))
50 }
51 _ => {
52 while conn.get_next_result().map_or(true, |r| r.is_some()) {}
57
58 let mut error_kind = match internal_result.get_result_field(ResultField::SqlState) {
59 Some(error_codes::UNIQUE_VIOLATION) => DatabaseErrorKind::UniqueViolation,
60 Some(error_codes::FOREIGN_KEY_VIOLATION) => {
61 DatabaseErrorKind::ForeignKeyViolation
62 }
63 Some(error_codes::SERIALIZATION_FAILURE) => {
64 DatabaseErrorKind::SerializationFailure
65 }
66 Some(error_codes::READ_ONLY_TRANSACTION) => {
67 DatabaseErrorKind::ReadOnlyTransaction
68 }
69 Some(error_codes::NOT_NULL_VIOLATION) => DatabaseErrorKind::NotNullViolation,
70 Some(error_codes::CHECK_VIOLATION) => DatabaseErrorKind::CheckViolation,
71 Some(error_codes::RESTRICT_VIOLATION) => DatabaseErrorKind::RestrictViolation,
72 Some(error_codes::EXCLUSION_VIOLATION) => DatabaseErrorKind::ExclusionViolation,
73 Some(error_codes::CONNECTION_EXCEPTION)
74 | Some(error_codes::CONNECTION_FAILURE)
75 | Some(error_codes::SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION)
76 | Some(error_codes::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION) => {
77 DatabaseErrorKind::ClosedConnection
78 }
79 _ => DatabaseErrorKind::Unknown,
80 };
81 let error_information = Box::new(PgErrorInformation {
82 result: internal_result,
83 });
84 let conn_status = conn.get_status();
85 if conn_status == ConnStatusType::CONNECTION_BAD {
86 error_kind = DatabaseErrorKind::ClosedConnection;
87 }
88 Err(Error::DatabaseError(error_kind, error_information))
89 }
90 }
91 }
92
93 pub(super) fn rows_affected(&self) -> QueryResult<usize> {
94 let rows = self.internal_result.rows_affected();
95 let count_str = rows
96 .to_str()
97 .map_err(|e| Error::DeserializationError(Box::new(e)))?;
98 match count_str {
99 "" => Ok(0),
100 _ => count_str
101 .parse()
102 .map_err(|e| Error::DeserializationError(Box::new(e))),
103 }
104 }
105
106 pub(super) fn num_rows(&self) -> usize {
107 self.row_count.try_into().expect(
108 "Diesel expects to run on a >= 32 bit OS \
109 (or libpq is giving out negative row count)",
110 )
111 }
112
113 pub(super) fn get_row(self: Rc<Self>, idx: usize) -> PgRow {
114 PgRow::new(self, idx)
115 }
116
117 pub(super) fn get(&self, row_idx: usize, col_idx: usize) -> Option<&[u8]> {
118 if self.is_null(row_idx, col_idx) {
119 None
120 } else {
121 let row_idx = row_idx.try_into().ok()?;
122 let col_idx = col_idx.try_into().ok()?;
123 Some(self.internal_result.get_bytes(row_idx, col_idx))
124 }
125 }
126
127 pub(super) fn is_null(&self, row_idx: usize, col_idx: usize) -> bool {
128 let row_idx = row_idx
129 .try_into()
130 .expect("Row indices are expected to fit into 32 bit");
131 let col_idx = col_idx
132 .try_into()
133 .expect("Column indices are expected to fit into 32 bit");
134
135 self.internal_result.is_null(row_idx, col_idx) != 0
136 }
137
138 pub(in crate::pg) fn column_type(&self, col_idx: usize) -> NonZeroU32 {
139 let col_idx: i32 = col_idx
140 .try_into()
141 .expect("Column indices are expected to fit into 32 bit");
142 let type_oid = self.internal_result.column_type(col_idx);
143 NonZeroU32::new(type_oid).expect(
144 "Got a zero oid from postgres. If you see this error message \
145 please report it as issue on the diesel github bug tracker.",
146 )
147 }
148
149 #[inline(always)] pub(super) fn column_name(&self, col_idx: usize) -> Option<&str> {
151 self.column_name_map
152 .get_or_init(|| {
153 (0..self.column_count)
154 .map(|idx| {
155 self.internal_result
156 .column_name(idx as libc::c_int)
157 .map(|name| {
158 name.to_str().expect(
159 "Expect postgres field names to be UTF-8, because we \
160- requested UTF-8 encoding on connection setup",
161 ) as *const str
162 })
163 })
164 .collect()
165 })
166 .get(col_idx)
167 .and_then(|n| {
168 n.map(|n: *const str| unsafe {
169 &*n
172 })
173 })
174 }
175
176 pub(super) fn column_count(&self) -> usize {
177 self.column_count.try_into().expect(
178 "Diesel expects to run on a >= 32 bit OS \
179 (or libpq is giving out negative column count)",
180 )
181 }
182}
183
184struct PgErrorInformation {
185 result: RawResult,
186}
187
188impl DatabaseErrorInformation for PgErrorInformation {
189 fn message(&self) -> &str {
190 self.result
191 .get_result_field(ResultField::MessagePrimary)
192 .unwrap_or_else(|| self.result.error_message())
193 }
194
195 fn details(&self) -> Option<&str> {
196 self.result.get_result_field(ResultField::MessageDetail)
197 }
198
199 fn hint(&self) -> Option<&str> {
200 self.result.get_result_field(ResultField::MessageHint)
201 }
202
203 fn table_name(&self) -> Option<&str> {
204 self.result.get_result_field(ResultField::TableName)
205 }
206
207 fn column_name(&self) -> Option<&str> {
208 self.result.get_result_field(ResultField::ColumnName)
209 }
210
211 fn constraint_name(&self) -> Option<&str> {
212 self.result.get_result_field(ResultField::ConstraintName)
213 }
214
215 fn statement_position(&self) -> Option<i32> {
216 let str_pos = self
217 .result
218 .get_result_field(ResultField::StatementPosition)?;
219 str_pos.parse::<i32>().ok()
220 }
221}
222
223mod error_codes {
224 pub(in crate::pg::connection) const CONNECTION_EXCEPTION: &str = "08000";
229 pub(in crate::pg::connection) const CONNECTION_FAILURE: &str = "08006";
230 pub(in crate::pg::connection) const SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION: &str = "08001";
231 pub(in crate::pg::connection) const SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION: &str =
232 "08004";
233 pub(in crate::pg::connection) const RESTRICT_VIOLATION: &str = "23001";
234 pub(in crate::pg::connection) const NOT_NULL_VIOLATION: &str = "23502";
235 pub(in crate::pg::connection) const FOREIGN_KEY_VIOLATION: &str = "23503";
236 pub(in crate::pg::connection) const UNIQUE_VIOLATION: &str = "23505";
237 pub(in crate::pg::connection) const CHECK_VIOLATION: &str = "23514";
238 pub(in crate::pg::connection) const EXCLUSION_VIOLATION: &str = "23P01";
239 pub(in crate::pg::connection) const READ_ONLY_TRANSACTION: &str = "25006";
240 pub(in crate::pg::connection) const SERIALIZATION_FAILURE: &str = "40001";
241}