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