Skip to content

Commit

Permalink
Finish Up migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
Einliterflasche committed Oct 3, 2023
1 parent dfa3abd commit 24b4090
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 44 deletions.
71 changes: 71 additions & 0 deletions pg-worm/examples/simple_query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use futures_util::Future;
use pg_worm::{prelude::*, query::Prepared};
use tokio::time::Instant;

#[allow(dead_code)]
#[derive(Model, Debug)]
struct Book {
#[column(primary_key, auto)]
id: i64,
title: String
}

pub trait Average {
fn avg(&self) -> f64;
}

impl Average for Vec<u128> {
fn avg(&self) -> f64 {
(self.iter().sum::<u128>() as f64) / (self.len() as f64)
}
}

async fn test_n_times_nanos<T>(n: usize, f: impl Fn(i64) -> T) -> Vec<u128>
where
T: Future
{
let mut v = Vec::with_capacity(n);

for i in 0..n {
let f = f(i.try_into().unwrap());
let before = Instant::now();
f.await;
let after = Instant::now();

v.push(after.duration_since(before).as_nanos())
}

v
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Connection::build("postgres://postgres:postgres@localhost:5432")
.connect()
.await?;

force_create_table!(Book).await?;

Book::insert("Foo - Part I").await?;
Book::insert("Foo - Part II").await?;
Book::insert("Foo - Part III").await?;

const N: usize = 1_000;
let normal = test_n_times_nanos(N, |n| async move {
Book::update()
.set(Book::title, &format!("Foo {n}"))
.where_(Book::id.lt(&n))
.await.expect("err in book select");
}).await;
let prepared = test_n_times_nanos(N, |n| async move {
Book::update()
.set(Book::title, &format!("Foo {n}"))
.where_(Book::id.lt(&n))
.prepared()
.await.expect("err in book select");
}).await;

println!("normal avg: {}µs\nprepared avg: {}µs", normal.avg() / 1_000f64, prepared.avg() / 1_000f64);

Ok(())
}
177 changes: 133 additions & 44 deletions pg-worm/src/migration/mod.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,52 @@
//! This module contains the logic needed to create automatic migrations.
#[derive(Debug, PartialEq, Eq, Hash)]
use std::fmt::Display;

