diff --git a/migrations/2024-05-09-173338_initial_db_setup/down.sql b/migrations/2024-05-09-173338_initial_db_setup/down.sql index 95b54ab..c6dcd9c 100644 --- a/migrations/2024-05-09-173338_initial_db_setup/down.sql +++ b/migrations/2024-05-09-173338_initial_db_setup/down.sql @@ -10,4 +10,6 @@ DROP TABLE images; DROP TABLE meal_images; DROP TYPE node_type; +DROP FUNCTION check_node_type(); +DROP FUNCTION check_linked_restaurants(); DROP EXTENSION postgis CASCADE; diff --git a/migrations/2024-05-09-173338_initial_db_setup/up.sql b/migrations/2024-05-09-173338_initial_db_setup/up.sql index 21a2b89..ea29ca5 100644 --- a/migrations/2024-05-09-173338_initial_db_setup/up.sql +++ b/migrations/2024-05-09-173338_initial_db_setup/up.sql @@ -4,17 +4,53 @@ CREATE EXTENSION postgis; CREATE TABLE nodes( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, - type NODE_TYPE NOT NULL, + node_type NODE_TYPE NOT NULL, coordinates GEOMETRY(POINT,4326) NOT NULL ); +CREATE OR REPLACE FUNCTION check_linked_restaurants() +RETURNS TRIGGER AS $$ +BEGIN + -- Check if the node is being updated from 'restaurant' to a different type + IF OLD.node_type = 'restaurant' AND NEW.node_type <> 'restaurant' THEN + -- Check if the node still has linked restaurants + IF EXISTS (SELECT 1 FROM restaurants WHERE node_id = OLD.id) THEN + RAISE EXCEPTION 'Cannot change node type from "restaurant" as it still has linked restaurants'; + END IF; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER check_linked_restaurants_trigger +BEFORE UPDATE OF node_type ON nodes +FOR EACH ROW +EXECUTE FUNCTION check_linked_restaurants(); + + CREATE TABLE restaurants( id SERIAL PRIMARY KEY, node_id INTEGER REFERENCES nodes(id) NOT NULL, address VARCHAR(30) NOT NULL, - price INTEGER CHECK ( price >= 0 AND price <= 10 ) NOT NULL + price SMALLINT CHECK ( price >= 0 AND price <= 10 ) NOT NULL ); +CREATE FUNCTION check_node_type() +RETURNS TRIGGER AS $$ +BEGIN + -- Check if the node referenced by the foreign key is of type 'restaurant' + IF (SELECT node_type FROM nodes WHERE id = NEW.node_id) <> 'restaurant' THEN + RAISE EXCEPTION 'The referenced node has not type "restaurant"'; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER enforce_node_type +BEFORE INSERT OR UPDATE ON restaurants +FOR EACH ROW +EXECUTE FUNCTION check_node_type(); + CREATE TABLE images( id SERIAL PRIMARY KEY, url VARCHAR(50) NOT NULL @@ -41,7 +77,7 @@ CREATE TABLE meals( restaurant_rating_id INTEGER REFERENCES restaurant_ratings(id) NOT NULL, user_id INTEGER REFERENCES users(id) NOT NULL, name VARCHAR(30) NOT NULL, - rating INTEGER CHECK ( rating >= 0 AND rating <= 10 ) NOT NULL, + rating SMALLINT CHECK ( rating >= 0 AND rating <= 10 ) NOT NULL, price DECIMAL(12,2) CHECK (price >= 0) NOT NULL, price_currency VARCHAR(1) CHECK (TRIM(price_currency) <> '') NOT NULL, note VARCHAR(100) NOT NULL DEFAULT '', diff --git a/patches/schema.patch b/patches/schema.patch index 838943e..4b2dda0 100644 --- a/patches/schema.patch +++ b/patches/schema.patch @@ -1,6 +1,21 @@ ---- src/full_schema.rs 2024-05-10 11:34:46.376360145 +0200 -+++ src/schema.rs 2024-05-10 11:34:24.009031704 +0200 -@@ -52,13 +52,12 @@ +--- src/full_schema.rs 2024-05-10 22:44:39.336282167 +0200 ++++ src/schema.rs 2024-05-10 22:45:22.991271745 +0200 +@@ -1,14 +1,10 @@ + // @generated automatically by Diesel CLI. + + pub mod sql_types { + #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] +- #[diesel(postgres_type(name = "geometry"))] +- pub struct Geometry; +- +- #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] + #[diesel(postgres_type(name = "node_type"))] + pub struct NodeType; + } + + diesel::table! { + use diesel::sql_types::*; +@@ -52,13 +48,12 @@ } diesel::table! { @@ -13,4 +28,4 @@ id -> Int4, #[max_length = 100] name -> Varchar, - #[sql_name = "type"] + node_type -> NodeType, diff --git a/src/main.rs b/src/main.rs index 2e43661..f5148e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,12 @@ extern crate diesel; mod db; mod models; +mod ops; mod schema; -use diesel::prelude::*; -fn main() {} +fn main() { + ops::node_ops::create_node(); + ops::restaurant_ops::create_restaurant(); + // ops::node_ops::update_node(); + ops::node_ops::show_nodes(); +} diff --git a/src/models.rs b/src/models.rs index 2efc642..d8ec79e 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,4 +1,4 @@ -use crate::schema::nodes; +use crate::schema::{nodes, restaurants}; use diesel::prelude::*; use postgis_diesel::types::Point; @@ -13,6 +13,30 @@ pub enum NodeType { #[diesel(table_name = nodes)] pub struct NewNode<'a> { pub name: &'a str, - pub type_: NodeType, + pub node_type: NodeType, pub coordinates: Point, } + +#[derive(Debug, Queryable, AsChangeset)] +pub struct Node { + pub id: i32, + pub name: String, + pub node_type: NodeType, + pub coordinates: Point, +} + +#[derive(Insertable)] +#[diesel(table_name = restaurants)] +pub struct NewRestaurant<'a> { + pub node_id: i32, + pub address: &'a str, + pub price: i16, +} + +#[derive(Debug, Queryable, AsChangeset)] +pub struct Restaurant { + pub id: i32, + pub node_id: i32, + pub address: String, + pub price: i16, +} diff --git a/src/ops.rs b/src/ops.rs new file mode 100644 index 0000000..b76f8b4 --- /dev/null +++ b/src/ops.rs @@ -0,0 +1,2 @@ +pub mod node_ops; +pub mod restaurant_ops; diff --git a/src/ops/node_ops.rs b/src/ops/node_ops.rs new file mode 100644 index 0000000..3b7e646 --- /dev/null +++ b/src/ops/node_ops.rs @@ -0,0 +1,55 @@ +use crate::db::establish_connection; +use crate::models::*; +use diesel::prelude::*; + +pub fn create_node() { + use crate::schema::nodes::dsl::*; + + let mut conn = establish_connection(); + + let new_node = NewNode { + name: "Foo", + node_type: NodeType::Restaurant, + coordinates: postgis_diesel::types::Point { + x: 0.0, + y: 0.0, + srid: Some(4326), + }, + }; + + diesel::insert_into(nodes) + .values(&new_node) + .execute(&mut conn) + .expect("Error saving new Node"); +} + +pub fn update_node() { + use crate::schema::nodes::dsl::*; + + let node_id = 1; + let mut conn = establish_connection(); + + let node = Node { + id: node_id, + name: "Bar".to_string(), + node_type: NodeType::Marker, + coordinates: postgis_diesel::types::Point { + x: 6.9, + y: 0.0, + srid: Some(4326), + }, + }; + + diesel::update(nodes.find(node_id)) + .set(&node) + .execute(&mut conn) + .expect("Error saving new Node"); +} + +pub fn show_nodes() { + use crate::schema::nodes::dsl::*; + + let mut conn = establish_connection(); + let results = nodes.load::(&mut conn).expect("Error loading Nodes"); + println!("{:?}", results); +} diff --git a/src/ops/restaurant_ops.rs b/src/ops/restaurant_ops.rs new file mode 100644 index 0000000..9e549ab --- /dev/null +++ b/src/ops/restaurant_ops.rs @@ -0,0 +1,51 @@ +use crate::db::establish_connection; +use crate::models::*; +use diesel::prelude::*; + +pub fn create_restaurant() { + use crate::schema::restaurants::dsl::*; + + let mut conn = establish_connection(); + + let new_node = NewRestaurant { + node_id: 1, + address: "Hauptstraße 8", + price: 1, + }; + + diesel::insert_into(restaurants) + .values(&new_node) + .execute(&mut conn) + .expect("Error saving new Node"); +} + +// pub fn update_node() { +// use crate::schema::nodes::dsl::*; +// +// let node_id = 1; +// let mut conn = establish_connection(); +// +// let node = Node { +// id: node_id, +// name: "Bar".to_string(), +// type_: NodeType::Restaurant, +// coordinates: postgis_diesel::types::Point { +// x: 6.9, +// y: 0.0, +// srid: Some(4326), +// }, +// }; +// +// diesel::update(nodes.find(node_id)) +// .set(&node) +// .execute(&mut conn) +// .expect("Error saving new Node"); +// } +// +// pub fn show_nodes() { +// use crate::schema::nodes::dsl::*; +// +// let mut conn = establish_connection(); +// let results = nodes.load::(&mut conn).expect("Error loading Nodes"); +// println!("{:?}", results); +// } diff --git a/src/schema.rs b/src/schema.rs index 75f8a68..66a3192 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,10 +1,6 @@ // @generated automatically by Diesel CLI. pub mod sql_types { - #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] - #[diesel(postgres_type(name = "geometry"))] - pub struct Geometry; - #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "node_type"))] pub struct NodeType; @@ -41,7 +37,7 @@ diesel::table! { user_id -> Int4, #[max_length = 30] name -> Varchar, - rating -> Int4, + rating -> Int2, price -> Numeric, #[max_length = 1] price_currency -> Varchar, @@ -60,8 +56,7 @@ diesel::table! { id -> Int4, #[max_length = 100] name -> Varchar, - #[sql_name = "type"] - type_ -> NodeType, + node_type -> NodeType, coordinates -> Geometry, } } @@ -88,7 +83,7 @@ diesel::table! { node_id -> Int4, #[max_length = 30] address -> Varchar, - price -> Int4, + price -> Int2, } }