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