-- 01-partition.sql
-- First-run initialization for new-api's partitioned logs table.
-- Executed by the postgres official entrypoint against the database named by
-- POSTGRES_DB (new-api), as a superuser.
--
-- This script is the SINGLE source of truth for the logs partitioning setup.
-- The new-api Go application does NOT create or maintain partitions; it only
-- skips GORM AutoMigrate for the logs table on PostgreSQL (see
-- model/main.go::ensureLogTable). pg_partman owns partition lifecycle, pg_cron
-- drives periodic maintenance.

-- 1. Extensions --------------------------------------------------------------
-- Install pg_partman into a dedicated schema named "partman" so its functions
-- are referenced as partman.xxx (pg_partman 5.x does not create the schema
-- automatically; without this CREATE EXTENSION lands in public and
-- partman.create_parent fails with "schema partman does not exist").
CREATE SCHEMA IF NOT EXISTS partman;
CREATE EXTENSION IF NOT EXISTS pg_partman WITH SCHEMA partman;
-- pg_cron must be created in the database set by cron.database_name (new-api),
-- which requires shared_preload_libraries='pg_cron' (configured in the image).
-- Include partman in the default search_path so its functions resolve without
-- schema-qualifying every call below.
SET search_path = partman, public;
CREATE EXTENSION IF NOT EXISTS pg_cron;

-- 2. Partitioned parent table -----------------------------------------------
-- Columns mirror model.Log exactly. PG requires the partition key (created_at)
-- to be part of the primary key, so it is a composite (id, created_at). The app
-- never queries logs by id alone, so this does not affect business logic.
CREATE TABLE IF NOT EXISTS public.logs (
    id               BIGSERIAL,
    user_id          INTEGER,
    created_at       BIGINT   NOT NULL,
    type             INTEGER,
    content          TEXT,
    username         VARCHAR(64)  DEFAULT '',
    token_name       VARCHAR(255) DEFAULT '',
    model_name       VARCHAR(255) DEFAULT '',
    quota            INTEGER  DEFAULT 0,
    prompt_tokens    INTEGER  DEFAULT 0,
    completion_tokens INTEGER DEFAULT 0,
    use_time         INTEGER  DEFAULT 0,
    is_stream        BOOLEAN,
    channel_id       INTEGER,
    token_id         INTEGER  DEFAULT 0,
    "group"          VARCHAR(255),
    ip               VARCHAR(64)  DEFAULT '',
    request_id       VARCHAR(64)  DEFAULT '',
    chat_id          VARCHAR(128) DEFAULT '',
    upstream_id      VARCHAR(128) DEFAULT '',
    other            TEXT,
    PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);

-- 3. Hand off to pg_partman --------------------------------------------------
-- Weekly native range partitioning on the bigint epoch (seconds) column.
--   p_epoch='seconds'           -> created_at is a unix-seconds bigint
--   p_type='range'              -> pg_partman 5.x uses PG-native partitioning with
--                                  p_type values 'range'/'list' (the old 'native'
--                                  alias from 4.x was removed)
--   p_interval='1 week'         -> one partition per week (pg_partman 5.x dropped
--                                  the 'weekly' preset in favor of native PG
--                                  interval values)
--   p_date_trunc_interval='week' -> align partition boundaries to ISO weeks (Monday)
--   p_premake=8                  -> always keep 8 future weeks pre-created
--   p_default_table=true         -> create a DEFAULT partition catching out-of-range
--                                   inserts so they never fail silently (RecordConsumeLog
--                                   only logs errors without retrying)
-- No retention is set: per current requirement we do NOT auto-drop old partitions.
SELECT partman.create_parent(
    p_parent_table        => 'public.logs',
    p_control             => 'created_at',
    p_type                => 'range',
    p_interval            => '1 week',
    p_epoch               => 'seconds',
    p_date_trunc_interval => 'week',
    p_premake             => 8,
    p_default_table       => true
);

-- 4. Core indexes on the parent table ---------------------------------------
-- PG 11+ propagates indexes created on a partitioned parent to all child
-- partitions automatically. These cover the hot query paths in model/log.go
-- (GetAllLogs / GetUserLogs ordering by created_at desc, id desc; lookups by
-- user/model/channel/request/token). Trimmed from the model's 16 index tags to
-- the 6 actually used, cutting index write overhead.
CREATE INDEX IF NOT EXISTS idx_logs_created_at_id     ON public.logs (created_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS idx_logs_user_id_created   ON public.logs (user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_logs_model_name        ON public.logs (model_name);
CREATE INDEX IF NOT EXISTS idx_logs_channel_id        ON public.logs (channel_id);
CREATE INDEX IF NOT EXISTS idx_logs_request_id        ON public.logs (request_id);
CREATE INDEX IF NOT EXISTS idx_logs_token_id          ON public.logs (token_id);

-- 5. Schedule periodic maintenance ------------------------------------------
-- run_maintenance_proc() inspects partman.part_config and premakes the next
-- partitions when needed. Every 30 minutes is more than enough; weekly partitions
-- only need creation roughly once a week. No retention => no drops.
SELECT cron.schedule(
    'log-partition-maint',
    '*/30 * * * *',
    $$CALL partman.run_maintenance_proc()$$
);
