Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions crates/buzz-db/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,27 @@ impl Db {
Ok(result)
}

/// Backfill `d_tag` for existing NIP-33 events (kind 30000–39999) that have `d_tag IS NULL`.
///
/// Idempotent — safe to call on every startup. No-ops when all rows are already populated.
/// Runs a single UPDATE touching only NIP-33 rows with NULL d_tag.
#[datastore_span(name = "backfill_d_tags", system = "postgresql")]
pub async fn backfill_d_tags(&self) -> Result<u64> {
let result = sqlx::query(
"UPDATE events \
SET d_tag = COALESCE( \
(SELECT elem->>1 FROM jsonb_array_elements(tags) AS elem \
WHERE elem->>0 = 'd' LIMIT 1), \
'' \
) \
WHERE kind BETWEEN 30000 AND 39999 AND d_tag IS NULL \
AND community_write_allowed(community_id)",
)
.execute(&self.pool)
.await?;
Ok(result.rows_affected())
}

/// Soft-delete NIP-29 discovery events for a channel created by a specific relay pubkey.
#[datastore_span(name = "soft_delete_discovery_events", system = "postgresql")]
pub async fn soft_delete_discovery_events(
Expand Down
27 changes: 0 additions & 27 deletions crates/buzz-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,33 +1138,6 @@ impl Db {
}
}
}

/// Ensures monthly partitions exist for the next N months.
#[datastore_span(name = "ensure_future_partitions", system = "postgresql")]
pub async fn ensure_future_partitions(&self, months_ahead: u32) -> Result<()> {
partition::ensure_future_partitions(&self.pool, months_ahead).await
}

/// Backfill `d_tag` for existing NIP-33 events (kind 30000–39999) that have `d_tag IS NULL`.
///
/// Idempotent — safe to call on every startup. No-ops when all rows are already populated.
/// Runs a single UPDATE touching only NIP-33 rows with NULL d_tag.
#[datastore_span(name = "backfill_d_tags", system = "postgresql")]
pub async fn backfill_d_tags(&self) -> Result<u64> {
let result = sqlx::query(
"UPDATE events \
SET d_tag = COALESCE( \
(SELECT elem->>1 FROM jsonb_array_elements(tags) AS elem \
WHERE elem->>0 = 'd' LIMIT 1), \
'' \
) \
WHERE kind BETWEEN 30000 AND 39999 AND d_tag IS NULL \
AND community_write_allowed(community_id)",
)
.execute(&self.pool)
.await?;
Ok(result.rows_affected())
}
}

#[cfg(test)]
Expand Down
10 changes: 10 additions & 0 deletions crates/buzz-db/src/partition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
//!
//! Call `ensure_future_partitions` on startup and monthly via cron.

use buzz_datastore_tracing::datastore_span;
use chrono::{Datelike, TimeZone, Utc};
use sqlx::{PgPool, Row};
use tracing::info;

use crate::error::{DbError, Result};
use crate::Db;

/// Tables that may be partition-managed. Allowlist prevents DDL injection.
const PARTITIONED_TABLES: &[&str] = &["events", "delivery_log"];
Expand Down Expand Up @@ -55,6 +57,14 @@ pub async fn ensure_future_partitions(pool: &PgPool, months_ahead: u32) -> Resul
Ok(())
}

impl Db {
/// Ensures monthly partitions exist for the next N months.
#[datastore_span(name = "ensure_future_partitions", system = "postgresql")]
pub async fn ensure_future_partitions(&self, months_ahead: u32) -> Result<()> {
ensure_future_partitions(&self.pool, months_ahead).await
}
}

/// Validate that a partition suffix is digits and underscores only.
fn validate_partition_suffix(suffix: &str) -> bool {
!suffix.is_empty() && suffix.chars().all(|c| c.is_ascii_digit() || c == '_')
Expand Down
Loading