#[derive(Clone, Debug, PartialEq)]
pub struct Info {
keyword: String,
envvar: String,
compiled: String,
val: String,
label: String,
dispchar: String,
dispsize: i32,
}
impl From<*mut pq_sys::_PQconninfoOption> for Info {
fn from(info: *mut pq_sys::_PQconninfoOption) -> Self {
unsafe {
Self {
keyword: crate::ffi::to_string((*info).keyword),
envvar: crate::ffi::to_string((*info).envvar),
compiled: crate::ffi::to_string((*info).compiled),
val: crate::ffi::to_string((*info).val),
label: crate::ffi::to_string((*info).label),
dispchar: crate::ffi::to_string((*info).dispchar),
dispsize: (*info).dispsize,
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Status {
Ok,
Bad,
Started,
Made,
AwaitingResponse,
AuthOk,
Setenv,
SslStartup,
Needed,
}
impl From<pq_sys::_bindgen_ty_2> for Status {
fn from(status: pq_sys::_bindgen_ty_2) -> Self {
match status {
pq_sys::_bindgen_ty_2::CONNECTION_OK => Self::Ok,
pq_sys::_bindgen_ty_2::CONNECTION_BAD => Self::Bad,
pq_sys::_bindgen_ty_2::CONNECTION_STARTED => Self::Started,
pq_sys::_bindgen_ty_2::CONNECTION_MADE => Self::Made,
pq_sys::_bindgen_ty_2::CONNECTION_AWAITING_RESPONSE => Self::AwaitingResponse,
pq_sys::_bindgen_ty_2::CONNECTION_AUTH_OK => Self::AuthOk,
pq_sys::_bindgen_ty_2::CONNECTION_SETENV => Self::Setenv,
pq_sys::_bindgen_ty_2::CONNECTION_SSL_STARTUP => Self::SslStartup,
pq_sys::_bindgen_ty_2::CONNECTION_NEEDED => Self::Needed,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TransactionStatus {
Idle,
Active,
InTrans,
InError,
Unknow,
}
impl From<pq_sys::_bindgen_ty_5> for TransactionStatus {
fn from(status: pq_sys::_bindgen_ty_5) -> Self {
match status {
pq_sys::_bindgen_ty_5::PQTRANS_IDLE => Self::Idle,
pq_sys::_bindgen_ty_5::PQTRANS_ACTIVE => Self::Active,
pq_sys::_bindgen_ty_5::PQTRANS_INTRANS => Self::InTrans,
pq_sys::_bindgen_ty_5::PQTRANS_INERROR => Self::InError,
pq_sys::_bindgen_ty_5::PQTRANS_UNKNOWN => Self::Unknow,
}
}
}
pub struct Connection {
conn: *mut pq_sys::PGconn,
}
impl Connection {
pub fn new(dsn: &str) -> Self {
unsafe { pq_sys::PQconnectdb(crate::cstr!(dsn)) }.into()
}
pub fn start(conninfo: &str) -> Self {
unsafe { pq_sys::PQconnectStart(crate::cstr!(conninfo)) }.into()
}
pub fn set_db_login(
pghost: Option<&str>,
pgport: Option<&str>,
pgoptions: Option<&str>,
pgtty: Option<&str>,
db_name: Option<&str>,
login: Option<&str>,
pwd: Option<&str>,
) -> Self {
unsafe {
pq_sys::PQsetdbLogin(
crate::cstr!(pghost.unwrap_or_default()),
crate::cstr!(pgport.unwrap_or_default()),
crate::cstr!(pgoptions.unwrap_or_default()),
crate::cstr!(pgtty.unwrap_or_default()),
crate::cstr!(db_name.unwrap_or_default()),
crate::cstr!(login.unwrap_or_default()),
crate::cstr!(pwd.unwrap_or_default()),
)
}
.into()
}
pub fn db(&self) -> String {
crate::ffi::to_string(unsafe { pq_sys::PQdb(self.into()) })
}
pub fn user(&self) -> String {
crate::ffi::to_string(unsafe { pq_sys::PQuser(self.into()) })
}
pub fn pass(&self) -> Option<String> {
crate::ffi::to_option_string(unsafe { pq_sys::PQpass(self.into()) })
}
pub fn host(&self) -> String {
crate::ffi::to_string(unsafe { pq_sys::PQhost(self.into()) })
}
pub fn port(&self) -> String {
crate::ffi::to_string(unsafe { pq_sys::PQport(self.into()) })
}
pub fn tty(&self) -> Option<String> {
crate::ffi::to_option_string(unsafe { pq_sys::PQtty(self.into()) })
}
pub fn options(&self) -> Option<String> {
crate::ffi::to_option_string(unsafe { pq_sys::PQoptions(self.into()) })
}
pub fn status(&self) -> Status {
unsafe { pq_sys::PQstatus(self.into()) }.into()
}
pub fn transaction_status(&self) -> TransactionStatus {
unsafe { pq_sys::PQtransactionStatus(self.into()) }.into()
}
pub fn parameter_status(&self, param: &str) -> String {
crate::ffi::to_string(unsafe {
pq_sys::PQparameterStatus(self.into(), crate::cstr!(param))
})
}
pub fn protocol_version(&self) -> i32 {
unsafe { pq_sys::PQprotocolVersion(self.into()) }
}
pub fn server_version(&self) -> i32 {
unsafe { pq_sys::PQserverVersion(self.into()) }
}
pub fn error_message(&self) -> Option<String> {
crate::ffi::to_option_string(unsafe { pq_sys::PQerrorMessage(self.into()) })
}
pub fn socket(&self) -> std::result::Result<i32, ()> {
let socket = unsafe { pq_sys::PQsocket(self.into()) };
if socket < 0 {
Err(())
} else {
Ok(socket)
}
}
pub fn backend_pid(&self) -> i32 {
unsafe { pq_sys::PQbackendPID(self.into()) }
}
pub fn poll(&self) -> crate::poll::Status {
unsafe { pq_sys::PQconnectPoll(self.into()) }.into()
}
pub fn reset(&self) {
unsafe { pq_sys::PQreset(self.into()) };
}
pub fn reset_start(&self) {
unsafe { pq_sys::PQresetStart(self.into()) };
}
pub fn reset_poll(&self) -> crate::poll::Status {
unsafe { pq_sys::PQresetPoll(self.into()) }.into()
}
pub fn exec(&self, query: &str) -> crate::Result {
unsafe { pq_sys::PQexec(self.conn, crate::cstr!(query)) }.into()
}
pub fn exec_params(
&self,
command: &str,
param_types: &[crate::Type],
param_values: &[Option<&[u8]>],
param_formats: &[i32],
result_format: crate::result::Format,
) -> crate::Result {
let types = Self::param_types(param_types);
let param_lengths = Self::param_lengths(param_values);
unsafe {
pq_sys::PQexecParams(
self.conn,
crate::cstr!(command),
param_values.len() as i32,
if types.is_empty() {
std::ptr::null()
} else {
types.as_ptr()
},
Self::param_values(param_values).as_ptr(),
if param_lengths.is_empty() {
std::ptr::null()
} else {
param_lengths.as_ptr()
},
if param_formats.is_empty() {
std::ptr::null()
} else {
param_formats.as_ptr()
},
result_format as i32,
)
}
.into()
}
pub fn prepare(
&self,
name: Option<&str>,
query: &str,
param_types: &[crate::Type],
) -> crate::Result {
let types = Self::param_types(param_types);
unsafe {
pq_sys::PQprepare(
self.conn,
crate::cstr!(name.unwrap_or_default()),
crate::cstr!(query),
types.len() as i32,
types.as_ptr(),
)
}
.into()
}
pub fn exec_prepared(
&self,
name: &str,
params: &[Option<&[u8]>],
param_formats: &[i32],
result_format: crate::result::Format,
) -> crate::Result {
let param_lengths = Self::param_lengths(params);
unsafe {
pq_sys::PQexecPrepared(
self.conn,
crate::cstr!(name),
params.len() as i32,
Self::param_values(params).as_ptr(),
if param_lengths.is_empty() {
std::ptr::null()
} else {
param_lengths.as_ptr()
},
if param_formats.is_empty() {
std::ptr::null()
} else {
param_formats.as_ptr()
},
result_format as i32,
)
}
.into()
}
pub fn escape_string(&self, from: &str) -> std::result::Result<String, i32> {
crate::escape::string_conn(&self, from)
}
pub fn escape_bytea(&self, from: &[u8]) -> std::result::Result<Vec<u8>, String> {
crate::escape::bytea_conn(&self, from)
}
pub fn client_encoding(&self) -> crate::Encoding {
unsafe { pq_sys::PQclientEncoding(self.conn) }.into()
}
pub fn set_client_encoding(&self, encoding: crate::Encoding) {
unsafe {
pq_sys::PQsetClientEncoding(self.conn, crate::cstr!(&encoding.to_string()));
}
}
pub fn defaults(&self) -> Info {
unsafe { pq_sys::PQconndefaults() }.into()
}
fn param_values(param_values: &[Option<&[u8]>]) -> Vec<*const i8> {
param_values
.iter()
.map(|x| {
x.as_ref()
.map(|x| x.as_ptr() as *const i8)
.unwrap_or(std::ptr::null())
})
.collect()
}
fn param_lengths(param_values: &[Option<&[u8]>]) -> Vec<i32> {
param_values
.iter()
.map(|x| x.as_ref().map(|x| x.len() as i32).unwrap_or(0))
.collect()
}
fn param_types(param_types: &[crate::Type]) -> Vec<u32> {
param_types.iter().map(|x| x.oid()).collect()
}
}
impl Into<*mut pq_sys::pg_conn> for &Connection {
fn into(self) -> *mut pq_sys::pg_conn {
self.conn
}
}
impl Into<*const pq_sys::pg_conn> for &Connection {
fn into(self) -> *const pq_sys::pg_conn {
self.conn
}
}
impl From<*mut pq_sys::pg_conn> for Connection {
fn from(conn: *mut pq_sys::pg_conn) -> Self {
Self { conn }
}
}
impl Drop for Connection {
fn drop(&mut self) {
unsafe {
pq_sys::PQfinish(self.conn);
}
}
}
impl std::fmt::Debug for Connection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Connection")
.field("inner", &self.conn)
.field("db", &self.db())
.field("user", &self.user())
.field("pass", &self.pass())
.field("host", &self.host())
.field("port", &self.port())
.field("tty", &self.tty())
.field("options", &self.options())
.field("status", &self.status())
.field("transaction_status", &self.transaction_status())
.field("protocol_version", &self.protocol_version())
.field("server_version", &self.server_version())
.field("error_message", &self.error_message())
.field("socket", &self.socket())
.field("backend_pid", &self.backend_pid())
.finish()
}
}
#[cfg(test)]
mod test {
#[test]
fn reset() {
let conn = crate::test::new_conn();
conn.reset();
}
#[test]
fn poll() {
let dsn = std::env::var("PQ_DSN").unwrap_or_else(|_| "host=localhost".to_string());
let conn = crate::Connection::start(&dsn);
assert_eq!(conn.poll(), crate::poll::Status::Writing);
conn.reset_start();
assert_eq!(conn.reset_poll(), crate::poll::Status::Writing);
}
#[test]
fn exec() {
let conn = crate::test::new_conn();
let results = conn.exec("SELECT 1 as one, 2 as two from generate_series(1,2)");
assert_eq!(results.status(), crate::Status::TupplesOk);
assert_eq!(results.ntuples(), 2);
assert_eq!(results.nfields(), 2);
assert_eq!(results.value(0, 0), "1".to_string());
assert_eq!(results.value(0, 1), "2".to_string());
}
#[test]
fn exec_params() {
let conn = crate::test::new_conn();
let results = conn.exec_params(
"SELECT $1",
&[crate::Type::TEXT],
&[Some(b"fooo\0")],
&[],
crate::result::Format::Text,
);
assert_eq!(results.status(), crate::Status::TupplesOk);
assert_eq!(results.value(0, 0), "fooo".to_string());
}
#[test]
fn exec_prepared() {
let conn = crate::test::new_conn();
let results = conn.prepare(Some("test1"), "SELECT $1", &[crate::Type::TEXT]);
assert_eq!(results.status(), crate::Status::CommandOk);
let results = conn.exec_prepared(
"test1",
&[Some(b"fooo\0")],
&[],
crate::result::Format::Text,
);
assert_eq!(results.value(0, 0), "fooo".to_string());
}
#[test]
fn client_encoding() {
let conn = crate::test::new_conn();
assert_eq!(conn.client_encoding(), crate::Encoding::UTF8);
}
#[test]
fn set_client_encoding() {
let conn = crate::test::new_conn();
conn.set_client_encoding(crate::Encoding::SQL_ASCII);
assert_eq!(conn.client_encoding(), crate::Encoding::SQL_ASCII);
}
#[test]
fn info() {
let conn = crate::test::new_conn();
let _ = conn.defaults();
}
}