Import the committed KnowledgeFS snapshot dc4072ee302317145612087ce7440851dc329fd0 under knowledge-fs/ without its Git history, local IDE settings, or build artifacts.
480 lines
16 KiB
SQL
480 lines
16 KiB
SQL
-- Knowledge Platform schema migration
|
|
-- Migration id: 0022_logical_document_revisions
|
|
-- Dialect: postgres
|
|
|
|
ALTER TABLE "deletion_jobs" DROP CONSTRAINT IF EXISTS "deletion_jobs_target_ck";
|
|
ALTER TABLE "deletion_jobs" ADD CONSTRAINT "deletion_jobs_target_ck" CHECK (
|
|
"target_type" IN ('knowledge_space', 'source', 'document_asset', 'logical_document')
|
|
AND (
|
|
("target_type" = 'source' AND "delete_mode" IN ('keep', 'cascade') AND "name_challenge_digest" IS NULL)
|
|
OR ("target_type" = 'knowledge_space' AND "delete_mode" = 'cascade' AND "name_challenge_digest" IS NOT NULL)
|
|
OR ("target_type" IN ('document_asset', 'logical_document') AND "delete_mode" = 'cascade' AND "name_challenge_digest" IS NULL)
|
|
)
|
|
);
|
|
ALTER TABLE "deletion_tombstones" DROP CONSTRAINT IF EXISTS "deletion_tombstones_target_ck";
|
|
ALTER TABLE "deletion_tombstones" ADD CONSTRAINT "deletion_tombstones_target_ck"
|
|
CHECK ("target_type" IN ('knowledge_space', 'source', 'document_asset', 'logical_document'));
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "sources_space_id_uq"
|
|
ON "sources" ("knowledge_space_id", "id");
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "document_compilation_attempts_scope_id_uq"
|
|
ON "document_compilation_attempts" ("tenant_id", "knowledge_space_id", "id");
|
|
|
|
CREATE TABLE IF NOT EXISTS "logical_documents" (
|
|
"id" UUID PRIMARY KEY NOT NULL,
|
|
"tenant_id" VARCHAR(255) NOT NULL,
|
|
"knowledge_space_id" UUID NOT NULL,
|
|
"source_id" UUID,
|
|
"provider_item_id" VARCHAR(1024),
|
|
"provider_item_digest" CHAR(64),
|
|
"title" TEXT NOT NULL,
|
|
"status" VARCHAR(16) NOT NULL,
|
|
"deletion_job_id" UUID,
|
|
"deleting_at" TIMESTAMPTZ,
|
|
"active_revision" INTEGER,
|
|
"row_version" INTEGER NOT NULL,
|
|
"system_metadata" JSONB NOT NULL,
|
|
"user_metadata" JSONB NOT NULL,
|
|
"created_at" TIMESTAMPTZ NOT NULL,
|
|
"updated_at" TIMESTAMPTZ NOT NULL,
|
|
CONSTRAINT "logical_documents_status_ck"
|
|
CHECK ("status" IN ('pending', 'ready', 'failed', 'deleting')),
|
|
CONSTRAINT "logical_documents_deletion_lifecycle_ck"
|
|
CHECK (
|
|
("status" = 'deleting' AND "deletion_job_id" IS NOT NULL AND "deleting_at" IS NOT NULL)
|
|
OR ("status" <> 'deleting' AND "deletion_job_id" IS NULL AND "deleting_at" IS NULL)
|
|
),
|
|
CONSTRAINT "logical_documents_active_revision_ck"
|
|
CHECK ("active_revision" IS NULL OR "active_revision" > 0),
|
|
CONSTRAINT "logical_documents_row_version_ck"
|
|
CHECK ("row_version" >= 0),
|
|
CONSTRAINT "logical_documents_provider_identity_ck"
|
|
CHECK (
|
|
(
|
|
"source_id" IS NULL
|
|
AND "provider_item_id" IS NULL
|
|
AND "provider_item_digest" IS NULL
|
|
)
|
|
OR (
|
|
"source_id" IS NOT NULL
|
|
AND "provider_item_id" IS NOT NULL
|
|
AND "provider_item_digest" ~ '^[a-f0-9]{64}$'
|
|
)
|
|
),
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id")
|
|
REFERENCES "knowledge_spaces" ("tenant_id", "id")
|
|
ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "logical_documents_scope_id_uq"
|
|
ON "logical_documents" ("tenant_id", "knowledge_space_id", "id");
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "logical_documents_provider_item_uq"
|
|
ON "logical_documents" ("provider_item_digest")
|
|
WHERE "provider_item_digest" IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS "logical_documents_space_cursor_idx"
|
|
ON "logical_documents" ("tenant_id", "knowledge_space_id", "created_at", "id");
|
|
|
|
CREATE TABLE IF NOT EXISTS "document_revisions" (
|
|
"tenant_id" VARCHAR(255) NOT NULL,
|
|
"knowledge_space_id" UUID NOT NULL,
|
|
"document_id" UUID NOT NULL,
|
|
"revision" INTEGER NOT NULL,
|
|
"document_asset_id" UUID NOT NULL,
|
|
"document_asset_version" INTEGER NOT NULL,
|
|
"compilation_attempt_id" UUID,
|
|
"expected_active_revision" INTEGER,
|
|
"expected_document_row_version" INTEGER NOT NULL,
|
|
"content_hash" VARCHAR(64) NOT NULL,
|
|
"mime_type" VARCHAR(255) NOT NULL,
|
|
"size_bytes" BIGINT NOT NULL,
|
|
"state" VARCHAR(16) NOT NULL,
|
|
"system_metadata" JSONB NOT NULL,
|
|
"created_at" TIMESTAMPTZ NOT NULL,
|
|
"activated_at" TIMESTAMPTZ,
|
|
PRIMARY KEY ("tenant_id", "knowledge_space_id", "document_id", "revision"),
|
|
CONSTRAINT "document_revisions_revision_ck" CHECK ("revision" > 0),
|
|
CONSTRAINT "document_revisions_asset_version_ck" CHECK ("document_asset_version" > 0),
|
|
CONSTRAINT "document_revisions_expected_active_ck"
|
|
CHECK ("expected_active_revision" IS NULL OR "expected_active_revision" > 0),
|
|
CONSTRAINT "document_revisions_expected_row_version_ck"
|
|
CHECK ("expected_document_row_version" >= 0),
|
|
CONSTRAINT "document_revisions_size_ck" CHECK ("size_bytes" >= 0),
|
|
CONSTRAINT "document_revisions_hash_ck" CHECK ("content_hash" ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT "document_revisions_state_ck"
|
|
CHECK ("state" IN ('candidate', 'active', 'superseded', 'failed')),
|
|
CONSTRAINT "document_revisions_activation_ck"
|
|
CHECK (
|
|
("state" IN ('active', 'superseded') AND "activated_at" IS NOT NULL)
|
|
OR ("state" IN ('candidate', 'failed') AND "activated_at" IS NULL)
|
|
),
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id", "document_id")
|
|
REFERENCES "logical_documents" ("tenant_id", "knowledge_space_id", "id")
|
|
ON DELETE CASCADE,
|
|
FOREIGN KEY ("knowledge_space_id", "document_asset_id", "document_asset_version")
|
|
REFERENCES "document_assets" ("knowledge_space_id", "id", "version")
|
|
ON DELETE RESTRICT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS "document_revisions_asset_idx"
|
|
ON "document_revisions" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_asset_id",
|
|
"document_asset_version"
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "document_revisions_compilation_attempt_uq"
|
|
ON "document_revisions" ("tenant_id", "knowledge_space_id", "compilation_attempt_id")
|
|
WHERE "compilation_attempt_id" IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS "document_revisions_history_idx"
|
|
ON "document_revisions" ("tenant_id", "knowledge_space_id", "document_id", "revision" DESC);
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM pg_constraint
|
|
WHERE conname = 'logical_documents_active_revision_fk'
|
|
AND conrelid = 'logical_documents'::regclass
|
|
) THEN
|
|
ALTER TABLE "logical_documents"
|
|
ADD CONSTRAINT "logical_documents_active_revision_fk"
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id", "id", "active_revision")
|
|
REFERENCES "document_revisions" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"revision"
|
|
)
|
|
DEFERRABLE INITIALLY DEFERRED;
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
CREATE TABLE IF NOT EXISTS "document_revision_chunks" (
|
|
"id" UUID PRIMARY KEY NOT NULL,
|
|
"tenant_id" VARCHAR(255) NOT NULL,
|
|
"knowledge_space_id" UUID NOT NULL,
|
|
"document_id" UUID NOT NULL,
|
|
"document_revision" INTEGER NOT NULL,
|
|
"parent_chunk_id" UUID,
|
|
"ordinal" INTEGER NOT NULL,
|
|
"token_count" INTEGER NOT NULL,
|
|
"text" TEXT NOT NULL,
|
|
"system_metadata" JSONB NOT NULL,
|
|
"user_metadata" JSONB NOT NULL,
|
|
"created_at" TIMESTAMPTZ NOT NULL,
|
|
UNIQUE ("tenant_id", "knowledge_space_id", "document_id", "document_revision", "id"),
|
|
CONSTRAINT "document_revision_chunks_ordinal_ck" CHECK ("ordinal" >= 0),
|
|
CONSTRAINT "document_revision_chunks_tokens_ck" CHECK ("token_count" >= 0),
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id", "document_id", "document_revision")
|
|
REFERENCES "document_revisions" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"revision"
|
|
)
|
|
ON DELETE CASCADE,
|
|
FOREIGN KEY (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"document_revision",
|
|
"parent_chunk_id"
|
|
) REFERENCES "document_revision_chunks" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"document_revision",
|
|
"id"
|
|
) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "document_revision_chunks_ordinal_uq"
|
|
ON "document_revision_chunks" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"document_revision",
|
|
"ordinal"
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "document_revision_chunks_cursor_idx"
|
|
ON "document_revision_chunks" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"document_revision",
|
|
"id"
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS "document_chunk_state_changes" (
|
|
"id" UUID PRIMARY KEY NOT NULL,
|
|
"tenant_id" VARCHAR(255) NOT NULL,
|
|
"knowledge_space_id" UUID NOT NULL,
|
|
"document_id" UUID NOT NULL,
|
|
"document_revision" INTEGER NOT NULL,
|
|
"chunk_id" UUID NOT NULL,
|
|
"enabled" BOOLEAN NOT NULL,
|
|
"state" VARCHAR(16) NOT NULL,
|
|
"compilation_attempt_id" UUID NOT NULL,
|
|
"candidate_publication_id" UUID,
|
|
"candidate_fingerprint" VARCHAR(86),
|
|
"created_at" TIMESTAMPTZ NOT NULL,
|
|
"activated_at" TIMESTAMPTZ,
|
|
CONSTRAINT "document_chunk_state_changes_state_ck"
|
|
CHECK ("state" IN ('candidate', 'active', 'superseded', 'failed')),
|
|
CONSTRAINT "document_chunk_state_changes_activation_ck"
|
|
CHECK (
|
|
("state" IN ('active', 'superseded') AND "activated_at" IS NOT NULL)
|
|
OR ("state" IN ('candidate', 'failed') AND "activated_at" IS NULL)
|
|
),
|
|
CONSTRAINT "document_chunk_state_changes_candidate_pair_ck"
|
|
CHECK (
|
|
("candidate_publication_id" IS NULL AND "candidate_fingerprint" IS NULL)
|
|
OR ("candidate_publication_id" IS NOT NULL AND "candidate_fingerprint" IS NOT NULL)
|
|
),
|
|
FOREIGN KEY (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"document_revision",
|
|
"chunk_id"
|
|
) REFERENCES "document_revision_chunks" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"document_revision",
|
|
"id"
|
|
) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "document_chunk_state_changes_candidate_uq"
|
|
ON "document_chunk_state_changes" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"document_revision",
|
|
"chunk_id",
|
|
"candidate_publication_id"
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "document_chunk_state_changes_attempt_uq"
|
|
ON "document_chunk_state_changes" (
|
|
"tenant_id", "knowledge_space_id", "compilation_attempt_id"
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "document_chunk_state_changes_active_idx"
|
|
ON "document_chunk_state_changes" ("chunk_id", "state", "activated_at", "id");
|
|
|
|
CREATE TABLE IF NOT EXISTS "document_settings_revisions" (
|
|
"tenant_id" VARCHAR(255) NOT NULL,
|
|
"knowledge_space_id" UUID NOT NULL,
|
|
"document_id" UUID NOT NULL,
|
|
"revision" INTEGER NOT NULL,
|
|
"settings" JSONB NOT NULL,
|
|
"state" VARCHAR(16) NOT NULL,
|
|
"created_by_subject_id" VARCHAR(255) NOT NULL,
|
|
"created_at" TIMESTAMPTZ NOT NULL,
|
|
"activated_at" TIMESTAMPTZ,
|
|
PRIMARY KEY ("tenant_id", "knowledge_space_id", "document_id", "revision"),
|
|
CONSTRAINT "document_settings_revisions_revision_ck" CHECK ("revision" > 0),
|
|
CONSTRAINT "document_settings_revisions_state_ck"
|
|
CHECK ("state" IN ('candidate', 'active', 'superseded', 'failed')),
|
|
CONSTRAINT "document_settings_revisions_activation_ck"
|
|
CHECK (
|
|
("state" IN ('active', 'superseded') AND "activated_at" IS NOT NULL)
|
|
OR ("state" IN ('candidate', 'failed') AND "activated_at" IS NULL)
|
|
),
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id", "document_id")
|
|
REFERENCES "logical_documents" ("tenant_id", "knowledge_space_id", "id")
|
|
ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS "document_settings_heads" (
|
|
"tenant_id" VARCHAR(255) NOT NULL,
|
|
"knowledge_space_id" UUID NOT NULL,
|
|
"document_id" UUID NOT NULL,
|
|
"active_revision" INTEGER NOT NULL,
|
|
"row_version" INTEGER NOT NULL,
|
|
"updated_at" TIMESTAMPTZ NOT NULL,
|
|
PRIMARY KEY ("tenant_id", "knowledge_space_id", "document_id"),
|
|
CONSTRAINT "document_settings_heads_revision_ck" CHECK ("active_revision" > 0),
|
|
CONSTRAINT "document_settings_heads_row_version_ck" CHECK ("row_version" >= 0),
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id", "document_id", "active_revision")
|
|
REFERENCES "document_settings_revisions" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"revision"
|
|
)
|
|
ON DELETE RESTRICT
|
|
DEFERRABLE INITIALLY DEFERRED
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS "document_reindex_attempts" (
|
|
"id" UUID PRIMARY KEY NOT NULL,
|
|
"tenant_id" VARCHAR(255) NOT NULL,
|
|
"knowledge_space_id" UUID NOT NULL,
|
|
"document_id" UUID NOT NULL,
|
|
"document_revision" INTEGER NOT NULL,
|
|
"settings_revision" INTEGER NOT NULL,
|
|
"expected_settings_head_revision" INTEGER NOT NULL,
|
|
"state" VARCHAR(16) NOT NULL,
|
|
"active_slot" INTEGER,
|
|
"compilation_attempt_id" UUID NOT NULL,
|
|
"candidate_publication_id" UUID,
|
|
"candidate_fingerprint" VARCHAR(86),
|
|
"row_version" INTEGER NOT NULL,
|
|
"error_code" VARCHAR(64),
|
|
"error_message" TEXT,
|
|
"created_at" TIMESTAMPTZ NOT NULL,
|
|
"updated_at" TIMESTAMPTZ NOT NULL,
|
|
"completed_at" TIMESTAMPTZ,
|
|
CONSTRAINT "document_reindex_attempts_state_ck"
|
|
CHECK ("state" IN ('queued', 'running', 'succeeded', 'failed', 'canceled')),
|
|
CONSTRAINT "document_reindex_attempts_active_slot_ck"
|
|
CHECK ("active_slot" IS NULL OR "active_slot" = 1),
|
|
CONSTRAINT "document_reindex_attempts_row_version_ck" CHECK ("row_version" >= 0),
|
|
CONSTRAINT "document_reindex_attempts_expected_settings_head_revision_ck"
|
|
CHECK ("expected_settings_head_revision" > 0),
|
|
CONSTRAINT "document_reindex_attempts_lifecycle_ck"
|
|
CHECK (
|
|
("state" IN ('queued', 'running') AND "active_slot" = 1 AND "completed_at" IS NULL)
|
|
OR
|
|
("state" IN ('succeeded', 'failed', 'canceled') AND "active_slot" IS NULL AND "completed_at" IS NOT NULL)
|
|
),
|
|
CONSTRAINT "document_reindex_attempts_candidate_pair_ck"
|
|
CHECK (
|
|
("candidate_publication_id" IS NULL AND "candidate_fingerprint" IS NULL)
|
|
OR
|
|
("candidate_publication_id" IS NOT NULL AND "candidate_fingerprint" IS NOT NULL)
|
|
),
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id", "document_id", "document_revision")
|
|
REFERENCES "document_revisions" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"revision"
|
|
) ON DELETE RESTRICT,
|
|
FOREIGN KEY ("tenant_id", "knowledge_space_id", "document_id", "settings_revision")
|
|
REFERENCES "document_settings_revisions" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"revision"
|
|
) ON DELETE RESTRICT
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "document_reindex_attempts_active_uq"
|
|
ON "document_reindex_attempts" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"active_slot"
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "document_reindex_attempts_cursor_idx"
|
|
ON "document_reindex_attempts" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"created_at",
|
|
"id"
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS "document_compilation_attempts_space_cursor_idx"
|
|
ON "document_compilation_attempts" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"created_at",
|
|
"id"
|
|
);
|
|
|
|
-- Compatibility bridge: every pre-existing DocumentAsset remains addressable as a logical
|
|
-- document with one immutable active revision. New provider imports may instead attach additional
|
|
-- assets as later revisions of a stable logical document.
|
|
INSERT INTO "logical_documents" (
|
|
"id",
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"source_id",
|
|
"provider_item_id",
|
|
"title",
|
|
"status",
|
|
"active_revision",
|
|
"row_version",
|
|
"system_metadata",
|
|
"user_metadata",
|
|
"created_at",
|
|
"updated_at"
|
|
)
|
|
SELECT
|
|
asset."id",
|
|
space."tenant_id",
|
|
asset."knowledge_space_id",
|
|
NULL,
|
|
NULL,
|
|
asset."filename",
|
|
CASE
|
|
WHEN asset."parser_status" = 'parsed' THEN 'ready'
|
|
WHEN asset."parser_status" = 'failed' THEN 'failed'
|
|
ELSE 'pending'
|
|
END,
|
|
NULL,
|
|
0,
|
|
jsonb_build_object(
|
|
'legacyDocumentAssetId', asset."id"::text,
|
|
'provenance', asset."metadata"
|
|
),
|
|
'{}'::jsonb,
|
|
asset."created_at",
|
|
COALESCE(asset."updated_at", asset."created_at")
|
|
FROM "document_assets" asset
|
|
JOIN "knowledge_spaces" space ON space."id" = asset."knowledge_space_id"
|
|
ON CONFLICT ("id") DO NOTHING;
|
|
|
|
INSERT INTO "document_revisions" (
|
|
"tenant_id",
|
|
"knowledge_space_id",
|
|
"document_id",
|
|
"revision",
|
|
"document_asset_id",
|
|
"document_asset_version",
|
|
"compilation_attempt_id",
|
|
"expected_active_revision",
|
|
"expected_document_row_version",
|
|
"content_hash",
|
|
"mime_type",
|
|
"size_bytes",
|
|
"state",
|
|
"system_metadata",
|
|
"created_at",
|
|
"activated_at"
|
|
)
|
|
SELECT
|
|
space."tenant_id",
|
|
asset."knowledge_space_id",
|
|
asset."id",
|
|
asset."version",
|
|
asset."id",
|
|
asset."version",
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
asset."sha256",
|
|
asset."mime_type",
|
|
asset."size_bytes",
|
|
'active',
|
|
jsonb_build_object('provenance', asset."metadata"),
|
|
asset."created_at",
|
|
COALESCE(asset."updated_at", asset."created_at")
|
|
FROM "document_assets" asset
|
|
JOIN "knowledge_spaces" space ON space."id" = asset."knowledge_space_id"
|
|
ON CONFLICT ("tenant_id", "knowledge_space_id", "document_id", "revision") DO NOTHING;
|
|
|
|
UPDATE "logical_documents" document
|
|
SET
|
|
"active_revision" = asset."version",
|
|
"row_version" = CASE WHEN document."active_revision" IS NULL THEN document."row_version" + 1 ELSE document."row_version" END
|
|
FROM "document_assets" asset
|
|
WHERE document."id" = asset."id"
|
|
AND document."knowledge_space_id" = asset."knowledge_space_id"
|
|
AND document."active_revision" IS NULL;
|