15 releases
Uses old Rust 2015
0.4.5 | Mar 21, 2019 |
---|---|
0.4.4 | Mar 21, 2019 |
0.4.2 | Dec 24, 2018 |
0.4.1 | Jul 14, 2018 |
0.1.2 | May 15, 2017 |
#1556 in Database interfaces
29 downloads per month
110KB
2.5K
SLoC
A fully-asynchronous Postgres client. Postgres has a notion of "statements", which are named or unnamed SQL commands, and of "portals", where a portal is an instance of a result. All queries to the database follow the same sequence: parse, bind, execute, close (portal and/or statement).
Both statements and portals can be named or unnamed, although
prototype code might be easier to write using only the query
method of the Connection
type, which does the correct sequence
using the unnamed statement and unnamed portal.
Contrarily to synchronous clients, this client needs to store
requests in the event loop while doing asynchronous I/O. The
precise type for this storage needs to be provided by the user of
this crate, and needs to implement both the Request
and
HandleRow
traits.
This crate provides two API levels:
-
A higher-level one based using
spawn_connection
, which uses an existing event loop. This API requires an existing instance ofRequest+HandleRow
. -
A lower-level one based on the
Connection
type, which might be used to build more complex, or more direct pipelines, such as tunneling through another protocol, or waiting for a query to be executed by the server.
extern crate tokio;
extern crate pleingres;
extern crate futures;
extern crate env_logger;
extern crate uuid;
use uuid::Uuid;
use std::rc::Rc;
use std::net::ToSocketAddrs;
use futures::Future;
// A `Request` is the type used for communication with the
// client event loop. It is used both for sending input and
// receiving output.
struct Request {
login: String,
id: Option<Uuid>
}
// Requests must implement the `pleingres::Request` trait,
// meaning they can be converted to commands to be sent to
// the server.
impl pleingres::Request for Request {
fn request(&mut self, mut buf: pleingres::Buffer) {
buf.bind("SELECT id FROM users WHERE login=$1", &[&self.login]).execute(0);
}
}
impl pleingres::HandleRow for Request {
fn row(&mut self, mut row: pleingres::Row) -> bool {
if let Some(id) = row.next() {
self.id = Some(pleingres::FromSql::from_sql(id).unwrap())
}
true
}
}
fn main() {
env_logger::try_init().unwrap_or(());
let p = Rc::new(pleingres::Parameters {
addr: "::1:5432".to_socket_addrs().unwrap().next().unwrap(),
user: "pe".to_string(),
password: "password".to_string(),
database: Some("pijul".to_string()),
idle_timeout: Some(std::time::Duration::from_millis(20_000)),
tcp_keepalive: Some(std::time::Duration::from_millis(10000)),
ssl: None,
});
let mut l = tokio::reactor::Core::new().unwrap();
let db:pleingres::Handle<Request> =
pleingres::spawn_connection(p.clone(), l.handle()).unwrap();
l.run(db
.send_request(
Request { login: "me".to_string(), id: None }
)
.and_then(|dbreq| {
if let Some(ref id) = dbreq.id {
println!("id: {:?}", id)
}
futures::finished(())
})).unwrap()
}
Alternatively, the pleingres::Request
can be implemented using
the sql plugin. On the above example, we would replace
struct Request {
login: String,
id: Option<Uuid>
}
impl pleingres::Request for Request {
fn request(&mut self, mut buf: pleingres::Buffer) {
buf.bind("SELECT id FROM users WHERE login=$1", &[&self.login]).execute(0);
}
}
With just
#[sql("SELECT id FROM users WHERE login = $login")]
struct Request {
login: String,
id: Option<Uuid>
}
Dependencies
~12MB
~213K SLoC