dify/knowledge-fs/packages/database/migrations/0011_tidb_fts_postings.postgres.sql
Jyong 4ee43b8afc chore: migrate knowledge-fs source tree
Import the committed KnowledgeFS snapshot dc4072ee302317145612087ce7440851dc329fd0 under knowledge-fs/ without its Git history, local IDE settings, or build artifacts.
2026-07-20 04:54:20 -04:00

126 lines
4.9 KiB
SQL

-- Knowledge Platform schema migration
-- Migration id: 0011_tidb_fts_postings
-- Dialect: postgres
-- PostgreSQL continues to query index_projections.fts_document through its native GIN index.
-- Keep the portable posting catalog available so the schema and lifecycle contract remain the
-- same across database dialects; application writes populate it only for TiDB.
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'projection_set_publications_status_ck'
AND conrelid = 'projection_set_publications'::regclass
) THEN
ALTER TABLE "projection_set_publications"
ADD CONSTRAINT "projection_set_publications_status_ck"
CHECK ("status" IN ('candidate', 'inactive', 'published', 'superseded', 'validating'));
END IF;
END
$$;
CREATE UNIQUE INDEX IF NOT EXISTS "index_projections_space_id_uq"
ON "index_projections" ("knowledge_space_id", "id");
CREATE INDEX IF NOT EXISTS "index_projections_fts_backfill_idx"
ON "index_projections" ("knowledge_space_id", "id");
CREATE TABLE IF NOT EXISTS "index_projection_fts_postings" (
"id" UUID PRIMARY KEY NOT NULL,
"knowledge_space_id" UUID NOT NULL,
"projection_id" UUID NOT NULL,
"tokenizer_version" VARCHAR(64) NOT NULL,
"term_hash" CHAR(64) NOT NULL,
"term" VARCHAR(128) NOT NULL,
"term_frequency" INTEGER NOT NULL,
"document_token_count" INTEGER NOT NULL,
CONSTRAINT "index_projection_fts_postings_frequency_ck"
CHECK ("term_frequency" > 0 AND "document_token_count" >= "term_frequency"),
FOREIGN KEY ("knowledge_space_id") REFERENCES "knowledge_spaces" ("id") ON DELETE CASCADE,
FOREIGN KEY ("knowledge_space_id", "projection_id")
REFERENCES "index_projections" ("knowledge_space_id", "id") ON DELETE CASCADE
);
CREATE UNIQUE INDEX IF NOT EXISTS "index_projection_fts_postings_projection_term_uq"
ON "index_projection_fts_postings" ("projection_id", "tokenizer_version", "term_hash");
CREATE INDEX IF NOT EXISTS "index_projection_fts_postings_lookup_idx"
ON "index_projection_fts_postings" ("knowledge_space_id", "term_hash", "projection_id");
-- A durable, tenant-scoped cursor replaces any migration-runner backfill. Deployments can first
-- enable the transactional dual writer, then let the bounded runtime lease and repair old spaces.
-- PostgreSQL does not consume this ledger for native GIN reads, but retaining the table in both
-- dialects keeps schema replay, operational tooling, and disaster-recovery artifacts symmetric.
CREATE TABLE IF NOT EXISTS "tidb_fts_posting_backfills" (
"id" UUID PRIMARY KEY NOT NULL,
"tenant_id" VARCHAR(255) NOT NULL,
"knowledge_space_id" UUID NOT NULL,
"tokenizer_version" VARCHAR(64) NOT NULL,
"run_state" VARCHAR(16) NOT NULL,
"cursor_projection_id" UUID,
"scanned_projections" INTEGER NOT NULL,
"written_postings" INTEGER NOT NULL,
"worker_id" VARCHAR(255),
"lease_token" UUID,
"lease_expires_at" TIMESTAMPTZ,
"heartbeat_at" TIMESTAMPTZ,
"retry_count" INTEGER NOT NULL,
"row_version" INTEGER NOT NULL,
"last_error_code" VARCHAR(64),
"last_error_message" TEXT,
"created_at" TIMESTAMPTZ NOT NULL,
"updated_at" TIMESTAMPTZ NOT NULL,
"completed_at" TIMESTAMPTZ,
CONSTRAINT "tidb_fts_posting_backfills_state_ck"
CHECK ("run_state" IN ('queued', 'running', 'succeeded', 'failed')),
CONSTRAINT "tidb_fts_posting_backfills_counts_ck"
CHECK (
"scanned_projections" >= 0
AND "written_postings" >= 0
AND "retry_count" >= 0
AND "row_version" >= 0
),
CONSTRAINT "tidb_fts_posting_backfills_lease_ck"
CHECK (
(
"run_state" = 'running'
AND "worker_id" IS NOT NULL
AND "lease_token" IS NOT NULL
AND "lease_expires_at" IS NOT NULL
AND "heartbeat_at" IS NOT NULL
AND "completed_at" IS NULL
)
OR (
"run_state" <> 'running'
AND "worker_id" IS NULL
AND "lease_token" IS NULL
AND "lease_expires_at" IS NULL
AND "heartbeat_at" IS NULL
)
),
CONSTRAINT "tidb_fts_posting_backfills_terminal_ck"
CHECK (
("run_state" IN ('succeeded', 'failed') AND "completed_at" IS NOT NULL)
OR ("run_state" IN ('queued', 'running') AND "completed_at" IS NULL)
),
CONSTRAINT "tidb_fts_posting_backfills_lease_token_ck"
CHECK (
"lease_token" IS NULL
OR "lease_token" <> '00000000-0000-0000-0000-000000000000'::uuid
),
FOREIGN KEY ("tenant_id", "knowledge_space_id")
REFERENCES "knowledge_spaces" ("tenant_id", "id") ON DELETE CASCADE
);
CREATE UNIQUE INDEX IF NOT EXISTS "tidb_fts_posting_backfills_space_tokenizer_uq"
ON "tidb_fts_posting_backfills" (
"tenant_id", "knowledge_space_id", "tokenizer_version"
);
CREATE INDEX IF NOT EXISTS "tidb_fts_posting_backfills_claim_idx"
ON "tidb_fts_posting_backfills" (
"run_state", "lease_expires_at", "updated_at", "id"
);
CREATE INDEX IF NOT EXISTS "tidb_fts_posting_backfills_scope_idx"
ON "tidb_fts_posting_backfills" (
"tenant_id", "knowledge_space_id", "tokenizer_version", "id"
);