new data format with more information

This commit is contained in:
Khaïs COLIN 2025-10-24 23:26:26 +02:00
parent 18de2714d0
commit 07bfc65b67
Signed by: logistic-bot
SSH key fingerprint: SHA256:RlpiqKeXpcPFZZ4y9Ou4xi2M8OhRJovIwDlbCaMsuAo
8 changed files with 109 additions and 2 deletions

BIN
foods.db

Binary file not shown.

View file

@ -8,3 +8,5 @@ SELECT
color
FROM
food
ORDER BY
sort_order, name;

1
src/get_version.sql Normal file
View file

@ -0,0 +1 @@
PRAGMA user_version;

View file

@ -9,7 +9,7 @@ use axum::{
routing::{get, post},
};
use parking_lot::Mutex;
use rusqlite::{CachedStatement, Connection, Row};
use rusqlite::{CachedStatement, Connection, Row, Transaction};
use tower_http::trace::TraceLayer;
use tower_request_id::{RequestId, RequestIdLayer};
use tracing::{debug, error, info, info_span};
@ -123,6 +123,50 @@ impl<'conn> PreparedStatements {
type ConnState = Arc<Mutex<Connection>>;
fn get_version(tx: &Transaction) -> rusqlite::Result<usize> {
tx.query_one(include_str!("get_version.sql"), (), |row| row.get(0))
}
fn get_migrations() -> [&'static str; 4] {
[
include_str!("migrations/1.sql"),
include_str!("migrations/2.sql"),
include_str!("migrations/3.sql"),
include_str!("migrations/4.sql"),
]
}
fn do_migrations(conn: &mut Connection) -> rusqlite::Result<()> {
let migrations = get_migrations();
let num_migrations = migrations.len();
let tx = conn.transaction()?;
let version = get_version(&tx)?;
if version < migrations.len() {
info!(
migrations_to_apply = num_migrations - version,
"need to apply some migrations"
);
let mut mig_number = version;
for migration in migrations.iter().skip(version) {
mig_number += 1;
info!(mig_number, "applying migration");
debug!(migration = migration);
tx.execute_batch(migration)?;
if get_version(&tx)? != mig_number {
panic!(
"expected user_version to eq {} after applying migration {}. maybe a missing 'PRAGMA user_version =' ?",
mig_number, mig_number
);
}
}
tx.commit()?;
info!("applied all migrations");
} else {
info!("no migrations to apply");
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
tracing_subscriber::registry()
@ -142,13 +186,15 @@ async fn main() -> Result<(), std::io::Error> {
let db_connecion_str = "./foods.db".to_string();
debug!(db_connecion_str, "opening database");
let conn = Connection::open(db_connecion_str).expect("failed to open database");
let mut conn = Connection::open(db_connecion_str).expect("failed to open database");
if let Err(e) = conn.execute(include_str!("create_tables.sql"), ()) {
error!(?e, "failed to create tables");
panic!("failed to create tables: {:#?}", e);
}
do_migrations(&mut conn).expect("failed to do database migrations");
PreparedStatements::check(&conn).expect("failed to prepare sql statements");
let app = Router::new()

6
src/migrations/1.sql Normal file
View file

@ -0,0 +1,6 @@
ALTER TABLE
food
ADD COLUMN
portion_weight INTEGER NOT NULL DEFAULT 100 CHECK (portion_weight > 0);
PRAGMA user_version = 1;

23
src/migrations/2.sql Normal file
View file

@ -0,0 +1,23 @@
ALTER TABLE
food
ADD COLUMN
-- per 100g
protein REAL NOT NULL DEFAULT 5.0 CHECK (protein > 0);
ALTER TABLE
food
ADD COLUMN
-- per 100g
fiber REAL NOT NULL DEFAULT 5.0 CHECK (fiber > 0);
ALTER TABLE
food
ADD COLUMN
protein_per_portion REAL NOT NULL GENERATED ALWAYS AS ((protein / 100) * portion_weight) VIRTUAL;
ALTER TABLE
food
ADD COLUMN
fiber_per_portion REAL NOT NULL GENERATED ALWAYS AS ((fiber / 100) * portion_weight) VIRTUAL;
PRAGMA user_version = 2;

6
src/migrations/3.sql Normal file
View file

@ -0,0 +1,6 @@
ALTER TABLE
food
ADD COLUMN
sort_order INTEGER NOT NULL DEFAULT 0;
PRAGMA user_version = 3;

23
src/migrations/4.sql Normal file
View file

@ -0,0 +1,23 @@
CREATE TABLE "sqlb_temp_table_1" (
"id" INTEGER,
"portion" TEXT NOT NULL,
"name" TEXT NOT NULL,
"kc_per_serving" INTEGER NOT NULL DEFAULT 0,
"target_servings" INTEGER NOT NULL DEFAULT 1,
"actual_servings" INTEGER NOT NULL DEFAULT 0,
"color" TEXT NOT NULL DEFAULT 'white',
"portion_weight" INTEGER NOT NULL DEFAULT 100 CHECK("portion_weight" > 0),
"protein" REAL NOT NULL DEFAULT 5.0 CHECK("protein" >= 0),
"fiber" REAL NOT NULL DEFAULT 5.0 CHECK("fiber" >= 0),
"protein_per_portion" REAL NOT NULL GENERATED ALWAYS AS (("protein" / 100) * "portion_weight") VIRTUAL,
"fiber_per_portion" REAL NOT NULL GENERATED ALWAYS AS (("fiber" / 100) * "portion_weight") VIRTUAL,
"sort_order" INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY("id")
) STRICT;
INSERT INTO "main"."sqlb_temp_table_1" ("actual_servings","color","fiber","id","kc_per_serving","name","portion","portion_weight","protein","sort_order","target_servings") SELECT "actual_servings","color","fiber","id","kc_per_serving","name","portion","portion_weight","protein","sort_order","target_servings" FROM "main"."food";
PRAGMA defer_foreign_keys = '1';
DROP TABLE "main"."food";
ALTER TABLE "main"."sqlb_temp_table_1" RENAME TO "food";
PRAGMA defer_foreign_keys = '0';
PRAGMA user_version = 4;