From 4bad891e3949633213d3d658d90a867942268fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 27 Jul 2026 15:26:29 +0200 Subject: [PATCH] fix in mysql query --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/queries.rs | 43 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44187e3..ace8e98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "rustpad" -version = "0.1.1" +version = "0.1.3" dependencies = [ "argon2", "aws-config", diff --git a/Cargo.toml b/Cargo.toml index ef20dd7..4345496 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpad" -version = "0.1.2" +version = "0.1.3" edition = "2024" rust-version = "1.94" description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL" diff --git a/src/queries.rs b/src/queries.rs index ad64f84..5f5a38b 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -111,7 +111,10 @@ pub const SHARE_LINK_REVOKE: &str = "UPDATE resource_share_links SET revoked_at pub const RESOURCE_PERMISSION_BY_USER: &str = "SELECT permission FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"; pub const SHARE_LINK_PERMISSION: &str = "SELECT permission FROM resource_share_links WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > ?)"; -pub const Q001: &str = "SELECT id, slug, title, password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS BIGINT) AS is_private FROM workspaces WHERE slug = ?"; +// SQLite uses INTEGER as the portable 64-bit integer cast target. +pub const Q001: &str = "SELECT id, slug, title, password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS INTEGER) AS is_private FROM workspaces WHERE slug = ?"; +pub const Q001_MYSQL: &str = "SELECT id, slug, title, password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS SIGNED) AS is_private FROM workspaces WHERE slug = ?"; +pub const Q001_POSTGRES: &str = "SELECT id, slug, title, password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS BIGINT) AS is_private FROM workspaces WHERE slug = ?"; pub const Q002: &str = "INSERT INTO workspaces (slug, title, password_hash) VALUES (?, ?, ?)"; pub const Q003: &str = "SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, protected, created_by FROM notes WHERE workspace_id = ? ORDER BY updated_at DESC, id DESC"; pub const Q004: &str = "SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, protected, created_by FROM notes WHERE workspace_id = ? AND slug = ?"; @@ -124,7 +127,10 @@ pub const Q008: &str = "INSERT INTO note_revisions (note_id, content, author, owner_map) VALUES (?, ?, ?, ?)"; pub const Q009: &str = "SELECT updated_at FROM notes WHERE id = ?"; pub const Q010: &str = "SELECT id, content, created_at, author, owner_map FROM note_revisions WHERE note_id = ? ORDER BY id DESC LIMIT 100"; -pub const Q011: &str = "SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS BIGINT) AS is_private FROM pads WHERE slug = ?"; +// Keep privacy flags normalized to i64 across all SQLx Any drivers. +pub const Q011: &str = "SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS INTEGER) AS is_private FROM pads WHERE slug = ?"; +pub const Q011_MYSQL: &str = "SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS SIGNED) AS is_private FROM pads WHERE slug = ?"; +pub const Q011_POSTGRES: &str = "SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS BIGINT) AS is_private FROM pads WHERE slug = ?"; pub const Q012: &str = "INSERT INTO pads (slug, title, password_hash) VALUES (?, ?, ?)"; pub const Q013: &str = "UPDATE pads SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"; @@ -163,6 +169,14 @@ pub const Q047: &str = "DELETE FROM pad_files WHERE id = ? AND pad_id = ?"; static POSTGRES_QUERIES: OnceLock>> = OnceLock::new(); pub fn get(kind: DatabaseKind, query: &'static str) -> &'static str { + let query = match (kind, query) { + (DatabaseKind::MySql, Q001) => Q001_MYSQL, + (DatabaseKind::Postgres, Q001) => Q001_POSTGRES, + (DatabaseKind::MySql, Q011) => Q011_MYSQL, + (DatabaseKind::Postgres, Q011) => Q011_POSTGRES, + _ => query, + }; + if kind != DatabaseKind::Postgres { return query; } @@ -202,3 +216,28 @@ pub const Q045: &str = pub const Q021_POSTGRES: &str = "SELECT pp.token, pp.pad_id, pp.note_id, pp.allow_task_updates, COALESCE(p.title, n.title) AS title, COALESCE(p.content, n.content) AS content, COALESCE(p.updated_at, n.updated_at) AS updated_at FROM published_pages pp LEFT JOIN pads p ON p.id = pp.pad_id LEFT JOIN notes n ON n.id = pp.note_id WHERE pp.token = $1"; pub const Q044_POSTGRES: &str = "SELECT allow_task_updates FROM published_pages WHERE pad_id = $1"; pub const Q045_POSTGRES: &str = "SELECT allow_task_updates FROM published_pages WHERE note_id = $1"; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn privacy_cast_matches_database_dialect() { + assert!(get(DatabaseKind::Sqlite, Q001).contains(" AS INTEGER)")); + assert!(get(DatabaseKind::MySql, Q001).contains(" AS SIGNED)")); + + let postgres = get(DatabaseKind::Postgres, Q001); + assert!(postgres.contains(" AS BIGINT)")); + assert!(postgres.ends_with("slug = $1")); + } + + #[test] + fn pad_privacy_cast_matches_database_dialect() { + assert!(get(DatabaseKind::Sqlite, Q011).contains(" AS INTEGER)")); + assert!(get(DatabaseKind::MySql, Q011).contains(" AS SIGNED)")); + + let postgres = get(DatabaseKind::Postgres, Q011); + assert!(postgres.contains(" AS BIGINT)")); + assert!(postgres.ends_with("slug = $1")); + } +}