From 55c067465864d9766ccb892b6e287a7cd71dea16 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 2 Sep 2013 23:19:03 -0700 Subject: [PATCH] Fill out README --- README.md | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/types.rs | 14 ++++ 2 files changed, 235 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c3bf178..01357a21 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,222 @@ -rust-postgres +Rust-Postgres ============= +A native PostgreSQL driver for Rust. + +Overview +======== +Rust-Postgres is a pure-Rust frontend for the popular PostgreSQL database. It +exposes a high level interface in the vein of JDBC or Go's `database/sql` +package. +```rust +extern mod postgres; + +use postgres::PostgresConnection; +use postgres::types::ToSql; + +#[deriving(ToStr)] +struct Person { + id: i32, + name: ~str, + awesome: bool, + data: Option<~[u8]> +} + +fn main() { + let conn = PostgresConnection::connect("postgres://postgres@127.0.0.1"); + + conn.update("CREATE TABLE person ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + awesome BOOL NOT NULL, + data BYTEA + )", []); + let me = Person { + id: 0, + name: ~"Steven", + awesome: true, + data: None + }; + conn.update("INSERT INTO person (name, awesome, data) + VALUES ($1, $2, $3)", + [&me.name as &ToSql, &me.awesome as &ToSql, + &me.data as &ToSql]); + + let stmt = conn.prepare("SELECT id, name, awesome, data FROM person"); + for row in stmt.query([]) { + let person = Person { + id: row[0], + name: row[1], + awesome: row[2], + data: row[3] + }; + println!("Found person {}", person.to_str()); + } +} +``` + +Requirements +============ + +* **Rust** - Rust-Postgres is developed against the *master* branch of the Rust + repository. It will most likely not build against the releases on + http://www.rust-lang.org. + +* **PostgreSQL 7.4 or later** - Rust-Postgres speaks version 3 of the + PostgreSQL protocol, which corresponds to versions 7.4 and later. If your + version of Postgres was compiled in the last decade, you should be okay. + +Usage +===== + +Connecting +---------- +Connect to a Postgres server using the standard URI format: +```rust +let conn = PostgresConnection::connect("postgres://user:pass@host:port/database?arg1=val1&arg2=val2"); +``` +As the Rust standard library currently lacks DNS lookup functionality, `host` +must currently be an IP address. + +`pass` may be omitted if not needed. `port` defaults to `5432` and `database` +defaults to the value of `user` if not specified. The driver supports `trust`, +`password` and `md5` authentication. + +Statement Preparation +--------------------- +Prepared statements can have parameters, represented as `$n` where `n` is an +index into the parameter array starting from 1: +```rust +let stmt = conn.prepare("SELECT * FROM foo WHERE bar = $1 AND baz = $2"); +``` + +Querying +-------- +A prepared statement can be executed with the `query` and `update` methods. +Both methods take an array of parameters to bind to the query represented as +`&ToSql` trait objects. `update` returns the number of rows affected by the +query (or 0 if not applicable): +``` +let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2"); +let updates = stmt.update([&1i32 as &ToSql, & &"biz" as &ToSql]); +println!("{} rows were updated", updates); +``` +`query` returns a result iterator. Fields of each row in the result can be +accessed either by its index or their column name: +```rust +let stmt = conn.prepare("SELECT bar, baz FROM foo"); +for row in stmt.query([]) { + let bar: i32 = row[0]; + let baz: ~str = row["baz"]; + println!("bar: {}, baz: {}", bar, baz); +} +``` +In addition, `PostgresConnection` has a utility `update` method which is useful +if a statement is only going to be executed once: +```rust +let updates = conn.update("UPDATE foo SET bar = $1 WHERE baz = $2", + [&1i32 as &ToSql, & &"biz" as &ToSql]); +println!("{} rows were updated", updates); +``` + +Transactions +------------ +Transactions are encapsulated by the `in_transaction` method. `in_transaction` +takes a closure which is passed a `PostgresTransaction` object which has the +functionality of a `PostgresConnection` as well as methods to control the +result of the transaction: +```rust +do conn.in_transaction |trans| { + trans.update(...); + let stmt = trans.prepare(...); + + if a_bad_thing_happened { + trans.set_rollback(); + } + + if the_coast_is_clear { + trans.set_commit(); + } +} +``` +A transaction will commit by default. Transactions cannot currently be nested. + +Error Handling +-------------- +The methods described above will fail if there is an error. For each of these +methods, there is a second variant prefixed with `try_` which returns a +`Result`: +```rust +match conn.try_update(query) { + Ok(updates) => println!("{} rows were updated", updates), + Err(err) => println!("An error occurred: {}", err.to_str()) +} +``` + +Type Correspondence +------------------- +Rust-Postgres enforces a strict correspondence between Rust types and Postgres +types. The driver currently natively supports the following conversions: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Rust TypePostgres Type
boolBOOL
i8"char"
i16SMALLINT
i32INT
i64BIGINT
f32FLOAT4
f64FLOAT8
strVARCHAR, CHAR(n), TEXT
[u8]BYTEA
extra::json::JsonJSON
extra::uuid::UuidUUID
+ +More conversions can be defined by implementing the `ToSql` and `FromSql` +traits. + +Development +=========== +Rust-Postgres is still under active development, so don't be surprised if APIs +change and things break. If something's not working properly, file an issue or +submit a pull request! diff --git a/src/types.rs b/src/types.rs index e2e4faa8..ef480853 100644 --- a/src/types.rs +++ b/src/types.rs @@ -195,6 +195,13 @@ to_option_impl!(FLOAT4OID, f32) to_conversions_impl!(FLOAT8OID, f64, write_be_f64_) to_option_impl!(FLOAT8OID, f64) +impl ToSql for ~str { + fn to_sql(&self, ty: Oid) -> (Format, Option<~[u8]>) { + check_oid!(VARCHAROID | TEXTOID | BPCHAROID, ty) + (Text, Some(self.as_bytes().to_owned())) + } +} + impl<'self> ToSql for &'self str { fn to_sql(&self, ty: Oid) -> (Format, Option<~[u8]>) { check_oid!(VARCHAROID | TEXTOID | BPCHAROID, ty) @@ -205,6 +212,13 @@ impl<'self> ToSql for &'self str { to_option_impl!(VARCHAROID | TEXTOID | BPCHAROID, ~str) to_option_impl!(self, VARCHAROID | TEXTOID | BPCHAROID, &'self str) +impl ToSql for ~[u8] { + fn to_sql(&self, ty: Oid) -> (Format, Option<~[u8]>) { + check_oid!(BYTEAOID, ty) + (Binary, Some(self.to_owned())) + } +} + impl<'self> ToSql for &'self [u8] { fn to_sql(&self, ty: Oid) -> (Format, Option<~[u8]>) { check_oid!(BYTEAOID, ty)