mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-21 18:13:26 +00:00
6461aafd16
Adds the schema groundwork for inline images and file uploads — see DOC-865 (Attachments — architecture & migration design). - migrations/047_attachments.sql — SQLite migration. Table + 4 indexes (workspace, item, hash, parent). Partial indexes on workspace/item/parent match the items table convention. The hash index is full (not partial) so dedupe can resurrect a soft-deleted blob if the same bytes are re-uploaded without writing a duplicate. - pgmigrations/026_attachments.sql — Postgres mirror with BIGINT for size_bytes; same partial-index pattern. - internal/models/attachment.go — Go model with all columns. Uses pointer types for nullable columns (item_id, width, height, parent_id, variant, deleted_at) so JSON omitempty works correctly. No call sites yet — purely schema groundwork. Verified the migration runs cleanly on a fresh install and on the live dev DB. Parent: PLAN-866.
48 lines
2.2 KiB
SQL
48 lines
2.2 KiB
SQL
-- Migration 047: Attachments table for inline images + file uploads (TASK-869).
|
|
--
|
|
-- See [[Attachments — architecture & migration design]] (DOC-865) for the full
|
|
-- design. This migration is schema groundwork only — no Go consumers yet.
|
|
--
|
|
-- Key columns:
|
|
-- storage_key "<backend>:<hash>" so the driver registry can route by
|
|
-- prefix. Phase 1 ships fs:; Phase 2 introduces s3:.
|
|
-- content_hash Raw sha256 of the original bytes. Same hash across rows
|
|
-- means the same physical blob is referenced — content-
|
|
-- addressed dedupe is a free side effect.
|
|
-- item_id Nullable. NULL = orphan, eligible for GC after the grace
|
|
-- period expires.
|
|
-- parent_id Non-null on derived blobs (thumbnails) so the GC can
|
|
-- reclaim them when the original goes away.
|
|
-- variant 'original' | 'thumb-sm' | 'thumb-md' | NULL.
|
|
-- deleted_at Soft delete; orphan GC reclaims later.
|
|
|
|
CREATE TABLE IF NOT EXISTS attachments (
|
|
id TEXT PRIMARY KEY,
|
|
workspace_id TEXT NOT NULL,
|
|
item_id TEXT,
|
|
uploaded_by TEXT NOT NULL,
|
|
storage_key TEXT NOT NULL,
|
|
content_hash TEXT NOT NULL,
|
|
mime_type TEXT NOT NULL,
|
|
size_bytes INTEGER NOT NULL,
|
|
filename TEXT NOT NULL,
|
|
width INTEGER,
|
|
height INTEGER,
|
|
parent_id TEXT,
|
|
variant TEXT,
|
|
created_at TEXT NOT NULL,
|
|
deleted_at TEXT
|
|
);
|
|
|
|
-- Hot paths:
|
|
-- workspace lookup for storage usage SUM and listing
|
|
-- item lookup for "what's attached to this item" + GC orphan scan
|
|
-- hash lookup for dedupe (full index, not partial — dedupe must see
|
|
-- soft-deleted rows so a re-upload of the same bytes can resurrect
|
|
-- the existing blob without writing a duplicate)
|
|
-- parent lookup so the GC can find a derived blob's siblings
|
|
CREATE INDEX IF NOT EXISTS idx_attach_workspace ON attachments(workspace_id) WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_attach_item ON attachments(item_id) WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_attach_hash ON attachments(content_hash);
|
|
CREATE INDEX IF NOT EXISTS idx_attach_parent ON attachments(parent_id) WHERE parent_id IS NOT NULL;
|