#[derive(Debug, Clone)]
struct Table {
name: &'static str,
name: String,
columns: Vec<Column>,
primary_key: Vec<&'static str>,
constraints: Vec<TableConstraint>
}

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Debug, Clone)]
struct Column {
table: &'static str,
name: &'static str,
data_type: &'static str,
name: String,
data_type: String,
constraints: Vec<ColumnConstraint>,
}

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Debug, Clone)]
enum ColumnConstraint {
Unique,
UniqueNamed(&'static str),
UniqueNamed(String),
NotNull,
PrimaryKey,
ForeignKey(&'static str, &'static str),
ForeignKeyNamed(&'static str, &'static str, &'static str),
RawCheck(&'static str),
ForeignKey(String, String),
ForeignKeyNamed(String, String, String),
RawCheck(String),
RawCheckNamed(String, String)
}

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Debug, Clone)]
enum TableConstraint {
PrimaryKey(Vec<&'static str>),
ForeignKey(Vec<(&'static str, &'static str, &'static str)>),
Unique(Vec<&'static str>),
RawCheck(&'static str),
PrimaryKey(Vec<String>),
ForeignKey(String, Vec<(String, String)>),
ForeignKeyNamed(String, String, Vec<(String, String)>),
Unique(Vec<String>),
UniqueNamed(String, Vec<String>),
RawCheck(String),
RawCheckNamed(String, String)
}

impl Table {
fn new(name: &'static str) -> Self {
fn new(name: impl Into<String>) -> Self {
let columns: Vec<Column> = Vec::new();
let primary_key = Vec::new();

Table {
name,
name: name.into(),
columns,
primary_key,
constraints: Vec::new()
}
}

Expand All @@ -54,11 +58,10 @@ impl Table {
}

impl Column {
fn new(table: &'static str, name: &'static str, data_type: &'static str) -> Self {
fn new(name: impl Into<String>, data_type: impl Into<String>) -> Self {
Column {
table,
name,
data_type,
name: name.into(),
data_type: data_type.into(),
constraints: Vec::new(),
}
}
Expand All @@ -75,47 +78,133 @@ impl Column {
self
}

fn unique_named(mut self, name: &'static str) -> Self {
fn unique_named(mut self, name: String) -> Self {
self.constraints.push(ColumnConstraint::UniqueNamed(name));

self
}

fn primary_key(mut self, name: &'static str) -> Self {
fn primary_key(mut self) -> Self {
self.constraints.push(ColumnConstraint::PrimaryKey);

self
}

fn foreign_key(mut self, table_name: &'static str, column_name: &'static str) -> Self {
fn foreign_key(mut self, table_name: String, column_name: String) -> Self {
self.constraints
.push(ColumnConstraint::ForeignKey(table_name, column_name));

self
}
}

impl ToString for ColumnConstraint {
fn to_string(&self) -> String {
trait Up {
fn up(&self) -> String;
}

impl Up for Table {
fn up(&self) -> String {
let mut up = format!(
"CREATE TABLE {} ({})",
self.name,
self.columns.iter().map(|i| i.up())._join(", ")
);

if !self.constraints.is_empty() {
up.push_str(&format!(
", {}",
self.constraints.iter().map(|i| i.up())._join(", ")
));
}

up
}
}

impl Up for Column {
fn up(&self) -> String {
let mut up = format!("{} {}", self.name, self.data_type);

if !self.constraints.is_empty() {
up.push(' ');
up.push_str(&self.constraints
.iter()
.map(|i| i.up())
.collect::<Vec<String>>()
._join(" "));
}

up
}
}

impl Up for TableConstraint {
fn up(&self) -> String {
use TableConstraint as C;

match self {
C::PrimaryKey(cols) => format!("PRIMARY KEY ({})", cols._join(", ")),
C::ForeignKey(table, cols) => format!(
"FOREIGN KEY ({}) REFERENCES {table} ({})",
cols.iter().map(|i| &i.0)._join(", "),
cols.iter().map(|i| &i.1)._join(", ")
),
C::ForeignKeyNamed(name, table, cols) => format!(
"CONSTRAINT {name} FOREIGN KEY ({}) REFERENCES {table} ({})",
cols.iter().map(|i| &i.0)._join(", "),
cols.iter().map(|i| &i.1)._join(", ")
),
C::Unique(cols) => format!("UNIQUE ({})", cols.join(", ")),
C::UniqueNamed(name, cols) => format!("CONSTRAINT {name} UNIQUE ({})", cols.join(", ")),
C::RawCheck(check) => format!("CHECK ({check})"),
C::RawCheckNamed(name, check) => format!("CONSTRAINT {name} CHECK ({check})"),
}
}
}

impl Up for ColumnConstraint {
fn up(&self) -> String {
use ColumnConstraint as C;

match self {
C::Unique => "UNIQUE".into(),
C::UniqueNamed(name) => format!("CONSTRAINT {} UNIQUE", name),
C::NotNull => "NOT NULL".into(),
C::PrimaryKey => "PRIMARY KEY".into(),
C::ForeignKey(table, column) => format!("REFERENCES {} ({})", table, column),
C::ForeignKeyNamed(name, table, column) => {
format!("CONSTRAINT {} REFERENCES {} ({})", name, table, column)
}
C::RawCheck(raw) => (*raw).into(),
C::NotNull => format!("NOT NULL"),
C::PrimaryKey => format!("PRIMARY KEY"),
C::ForeignKeyNamed(name, table, col) => format!("CONSTRAINT {name} REFERENCES {table} ({col}"),
C::ForeignKey(table, col) => format!("REFERENCES {table} ({col})"),
C::Unique => format!("UNIQUE"),
C::UniqueNamed(name) => format!("CONSTRAINT {name} UNIQUE"),
C::RawCheck(check) => format!("CHECK ({check})"),
C::RawCheckNamed(name, check) => format!("CONSTRAINT {name} CHECK ({check})")
}
}
}

fn foo() {
let table_name = "books";
let table = Table::new(table_name)
.column(Column::new(table_name, "id", "BIGINT"))
.column(Column::new(table_name, "title", "TEXT"));
trait Join {
fn _join(self, _: &str) -> String;
}

impl<T, U> Join for T
where
T: IntoIterator<Item = U>,
U: Display
{
fn _join(self, sep: &str) -> String {
(*self.into_iter().map(|i| i.to_string()).collect::<Vec<String>>()).join(sep)
}
}

#[cfg(test)]
mod tests {
use crate::migration::Up;

use super::{Table, Column};

#[test]
fn migrate() {
let dest = Table::new("book")
.column(Column::new("id", "BIGINT").primary_key().not_null())
.column(Column::new("title", "TEXT").unique().not_null());

dbg!(dest.up());
}
}

0 comments on commit 24b4090

Please sign in to comment.