max / makenotwork
- Co-Authored-By
- Claude Opus 4.8 <noreply@anthropic.com>
114 files changed,
+3497 insertions,
-239 deletions
| @@ -57,3 +57,7 @@ | |||
| 57 | 57 | sando/daemon/work/ | |
| 58 | 58 | sando/daemon/releases/ | |
| 59 | 59 | sando/daemon/cargo-target/ | |
| 60 | + | ||
| 61 | + | # mt-db uses sqlx compile-time macros; its offline query cache must be committed | |
| 62 | + | # so build hosts (and the Sando gate) compile without a live DB. | |
| 63 | + | !multithreaded/.sqlx/ |
| @@ -13,17 +13,16 @@ | |||
| 13 | 13 | slug: &str, | |
| 14 | 14 | description: Option<&str>, | |
| 15 | 15 | ) -> Result<Uuid, sqlx::Error> { | |
| 16 | - | let row: (Uuid,) = sqlx::query_as( | |
| 16 | + | sqlx::query_scalar!( | |
| 17 | 17 | "INSERT INTO communities (name, slug, description) | |
| 18 | 18 | VALUES ($1, $2, $3) | |
| 19 | 19 | RETURNING id", | |
| 20 | + | name, | |
| 21 | + | slug, | |
| 22 | + | description, | |
| 20 | 23 | ) | |
| 21 | - | .bind(name) | |
| 22 | - | .bind(slug) | |
| 23 | - | .bind(description) | |
| 24 | 24 | .fetch_one(pool) | |
| 25 | - | .await?; | |
| 26 | - | Ok(row.0) | |
| 25 | + | .await | |
| 27 | 26 | } | |
| 28 | 27 | ||
| 29 | 28 | /// Upsert a user from MNW account data. Creates the user if they don't exist, | |
| @@ -35,15 +34,15 @@ | |||
| 35 | 34 | username: &str, | |
| 36 | 35 | display_name: Option<&str>, | |
| 37 | 36 | ) -> Result<(), sqlx::Error> { | |
| 38 | - | sqlx::query( | |
| 37 | + | sqlx::query!( | |
| 39 | 38 | "INSERT INTO users (mnw_account_id, username, display_name) | |
| 40 | 39 | VALUES ($1, $2, $3) | |
| 41 | 40 | ON CONFLICT (mnw_account_id) DO UPDATE | |
| 42 | 41 | SET username = $2, display_name = $3, updated_at = now()", | |
| 42 | + | mnw_account_id, | |
| 43 | + | username, | |
| 44 | + | display_name, | |
| 43 | 45 | ) | |
| 44 | - | .bind(mnw_account_id) | |
| 45 | - | .bind(username) | |
| 46 | - | .bind(display_name) | |
| 47 | 46 | .execute(pool) | |
| 48 | 47 | .await?; | |
| 49 | 48 | Ok(()) | |
| @@ -58,14 +57,14 @@ | |||
| 58 | 57 | community_id: Uuid, | |
| 59 | 58 | role: &str, | |
| 60 | 59 | ) -> Result<(), sqlx::Error> { | |
| 61 | - | sqlx::query( | |
| 60 | + | sqlx::query!( | |
| 62 | 61 | "INSERT INTO memberships (user_id, community_id, role) | |
| 63 | 62 | VALUES ($1, $2, $3) | |
| 64 | 63 | ON CONFLICT (user_id, community_id) DO NOTHING", | |
| 64 | + | user_id, | |
| 65 | + | community_id, | |
| 66 | + | role, | |
| 65 | 67 | ) | |
| 66 | - | .bind(user_id) | |
| 67 | - | .bind(community_id) | |
| 68 | - | .bind(role) | |
| 69 | 68 | .execute(pool) | |
| 70 | 69 | .await?; | |
| 71 | 70 | Ok(()) | |
| @@ -89,32 +88,32 @@ | |||
| 89 | 88 | ) -> Result<(Uuid, Uuid), sqlx::Error> { | |
| 90 | 89 | let mut tx = pool.begin().await?; | |
| 91 | 90 | ||
| 92 | - | let thread: (Uuid,) = sqlx::query_as( | |
| 91 | + | let thread_id = sqlx::query_scalar!( | |
| 93 | 92 | "INSERT INTO threads (category_id, author_id, title, external_ref) | |
| 94 | 93 | VALUES ($1, $2, $3, $4) | |
| 95 | 94 | RETURNING id", | |
| 95 | + | category_id, | |
| 96 | + | author_id, | |
| 97 | + | title, | |
| 98 | + | external_ref, | |
| 96 | 99 | ) | |
| 97 | - | .bind(category_id) | |
| 98 | - | .bind(author_id) | |
| 99 | - | .bind(title) | |
| 100 | - | .bind(external_ref) | |
| 101 | 100 | .fetch_one(&mut *tx) | |
| 102 | 101 | .await?; | |
| 103 | 102 | ||
| 104 | - | let post: (Uuid,) = sqlx::query_as( | |
| 103 | + | let post_id = sqlx::query_scalar!( | |
| 105 | 104 | "INSERT INTO posts (thread_id, author_id, body_markdown, body_html) | |
| 106 | 105 | VALUES ($1, $2, $3, $4) | |
| 107 | 106 | RETURNING id", | |
| 107 | + | thread_id, | |
| 108 | + | author_id, | |
| 109 | + | body_markdown, | |
| 110 | + | body_html, | |
| 108 | 111 | ) | |
| 109 | - | .bind(thread.0) | |
| 110 | - | .bind(author_id) | |
| 111 | - | .bind(body_markdown) | |
| 112 | - | .bind(body_html) | |
| 113 | 112 | .fetch_one(&mut *tx) | |
| 114 | 113 | .await?; | |
| 115 | 114 | ||
| 116 | 115 | tx.commit().await?; | |
| 117 | - | Ok((thread.0, post.0)) | |
| 116 | + | Ok((thread_id, post_id)) | |
| 118 | 117 | } | |
| 119 | 118 | ||
| 120 | 119 | /// Ensure a user has a membership in a community. Creates a 'member' role if none exists. | |
| @@ -124,13 +123,13 @@ | |||
| 124 | 123 | user_id: Uuid, | |
| 125 | 124 | community_id: Uuid, | |
| 126 | 125 | ) -> Result<(), sqlx::Error> { | |
| 127 | - | sqlx::query( | |
| 126 | + | sqlx::query!( | |
| 128 | 127 | "INSERT INTO memberships (user_id, community_id, role) | |
| 129 | 128 | VALUES ($1, $2, 'member') | |
| 130 | 129 | ON CONFLICT (user_id, community_id) DO NOTHING", | |
| 130 | + | user_id, | |
| 131 | + | community_id, | |
| 131 | 132 | ) | |
| 132 | - | .bind(user_id) | |
| 133 | - | .bind(community_id) | |
| 134 | 133 | .execute(pool) | |
| 135 | 134 | .await?; | |
| 136 | 135 | Ok(()) | |
| @@ -149,17 +148,16 @@ | |||
| 149 | 148 | author_id: Uuid, | |
| 150 | 149 | title: &str, | |
| 151 | 150 | ) -> Result<Uuid, sqlx::Error> { | |
| 152 | - | let row: (Uuid,) = sqlx::query_as( | |
| 151 | + | sqlx::query_scalar!( | |
| 153 | 152 | "INSERT INTO threads (category_id, author_id, title) | |
| 154 | 153 | VALUES ($1, $2, $3) | |
| 155 | 154 | RETURNING id", | |
| 155 | + | category_id, | |
| 156 | + | author_id, | |
| 157 | + | title, | |
| 156 | 158 | ) | |
| 157 | - | .bind(category_id) | |
| 158 | - | .bind(author_id) | |
| 159 | - | .bind(title) | |
| 160 | 159 | .fetch_one(pool) | |
| 161 | - | .await?; | |
| 162 | - | Ok(row.0) | |
| 160 | + | .await | |
| 163 | 161 | } | |
| 164 | 162 | ||
| 165 | 163 | /// Atomically create a thread and its opening post in one transaction. | |
| @@ -178,31 +176,31 @@ | |||
| 178 | 176 | ) -> Result<(Uuid, Uuid), sqlx::Error> { | |
| 179 | 177 | let mut tx = pool.begin().await?; | |
| 180 | 178 | ||
| 181 | - | let thread: (Uuid,) = sqlx::query_as( | |
| 179 | + | let thread_id = sqlx::query_scalar!( | |
| 182 | 180 | "INSERT INTO threads (category_id, author_id, title) | |
| 183 | 181 | VALUES ($1, $2, $3) | |
| 184 | 182 | RETURNING id", | |
| 183 | + | category_id, | |
| 184 | + | author_id, | |
| 185 | + | title, | |
| 185 | 186 | ) | |
| 186 | - | .bind(category_id) | |
| 187 | - | .bind(author_id) | |
| 188 | - | .bind(title) | |
| 189 | 187 | .fetch_one(&mut *tx) | |
| 190 | 188 | .await?; | |
| 191 | 189 | ||
| 192 | - | let post: (Uuid,) = sqlx::query_as( | |
| 190 | + | let post_id = sqlx::query_scalar!( | |
| 193 | 191 | "INSERT INTO posts (thread_id, author_id, body_markdown, body_html) | |
| 194 | 192 | VALUES ($1, $2, $3, $4) | |
| 195 | 193 | RETURNING id", | |
| 194 | + | thread_id, | |
| 195 | + | author_id, | |
| 196 | + | body_markdown, | |
| 197 | + | body_html, | |
| 196 | 198 | ) | |
| 197 | - | .bind(thread.0) | |
| 198 | - | .bind(author_id) | |
| 199 | - | .bind(body_markdown) | |
| 200 | - | .bind(body_html) | |
| 201 | 199 | .fetch_one(&mut *tx) | |
| 202 | 200 | .await?; | |
| 203 | 201 | ||
| 204 | 202 | tx.commit().await?; | |
| 205 | - | Ok((thread.0, post.0)) | |
| 203 | + | Ok((thread_id, post_id)) | |
| 206 | 204 | } | |
| 207 | 205 | ||
| 208 | 206 | /// Insert a reply and bump the thread's last_activity_at atomically. | |
| @@ -221,25 +219,24 @@ | |||
| 221 | 219 | ) -> Result<Uuid, sqlx::Error> { | |
| 222 | 220 | let mut tx = pool.begin().await?; | |
| 223 | 221 | ||
| 224 | - | let row: (Uuid,) = sqlx::query_as( | |
| 222 | + | let post_id = sqlx::query_scalar!( | |
| 225 | 223 | "INSERT INTO posts (thread_id, author_id, body_markdown, body_html) | |
| 226 | 224 | VALUES ($1, $2, $3, $4) | |
| 227 | 225 | RETURNING id", | |
| 226 | + | thread_id, | |
| 227 | + | author_id, | |
| 228 | + | body_markdown, | |
| 229 | + | body_html, | |
| 228 | 230 | ) | |
| 229 | - | .bind(thread_id) | |
| 230 | - | .bind(author_id) | |
| 231 | - | .bind(body_markdown) | |
| 232 | - | .bind(body_html) | |
| 233 | 231 | .fetch_one(&mut *tx) | |
| 234 | 232 | .await?; | |
| 235 | 233 | ||
| 236 | - | sqlx::query("UPDATE threads SET last_activity_at = now() WHERE id = $1") | |
| 237 | - | .bind(thread_id) | |
| 234 | + | sqlx::query!("UPDATE threads SET last_activity_at = now() WHERE id = $1", thread_id) | |
| 238 | 235 | .execute(&mut *tx) | |
| 239 | 236 | .await?; | |
| 240 | 237 | ||
| 241 | 238 | tx.commit().await?; | |
| 242 | - | Ok(row.0) | |
| 239 | + | Ok(post_id) | |
| 243 | 240 | } | |
| 244 | 241 | ||
| 245 | 242 | /// Idempotent reply insert keyed on `external_ref`, for the internal API. | |
| @@ -262,37 +259,37 @@ | |||
| 262 | 259 | ) -> Result<(Uuid, bool), sqlx::Error> { | |
| 263 | 260 | let mut tx = pool.begin().await?; | |
| 264 | 261 | ||
| 265 | - | let inserted: Option<(Uuid,)> = sqlx::query_as( | |
| 262 | + | let inserted = sqlx::query_scalar!( | |
| 266 | 263 | "INSERT INTO posts (thread_id, author_id, body_markdown, body_html, external_ref) | |
| 267 | 264 | VALUES ($1, $2, $3, $4, $5) | |
| 268 | 265 | ON CONFLICT (external_ref) WHERE external_ref IS NOT NULL DO NOTHING | |
| 269 | 266 | RETURNING id", | |
| 267 | + | thread_id, | |
| 268 | + | author_id, | |
| 269 | + | body_markdown, | |
| 270 | + | body_html, | |
| 271 | + | external_ref, | |
| 270 | 272 | ) | |
| 271 | - | .bind(thread_id) | |
| 272 | - | .bind(author_id) | |
| 273 | - | .bind(body_markdown) | |
| 274 | - | .bind(body_html) | |
| 275 | - | .bind(external_ref) | |
| 276 | 273 | .fetch_optional(&mut *tx) | |
| 277 | 274 | .await?; | |
| 278 | 275 | ||
| 279 | 276 | let result = match inserted { | |
| 280 | - | Some((id,)) => { | |
| 277 | + | Some(id) => { | |
| 281 | 278 | // Fresh reply: bump thread activity (the trigger already counted it). | |
| 282 | - | sqlx::query("UPDATE threads SET last_activity_at = now() WHERE id = $1") | |
| 283 | - | .bind(thread_id) | |
| 279 | + | sqlx::query!("UPDATE threads SET last_activity_at = now() WHERE id = $1", thread_id) | |
| 284 | 280 | .execute(&mut *tx) | |
| 285 | 281 | .await?; | |
| 286 | 282 | (id, true) | |
| 287 | 283 | } | |
| 288 | 284 | None => { | |
| 289 | 285 | // Replay: the reply already exists. Return its id, don't re-bump. | |
| 290 | - | let existing: (Uuid,) = | |
| 291 | - | sqlx::query_as("SELECT id FROM posts WHERE external_ref = $1") | |
| 292 | - | .bind(external_ref) | |
| 293 | - | .fetch_one(&mut *tx) | |
| 294 | - | .await?; | |
| 295 | - | (existing.0, false) | |
| 286 | + | let existing = sqlx::query_scalar!( | |
| 287 | + | "SELECT id FROM posts WHERE external_ref = $1", | |
| 288 | + | external_ref, | |
| 289 | + | ) | |
| 290 | + | .fetch_one(&mut *tx) | |
| 291 | + | .await?; | |
| 292 | + | (existing, false) | |
| 296 | 293 | } | |
| 297 | 294 | }; | |
| 298 | 295 | ||
| @@ -309,18 +306,17 @@ | |||
| 309 | 306 | body_markdown: &str, | |
| 310 | 307 | body_html: &str, | |
| 311 | 308 | ) -> Result<Uuid, sqlx::Error> { | |
| 312 | - | let row: (Uuid,) = sqlx::query_as( | |
| 309 | + | sqlx::query_scalar!( | |
| 313 | 310 | "INSERT INTO post_footnotes (post_id, author_id, body_markdown, body_html) | |
| 314 | 311 | VALUES ($1, $2, $3, $4) | |
| 315 | 312 | RETURNING id", | |
| 313 | + | post_id, | |
| 314 | + | author_id, | |
| 315 | + | body_markdown, | |
| 316 | + | body_html, | |
| 316 | 317 | ) | |
| 317 | - | .bind(post_id) | |
| 318 | - | .bind(author_id) | |
| 319 | - | .bind(body_markdown) | |
| 320 | - | .bind(body_html) | |
| 321 | 318 | .fetch_one(pool) | |
| 322 | - | .await?; | |
| 323 | - | Ok(row.0) | |
| 319 | + | .await | |
| 324 | 320 | } | |
| 325 | 321 | ||
| 326 | 322 | /// Mod-remove a post: set removed_by/removed_at. Content stays intact for audit. | |
| @@ -331,12 +327,12 @@ | |||
| 331 | 327 | post_id: Uuid, | |
| 332 | 328 | removed_by_id: Uuid, | |
| 333 | 329 | ) -> Result<bool, sqlx::Error> { | |
| 334 | - | let result = sqlx::query( | |
| 330 | + | let result = sqlx::query!( | |
| 335 | 331 | "UPDATE posts SET removed_by = $2, removed_at = now() | |
| 336 | 332 | WHERE id = $1 AND removed_at IS NULL", | |
| 333 | + | post_id, | |
| 334 | + | removed_by_id, | |
| 337 | 335 | ) | |
| 338 | - | .bind(post_id) | |
| 339 | - | .bind(removed_by_id) | |
| 340 | 336 | .execute(pool) | |
| 341 | 337 | .await?; | |
| 342 | 338 | Ok(result.rows_affected() > 0) | |
| @@ -369,12 +365,12 @@ | |||
| 369 | 365 | ) -> Result<PostRemoval, sqlx::Error> { | |
| 370 | 366 | let mut tx = pool.begin().await?; | |
| 371 | 367 | ||
| 372 | - | let post_removed = sqlx::query( | |
| 368 | + | let post_removed = sqlx::query!( | |
| 373 | 369 | "UPDATE posts SET removed_by = $2, removed_at = now() | |
| 374 | 370 | WHERE id = $1 AND removed_at IS NULL", | |
| 371 | + | post_id, | |
| 372 | + | removed_by_id, | |
| 375 | 373 | ) | |
| 376 | - | .bind(post_id) | |
| 377 | - | .bind(removed_by_id) | |
| 378 | 374 | .execute(&mut *tx) | |
| 379 | 375 | .await? | |
| 380 | 376 | .rows_affected() | |
| @@ -383,7 +379,7 @@ | |||
| 383 | 379 | let mut thread_removed = false; | |
| 384 | 380 | if post_removed { | |
| 385 | 381 | // Soft-delete the thread only when this post is its opening post. | |
| 386 | - | thread_removed = sqlx::query( | |
| 382 | + | thread_removed = sqlx::query!( | |
| 387 | 383 | "UPDATE threads SET deleted_at = now() | |
| 388 | 384 | WHERE deleted_at IS NULL | |
| 389 | 385 | AND id = (SELECT thread_id FROM posts WHERE id = $1) | |
| @@ -393,8 +389,8 @@ | |||
| 393 | 389 | ORDER BY created_at ASC, id ASC | |
| 394 | 390 | LIMIT 1 | |
| 395 | 391 | )", | |
| 392 | + | post_id, | |
| 396 | 393 | ) | |
| 397 | - | .bind(post_id) | |
| 398 | 394 | .execute(&mut *tx) | |
| 399 | 395 | .await? | |
| 400 | 396 | .rows_affected() | |
| @@ -415,13 +411,13 @@ | |||
| 415 | 411 | post_id: Uuid, | |
| 416 | 412 | threshold: i32, | |
| 417 | 413 | ) -> Result<bool, sqlx::Error> { | |
| 418 | - | let result = sqlx::query( | |
| 414 | + | let result = sqlx::query!( | |
| 419 | 415 | "UPDATE posts SET removed_by = NULL, removed_at = now() | |
| 420 | 416 | WHERE id = $1 AND removed_at IS NULL | |
| 421 | 417 | AND (SELECT COUNT(*) FROM post_flags WHERE post_id = $1 AND resolved_at IS NULL) >= $2", | |
| 418 | + | post_id, | |
| 419 | + | threshold as i64, | |
| 422 | 420 | ) | |
| 423 | - | .bind(post_id) | |
| 424 | - | .bind(threshold as i64) | |
| 425 | 421 | .execute(pool) | |
| 426 | 422 | .await?; | |
| 427 | 423 | Ok(result.rows_affected() > 0) | |
| @@ -434,9 +430,7 @@ | |||
| 434 | 430 | thread_id: Uuid, | |
| 435 | 431 | title: &str, | |
| 436 | 432 | ) -> Result<(), sqlx::Error> { | |
| 437 | - | sqlx::query("UPDATE threads SET title = $2 WHERE id = $1") | |
| 438 | - | .bind(thread_id) | |
| 439 | - | .bind(title) | |
| 433 | + | sqlx::query!("UPDATE threads SET title = $2 WHERE id = $1", thread_id, title) | |
| 440 | 434 | .execute(pool) | |
| 441 | 435 | .await?; | |
| 442 | 436 | Ok(()) | |
| @@ -445,8 +439,7 @@ | |||
| 445 | 439 | /// Soft-delete a thread: set deleted_at (hides from listings). | |
| 446 | 440 | #[tracing::instrument(skip_all)] | |
| 447 | 441 | pub async fn soft_delete_thread(pool: &PgPool, thread_id: Uuid) -> Result<(), sqlx::Error> { | |
| 448 | - | sqlx::query("UPDATE threads SET deleted_at = now() WHERE id = $1") | |
| 449 | - | .bind(thread_id) | |
| 442 | + | sqlx::query!("UPDATE threads SET deleted_at = now() WHERE id = $1", thread_id) | |
| 450 | 443 | .execute(pool) | |
| 451 | 444 | .await?; | |
| 452 | 445 | Ok(()) | |
| @@ -459,9 +452,7 @@ | |||
| 459 | 452 | thread_id: Uuid, | |
| 460 | 453 | pinned: bool, | |
| 461 | 454 | ) -> Result<(), sqlx::Error> { | |
| 462 | - | sqlx::query("UPDATE threads SET pinned = $2 WHERE id = $1") | |
| 463 | - | .bind(thread_id) | |
| 464 | - | .bind(pinned) | |
| 455 | + | sqlx::query!("UPDATE threads SET pinned = $2 WHERE id = $1", thread_id, pinned) | |
| 465 | 456 | .execute(pool) | |
| 466 | 457 | .await?; | |
| 467 | 458 | Ok(()) | |
| @@ -474,9 +465,7 @@ | |||
| 474 | 465 | thread_id: Uuid, | |
| 475 | 466 | locked: bool, | |
| 476 | 467 | ) -> Result<(), sqlx::Error> { | |
| 477 | - | sqlx::query("UPDATE threads SET locked = $2 WHERE id = $1") | |
| 478 | - | .bind(thread_id) | |
| 479 | - | .bind(locked) | |
| 468 | + | sqlx::query!("UPDATE threads SET locked = $2 WHERE id = $1", thread_id, locked) | |
| 480 | 469 | .execute(pool) | |
| 481 | 470 | .await?; | |
| 482 | 471 | Ok(()) | |
| @@ -491,13 +480,13 @@ | |||
| 491 | 480 | description: Option<&str>, | |
| 492 | 481 | auto_hide_threshold: Option<i32>, | |
| 493 | 482 | ) -> Result<(), sqlx::Error> { | |
| 494 | - | sqlx::query( | |
| 483 | + | sqlx::query!( | |
| 495 | 484 | "UPDATE communities SET name = $2, description = $3, auto_hide_threshold = $4 WHERE id = $1", | |
| 485 | + | community_id, | |
| 486 | + | name, | |
| 487 | + | description, | |
| 488 | + | auto_hide_threshold, | |
| 496 | 489 | ) | |
| 497 | - | .bind(community_id) | |
| 498 | - | .bind(name) | |
| 499 | - | .bind(description) | |
| 500 | - | .bind(auto_hide_threshold) | |
| 501 | 490 | .execute(pool) | |
| 502 | 491 | .await?; | |
| 503 | 492 | Ok(()) | |
| @@ -513,19 +502,18 @@ | |||
| 513 | 502 | description: Option<&str>, | |
| 514 | 503 | sort_order: i32, | |
| 515 | 504 | ) -> Result<Uuid, sqlx::Error> { | |
| 516 | - | let row: (Uuid,) = sqlx::query_as( | |
| 505 | + | sqlx::query_scalar!( | |
| 517 | 506 | "INSERT INTO categories (community_id, name, slug, description, sort_order) | |
| 518 | 507 | VALUES ($1, $2, $3, $4, $5) | |
| 519 | 508 | RETURNING id", | |
| 509 | + | community_id, | |
| 510 | + | name, | |
| 511 | + | slug, | |
| 512 | + | description, | |
| 513 | + | sort_order, | |
| 520 | 514 | ) | |
| 521 | - | .bind(community_id) | |
| 522 | - | .bind(name) | |
| 523 | - | .bind(slug) | |
| 524 | - | .bind(description) | |
| 525 | - | .bind(sort_order) | |
| 526 | 515 | .fetch_one(pool) | |
| 527 | - | .await?; | |
| 528 | - | Ok(row.0) | |
| 516 | + | .await | |
| 529 | 517 | } | |
| 530 | 518 | ||
| 531 | 519 | /// Update a category's name and description (scoped to community). | |
| @@ -537,13 +525,13 @@ | |||
| 537 | 525 | name: &str, | |
| 538 | 526 | description: Option<&str>, | |
| 539 | 527 | ) -> Result<bool, sqlx::Error> { | |
| 540 | - | let result = sqlx::query( | |
| 528 | + | let result = sqlx::query!( | |
| 541 | 529 | "UPDATE categories SET name = $2, description = $3 WHERE id = $1 AND community_id = $4", | |
| 530 | + | category_id, | |
| 531 | + | name, | |
| 532 | + | description, | |
| 533 | + | community_id, | |
| 542 | 534 | ) | |
| 543 | - | .bind(category_id) | |
| 544 | - | .bind(name) | |
| 545 | - | .bind(description) | |
| 546 | - | .bind(community_id) | |
| 547 | 535 | .execute(pool) | |
| 548 | 536 | .await?; | |
| 549 | 537 | Ok(result.rows_affected() > 0) | |
| @@ -559,14 +547,10 @@ | |||
| 559 | 547 | order_b: i32, | |
| 560 | 548 | ) -> Result<(), sqlx::Error> { | |
| 561 | 549 | let mut tx = pool.begin().await?; | |
| 562 | - | sqlx::query("UPDATE categories SET sort_order = $2 WHERE id = $1") | |
| 563 | - | .bind(id_a) | |
| 564 | - | .bind(order_b) | |
| 550 | + | sqlx::query!("UPDATE categories SET sort_order = $2 WHERE id = $1", id_a, order_b) | |
| 565 | 551 | .execute(&mut *tx) | |
| 566 | 552 | .await?; | |
| 567 | - | sqlx::query("UPDATE categories SET sort_order = $2 WHERE id = $1") | |
| 568 | - | .bind(id_b) | |
| 569 | - | .bind(order_a) | |
| 553 | + | sqlx::query!("UPDATE categories SET sort_order = $2 WHERE id = $1", id_b, order_a) | |
| 570 | 554 | .execute(&mut *tx) | |
| 571 | 555 | .await?; | |
| 572 | 556 | tx.commit().await?; | |
| @@ -580,17 +564,16 @@ | |||
| 580 | 564 | community_slug: &str, | |
| 581 | 565 | category_slug: &str, | |
| 582 | 566 | ) -> Result<Option<Uuid>, sqlx::Error> { | |
| 583 | - | let row: Option<(Uuid,)> = sqlx::query_as( | |
| 567 | + | sqlx::query_scalar!( | |
| 584 | 568 | "SELECT c.id | |
| 585 | 569 | FROM categories c | |
| 586 | 570 | JOIN communities co ON co.id = c.community_id | |
| 587 | 571 | WHERE co.slug = $1 AND c.slug = $2", | |
| 572 | + | community_slug, | |
| 573 | + | category_slug, | |
| 588 | 574 | ) | |
| 589 | - | .bind(community_slug) | |
| 590 | - | .bind(category_slug) | |
| 591 | 575 | .fetch_optional(pool) | |
| 592 | - | .await?; | |
| 593 | - | Ok(row.map(|r| r.0)) | |
| 576 | + | .await | |
| 594 | 577 | } | |
| 595 | 578 | ||
| 596 | 579 | // ============================================================================ | |
| @@ -608,7 +591,14 @@ | |||
| 608 | 591 | reason: Option<&str>, | |
| 609 | 592 | expires_at: Option<DateTime<Utc>>, | |
| 610 | 593 | ) -> Result<Uuid, sqlx::Error> { | |
| 611 | - | let row: (Uuid,) = sqlx::query_as( | |
| 594 | + | // runtime-checked: the full server build unifies sqlx's `time` and `chrono` | |
| 595 | + | // features (the session store pulls in `time`), and with both on the | |
| 596 | + | // compile-time macro infers the TIMESTAMPTZ bind parameter as | |
| 597 | + | // `time::OffsetDateTime`, which a `chrono::DateTime<Utc>` won't satisfy. | |
| 598 | + | // Output-column type can be overridden in the macro; a bind parameter's | |
| 599 | + | // cannot. This is the one write path that binds a chrono timestamp, so it |
Lines truncated
| @@ -123,11 +123,12 @@ | |||
| 123 | 123 | community_id: Uuid, | |
| 124 | 124 | category_slug: &str, | |
| 125 | 125 | ) -> Result<Option<CategoryIdRow>, sqlx::Error> { | |
| 126 | - | sqlx::query_as::<_, CategoryIdRow>( | |
| 126 | + | sqlx::query_as!( | |
| 127 | + | CategoryIdRow, | |
| 127 | 128 | "SELECT id, name, slug FROM categories WHERE community_id = $1 AND slug = $2", | |
| 129 | + | community_id, | |
| 130 | + | category_slug, | |
| 128 | 131 | ) | |
| 129 | - | .bind(community_id) | |
| 130 | - | .bind(category_slug) | |
| 131 | 132 | .fetch_optional(pool) | |
| 132 | 133 | .await | |
| 133 | 134 | } | |
| @@ -138,12 +139,13 @@ | |||
| 138 | 139 | pool: &PgPool, | |
| 139 | 140 | external_ref: &str, | |
| 140 | 141 | ) -> Result<Option<(Uuid,)>, sqlx::Error> { | |
| 141 | - | sqlx::query_as::<_, (Uuid,)>( | |
| 142 | + | sqlx::query!( | |
| 142 | 143 | "SELECT id FROM threads WHERE external_ref = $1", | |
| 144 | + | external_ref, | |
| 143 | 145 | ) | |
| 144 | - | .bind(external_ref) | |
| 145 | 146 | .fetch_optional(pool) | |
| 146 | 147 | .await | |
| 148 | + | .map(|opt| opt.map(|r| (r.id,))) | |
| 147 | 149 | } | |
| 148 | 150 | ||
| 149 | 151 | /// Get thread stats: post count and last activity timestamp. | |
| @@ -158,16 +160,17 @@ | |||
| 158 | 160 | // excluding mod-removed and soft-deleted posts (matching the m029 trigger and | |
| 159 | 161 | // the profile tallies). Contrast `count_posts_in_thread`, which deliberately | |
| 160 | 162 | // counts tombstones because the in-app thread list renders them. | |
| 161 | - | sqlx::query_as::<_, (i64, Option<DateTime<Utc>>)>( | |
| 162 | - | "SELECT COUNT(p.id), MAX(p.created_at) | |
| 163 | - | FROM posts p | |
| 164 | - | WHERE p.thread_id = $1 | |
| 165 | - | AND p.removed_at IS NULL | |
| 166 | - | AND p.deleted_at IS NULL", | |
| 163 | + | sqlx::query!( | |
| 164 | + | r#"SELECT COUNT(p.id) AS "count!", MAX(p.created_at) AS "max_created: chrono::DateTime<chrono::Utc>" | |
| 165 | + | FROM posts p | |
| 166 | + | WHERE p.thread_id = $1 | |
| 167 | + | AND p.removed_at IS NULL | |
| 168 | + | AND p.deleted_at IS NULL"#, | |
| 169 | + | thread_id, | |
| 167 | 170 | ) | |
| 168 | - | .bind(thread_id) | |
| 169 | 171 | .fetch_optional(pool) | |
| 170 | 172 | .await | |
| 173 | + | .map(|opt| opt.map(|r| (r.count, r.max_created))) | |
| 171 | 174 | } | |
| 172 | 175 | ||
| 173 | 176 | // ============================================================================ | |
| @@ -189,26 +192,27 @@ | |||
| 189 | 192 | /// [`list_archived_communities`] for the explicit archived view. | |
| 190 | 193 | #[tracing::instrument(skip_all)] | |
| 191 | 194 | pub async fn list_communities(pool: &PgPool, limit: i64, offset: i64) -> Result<Vec<CommunityListRow>, sqlx::Error> { | |
| 192 | - | sqlx::query_as::<_, CommunityListRow>( | |
| 195 | + | sqlx::query_as!( | |
| 196 | + | CommunityListRow, | |
| 193 | 197 | // Per-community counts as scalar subqueries rather than a | |
| 194 | 198 | // categories⋈threads join + GROUP BY COUNT(DISTINCT): the join | |
| 195 | 199 | // multiplied rows (community × categories × threads) and forced | |
| 196 | 200 | // aggregating *every* community's threads before LIMIT. As scalar | |
| 197 | 201 | // subqueries in the target list they're evaluated only for the page's | |
| 198 | 202 | // output rows (after ORDER BY + LIMIT), bounding the work to one page. | |
| 199 | - | "SELECT co.name, co.slug, co.description, | |
| 200 | - | (SELECT COUNT(*) FROM categories c WHERE c.community_id = co.id) AS category_count, | |
| 203 | + | r#"SELECT co.name, co.slug, co.description, | |
| 204 | + | (SELECT COUNT(*) FROM categories c WHERE c.community_id = co.id) AS "category_count!", | |
| 201 | 205 | (SELECT COUNT(*) FROM threads t | |
| 202 | 206 | JOIN categories c2 ON c2.id = t.category_id | |
| 203 | - | WHERE c2.community_id = co.id) AS thread_count | |
| 207 | + | WHERE c2.community_id = co.id) AS "thread_count!" | |
| 204 | 208 | FROM communities co | |
| 205 | 209 | WHERE co.suspended_at IS NULL | |
| 206 | 210 | AND co.state <> 'archived' | |
| 207 | 211 | ORDER BY co.name | |
| 208 | - | LIMIT $1 OFFSET $2", | |
| 212 | + | LIMIT $1 OFFSET $2"#, | |
| 213 | + | limit, | |
| 214 | + | offset, | |
| 209 | 215 | ) | |
| 210 | - | .bind(limit) | |
| 211 | - | .bind(offset) | |
| 212 | 216 | .fetch_all(pool) | |
| 213 | 217 | .await | |
| 214 | 218 | } | |
| @@ -216,8 +220,8 @@ | |||
| 216 | 220 | /// Count non-suspended, non-archived communities. | |
| 217 | 221 | #[tracing::instrument(skip_all)] | |
| 218 | 222 | pub async fn count_communities(pool: &PgPool) -> Result<i64, sqlx::Error> { | |
| 219 | - | sqlx::query_scalar( | |
| 220 | - | "SELECT COUNT(*) FROM communities WHERE suspended_at IS NULL AND state <> 'archived'", | |
| 223 | + | sqlx::query_scalar!( | |
| 224 | + | r#"SELECT COUNT(*) AS "count!" FROM communities WHERE suspended_at IS NULL AND state <> 'archived'"#, | |
| 221 | 225 | ) | |
| 222 | 226 | .fetch_one(pool) | |
| 223 | 227 | .await | |
| @@ -231,29 +235,30 @@ | |||
| 231 | 235 | limit: i64, | |
| 232 | 236 | offset: i64, | |
| 233 | 237 | ) -> Result<Vec<CommunityListRow>, sqlx::Error> { | |
| 234 | - | sqlx::query_as::<_, CommunityListRow>( | |
| 238 | + | sqlx::query_as!( | |
| 239 | + | CommunityListRow, | |
| 235 | 240 | // Scalar-subquery counts (see `list_communities`) — bounded to the page. | |
| 236 | - | "SELECT co.name, co.slug, co.description, | |
| 237 | - | (SELECT COUNT(*) FROM categories c WHERE c.community_id = co.id) AS category_count, | |
| 241 | + | r#"SELECT co.name, co.slug, co.description, | |
| 242 | + | (SELECT COUNT(*) FROM categories c WHERE c.community_id = co.id) AS "category_count!", | |
| 238 | 243 | (SELECT COUNT(*) FROM threads t | |
| 239 | 244 | JOIN categories c2 ON c2.id = t.category_id | |
| 240 | - | WHERE c2.community_id = co.id) AS thread_count | |
| 245 | + | WHERE c2.community_id = co.id) AS "thread_count!" | |
| 241 | 246 | FROM communities co | |
| 242 | 247 | WHERE co.suspended_at IS NULL | |
| 243 | 248 | AND co.state = 'archived' | |
| 244 | 249 | ORDER BY co.name | |
| 245 | - | LIMIT $1 OFFSET $2", | |
| 250 | + | LIMIT $1 OFFSET $2"#, | |
| 251 | + | limit, | |
| 252 | + | offset, | |
| 246 | 253 | ) | |
| 247 | - | .bind(limit) | |
| 248 | - | .bind(offset) | |
| 249 | 254 | .fetch_all(pool) | |
| 250 | 255 | .await | |
| 251 | 256 | } | |
| 252 | 257 | ||
| 253 | 258 | #[tracing::instrument(skip_all)] | |
| 254 | 259 | pub async fn count_archived_communities(pool: &PgPool) -> Result<i64, sqlx::Error> { | |
| 255 | - | sqlx::query_scalar( | |
| 256 | - | "SELECT COUNT(*) FROM communities WHERE suspended_at IS NULL AND state = 'archived'", | |
| 260 | + | sqlx::query_scalar!( | |
| 261 | + | r#"SELECT COUNT(*) AS "count!" FROM communities WHERE suspended_at IS NULL AND state = 'archived'"#, | |
| 257 | 262 | ) | |
| 258 | 263 | .fetch_one(pool) | |
| 259 | 264 | .await | |
| @@ -264,11 +269,15 @@ | |||
| 264 | 269 | pool: &PgPool, | |
| 265 | 270 | slug: &str, | |
| 266 | 271 | ) -> Result<Option<CommunityRow>, sqlx::Error> { | |
| 267 | - | sqlx::query_as::<_, CommunityRow>( | |
| 268 | - | "SELECT id, name, slug, description, suspended_at, auto_hide_threshold, state | |
| 269 | - | FROM communities WHERE slug = $1", | |
| 272 | + | sqlx::query_as!( | |
| 273 | + | CommunityRow, | |
| 274 | + | r#"SELECT id, name, slug, description, | |
| 275 | + | suspended_at AS "suspended_at: chrono::DateTime<chrono::Utc>", | |
| 276 | + | auto_hide_threshold, | |
| 277 | + | state AS "state: CommunityState" | |
| 278 | + | FROM communities WHERE slug = $1"#, | |
| 279 | + | slug, | |
| 270 | 280 | ) | |
| 271 | - | .bind(slug) | |
| 272 | 281 | .fetch_optional(pool) | |
| 273 | 282 | .await | |
| 274 | 283 | } | |
| @@ -278,11 +287,15 @@ | |||
| 278 | 287 | pool: &PgPool, | |
| 279 | 288 | id: Uuid, | |
| 280 | 289 | ) -> Result<Option<CommunityRow>, sqlx::Error> { | |
| 281 | - | sqlx::query_as::<_, CommunityRow>( | |
| 282 | - | "SELECT id, name, slug, description, suspended_at, auto_hide_threshold, state | |
| 283 | - | FROM communities WHERE id = $1", | |
| 290 | + | sqlx::query_as!( | |
| 291 | + | CommunityRow, | |
| 292 | + | r#"SELECT id, name, slug, description, | |
| 293 | + | suspended_at AS "suspended_at: chrono::DateTime<chrono::Utc>", | |
| 294 | + | auto_hide_threshold, | |
| 295 | + | state AS "state: CommunityState" | |
| 296 | + | FROM communities WHERE id = $1"#, | |
| 297 | + | id, | |
| 284 | 298 | ) | |
| 285 | - | .bind(id) | |
| 286 | 299 | .fetch_optional(pool) | |
| 287 | 300 | .await | |
| 288 | 301 | } | |
| @@ -292,17 +305,18 @@ | |||
| 292 | 305 | pool: &PgPool, | |
| 293 | 306 | community_slug: &str, | |
| 294 | 307 | ) -> Result<Vec<CategoryWithCount>, sqlx::Error> { | |
| 295 | - | sqlx::query_as::<_, CategoryWithCount>( | |
| 296 | - | "SELECT c.name, c.slug, c.description, | |
| 297 | - | COUNT(t.id) AS thread_count | |
| 308 | + | sqlx::query_as!( | |
| 309 | + | CategoryWithCount, | |
| 310 | + | r#"SELECT c.name, c.slug, c.description, | |
| 311 | + | COUNT(t.id) AS "thread_count!" | |
| 298 | 312 | FROM categories c | |
| 299 | 313 | JOIN communities co ON co.id = c.community_id | |
| 300 | 314 | LEFT JOIN threads t ON t.category_id = c.id AND t.deleted_at IS NULL | |
| 301 | 315 | WHERE co.slug = $1 | |
| 302 | 316 | GROUP BY c.id, c.name, c.slug, c.description, c.sort_order | |
| 303 | - | ORDER BY c.sort_order", | |
| 317 | + | ORDER BY c.sort_order"#, | |
| 318 | + | community_slug, | |
| 304 | 319 | ) | |
| 305 | - | .bind(community_slug) | |
| 306 | 320 | .fetch_all(pool) | |
| 307 | 321 | .await | |
| 308 | 322 | } | |
| @@ -313,14 +327,15 @@ | |||
| 313 | 327 | community_slug: &str, | |
| 314 | 328 | category_slug: &str, | |
| 315 | 329 | ) -> Result<Option<CategoryRow>, sqlx::Error> { | |
| 316 | - | sqlx::query_as::<_, CategoryRow>( | |
| 330 | + | sqlx::query_as!( | |
| 331 | + | CategoryRow, | |
| 317 | 332 | "SELECT c.name, c.slug | |
| 318 | 333 | FROM categories c | |
| 319 | 334 | JOIN communities co ON co.id = c.community_id | |
| 320 | 335 | WHERE co.slug = $1 AND c.slug = $2", | |
| 336 | + | community_slug, | |
| 337 | + | category_slug, | |
| 321 | 338 | ) | |
| 322 | - | .bind(community_slug) | |
| 323 | - | .bind(category_slug) | |
| 324 | 339 | .fetch_optional(pool) | |
| 325 | 340 | .await | |
| 326 | 341 | } | |
| @@ -333,12 +348,13 @@ | |||
| 333 | 348 | limit: i64, | |
| 334 | 349 | offset: i64, | |
| 335 | 350 | ) -> Result<Vec<ThreadWithMeta>, sqlx::Error> { | |
| 336 | - | sqlx::query_as::<_, ThreadWithMeta>( | |
| 337 | - | "SELECT t.id, t.title, | |
| 338 | - | COALESCE(u.display_name, u.username) AS author_name, | |
| 351 | + | sqlx::query_as!( | |
| 352 | + | ThreadWithMeta, | |
| 353 | + | r#"SELECT t.id, t.title, | |
| 354 | + | COALESCE(u.display_name, u.username) AS "author_name!", | |
| 339 | 355 | u.username AS author_username, | |
| 340 | - | GREATEST(t.post_count - 1, 0)::BIGINT AS reply_count, | |
| 341 | - | t.last_activity_at, | |
| 356 | + | GREATEST(t.post_count - 1, 0)::BIGINT AS "reply_count!", | |
| 357 | + | t.last_activity_at AS "last_activity_at: chrono::DateTime<chrono::Utc>", | |
| 342 | 358 | t.pinned, t.locked | |
| 343 | 359 | FROM threads t | |
| 344 | 360 | JOIN categories c ON c.id = t.category_id | |
| @@ -346,12 +362,12 @@ | |||
| 346 | 362 | JOIN users u ON u.mnw_account_id = t.author_id | |
| 347 | 363 | WHERE co.slug = $1 AND c.slug = $2 AND t.deleted_at IS NULL | |
| 348 | 364 | ORDER BY t.pinned DESC, t.last_activity_at DESC | |
| 349 | - | LIMIT $3 OFFSET $4", | |
| 365 | + | LIMIT $3 OFFSET $4"#, | |
| 366 | + | community_slug, | |
| 367 | + | category_slug, | |
| 368 | + | limit, | |
| 369 | + | offset, | |
| 350 | 370 | ) | |
| 351 | - | .bind(community_slug) | |
| 352 | - | .bind(category_slug) | |
| 353 | - | .bind(limit) | |
| 354 | - | .bind(offset) | |
| 355 | 371 | .fetch_all(pool) | |
| 356 | 372 | .await | |
| 357 | 373 | } | |
| @@ -391,6 +407,7 @@ | |||
| 391 | 407 | LIMIT $3 OFFSET $4" | |
| 392 | 408 | ); | |
| 393 | 409 | ||
| 410 | + | // runtime-checked: dynamic SQL (cannot use compile-time macro) | |
| 394 | 411 | sqlx::query_as::<_, ThreadWithMeta>(&query) | |
| 395 | 412 | .bind(community_slug) | |
| 396 | 413 | .bind(category_slug) | |
| @@ -406,15 +423,15 @@ | |||
| 406 | 423 | community_slug: &str, | |
| 407 | 424 | category_slug: &str, | |
| 408 | 425 | ) -> Result<i64, sqlx::Error> { | |
| 409 | - | sqlx::query_scalar( | |
| 410 | - | "SELECT COUNT(*) | |
| 426 | + | sqlx::query_scalar!( | |
| 427 | + | r#"SELECT COUNT(*) AS "count!" | |
| 411 | 428 | FROM threads t | |
| 412 | 429 | JOIN categories c ON c.id = t.category_id | |
| 413 | 430 | JOIN communities co ON co.id = c.community_id | |
| 414 | - | WHERE co.slug = $1 AND c.slug = $2 AND t.deleted_at IS NULL", | |
| 431 | + | WHERE co.slug = $1 AND c.slug = $2 AND t.deleted_at IS NULL"#, | |
| 432 | + | community_slug, | |
| 433 | + | category_slug, | |
| 415 | 434 | ) | |
| 416 | - | .bind(community_slug) | |
| 417 | - | .bind(category_slug) | |
| 418 | 435 | .fetch_one(pool) | |
| 419 | 436 | .await | |
| 420 | 437 | } | |
| @@ -424,17 +441,18 @@ | |||
| 424 | 441 | pool: &PgPool, | |
| 425 | 442 | thread_id: Uuid, | |
| 426 | 443 | ) -> Result<Option<ThreadWithBreadcrumb>, sqlx::Error> { | |
| 427 | - | sqlx::query_as::<_, ThreadWithBreadcrumb>( | |
| 428 | - | "SELECT t.id, t.title, t.locked, t.pinned, t.author_id, | |
| 444 | + | sqlx::query_as!( | |
| 445 | + | ThreadWithBreadcrumb, | |
| 446 | + | r#"SELECT t.id, t.title, t.locked, t.pinned, t.author_id, | |
| 429 | 447 | co.id AS community_id, | |
| 430 | 448 | co.name AS community_name, co.slug AS community_slug, | |
| 431 | 449 | c.name AS category_name, c.slug AS category_slug | |
| 432 | 450 | FROM threads t | |
| 433 | 451 | JOIN categories c ON c.id = t.category_id | |
| 434 | 452 | JOIN communities co ON co.id = c.community_id | |
| 435 | - | WHERE t.id = $1 AND t.deleted_at IS NULL", | |
| 453 | + | WHERE t.id = $1 AND t.deleted_at IS NULL"#, | |
| 454 | + | thread_id, | |
| 436 | 455 | ) | |
| 437 | - | .bind(thread_id) | |
| 438 | 456 | .fetch_optional(pool) | |
| 439 | 457 | .await | |
| 440 | 458 | } | |
| @@ -444,20 +462,24 @@ | |||
| 444 | 462 | pool: &PgPool, | |
| 445 | 463 | thread_id: Uuid, | |
| 446 | 464 | ) -> Result<Vec<PostWithAuthor>, sqlx::Error> { | |
| 447 | - | sqlx::query_as::<_, PostWithAuthor>( | |
| 448 | - | "SELECT p.id, p.author_id, | |
| 449 | - | COALESCE(u.display_name, u.username) AS author_name, | |
| 465 | + | sqlx::query_as!( | |
| 466 | + | PostWithAuthor, | |
| 467 | + | r#"SELECT p.id, p.author_id, | |
| 468 | + | COALESCE(u.display_name, u.username) AS "author_name!", | |
| 450 | 469 | u.username AS author_username, | |
| 451 | - | p.body_html, p.created_at, p.edited_at, p.deleted_at, | |
| 452 | - | p.removed_at, | |
| 470 | + | p.body_html, | |
| 471 | + | p.created_at AS "created_at: chrono::DateTime<chrono::Utc>", | |
| 472 | + | p.edited_at AS "edited_at: chrono::DateTime<chrono::Utc>", | |
| 473 | + | p.deleted_at AS "deleted_at: chrono::DateTime<chrono::Utc>", | |
| 474 | + | p.removed_at AS "removed_at: chrono::DateTime<chrono::Utc>", | |
| 453 | 475 | u.is_fan_plus AS author_is_fan_plus, | |
| 454 | 476 | u.signature_html AS author_signature_html | |
| 455 | 477 | FROM posts p | |
| 456 | 478 | JOIN users u ON u.mnw_account_id = p.author_id | |
| 457 | 479 | WHERE p.thread_id = $1 | |
| 458 | - | ORDER BY p.created_at", | |
| 480 | + | ORDER BY p.created_at"#, | |
| 481 | + | thread_id, | |
| 459 | 482 | ) | |
| 460 | - | .bind(thread_id) | |
| 461 | 483 | .fetch_all(pool) | |
| 462 | 484 | .await | |
| 463 | 485 | } | |
| @@ -469,23 +491,27 @@ | |||
| 469 | 491 | limit: i64, | |
| 470 | 492 | offset: i64, | |
| 471 | 493 | ) -> Result<Vec<PostWithAuthor>, sqlx::Error> { | |
| 472 | - | sqlx::query_as::<_, PostWithAuthor>( | |
| 473 | - | "SELECT p.id, p.author_id, | |
| 474 | - | COALESCE(u.display_name, u.username) AS author_name, | |
| 494 | + | sqlx::query_as!( | |
| 495 | + | PostWithAuthor, | |
| 496 | + | r#"SELECT p.id, p.author_id, | |
| 497 | + | COALESCE(u.display_name, u.username) AS "author_name!", | |
| 475 | 498 | u.username AS author_username, | |
| 476 | - | p.body_html, p.created_at, p.edited_at, p.deleted_at, | |
| 477 | - | p.removed_at, | |
| 499 | + | p.body_html, | |
| 500 | + | p.created_at AS "created_at: chrono::DateTime<chrono::Utc>", | |
| 501 | + | p.edited_at AS "edited_at: chrono::DateTime<chrono::Utc>", | |
| 502 | + | p.deleted_at AS "deleted_at: chrono::DateTime<chrono::Utc>", | |
| 503 | + | p.removed_at AS "removed_at: chrono::DateTime<chrono::Utc>", | |
| 478 | 504 | u.is_fan_plus AS author_is_fan_plus, | |
| 479 | 505 | u.signature_html AS author_signature_html | |
| 480 | 506 | FROM posts p | |
| 481 | 507 | JOIN users u ON u.mnw_account_id = p.author_id | |
| 482 | 508 | WHERE p.thread_id = $1 | |
| 483 | 509 | ORDER BY p.created_at | |
| 484 | - | LIMIT $2 OFFSET $3", | |
| 510 | + | LIMIT $2 OFFSET $3"#, | |
| 511 | + | thread_id, | |
| 512 | + | limit, | |
| 513 | + | offset, | |
| 485 | 514 | ) | |
| 486 | - | .bind(thread_id) | |
| 487 | - | .bind(limit) | |
| 488 | - | .bind(offset) | |
| 489 | 515 | .fetch_all(pool) | |
| 490 | 516 | .await | |
| 491 | 517 | } | |
| @@ -500,10 +526,10 @@ | |||
| 500 | 526 | // returns those rows so they render as tombstones. The count must match the | |
| 501 | 527 | // list it paginates, or the last page of tombstones would be cut off. For an | |
| 502 | 528 | // active-only count (e.g. the server-facing stats), use `get_thread_stats`. | |
| 503 | - | sqlx::query_scalar( | |
| 504 | - | "SELECT COUNT(*) FROM posts WHERE thread_id = $1", | |
| 529 | + | sqlx::query_scalar!( | |
| 530 | + | r#"SELECT COUNT(*) AS "count!" FROM posts WHERE thread_id = $1"#, | |
| 531 | + | thread_id, | |
| 505 | 532 | ) | |
| 506 | - | .bind(thread_id) | |
| 507 | 533 | .fetch_one(pool) | |
| 508 | 534 | .await | |
| 509 | 535 | } | |
| @@ -515,12 +541,12 @@ | |||
| 515 | 541 | user_id: Uuid, | |
| 516 | 542 | seconds: i64, | |
| 517 | 543 | ) -> Result<i64, sqlx::Error> { | |
| 518 | - | sqlx::query_scalar( | |
| 519 | - | "SELECT (SELECT COUNT(*) FROM posts WHERE author_id = $1 AND created_at > NOW() - make_interval(secs => $2)) | |
| 520 | - | + (SELECT COUNT(*) FROM post_footnotes WHERE author_id = $1 AND created_at > NOW() - make_interval(secs => $2))", | |
| 544 | + | sqlx::query_scalar!( | |
| 545 | + | r#"SELECT (SELECT COUNT(*) FROM posts WHERE author_id = $1 AND created_at > NOW() - make_interval(secs => $2)) | |
| 546 | + | + (SELECT COUNT(*) FROM post_footnotes WHERE author_id = $1 AND created_at > NOW() - make_interval(secs => $2)) AS "count!""#, | |
| 547 | + | user_id, | |
| 548 | + | seconds as f64, | |
| 521 | 549 | ) | |
| 522 | - | .bind(user_id) | |
| 523 | - | .bind(seconds as f64) | |
| 524 | 550 | .fetch_one(pool) | |
| 525 | 551 | .await | |
| 526 | 552 | } | |
| @@ -530,8 +556,11 @@ | |||
| 530 | 556 | pool: &PgPool, | |
| 531 | 557 | post_id: Uuid, | |
| 532 | 558 | ) -> Result<Option<PostForEdit>, sqlx::Error> { | |
| 533 | - | sqlx::query_as::<_, PostForEdit>( | |
| 534 | - | "SELECT p.id, p.author_id, p.body_markdown, p.created_at, p.deleted_at, | |
| 559 | + | sqlx::query_as!( | |
| 560 | + | PostForEdit, | |
| 561 | + | r#"SELECT p.id, p.author_id, p.body_markdown, | |
| 562 | + | p.created_at AS "created_at: chrono::DateTime<chrono::Utc>", | |
| 563 | + | p.deleted_at AS "deleted_at: chrono::DateTime<chrono::Utc>", | |
| 535 | 564 | p.thread_id, t.title AS thread_title, | |
| 536 | 565 | co.name AS community_name, co.slug AS community_slug, | |
| 537 | 566 | co.id AS community_id, | |
| @@ -540,9 +569,9 @@ | |||
| 540 | 569 | JOIN threads t ON t.id = p.thread_id | |
| 541 | 570 | JOIN categories c ON c.id = t.category_id | |
| 542 | 571 | JOIN communities co ON co.id = c.community_id | |
| 543 | - | WHERE p.id = $1", | |
| 572 | + | WHERE p.id = $1"#, | |
| 573 | + | post_id, | |
| 544 | 574 | ) | |
| 545 | - | .bind(post_id) | |
| 546 | 575 | .fetch_optional(pool) | |
| 547 | 576 | .await | |
| 548 | 577 | } | |
| @@ -553,14 +582,13 @@ | |||
| 553 | 582 | user_id: Uuid, | |
| 554 | 583 | community_id: Uuid, | |
| 555 | 584 | ) -> Result<Option<CommunityRole>, sqlx::Error> { | |
| 556 | - | let row: Option<(CommunityRole,)> = sqlx::query_as( | |
| 557 | - | "SELECT role FROM memberships WHERE user_id = $1 AND community_id = $2", | |
| 585 | + | sqlx::query_scalar!( | |
| 586 | + | r#"SELECT role AS "role: CommunityRole" FROM memberships WHERE user_id = $1 AND community_id = $2"#, | |
| 587 | + | user_id, | |
| 588 | + | community_id, | |
| 558 | 589 | ) | |
| 559 | - | .bind(user_id) | |
| 560 | - | .bind(community_id) | |
| 561 | 590 | .fetch_optional(pool) | |
| 562 | - | .await?; | |
| 563 | - | Ok(row.map(|r| r.0)) | |
| 591 | + | .await | |
| 564 | 592 | } | |
| 565 | 593 | ||
| 566 | 594 | #[derive(sqlx::FromRow)] | |
| @@ -577,13 +605,14 @@ | |||
| 577 | 605 | pool: &PgPool, | |
| 578 | 606 | community_id: Uuid, | |
| 579 | 607 | ) -> Result<Vec<CategoryForSettings>, sqlx::Error> { | |
| 580 | - | sqlx::query_as::<_, CategoryForSettings>( | |
| 608 | + | sqlx::query_as!( | |
| 609 | + | CategoryForSettings, | |
| 581 | 610 | "SELECT id, name, slug, description, sort_order | |
| 582 | 611 | FROM categories | |
| 583 | 612 | WHERE community_id = $1 | |
| 584 | 613 | ORDER BY sort_order", | |
| 614 | + | community_id, | |
| 585 | 615 | ) | |
| 586 | - | .bind(community_id) | |
| 587 | 616 | .fetch_all(pool) | |
| 588 | 617 | .await | |
| 589 | 618 | } | |
| @@ -603,11 +632,12 @@ | |||
| 603 | 632 | limit: i64, | |
| 604 | 633 | offset: i64, | |
| 605 | 634 | ) -> Result<Vec<MemberRow>, sqlx::Error> { | |
| 606 | - | sqlx::query_as::<_, MemberRow>( | |
| 607 | - | "SELECT u.username, | |
| 635 | + | sqlx::query_as!( | |
| 636 | + | MemberRow, | |
| 637 | + | r#"SELECT u.username, | |
| 608 | 638 | u.display_name, | |
| 609 | - | m.role, | |
| 610 | - | m.joined_at | |
| 639 | + | m.role AS "role: CommunityRole", | |
| 640 | + | m.joined_at AS "joined_at: chrono::DateTime<chrono::Utc>" | |
| 611 | 641 | FROM memberships m | |
| 612 | 642 | JOIN users u ON u.mnw_account_id = m.user_id | |
| 613 | 643 | WHERE m.community_id = $1 | |
| @@ -619,11 +649,11 @@ | |||
| 619 | 649 | ELSE 3 | |
| 620 | 650 | END, | |
| 621 | 651 | m.joined_at | |
| 622 | - | LIMIT $2 OFFSET $3", | |
| 652 | + | LIMIT $2 OFFSET $3"#, | |
| 653 | + | community_id, | |
| 654 | + | limit, | |
| 655 | + | offset, | |
| 623 | 656 | ) | |
| 624 | - | .bind(community_id) | |
| 625 | - | .bind(limit) | |
| 626 | - | .bind(offset) | |
| 627 | 657 | .fetch_all(pool) | |
| 628 | 658 | .await | |
| 629 | 659 | } | |
| @@ -634,10 +664,12 @@ | |||
| 634 | 664 | pool: &PgPool, | |
| 635 | 665 | community_id: Uuid, | |
| 636 | 666 | ) -> Result<i64, sqlx::Error> { | |
| 637 | - | sqlx::query_scalar("SELECT COUNT(*) FROM memberships WHERE community_id = $1") | |
| 638 | - | .bind(community_id) | |
| 639 | - | .fetch_one(pool) | |
| 640 | - | .await | |
| 667 | + | sqlx::query_scalar!( | |
| 668 | + | r#"SELECT COUNT(*) AS "count!" FROM memberships WHERE community_id = $1"#, | |
| 669 | + | community_id, | |
| 670 | + | ) | |
| 671 | + | .fetch_one(pool) | |
| 672 | + | .await |
Lines truncated
| @@ -1,0 +1,7 @@ | |||
| 1 | + | # sqlx compile-time query checking uses the committed `.sqlx/` cache, not a live | |
| 2 | + | # database. After changing or adding any `query!`/`query_as!`/`query_scalar!` in | |
| 3 | + | # mt-db, regenerate it with `DATABASE_URL=postgres:///multithreaded cargo sqlx prepare --workspace` | |
| 4 | + | # and commit the result. Override locally with `SQLX_OFFLINE=false` to verify | |
| 5 | + | # against a live DB instead. | |
| 6 | + | [env] | |
| 7 | + | SQLX_OFFLINE = "true" |
A
multithreaded/.sqlx/query-03ade415463b367871d02a5b82d14b7f544ebfbeb40080d3194bcb72fa9a47ae.json
+15
| @@ -1,0 +1,15 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "UPDATE users SET suspended_at = now(), suspension_reason = $2 WHERE mnw_account_id = $1", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [], | |
| 6 | + | "parameters": { | |
| 7 | + | "Left": [ | |
| 8 | + | "Uuid", | |
| 9 | + | "Text" | |
| 10 | + | ] | |
| 11 | + | }, | |
| 12 | + | "nullable": [] | |
| 13 | + | }, | |
| 14 | + | "hash": "03ade415463b367871d02a5b82d14b7f544ebfbeb40080d3194bcb72fa9a47ae" | |
| 15 | + | } |
A
multithreaded/.sqlx/query-0768d2c0c86038fc01f2b662ab65c9fe45979876679ce94ae6431ff1600ed4ba.json
+16
| @@ -1,0 +1,16 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "UPDATE post_flags SET resolved_at = now(), resolved_by = $2, resolution = $3\n WHERE id = $1 AND resolved_at IS NULL", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [], | |
| 6 | + | "parameters": { | |
| 7 | + | "Left": [ | |
| 8 | + | "Uuid", | |
| 9 | + | "Uuid", | |
| 10 | + | "Text" | |
| 11 | + | ] | |
| 12 | + | }, | |
| 13 | + | "nullable": [] | |
| 14 | + | }, | |
| 15 | + | "hash": "0768d2c0c86038fc01f2b662ab65c9fe45979876679ce94ae6431ff1600ed4ba" | |
| 16 | + | } |
A
multithreaded/.sqlx/query-0830906e69639e409caf641739ac5ccc16662f9800fdccea7c1c0eeef5193f9e.json
+58
| @@ -1,0 +1,58 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "SELECT f.id, f.post_id, f.author_id,\n COALESCE(u.display_name, u.username) AS \"author_name!\",\n u.username AS author_username,\n f.body_html,\n f.created_at AS \"created_at: chrono::DateTime<chrono::Utc>\"\n FROM post_footnotes f\n JOIN users u ON u.mnw_account_id = f.author_id\n WHERE f.post_id = ANY($1)\n ORDER BY f.created_at", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [ | |
| 6 | + | { | |
| 7 | + | "ordinal": 0, | |
| 8 | + | "name": "id", | |
| 9 | + | "type_info": "Uuid" | |
| 10 | + | }, | |
| 11 | + | { | |
| 12 | + | "ordinal": 1, | |
| 13 | + | "name": "post_id", | |
| 14 | + | "type_info": "Uuid" | |
| 15 | + | }, | |
| 16 | + | { | |
| 17 | + | "ordinal": 2, | |
| 18 | + | "name": "author_id", | |
| 19 | + | "type_info": "Uuid" | |
| 20 | + | }, | |
| 21 | + | { | |
| 22 | + | "ordinal": 3, | |
| 23 | + | "name": "author_name!", | |
| 24 | + | "type_info": "Text" | |
| 25 | + | }, | |
| 26 | + | { | |
| 27 | + | "ordinal": 4, | |
| 28 | + | "name": "author_username", | |
| 29 | + | "type_info": "Text" | |
| 30 | + | }, | |
| 31 | + | { | |
| 32 | + | "ordinal": 5, | |
| 33 | + | "name": "body_html", | |
| 34 | + | "type_info": "Text" | |
| 35 | + | }, | |
| 36 | + | { | |
| 37 | + | "ordinal": 6, | |
| 38 | + | "name": "created_at: chrono::DateTime<chrono::Utc>", | |
| 39 | + | "type_info": "Timestamptz" | |
| 40 | + | } | |
| 41 | + | ], | |
| 42 | + | "parameters": { | |
| 43 | + | "Left": [ | |
| 44 | + | "UuidArray" | |
| 45 | + | ] | |
| 46 | + | }, | |
| 47 | + | "nullable": [ | |
| 48 | + | false, | |
| 49 | + | false, | |
| 50 | + | false, | |
| 51 | + | null, | |
| 52 | + | false, | |
| 53 | + | false, | |
| 54 | + | false | |
| 55 | + | ] | |
| 56 | + | }, | |
| 57 | + | "hash": "0830906e69639e409caf641739ac5ccc16662f9800fdccea7c1c0eeef5193f9e" | |
| 58 | + | } |
A
multithreaded/.sqlx/query-0a8b8dc6d47eb404cf7fb308e9cfa9083797b7cc54a716b151532b3c6f0034dc.json
+29
| @@ -1,0 +1,29 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "SELECT c.name, c.slug\n FROM categories c\n JOIN communities co ON co.id = c.community_id\n WHERE co.slug = $1 AND c.slug = $2", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [ | |
| 6 | + | { | |
| 7 | + | "ordinal": 0, | |
| 8 | + | "name": "name", | |
| 9 | + | "type_info": "Text" | |
| 10 | + | }, | |
| 11 | + | { | |
| 12 | + | "ordinal": 1, | |
| 13 | + | "name": "slug", | |
| 14 | + | "type_info": "Text" | |
| 15 | + | } | |
| 16 | + | ], | |
| 17 | + | "parameters": { | |
| 18 | + | "Left": [ | |
| 19 | + | "Text", | |
| 20 | + | "Text" | |
| 21 | + | ] | |
| 22 | + | }, | |
| 23 | + | "nullable": [ | |
| 24 | + | false, | |
| 25 | + | false | |
| 26 | + | ] | |
| 27 | + | }, | |
| 28 | + | "hash": "0a8b8dc6d47eb404cf7fb308e9cfa9083797b7cc54a716b151532b3c6f0034dc" | |
| 29 | + | } |
A
multithreaded/.sqlx/query-0ba3bb2135932053e74fee53d32e33138819e31a184841df4b365f8e4c32b541.json
+71
| @@ -1,0 +1,71 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "SELECT cb.id, cb.user_id,\n u.username, u.display_name,\n cb.ban_type AS \"ban_type: BanType\", cb.reason,\n cb.expires_at AS \"expires_at: chrono::DateTime<chrono::Utc>\",\n cb.created_at AS \"created_at: chrono::DateTime<chrono::Utc>\",\n actor.username AS banned_by_username\n FROM community_bans cb\n JOIN users u ON u.mnw_account_id = cb.user_id\n JOIN users actor ON actor.mnw_account_id = cb.banned_by\n WHERE cb.community_id = $1\n AND (cb.expires_at IS NULL OR cb.expires_at > now())\n ORDER BY cb.created_at DESC\n LIMIT $2", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [ | |
| 6 | + | { | |
| 7 | + | "ordinal": 0, | |
| 8 | + | "name": "id", | |
| 9 | + | "type_info": "Uuid" | |
| 10 | + | }, | |
| 11 | + | { | |
| 12 | + | "ordinal": 1, | |
| 13 | + | "name": "user_id", | |
| 14 | + | "type_info": "Uuid" | |
| 15 | + | }, | |
| 16 | + | { | |
| 17 | + | "ordinal": 2, | |
| 18 | + | "name": "username", | |
| 19 | + | "type_info": "Text" | |
| 20 | + | }, | |
| 21 | + | { | |
| 22 | + | "ordinal": 3, | |
| 23 | + | "name": "display_name", | |
| 24 | + | "type_info": "Text" | |
| 25 | + | }, | |
| 26 | + | { | |
| 27 | + | "ordinal": 4, | |
| 28 | + | "name": "ban_type: BanType", | |
| 29 | + | "type_info": "Text" | |
| 30 | + | }, | |
| 31 | + | { | |
| 32 | + | "ordinal": 5, | |
| 33 | + | "name": "reason", | |
| 34 | + | "type_info": "Text" | |
| 35 | + | }, | |
| 36 | + | { | |
| 37 | + | "ordinal": 6, | |
| 38 | + | "name": "expires_at: chrono::DateTime<chrono::Utc>", | |
| 39 | + | "type_info": "Timestamptz" | |
| 40 | + | }, | |
| 41 | + | { | |
| 42 | + | "ordinal": 7, | |
| 43 | + | "name": "created_at: chrono::DateTime<chrono::Utc>", | |
| 44 | + | "type_info": "Timestamptz" | |
| 45 | + | }, | |
| 46 | + | { | |
| 47 | + | "ordinal": 8, | |
| 48 | + | "name": "banned_by_username", | |
| 49 | + | "type_info": "Text" | |
| 50 | + | } | |
| 51 | + | ], | |
| 52 | + | "parameters": { | |
| 53 | + | "Left": [ | |
| 54 | + | "Uuid", | |
| 55 | + | "Int8" | |
| 56 | + | ] | |
| 57 | + | }, | |
| 58 | + | "nullable": [ | |
| 59 | + | false, | |
| 60 | + | false, | |
| 61 | + | false, | |
| 62 | + | true, | |
| 63 | + | false, | |
| 64 | + | true, | |
| 65 | + | true, | |
| 66 | + | false, | |
| 67 | + | false | |
| 68 | + | ] | |
| 69 | + | }, | |
| 70 | + | "hash": "0ba3bb2135932053e74fee53d32e33138819e31a184841df4b365f8e4c32b541" | |
| 71 | + | } |
A
multithreaded/.sqlx/query-0c8bd7af0ecbc3729357019179bdd62a54de1c15b51ccb7d10febf32886a3a5a.json
+15
| @@ -1,0 +1,15 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "UPDATE communities SET state = $2 WHERE id = $1", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [], | |
| 6 | + | "parameters": { | |
| 7 | + | "Left": [ | |
| 8 | + | "Uuid", | |
| 9 | + | "Text" | |
| 10 | + | ] | |
| 11 | + | }, | |
| 12 | + | "nullable": [] | |
| 13 | + | }, | |
| 14 | + | "hash": "0c8bd7af0ecbc3729357019179bdd62a54de1c15b51ccb7d10febf32886a3a5a" | |
| 15 | + | } |
A
multithreaded/.sqlx/query-0d0bd14a16970a0c34e9e719b087616f5730863904aeed9caab237b8efb15e1f.json
+22
| @@ -1,0 +1,22 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "SELECT id FROM posts WHERE external_ref = $1", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [ | |
| 6 | + | { | |
| 7 | + | "ordinal": 0, | |
| 8 | + | "name": "id", | |
| 9 | + | "type_info": "Uuid" | |
| 10 | + | } | |
| 11 | + | ], | |
| 12 | + | "parameters": { | |
| 13 | + | "Left": [ | |
| 14 | + | "Text" | |
| 15 | + | ] | |
| 16 | + | }, | |
| 17 | + | "nullable": [ | |
| 18 | + | false | |
| 19 | + | ] | |
| 20 | + | }, | |
| 21 | + | "hash": "0d0bd14a16970a0c34e9e719b087616f5730863904aeed9caab237b8efb15e1f" | |
| 22 | + | } |
A
multithreaded/.sqlx/query-0feaed5a9d2365a4a0d18d12d3e52fadf77592fd7c31e1959787dffc87b830f4.json
+34
| @@ -1,0 +1,34 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "SELECT id, name, slug FROM tags WHERE community_id = $1 ORDER BY name", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [ | |
| 6 | + | { | |
| 7 | + | "ordinal": 0, | |
| 8 | + | "name": "id", | |
| 9 | + | "type_info": "Uuid" | |
| 10 | + | }, | |
| 11 | + | { | |
| 12 | + | "ordinal": 1, | |
| 13 | + | "name": "name", | |
| 14 | + | "type_info": "Text" | |
| 15 | + | }, | |
| 16 | + | { | |
| 17 | + | "ordinal": 2, | |
| 18 | + | "name": "slug", | |
| 19 | + | "type_info": "Text" | |
| 20 | + | } | |
| 21 | + | ], | |
| 22 | + | "parameters": { | |
| 23 | + | "Left": [ | |
| 24 | + | "Uuid" | |
| 25 | + | ] | |
| 26 | + | }, | |
| 27 | + | "nullable": [ | |
| 28 | + | false, | |
| 29 | + | false, | |
| 30 | + | false | |
| 31 | + | ] | |
| 32 | + | }, | |
| 33 | + | "hash": "0feaed5a9d2365a4a0d18d12d3e52fadf77592fd7c31e1959787dffc87b830f4" | |
| 34 | + | } |
A
multithreaded/.sqlx/query-11529b2b1e38fc99f7732f2080c172fd2691ce72ff8f231d462bf9d7a0d085d6.json
+25
| @@ -1,0 +1,25 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "INSERT INTO post_footnotes (post_id, author_id, body_markdown, body_html)\n VALUES ($1, $2, $3, $4)\n RETURNING id", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [ | |
| 6 | + | { | |
| 7 | + | "ordinal": 0, | |
| 8 | + | "name": "id", | |
| 9 | + | "type_info": "Uuid" | |
| 10 | + | } | |
| 11 | + | ], | |
| 12 | + | "parameters": { | |
| 13 | + | "Left": [ | |
| 14 | + | "Uuid", | |
| 15 | + | "Uuid", | |
| 16 | + | "Text", | |
| 17 | + | "Text" | |
| 18 | + | ] | |
| 19 | + | }, | |
| 20 | + | "nullable": [ | |
| 21 | + | false | |
| 22 | + | ] | |
| 23 | + | }, | |
| 24 | + | "hash": "11529b2b1e38fc99f7732f2080c172fd2691ce72ff8f231d462bf9d7a0d085d6" | |
| 25 | + | } |
A
multithreaded/.sqlx/query-150877ff6d3dd6d1c8034f0f5f9af152b5759f9c0b7e0c3549b636be95f65f89.json
+54
| @@ -1,0 +1,54 @@ | |||
| 1 | + | { | |
| 2 | + | "db_name": "PostgreSQL", | |
| 3 | + | "query": "SELECT t.id AS thread_id,\n t.title AS thread_title,\n c.name AS category_name,\n c.slug AS category_slug,\n p.created_at AS \"post_created_at: chrono::DateTime<chrono::Utc>\",\n (t.author_id = $2) AS \"is_thread_author!\"\n FROM posts p\n JOIN threads t ON t.id = p.thread_id\n JOIN categories c ON c.id = t.category_id\n WHERE c.community_id = $1\n AND p.author_id = $2\n AND p.removed_at IS NULL\n AND p.deleted_at IS NULL\n AND t.deleted_at IS NULL\n ORDER BY p.created_at DESC\n LIMIT $3", | |
| 4 | + | "describe": { | |
| 5 | + | "columns": [ | |
| 6 | + | { | |
| 7 | + | "ordinal": 0, | |
| 8 | + | "name": "thread_id", | |
| 9 | + | "type_info": "Uuid" | |
| 10 | + | }, | |
| 11 | + | { | |
| 12 | + | "ordinal": 1, | |
| 13 | + | "name": "thread_title", | |
| 14 | + | "type_info": "Text" | |
| 15 | + | }, | |
| 16 | + | { | |
| 17 | + | "ordinal": 2, | |
| 18 | + | "name": "category_name", | |
| 19 | + | "type_info": "Text" | |
| 20 | + | }, | |
| 21 | + | { | |
| 22 | + | "ordinal": 3, | |
| 23 | + | "name": "category_slug", | |
| 24 | + | "type_info": "Text" | |
| 25 | + | }, | |
| 26 | + | { | |
| 27 | + | "ordinal": 4, | |
| 28 | + | "name": "post_created_at: chrono::DateTime<chrono::Utc>", | |
| 29 | + | "type_info": "Timestamptz" | |
| 30 | + | }, | |
| 31 | + | { | |
| 32 | + | "ordinal": 5, | |
| 33 | + | "name": "is_thread_author!", | |
| 34 | + | "type_info": "Bool" | |
| 35 | + | } | |
| 36 | + | ], | |
| 37 | + | "parameters": { | |
| 38 | + | "Left": [ | |
| 39 | + | "Uuid", | |
| 40 | + | "Uuid", | |
| 41 | + | "Int8" | |
| 42 | + | ] | |
| 43 | + | }, | |
| 44 | + | "nullable": [ | |
| 45 | + | false, | |
| 46 | + | false, | |
| 47 | + | false, | |
| 48 | + | false, | |
| 49 | + | false, | |
| 50 | + | null | |
| 51 | + | ] | |
| 52 | + | }, | |
| 53 | + | "hash": "150877ff6d3dd6d1c8034f0f5f9af152b5759f9c0b7e0c3549b636be95f65f89" | |
| 54 | + | } |