Skip to main content

ff_backend_sqlite/queries/
budget.rs

1//! Budget-family SQL — SQLite dialect port of
2//! `ff-backend-postgres/src/budget.rs` raw-SQL constants.
3//!
4//! RFC-023 Phase 3.4 / RFC-020 Wave 9 Standalone-1.
5//!
6//! Placeholders are `?`-positional to match sqlx-sqlite; the PG
7//! reference uses `$1..$N`. Table shapes are identical (see
8//! `migrations/0002_budget.sql` + `migrations/0013_budget_policy_extensions.sql`).
9//!
10//! **Lock-mode note.** The PG reference uses `FOR NO KEY UPDATE` to
11//! serialise the breach-UPDATE path. SQLite's single-writer invariant
12//! (`BEGIN IMMEDIATE` acquires RESERVED) already serialises any two
13//! writers on the same connection pool, so no row-level locking hint
14//! is emitted. Write paths wrap in `retry_serializable` to absorb
15//! SQLITE_BUSY under contention per RFC-023 §4.3.
16
17// ── ff_budget_usage_dedup ──
18
19/// Idempotent dedup-row reservation. Inserts a placeholder row on
20/// first call; no-ops on replay. The `applied_at_ms` is returned by
21/// the caller via `RETURNING` for dedup-owned bookkeeping.
22pub(crate) const INSERT_DEDUP_PLACEHOLDER_SQL: &str = "\
23    INSERT INTO ff_budget_usage_dedup \
24        (partition_key, dedup_key, outcome_json, applied_at_ms, expires_at_ms) \
25    VALUES (?, ?, '{}', ?, ?) \
26    ON CONFLICT (partition_key, dedup_key) DO NOTHING \
27    RETURNING applied_at_ms";
28
29pub(crate) const SELECT_DEDUP_OUTCOME_SQL: &str = "\
30    SELECT outcome_json FROM ff_budget_usage_dedup \
31    WHERE partition_key = ? AND dedup_key = ?";
32
33pub(crate) const UPDATE_DEDUP_OUTCOME_SQL: &str = "\
34    UPDATE ff_budget_usage_dedup SET outcome_json = ? \
35    WHERE partition_key = ? AND dedup_key = ?";
36
37// ── ff_budget_policy ──
38
39/// `create_budget` upsert. Seeds `next_reset_at_ms = now + interval`
40/// when interval > 0 (matches Valkey `ZADD resets_zset` scheduling at
41/// flowfabric.lua:6522-6526). Idempotent on `(partition_key, budget_id)`.
42pub(crate) const INSERT_BUDGET_POLICY_SQL: &str = "\
43    INSERT INTO ff_budget_policy \
44        (partition_key, budget_id, policy_json, scope_type, scope_id, \
45         enforcement_mode, breach_count, soft_breach_count, \
46         last_breach_at_ms, last_breach_dim, next_reset_at_ms, \
47         created_at_ms, updated_at_ms) \
48    VALUES (?, ?, ?, ?, ?, ?, 0, 0, NULL, NULL, \
49            CASE WHEN ? > 0 THEN ? + ? ELSE NULL END, \
50            ?, ?) \
51    ON CONFLICT (partition_key, budget_id) DO NOTHING \
52    RETURNING created_at_ms";
53
54pub(crate) const SELECT_BUDGET_POLICY_ROW_SQL: &str = "\
55    SELECT policy_json FROM ff_budget_policy \
56    WHERE partition_key = ? AND budget_id = ?";
57
58pub(crate) const UPDATE_BUDGET_HARD_BREACH_SQL: &str = "\
59    UPDATE ff_budget_policy \
60    SET breach_count      = breach_count + 1, \
61        last_breach_at_ms = ?, \
62        last_breach_dim   = ?, \
63        updated_at_ms     = ? \
64    WHERE partition_key = ? AND budget_id = ?";
65
66pub(crate) const UPDATE_BUDGET_SOFT_BREACH_SQL: &str = "\
67    UPDATE ff_budget_policy \
68    SET soft_breach_count = soft_breach_count + 1, \
69        updated_at_ms     = ? \
70    WHERE partition_key = ? AND budget_id = ?";
71
72/// Zero breach metadata + reschedule `next_reset_at_ms` on reset.
73/// Returns the new `next_reset_at_ms` (possibly NULL when no interval).
74pub(crate) const UPDATE_BUDGET_RESET_SQL: &str = "\
75    UPDATE ff_budget_policy \
76    SET last_breach_at_ms = NULL, \
77        last_breach_dim   = NULL, \
78        updated_at_ms     = ?, \
79        next_reset_at_ms  = CASE WHEN ? > 0 THEN ? + ? ELSE NULL END \
80    WHERE partition_key = ? AND budget_id = ? \
81    RETURNING next_reset_at_ms";
82
83pub(crate) const SELECT_BUDGET_STATUS_SQL: &str = "\
84    SELECT scope_type, scope_id, enforcement_mode, \
85           breach_count, soft_breach_count, \
86           last_breach_at_ms, last_breach_dim, \
87           next_reset_at_ms, created_at_ms, \
88           policy_json \
89    FROM ff_budget_policy \
90    WHERE partition_key = ? AND budget_id = ?";
91
92// ── ff_budget_usage ──
93
94pub(crate) const INSERT_USAGE_ROW_SQL: &str = "\
95    INSERT INTO ff_budget_usage \
96        (partition_key, budget_id, dimensions_key, current_value, updated_at_ms) \
97    VALUES (?, ?, ?, 0, ?) \
98    ON CONFLICT (partition_key, budget_id, dimensions_key) DO NOTHING";
99
100pub(crate) const SELECT_USAGE_ROW_SQL: &str = "\
101    SELECT current_value FROM ff_budget_usage \
102    WHERE partition_key = ? AND budget_id = ? AND dimensions_key = ?";
103
104pub(crate) const UPDATE_USAGE_INCREMENT_SQL: &str = "\
105    UPDATE ff_budget_usage \
106    SET current_value = current_value + ?, updated_at_ms = ? \
107    WHERE partition_key = ? AND budget_id = ? AND dimensions_key = ?";
108
109pub(crate) const UPDATE_USAGE_ZERO_ALL_SQL: &str = "\
110    UPDATE ff_budget_usage \
111    SET current_value = 0, last_reset_at_ms = ?, updated_at_ms = ? \
112    WHERE partition_key = ? AND budget_id = ?";
113
114pub(crate) const SELECT_ALL_USAGE_ROWS_SQL: &str = "\
115    SELECT dimensions_key, current_value FROM ff_budget_usage \
116    WHERE partition_key = ? AND budget_id = ?";