Go to file
2013-11-05 20:59:54 -08:00
travis Fixes for Rust changes 2013-10-10 20:50:39 -07:00
types Simplify a FromSql impl 2013-11-05 20:59:54 -08:00
.gitignore Switch back to rustpkg test infrastructure 2013-11-02 15:06:19 -07:00
.travis.yml Switch over to rustpkg test 2013-11-04 09:09:11 -08:00
error.rs Document modules 2013-10-20 14:34:50 -07:00
lib.rs Minor cleanup 2013-11-03 20:49:17 -08:00
LICENSE Initial commit 2013-07-22 21:43:51 -07:00
message.rs Switch back to rustpkg test infrastructure 2013-11-02 15:06:19 -07:00
pool.rs Add cancel_data to pooled connection 2013-10-20 17:54:50 -07:00
POSTGRES_LICENSE Some minor cleanup 2013-10-24 22:30:34 -07:00
README.md Update doc link in readme 2013-11-03 20:54:07 -08:00
test.rs Switch over to rustpkg test 2013-11-04 09:09:11 -08:00

Rust-Postgres

A native PostgreSQL driver for Rust.

Documentation is available at http://docs.octayn.net/rust-postgres/.

Build Status

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.

extern mod postgres = "github.com/sfackler/rust-postgres";
extern mod extra;

use extra::time;
use extra::time::Timespec;

use postgres::{PostgresConnection, PostgresStatement};
use postgres::types::ToSql;

struct Person {
    id: i32,
    name: ~str,
    time_created: Timespec,
    data: Option<~[u8]>
}

fn main() {
    let conn = PostgresConnection::connect("postgres://postgres@localhost");

    conn.update("CREATE TABLE person (
                    id              SERIAL PRIMARY KEY,
                    name            VARCHAR NOT NULL,
                    time_created    TIMESTAMP NOT NULL,
                    data            BYTEA
                 )", []);
    let me = Person {
        id: 0,
        name: ~"Steven",
        time_created: time::get_time(),
        data: None
    };
    conn.update("INSERT INTO person (name, time_created, data)
                    VALUES ($1, $2, $3)",
                 [&me.name as &ToSql, &me.time_created as &ToSql,
                  &me.data as &ToSql]);

    let stmt = conn.prepare("SELECT id, name, time_created, data FROM person");
    for row in stmt.query([]) {
        let person = Person {
            id: row[0],
            name: row[1],
            time_created: row[2],
            data: row[3]
        };
        println!("Found person {}", person.name);
    }
}

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:

let conn = PostgresConnection::connect("postgres://user:pass@host:port/database?arg1=val1&arg2=val2");

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 and password authentication.

Statement Preparation

Prepared statements can have parameters, represented as $n where n is an index into the parameter array starting from 1:

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 their indicies or their column names. Unlike statement parameters, result columns are zero-indexed.

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:

let updates = conn.update("UPDATE foo SET bar = $1 WHERE baz = $2",
                          [&1i32 as &ToSql, & &"biz" as &ToSql]);
println!("{} rows were updated", updates);

Transactions

The transaction method will start a new transaction. It returns a PostgresTransaction object which has the functionality of a PostgresConnection as well as methods to control the result of the transaction:

{
    let trans = conn.transaction();
    trans.update(...);
    let stmt = trans.prepare(...);

    if a_bad_thing_happened {
        trans.set_rollback();
    }

    if the_coast_is_clear {
        trans.set_commit();
    }
}

The transaction will be active until the PostgresTransaction object falls out of scope. A transaction will commit by default. Nested transactions are supported via savepoints.

Lazy Queries

Some queries may return a large amount of data. Statements prepared within a transaction have an additional method, lazy_query. The rows returned from a call to lazy_query are pulled from the database in batches as needed:

{
    let trans = conn.transaction();
    let stmt = trans.prepare(query)

    // No more than 100 rows will be stored in memory at any time
    for row in stmt.lazy_query(100, params) {
        // do things
    }
}

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:

match conn.try_update(query, params) {
    Ok(updates) => println!("{} rows were updated", updates),
    Err(err) => match err.code {
        NotNullViolation => println!("Something was NULL that shouldn't be"),
        SyntaxError => println!("Invalid query syntax"),
        _ => println!("A bad thing happened: {}", err.message),
    }
}

Connection Pooling

A very basic fixed-size connection pool is provided in the pool module. A single pool can be shared across tasks and get_connection will block until a connection is available.

let pool = PostgresConnectionPool::new("postgres://postgres@localhost", 5);

for _ in range(0, 10) {
    do task::spawn_with(pool.clone()) |pool| {
        let conn = pool.get_connection();
        conn.query(...);
    }
}

Type Correspondence

Rust-Postgres enforces a strict correspondence between Rust types and Postgres types. The driver currently supports the following conversions:

Rust Type Postgres Type
bool BOOL
i8 "char"
i16 SMALLINT
i32 INT
i64 BIGINT
f32 FLOAT4
f64 FLOAT8
str VARCHAR, CHAR(n), TEXT
[u8] BYTEA
extra::json::Json JSON
extra::uuid::Uuid UUID
extra::time::Timespec TIMESTAMP, TIMESTAMP WITH TIME ZONE
types::range::Range<i32> INT4RANGE
types::range::Range<i64> INT8RANGE
types::range::Range<Timespec> TSRANGE, TSTZRANGE

More conversions can be defined by implementing the ToSql and FromSql traits.

Development

Rust-Postgres is still in the early stages of 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!