1 extern crate pq_sys;
2 
3 use self::pq_sys::*;
4 use std::ffi::{CStr, CString};
5 use std::os::raw as libc;
6 use std::{slice, str};
7 
8 use super::raw::RawResult;
9 use super::row::PgRow;
10 use result::{DatabaseErrorInformation, DatabaseErrorKind, Error, QueryResult};
11 
12 pub struct PgResult {
13     internal_result: RawResult,
14 }
15 
16 impl PgResult {
17     #[allow(clippy::new_ret_no_self)]
new(internal_result: RawResult) -> QueryResult<Self>18     pub fn new(internal_result: RawResult) -> QueryResult<Self> {
19         use self::ExecStatusType::*;
20 
21         let result_status = unsafe { PQresultStatus(internal_result.as_ptr()) };
22         match result_status {
23             PGRES_COMMAND_OK | PGRES_TUPLES_OK => Ok(PgResult {
24                 internal_result: internal_result,
25             }),
26             PGRES_EMPTY_QUERY => {
27                 let error_message = "Received an empty query".to_string();
28                 Err(Error::DatabaseError(
29                     DatabaseErrorKind::__Unknown,
30                     Box::new(error_message),
31                 ))
32             }
33             _ => {
34                 let error_kind =
35                     match get_result_field(internal_result.as_ptr(), ResultField::SqlState) {
36                         Some(error_codes::UNIQUE_VIOLATION) => DatabaseErrorKind::UniqueViolation,
37                         Some(error_codes::FOREIGN_KEY_VIOLATION) => {
38                             DatabaseErrorKind::ForeignKeyViolation
39                         }
40                         Some(error_codes::SERIALIZATION_FAILURE) => {
41                             DatabaseErrorKind::SerializationFailure
42                         }
43                         _ => DatabaseErrorKind::__Unknown,
44                     };
45                 let error_information = Box::new(PgErrorInformation(internal_result));
46                 Err(Error::DatabaseError(error_kind, error_information))
47             }
48         }
49     }
50 
rows_affected(&self) -> usize51     pub fn rows_affected(&self) -> usize {
52         unsafe {
53             let count_char_ptr = PQcmdTuples(self.internal_result.as_ptr());
54             let count_bytes = CStr::from_ptr(count_char_ptr).to_bytes();
55             // Using from_utf8_unchecked is ok here because, we've set the
56             // client encoding to utf8
57             let count_str = str::from_utf8_unchecked(count_bytes);
58             match count_str {
59                 "" => 0,
60                 _ => count_str
61                     .parse()
62                     .expect("Error parsing `rows_affected` as integer value"),
63             }
64         }
65     }
66 
num_rows(&self) -> usize67     pub fn num_rows(&self) -> usize {
68         unsafe { PQntuples(self.internal_result.as_ptr()) as usize }
69     }
70 
get_row(&self, idx: usize) -> PgRow71     pub fn get_row(&self, idx: usize) -> PgRow {
72         PgRow::new(self, idx)
73     }
74 
get(&self, row_idx: usize, col_idx: usize) -> Option<&[u8]>75     pub fn get(&self, row_idx: usize, col_idx: usize) -> Option<&[u8]> {
76         if self.is_null(row_idx, col_idx) {
77             None
78         } else {
79             let row_idx = row_idx as libc::c_int;
80             let col_idx = col_idx as libc::c_int;
81             unsafe {
82                 let value_ptr =
83                     PQgetvalue(self.internal_result.as_ptr(), row_idx, col_idx) as *const u8;
84                 let num_bytes = PQgetlength(self.internal_result.as_ptr(), row_idx, col_idx);
85                 Some(slice::from_raw_parts(value_ptr, num_bytes as usize))
86             }
87         }
88     }
89 
is_null(&self, row_idx: usize, col_idx: usize) -> bool90     pub fn is_null(&self, row_idx: usize, col_idx: usize) -> bool {
91         unsafe {
92             0 != PQgetisnull(
93                 self.internal_result.as_ptr(),
94                 row_idx as libc::c_int,
95                 col_idx as libc::c_int,
96             )
97         }
98     }
99 
field_number(&self, column_name: &str) -> Option<usize>100     pub fn field_number(&self, column_name: &str) -> Option<usize> {
101         let cstr = CString::new(column_name).unwrap_or_default();
102         let fnum = unsafe { PQfnumber(self.internal_result.as_ptr(), cstr.as_ptr()) };
103         match fnum {
104             -1 => None,
105             x => Some(x as usize),
106         }
107     }
108 }
109 
110 struct PgErrorInformation(RawResult);
111 
112 impl DatabaseErrorInformation for PgErrorInformation {
message(&self) -> &str113     fn message(&self) -> &str {
114         get_result_field(self.0.as_ptr(), ResultField::MessagePrimary)
115             .unwrap_or_else(|| self.0.error_message())
116     }
117 
details(&self) -> Option<&str>118     fn details(&self) -> Option<&str> {
119         get_result_field(self.0.as_ptr(), ResultField::MessageDetail)
120     }
121 
hint(&self) -> Option<&str>122     fn hint(&self) -> Option<&str> {
123         get_result_field(self.0.as_ptr(), ResultField::MessageHint)
124     }
125 
table_name(&self) -> Option<&str>126     fn table_name(&self) -> Option<&str> {
127         get_result_field(self.0.as_ptr(), ResultField::TableName)
128     }
129 
column_name(&self) -> Option<&str>130     fn column_name(&self) -> Option<&str> {
131         get_result_field(self.0.as_ptr(), ResultField::ColumnName)
132     }
133 
constraint_name(&self) -> Option<&str>134     fn constraint_name(&self) -> Option<&str> {
135         get_result_field(self.0.as_ptr(), ResultField::ConstraintName)
136     }
137 }
138 
139 /// Represents valid options to
140 /// [`PQresultErrorField`](https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQRESULTERRORFIELD)
141 /// Their values are defined as C preprocessor macros, and therefore are not exported by libpq-sys.
142 /// Their values can be found in `postgres_ext.h`
143 #[repr(i32)]
144 enum ResultField {
145     SqlState = 'C' as i32,
146     MessagePrimary = 'M' as i32,
147     MessageDetail = 'D' as i32,
148     MessageHint = 'H' as i32,
149     TableName = 't' as i32,
150     ColumnName = 'c' as i32,
151     ConstraintName = 'n' as i32,
152 }
153 
get_result_field<'a>(res: *mut PGresult, field: ResultField) -> Option<&'a str>154 fn get_result_field<'a>(res: *mut PGresult, field: ResultField) -> Option<&'a str> {
155     let ptr = unsafe { PQresultErrorField(res, field as libc::c_int) };
156     if ptr.is_null() {
157         return None;
158     }
159 
160     let c_str = unsafe { CStr::from_ptr(ptr) };
161     c_str.to_str().ok()
162 }
163 
164 mod error_codes {
165     //! These error codes are documented at
166     //! <https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html>
167     //!
168     //! They are not exposed programmatically through libpq.
169     pub const UNIQUE_VIOLATION: &str = "23505";
170     pub const FOREIGN_KEY_VIOLATION: &str = "23503";
171     pub const SERIALIZATION_FAILURE: &str = "40001";
172 }
173