Expand description
pg-worm
PostgreSQL’s Worst ORM
pg-worm is a straightforward, fully typed, async ORM and Query Builder for PostgreSQL.
Well, at least that’s the goal.
This library is based on tokio_postgres
and is intended to be used with tokio.
Usage
Fortunately, using pg_worm is very easy.
Simply derive the Model trait for your type, connect to your database
and you are ready to go!
Here’s a quick example:
use pg_worm::prelude::*;
#[derive(Model)]
struct Book {
// An auto-generated primary key column
#[column(primary_key, auto)]
id: i64,
title: String,
author_id: i64
}
#[derive(Model)]
struct Author {
#[column(primary_key, auto)]
id: i64,
name: String
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// First create a connection. This can be only done once.
connect!("postgres://postgres:postgres@localhost:5432", NoTls).await?;
// Then, create tables for your models.
// Use `register!` if you want to fail if a
// table with the same name already exists.
//
// `force_register` drops the old table,
// which is useful for development.
//
// If your tables already exist, skip this part.
force_register!(Author, Book)?;
// Next, insert some data.
// This works by passing values for all
// fields which aren't autogenerated.
Author::insert("Stephen King").await?;
Author::insert("Martin Luther King").await?;
Author::insert("Karl Marx").await?;
Book::insert("Foo - Part I", 1).await?;
Book::insert("Foo - Part II", 2).await?;
Book::insert("Foo - Part III", 3).await?;
// Let's start with a simple query for all books:
let books = Book::select().await?; // Vec<Book>
assert_eq!(books.len(), 3);
// You can also search for a specific book.
// Adding a `WHERE` clause is as simple as
// calling a method on the respective field:
let book = Book::select_one()
.where_(Book::title.eq(&"Foo - Part I".to_string()))
.await?; // Option<Book>
assert!(book.is_some());
// Or update exsisting records:
let books_updated = Book::update()
.set(Book::title, &"Foo - Part III".to_string())
.where_(Book::title.eq(&"Foo - Part II".to_string()))
.await?; // u64
assert_eq!(books_updated, 1);
// Or delete a book, you don't like:
let books_deleted = Book::delete()
.where_(Book::title.eq(&"Foo - Part III".to_string()))
.await?; // u64
assert_eq!(books_deleted, 2);
Ok(())
}If you want to see more code examples, have a look at the tests directory.
Query Builders
As you can see in the above example, pg_worm allows you to build queries by chaining methods on so called ‘builders’.
For each query type pg_worm provides a respective builder (except for INSERT which is handled differently).
These builders expose a set of methods for building queries. Here’s a list of them:
| Method | Description | Availability |
|---|---|---|
where_ | Attach a WHERE clause to the query. | All builders (Select, Update, Delete) |
set | SET a column’s value. Note: this method has to be called at least once before you can execute the query. | Update |
limit, offset | Attach a LIMIT or OFFSET to the query. | Select |
Filtering using WHERE
where_() can be used to easily include WHERE clauses in your queries.
This is done by passing a [Where] object which can be constructed by calling methods on the respective column.
pg_worm automatically constructs a constant for each field
of your Model.
A practical example could look like this:
let where_: Where<'_> = MyModel::my_field.eq(&5);Available methods
Currently, the following methods are implemented:
| Function | Description | Availability |
|---|---|---|
eq | Checks for equality. | Any type |
gt, gte, lt, lte | Check whether this column’s value is greater than, etc than some other value. | Any type which implement PartialOrd. Note: It’s not guaranteed that Postgres supports these operator for a type just because it’s PartialOrd. Be sure to check the documentation beforehand. |
null, not_null | Checks whether a column is NULL. | Any Option<T>. All other types are not NULLable and thus guaranteed not to be NULL. |
contains, contains_not, contains_all, conatains_none, contains_any | Array operations. Check whether this column’s array contains a value, a value not, or any/all/none values of another array. | Any Vec<T>. |
Boolean logic
You can also chain/modify these filters with standard boolean logic:
| Operator/Method | Description |
|---|---|
!, not | Negate a filter using a locigal NOT |
&, and | Combine two filters using a logical AND. |
\|, or | Combine two filters using a logical OR. |
Executing a query
After having finished building your query, you can simply call .await.
This will turn the builder into a Query object which is then executed asynchronously.
A query will always return a Result.
Raw queries
Though these features are nice, they are not sufficient for most applications. This is why you can easily execute custom queries and still take advantage of automatic parsing, etc:
// NOTE: You have to pass the exact type that Postgres is
// expecting. Doing otherwise will result in a runtime error.
let king_books = Book::query(r#"
SELECT * FROM book
JOIN author ON author.id = book.author_id
WHERE POSITION(? in author.name) > 0
"#,
vec![&"King".to_string()]
).await?;
assert_eq!(king_books.len(), 2);License
This project is dual-licensed under the MIT and Apache 2.0 licenses.
Re-exports
pub use query::Column;pub use query::TypedColumn;pub use tokio_postgres as pg;
Modules
- This module contains all necessary imports to get you started easily.
- This module contains the logic for building queries, as well as struct for representing columns.
Macros
- Convenience macro for connecting the
pg-wormclient to a database server. Essentially writes the boilerplate code needed. See thetokio_postgresdocumentation for more information on the config format. - Like
register!but if a table with the same name already exists, it is dropped instead of returning an error. - Registers a
Modelwith the database by creating a corresponding table.
Structs
- A
MakeTlsConnectandTlsConnectimplementation which simply returns an error. - A row of data returned from the database by a query.
Enums
- An enum representing the errors which are emitted by this crate.
Traits
- A trait signaling that a struct may be parsed from a Postgres Row.
- This is the trait which you should derive for your model structs.
Functions
- Connect the
pg_wormclient to a postgres database. - Same as
register_modelbut if a table with the same name already exists, it is dropped instead of returning an error. - Register your model with the database. This creates a table representing your model.
Attribute Macros
Derive Macros
- Automatically implement
Modelfor your struct.