#[cfg(test)]
mod tests;
#[must_use]
pub const fn cron_migration_sql() -> &'static str {
"\
CREATE TABLE IF NOT EXISTS _fraiseql_cron_state (
pk_cron_state BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
function_name TEXT NOT NULL,
cron_expr TEXT NOT NULL,
last_fired_at TIMESTAMPTZ NOT NULL,
next_fire_at TIMESTAMPTZ,
fire_count BIGINT NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (function_name, cron_expr)
);
CREATE INDEX IF NOT EXISTS idx_cron_state_function
ON _fraiseql_cron_state (function_name);
CREATE INDEX IF NOT EXISTS idx_cron_state_next_fire
ON _fraiseql_cron_state (next_fire_at)
WHERE next_fire_at IS NOT NULL;
"
}
#[must_use]
pub const fn inbound_migration_sql() -> &'static str {
"\
CREATE TABLE IF NOT EXISTS _fraiseql_inbound_message (
pk_inbound_message BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
id UUID NOT NULL DEFAULT gen_random_uuid(),
source TEXT NOT NULL,
idempotency_key TEXT NOT NULL,
thread_key TEXT,
payload JSONB NOT NULL,
received_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (source, idempotency_key)
);
CREATE INDEX IF NOT EXISTS idx_inbound_message_thread
ON _fraiseql_inbound_message (thread_key)
WHERE thread_key IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_inbound_message_received
ON _fraiseql_inbound_message (received_at);
"
}
#[must_use]
pub const fn send_tracking_migration_sql() -> &'static str {
"\
CREATE TABLE IF NOT EXISTS _fraiseql_send_status (
pk_send_status BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
send_id TEXT NOT NULL,
tenant_id TEXT,
recipient TEXT NOT NULL,
sending_address TEXT NOT NULL,
status TEXT NOT NULL,
challenge_count INT NOT NULL DEFAULT 0,
last_signal TEXT,
message_id TEXT,
sent_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_send_status_tenant_send
ON _fraiseql_send_status (COALESCE(tenant_id, ''), send_id);
-- The correlation path looks a send up by send_id alone (from the inbound
-- Return-Path plus-tag), without a session tenant, so it needs its own index.
CREATE INDEX IF NOT EXISTS idx_send_status_send_id
ON _fraiseql_send_status (send_id);
-- Fallback correlation: matching our sent message-id quoted in the inbound
-- References / In-Reply-To when the VERP plus-tag was stripped.
CREATE INDEX IF NOT EXISTS idx_send_status_message_id
ON _fraiseql_send_status (message_id)
WHERE message_id IS NOT NULL;
-- The challenge policy counts a recipient's pending challenges across campaigns.
CREATE INDEX IF NOT EXISTS idx_send_status_recipient
ON _fraiseql_send_status (recipient);
ALTER TABLE _fraiseql_send_status ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS p_send_status_tenant ON _fraiseql_send_status;
CREATE POLICY p_send_status_tenant ON _fraiseql_send_status
USING (tenant_id IS NOT DISTINCT FROM current_setting('fraiseql.tenant_id', true));
CREATE TABLE IF NOT EXISTS _fraiseql_suppression (
pk_suppression BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
tenant_id TEXT,
address_hash TEXT NOT NULL,
reason TEXT NOT NULL,
since TIMESTAMPTZ NOT NULL DEFAULT now(),
ttl TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_suppression_tenant_addr
ON _fraiseql_suppression (COALESCE(tenant_id, ''), address_hash);
ALTER TABLE _fraiseql_suppression ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS p_suppression_tenant ON _fraiseql_suppression;
CREATE POLICY p_suppression_tenant ON _fraiseql_suppression
USING (tenant_id IS NOT DISTINCT FROM current_setting('fraiseql.tenant_id', true));
"
}