Expand description
pg-worm
PostgreSQL’s Worst ORM
pg-worm is an opiniated, straightforward, async ORM for PostgreSQL servers.
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 this library is very easy.
Just 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::{connect, force_register, Filter, JoinType, Model, NoTls, Query, QueryBuilder};
use tokio::try_join;
// First easily define your models.
#[derive(Model)]
struct Book {
// `id` will be the primary key column and
// automatically generated/incremented
#[column(primary_key, auto)]
id: i64,
#[column(unique)]
title: String,
author_id: i64,
}
#[derive(Model)]
struct Author {
#[column(primary_key, auto)]
id: i64,
name: String,
}
#[tokio::main]
async fn main() -> Result<(), pg_worm::Error> {
// First connect to your server. This can be only done once.
connect!("postgres://me:me@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.
try_join!(
Author::insert("Stephen King"),
Author::insert("Martin Luther King"),
Author::insert("Karl Marx"),
Book::insert("Foo - Part I", 1),
Book::insert("Foo - Part II", 2),
Book::insert("Foo - Part III", 3)
)?;
// Do a simple query for all books
let books: Vec<Book> = Book::select(Filter::all()).await;
assert_eq!(books.len(), 3);
// Or search for a specific book
let book = Book::select_one(Book::title.eq("Foo - Part II")).await;
assert!(book.is_some());
// Or make more complex queries using the query builder
let king_books: Vec<Book> = Query::select(Book::COLUMNS)
.filter(Author::name.like("%King%"))
.join(&Book::author_id, &Author::id, JoinType::Inner)
.build()
.exec()
.await?;
assert_eq!(king_books.len(), 2);
// Or delete a book, you don't like
Book::delete(Book::title.eq("Foo - Part II")).await;
// Graceful shutdown
Ok(())
}Filters
Filters can be used to easily include WHERE clauses in your queries.
They can be constructed by calling functions of the respective column.
pg_worm automatically constructs a TypedColumn constant for each field
of your Model.
A practical example would look like this:
MyModel::select(MyModel::my_field.eq(5))Currently the following filter functions are supported:
Filter::all()- doesn’t check anythingeq(T)- checks whether the column value is equal to somethingone_of(Vec<T>)- checks whether the vector contains the column value.
You can also do filter logic using !, & and |: MyModel::my_field.eq(5) & !MyModel::other_field.eq("Foo").
This works as you expect logical OR and AND to work.
Please notice that, at this point, custom priorization via parantheses
is not possible.
Query Builder
Simply attaching a Filter to your query often does not suffice.
For this reason, pg-worm provides a QueryBuilder interface for
constructing more complex queries.
Start building your query by calling Query::select() and passing
the columns you want to select.
Normally you want to query all columns of a Model which you can do by passing
YourModel::columns().
You can modify your query using the following methods:
.filter()- add aWHEREclause.join()- add aJOINfor querying accross tables/models.limit()- add aLIMITto how many rows are returned
After you have configured your query, build it using the .build() method.
Then, execute it by calling .exec::<M>(), where M is the Model which
should be parsed from the query result. It may be inferred.
Opiniatedness
As mentioned before, pg_worm is opiniated in a number of ways.
These include:
-
panics. For the sake of conveniencepg_wormonly returns aResultwhen inserting data, since in that case Postgres might reject the data because of some constraint.This means that should something go wrong, like:
- the connection to the database collapsed,
pg_wormis unable to parse Postgres’ response,- …
the program will panic.
-
ease of use. The goal of
pg_wormis not to become an enterprise solution. If adding an option means infringing the ease of use then it will likely not be added.
Re-exports
pub use tokio_postgres as pg;pub use query::*;
Modules
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
Traits
- This is the trait which you should derive for your model structs.
Functions
- Get a reference to the client, if a connection has been made. Returns
Err(Error::NotConnected)otherwise. - 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.