Skip to content

Commit 6b595b9

Browse files
committed
Initial database schema
Diesel is used for schema migrations (written in raw SQL; targetting PostgreSQL at the moment) and conversions between SQL and Rust datatypes. Structs are defined, but not used yet.
1 parent f09b3d3 commit 6b595b9

File tree

13 files changed

+318
-14
lines changed

13 files changed

+318
-14
lines changed

.github/workflows/tests.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ jobs:
4848
toolchain: ${{ matrix.rust-version }}
4949
override: true
5050

51+
- name: Install libpq (Windows)
52+
if: matrix.os == 'windows-latest'
53+
shell: bash
54+
run: |
55+
choco install postgresql12 --force --params '/Password:root'
56+
echo '::add-path::C:\Program Files\PostgreSQL\12\bin'
57+
echo '::add-path::C:\Program Files\PostgreSQL\12\lib'
58+
5159
- uses: actions-rs/cargo@v1
5260
with:
5361
command: test

Cargo.lock

Lines changed: 119 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ categories = [
1616
publish = false
1717

1818
[dependencies]
19+
chrono = "0.4.13"
20+
diesel = { version = "1.4.5", features = ["postgres"] }
1921
rocket = "0.4.5"
2022
rocket_contrib = {version = "0.4.5", features = ["json"]}

diesel.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# For documentation on how to configure this file,
2+
# see diesel.rs/guides/configuring-diesel-cli
3+
4+
[print_schema]
5+
file = "src/storage/sql/schema.rs"
6+
7+
[migrations_directory]
8+
dir = "src/storage/sql/migrations"

src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
66
#![feature(proc_macro_hygiene, decl_macro)]
77

8+
#[macro_use]
9+
extern crate diesel;
810
#[macro_use]
911
extern crate rocket;
1012
#[macro_use]
1113
extern crate rocket_contrib;
1214

1315
mod application;
1416
mod routes;
17+
mod storage;
1518

1619
fn main() {
1720
let app = application::create_app();

src/storage/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Do not fail compilation until we've started using that code
2+
#[allow(dead_code)]
3+
pub mod sql;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- This file was automatically created by Diesel to setup helper functions
2+
-- and other internal bookkeeping. This file is safe to edit, any future
3+
-- changes will be added to existing projects as new migrations.
4+
5+
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
6+
DROP FUNCTION IF EXISTS diesel_set_updated_at();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
-- This file was automatically created by Diesel to setup helper functions
2+
-- and other internal bookkeeping. This file is safe to edit, any future
3+
-- changes will be added to existing projects as new migrations.
4+
5+
6+
7+
8+
-- Sets up a trigger for the given table to automatically set a column called
9+
-- `updated_at` whenever the row is modified (unless `updated_at` was included
10+
-- in the modified columns)
11+
--
12+
-- # Example
13+
--
14+
-- ```sql
15+
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
16+
--
17+
-- SELECT diesel_manage_updated_at('users');
18+
-- ```
19+
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
20+
BEGIN
21+
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
22+
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
23+
END;
24+
$$ LANGUAGE plpgsql;
25+
26+
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
27+
BEGIN
28+
IF (
29+
NEW IS DISTINCT FROM OLD AND
30+
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
31+
) THEN
32+
NEW.updated_at := current_timestamp;
33+
END IF;
34+
RETURN NEW;
35+
END;
36+
$$ LANGUAGE plpgsql;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DROP TABLE tags;
2+
DROP TABLE changesets;
3+
DROP TABLE snippets;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
CREATE TABLE snippets (
2+
-- an internal autoincrementing identifier used in foreign keys.
3+
-- Normally not visible publicly, except for the case when it is
4+
-- used for looking legacy snippets up by id
5+
id SERIAL PRIMARY KEY,
6+
-- a short unique snippet identifier visible to users
7+
slug TEXT NOT NULL,
8+
9+
title TEXT,
10+
syntax TEXT,
11+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
12+
updated_at TIMESTAMP WITH TIME ZONE,
13+
14+
-- slugs must be unique (this will also automatically create a unique index)
15+
CONSTRAINT uq_slug UNIQUE (slug)
16+
);
17+
18+
19+
-- will be used for pagination; slug guarantees uniqueness of the sorting key
20+
CREATE INDEX snippets_created_at_slug ON snippets (created_at, slug);
21+
22+
23+
CREATE TABLE changesets (
24+
id SERIAL PRIMARY KEY,
25+
snippet_id INTEGER NOT NULL,
26+
27+
-- numeric index used to determine the ordering of changesets for a given snippet
28+
version INTEGER DEFAULT 0 NOT NULL,
29+
content TEXT NOT NULL,
30+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
31+
updated_at TIMESTAMP WITH TIME ZONE,
32+
33+
-- there can be multiple changesets per snippet
34+
CONSTRAINT fk_snippet FOREIGN KEY (snippet_id) REFERENCES snippets(id),
35+
-- but each one is supposed to have a unique version number
36+
CONSTRAINT uq_version UNIQUE (snippet_id, version),
37+
-- sanity check: do not allow empty changesets
38+
CONSTRAINT check_not_empty CHECK (LENGTH(content) > 0),
39+
-- sanity check: version numbers are non-negative integers
40+
CONSTRAINT check_non_negative_version CHECK (version >= 0)
41+
);
42+
43+
44+
-- tags could have been associated with snippets as M:M via an auxiliary table,
45+
-- but Diesel only supports child-parent associations, so let's do that instead
46+
CREATE TABLE tags (
47+
id SERIAL PRIMARY KEY,
48+
snippet_id INTEGER NOT NULL,
49+
50+
value TEXT NOT NULL,
51+
52+
-- there can be multiple tags per snippet
53+
CONSTRAINT fk_snippet FOREIGN KEY (snippet_id) REFERENCES snippets(id),
54+
-- do not allow to abuse the tags for storing too much data
55+
CONSTRAINT check_length CHECK (LENGTH(value) < 128),
56+
-- do not allow repeated tags per snippet
57+
CONSTRAINT uq_snippet_tag UNIQUE (snippet_id, value)
58+
);

src/storage/sql/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mod models;
2+
mod schema;
3+
4+
pub use models::{Changeset, Snippet, Tag};

0 commit comments

Comments
 (0)