-- Insert post with direct actor_id and parent/root/embedded post natural keys -- Self-contained schema: No records table, metadata embedded directly in posts -- Parameters: $1=actor_id(INT4), $2=cid, $3=content(BYTEA, compressed), -- $4=languages(text[], up to 3), $5=tags, -- $6=parent_post_actor_id, $7=parent_post_rkey, -- $8=root_post_actor_id, $9=root_post_rkey, -- $10=embed, $11=embed_subtype, $12=violates_threadgate, $13=rkey(INT8), -- $14=tokens(text[], for search) -- $15=ext_embed(post_ext_embed), $16=video_embed(post_video_embed), -- $17=embedded_post_actor_id, $18=embedded_post_rkey, -- $19=record_detached(BOOL), -- $20-$23=image_1, image_2, image_3, image_4 (post_image_embed), -- $24-$31=facet_1 through facet_8 (post_facet_embed), -- $32=mentions(int[]) -- Note: created_at is derived from TID rkey -- Actor must already exist before calling this function -- Parent and root posts must exist before calling this function (use ensure_post_exists first) WITH embed_types AS ( SELECT CASE WHEN $10::text IS NOT NULL THEN $10::text::embed_type ELSE NULL::embed_type END as embed_type, CASE WHEN $11::text IS NOT NULL THEN $11::text::embed_type ELSE NULL::embed_type END as embed_subtype ) INSERT INTO posts ( actor_id, rkey, cid, content, langs, tags, parent_post_actor_id, parent_post_rkey, root_post_actor_id, root_post_rkey, embed_type, embed_subtype, violates_threadgate, status, tokens, ext_embed, video_embed, embedded_post_actor_id, embedded_post_rkey, record_detached, image_1, image_2, image_3, image_4, facet_1, facet_2, facet_3, facet_4, facet_5, facet_6, facet_7, facet_8, mentions ) SELECT $1, -- actor_id (passed directly as INT4) $13, -- rkey (INT8) $2::bytea, -- cid (embedded) $3, -- content (compressed BYTEA) -- Convert text array to language_code array (supports up to 3 languages per AT Protocol spec) -- Only include valid language codes that can be cast to the enum COALESCE( (SELECT array_agg(lang::language_code ORDER BY ordinality) FROM unnest($4::text[]) WITH ORDINALITY AS t(lang, ordinality) WHERE lang IS NOT NULL AND lang::text IN (SELECT enumlabel FROM pg_enum WHERE enumtypid = 'language_code'::regtype) LIMIT 3), ARRAY[]::language_code[] ), -- langs (array of up to 3 language codes) $5, -- tags $6, -- parent_post_actor_id $7, -- parent_post_rkey $8, -- root_post_actor_id $9, -- root_post_rkey (SELECT embed_type FROM embed_types), -- embed_type (SELECT embed_subtype FROM embed_types), -- embed_subtype $12, -- violates_threadgate 'complete'::post_status, -- status (this is a complete post with full content) $14, -- tokens (text array, pre-tokenized in Rust) $15, -- ext_embed $16, -- video_embed $17, -- embedded_post_actor_id $18, -- embedded_post_rkey $19, -- record_detached $20, $21, $22, $23, -- image_1, image_2, image_3, image_4 $24, $25, $26, $27, $28, $29, $30, $31, -- facet_1 through facet_8 $32 -- mentions -- Posts are immutable - cannot be edited in AT Protocol -- However, if a stub/missing post already exists, upgrade it to complete -- (missing posts can become complete if we later receive the actual data) ON CONFLICT (actor_id, rkey) DO UPDATE SET cid = EXCLUDED.cid, -- Ensure CID is correct (stubs may have placeholder) content = EXCLUDED.content, langs = EXCLUDED.langs, tags = EXCLUDED.tags, parent_post_actor_id = EXCLUDED.parent_post_actor_id, parent_post_rkey = EXCLUDED.parent_post_rkey, root_post_actor_id = EXCLUDED.root_post_actor_id, root_post_rkey = EXCLUDED.root_post_rkey, embed_type = EXCLUDED.embed_type, embed_subtype = EXCLUDED.embed_subtype, violates_threadgate = EXCLUDED.violates_threadgate, status = 'complete'::post_status, tokens = EXCLUDED.tokens, -- Update tokens when upgrading from stub/missing ext_embed = EXCLUDED.ext_embed, video_embed = EXCLUDED.video_embed, embedded_post_actor_id = EXCLUDED.embedded_post_actor_id, embedded_post_rkey = EXCLUDED.embedded_post_rkey, record_detached = EXCLUDED.record_detached, image_1 = EXCLUDED.image_1, image_2 = EXCLUDED.image_2, image_3 = EXCLUDED.image_3, image_4 = EXCLUDED.image_4, facet_1 = EXCLUDED.facet_1, facet_2 = EXCLUDED.facet_2, facet_3 = EXCLUDED.facet_3, facet_4 = EXCLUDED.facet_4, facet_5 = EXCLUDED.facet_5, facet_6 = EXCLUDED.facet_6, facet_7 = EXCLUDED.facet_7, facet_8 = EXCLUDED.facet_8, mentions = EXCLUDED.mentions WHERE posts.status IN ('stub'::post_status, 'missing'::post_status) RETURNING actor_id, rkey