Rust AppView - highly experimental!

chore: cleanup external sql

+149 -549
+149 -22
parakeet/src/entities/post.rs
··· 902 902 use diesel_async::RunQueryDsl; 903 903 use diesel::sql_types::{Integer, BigInt}; 904 904 905 - let rows: Vec<ThreadParentRow> = diesel::sql_query(include_str!("../sql/thread_parent_by_id.sql")) 906 - .bind::<Integer, _>(target_actor_id) 907 - .bind::<BigInt, _>(target_rkey) 908 - .bind::<Integer, _>(height) 909 - .bind::<Integer, _>(root_actor_id) 910 - .bind::<BigInt, _>(root_rkey) 911 - .load(&mut conn) 912 - .await?; 905 + // Recursive CTE to walk up the thread parent chain using natural primary keys 906 + let rows: Vec<ThreadParentRow> = diesel::sql_query( 907 + r#" 908 + WITH RECURSIVE parents AS ( 909 + -- Base case: parent of the target post 910 + SELECT 911 + p.actor_id, 912 + p.rkey, 913 + p.parent_post_actor_id, 914 + p.parent_post_rkey, 915 + p.root_post_actor_id, 916 + p.root_post_rkey, 917 + 0 as depth 918 + FROM posts target_p 919 + INNER JOIN posts p ON target_p.parent_post_actor_id = p.actor_id 920 + AND target_p.parent_post_rkey = p.rkey 921 + WHERE target_p.actor_id = $1 922 + AND target_p.rkey = $2 923 + AND p.violates_threadgate = FALSE 924 + -- Filter by root to use idx_posts_root and limit search space 925 + AND (p.root_post_actor_id = $4 AND p.root_post_rkey = $5 926 + OR (p.actor_id = $4 AND p.rkey = $5)) -- Parent might be the root itself 927 + 928 + UNION ALL 929 + 930 + -- Recursive case: parents of parents 931 + SELECT 932 + p.actor_id, 933 + p.rkey, 934 + p.parent_post_actor_id, 935 + p.parent_post_rkey, 936 + p.root_post_actor_id, 937 + p.root_post_rkey, 938 + parents.depth + 1 939 + FROM parents 940 + INNER JOIN posts p ON parents.parent_post_actor_id = p.actor_id 941 + AND parents.parent_post_rkey = p.rkey 942 + WHERE parents.depth < $3 943 + AND p.violates_threadgate = FALSE 944 + -- Filter by root to use idx_posts_root and limit search space 945 + AND (p.root_post_actor_id = $4 AND p.root_post_rkey = $5 946 + OR (p.actor_id = $4 AND p.rkey = $5)) -- Parent might be the root itself 947 + ) 948 + SELECT * FROM parents 949 + ORDER BY depth DESC 950 + "# 951 + ) 952 + .bind::<Integer, _>(target_actor_id) 953 + .bind::<BigInt, _>(target_rkey) 954 + .bind::<Integer, _>(height) 955 + .bind::<Integer, _>(root_actor_id) 956 + .bind::<BigInt, _>(root_rkey) 957 + .load(&mut conn) 958 + .await?; 913 959 914 960 Ok(rows.into_iter().map(|row| ThreadItem { 915 961 actor_id: row.actor_id, ··· 1171 1217 use diesel_async::RunQueryDsl; 1172 1218 use diesel::sql_types::{Integer, BigInt}; 1173 1219 1174 - let rows: Vec<ThreadChildRow> = diesel::sql_query(include_str!("../sql/thread.sql")) 1175 - .bind::<Integer, _>(target_actor_id) 1176 - .bind::<BigInt, _>(target_rkey) 1177 - .bind::<Integer, _>(depth) 1178 - .load(&mut conn) 1179 - .await?; 1220 + // Recursive CTE to traverse thread replies using natural primary keys 1221 + let rows: Vec<ThreadChildRow> = diesel::sql_query( 1222 + r#" 1223 + WITH RECURSIVE thread AS ( 1224 + -- Base case: direct replies to the target post 1225 + SELECT 1226 + p.actor_id, 1227 + p.rkey, 1228 + p.parent_post_actor_id, 1229 + p.parent_post_rkey, 1230 + p.root_post_actor_id, 1231 + p.root_post_rkey, 1232 + 1 as depth 1233 + FROM posts target_p 1234 + INNER JOIN posts p ON p.parent_post_actor_id = target_p.actor_id 1235 + AND p.parent_post_rkey = target_p.rkey 1236 + WHERE target_p.actor_id = $1 1237 + AND target_p.rkey = $2 1238 + AND p.violates_threadgate = FALSE 1239 + 1240 + UNION ALL 1241 + 1242 + -- Recursive case: replies to replies 1243 + SELECT 1244 + p.actor_id, 1245 + p.rkey, 1246 + p.parent_post_actor_id, 1247 + p.parent_post_rkey, 1248 + p.root_post_actor_id, 1249 + p.root_post_rkey, 1250 + thread.depth + 1 1251 + FROM thread 1252 + INNER JOIN posts p ON p.parent_post_actor_id = thread.actor_id 1253 + AND p.parent_post_rkey = thread.rkey 1254 + WHERE thread.depth < $3 1255 + AND p.violates_threadgate = FALSE 1256 + ) 1257 + SELECT * FROM thread 1258 + ORDER BY depth ASC 1259 + "# 1260 + ) 1261 + .bind::<Integer, _>(target_actor_id) 1262 + .bind::<BigInt, _>(target_rkey) 1263 + .bind::<Integer, _>(depth) 1264 + .load(&mut conn) 1265 + .await?; 1180 1266 1181 1267 Ok(rows.into_iter().map(|row| ThreadItem { 1182 1268 actor_id: row.actor_id, ··· 1232 1318 depth: i32, 1233 1319 } 1234 1320 1235 - let rows: Vec<ThreadChildRow> = diesel::sql_query(include_str!("../sql/thread_branching_by_id.sql")) 1236 - .bind::<Integer, _>(target_actor_id) 1237 - .bind::<BigInt, _>(target_rkey) 1238 - .bind::<Integer, _>(below) // $3 = max depth 1239 - .bind::<Integer, _>(branching_factor) // $4 = branching factor 1240 - .load(&mut conn) 1241 - .await 1242 - .unwrap_or_default(); // Return empty vector for non-existent posts 1321 + // Recursive CTE with branching factor limit using natural primary keys 1322 + let rows: Vec<ThreadChildRow> = diesel::sql_query( 1323 + r#" 1324 + WITH RECURSIVE thread AS ( 1325 + -- Base case: direct replies to the target post 1326 + SELECT 1327 + p.actor_id, 1328 + p.rkey, 1329 + p.parent_post_actor_id, 1330 + p.parent_post_rkey, 1331 + p.root_post_actor_id, 1332 + p.root_post_rkey, 1333 + 1 as depth 1334 + FROM posts target_p 1335 + INNER JOIN posts p ON p.parent_post_actor_id = target_p.actor_id 1336 + AND p.parent_post_rkey = target_p.rkey 1337 + WHERE target_p.actor_id = $1 1338 + AND target_p.rkey = $2 1339 + AND p.violates_threadgate = FALSE 1340 + 1341 + UNION ALL 1342 + 1343 + -- Recursive case: replies to replies with branching limit 1344 + (SELECT 1345 + p.actor_id, 1346 + p.rkey, 1347 + p.parent_post_actor_id, 1348 + p.parent_post_rkey, 1349 + p.root_post_actor_id, 1350 + p.root_post_rkey, 1351 + thread.depth + 1 1352 + FROM thread 1353 + INNER JOIN posts p ON p.parent_post_actor_id = thread.actor_id 1354 + AND p.parent_post_rkey = thread.rkey 1355 + WHERE thread.depth < $3 1356 + AND p.violates_threadgate = FALSE 1357 + LIMIT $4) 1358 + ) 1359 + SELECT * FROM thread 1360 + ORDER BY depth ASC 1361 + "# 1362 + ) 1363 + .bind::<Integer, _>(target_actor_id) 1364 + .bind::<BigInt, _>(target_rkey) 1365 + .bind::<Integer, _>(below) // $3 = max depth 1366 + .bind::<Integer, _>(branching_factor) // $4 = branching factor 1367 + .load(&mut conn) 1368 + .await 1369 + .unwrap_or_default(); // Return empty vector for non-existent posts 1243 1370 1244 1371 Ok(rows.into_iter().map(|row| ThreadItem { 1245 1372 actor_id: row.actor_id,
-14
parakeet/src/sql/get_list_members.sql
··· 1 - -- Fetch members of specific lists (for viewer state list checks) 2 - -- Parameters: 3 - -- $1 = array of list_owner_actor_ids (integer[]) 4 - -- $2 = array of list_rkeys (text[]) 5 - -- Returns which actors are in which lists 6 - SELECT 7 - li.list_owner_actor_id, 8 - li.list_rkey, 9 - ARRAY_AGG(li.subject_actor_id) as member_ids 10 - FROM list_items li 11 - WHERE 12 - li.list_owner_actor_id = ANY($1) 13 - AND li.list_rkey = ANY($2) 14 - GROUP BY li.list_owner_actor_id, li.list_rkey;
-11
parakeet/src/sql/get_subjects_relationships.sql
··· 1 - -- Fetch multiple actors' relationship arrays for reverse lookups 2 - -- Parameter: $1 = array of actor_ids (integer[]) 3 - -- Only fetches the arrays needed for reverse relationship checks 4 - SELECT 5 - id, 6 - did, 7 - following, -- To check if subject follows viewer 8 - blocks, -- To check if subject blocks viewer 9 - list_blocks -- To check if subject has viewer in blocked lists 10 - FROM actors 11 - WHERE id = ANY($1);
-13
parakeet/src/sql/get_viewer_actor.sql
··· 1 - -- Fetch a single actor with all arrays for viewer state caching 2 - -- Parameter: $1 = actor_id (integer) 3 - SELECT 4 - id, 5 - did, 6 - following, 7 - blocks, 8 - mutes, 9 - list_blocks, 10 - list_mutes 11 - FROM actors 12 - WHERE id = $1 13 - LIMIT 1;
-47
parakeet/src/sql/list_states.sql
··· 1 - -- Get list states (blocks, mutes) for a viewer (DENORMALIZED) 2 - -- $1 = viewer DID 3 - -- $2 = array of list dids 4 - -- $3 = array of list rkeys (text - lists use text rkeys) 5 - with viewer as ( 6 - select id from actors where did = $1 7 - ), 8 - lookup_lists as ( 9 - select 10 - a.did as list_did, 11 - a.id as list_actor_id, 12 - l.rkey as list_rkey 13 - from lists l 14 - inner join actors a on l.actor_id = a.id 15 - inner join unnest($2::text[], $3::text[]) AS lookup(lookup_did, lookup_rkey) 16 - ON a.did = lookup.lookup_did AND l.rkey = lookup.lookup_rkey 17 - ), 18 - list_blocks_unnested as ( 19 - select 20 - ll.list_did, 21 - ll.list_rkey, 22 - va.did as block_actor_did, 23 - (lb).rkey as block_rkey 24 - from viewer v 25 - inner join actors va on va.id = v.id 26 - cross join unnest(va.list_blocks) as lb 27 - inner join lookup_lists ll on (lb).list_actor_id = ll.list_actor_id and (lb).list_rkey = ll.list_rkey 28 - ), 29 - list_mutes_unnested as ( 30 - select 31 - ll.list_did, 32 - ll.list_rkey 33 - from viewer v 34 - inner join actors va on va.id = v.id 35 - cross join unnest(va.list_mutes) as lm 36 - inner join lookup_lists ll on (lm).list_actor_id = ll.list_actor_id and (lm).list_rkey = ll.list_rkey 37 - ) 38 - select 39 - ll.list_did, 40 - ll.list_rkey, 41 - lb.block_actor_did, 42 - lb.block_rkey, 43 - lm.list_did is not null as muted 44 - from lookup_lists ll 45 - left join list_blocks_unnested lb on ll.list_did = lb.list_did and ll.list_rkey = lb.list_rkey 46 - left join list_mutes_unnested lm on ll.list_did = lm.list_did and ll.list_rkey = lm.list_rkey 47 - where (lb.block_actor_did is not null or lm.list_did is not null)
-128
parakeet/src/sql/profile_state.sql
··· 1 - -- Profile state query using denormalized schema 2 - -- Reconstructs profile state from actors.following, actors.blocks, actors.mutes arrays 3 - -- and actors.list_blocks/list_mutes arrays 4 - -- Parameters: $1 = viewer DID, $2 = array of subject DIDs 5 - 6 - with viewer as ( 7 - select id from actors where did = $1 8 - ), 9 - subjects as ( 10 - select id, did from actors where did = any($2) 11 - ), 12 - -- Following relationship (return rkey bigint for encoding in Rust) 13 - -- Read from viewer's following[] array 14 - following_cte as ( 15 - select s.did as subject, (f).rkey as following 16 - from viewer v 17 - cross join lateral unnest(COALESCE( 18 - (select following from actors where id = v.id), 19 - ARRAY[]::follow_record[] 20 - )) as f 21 - inner join subjects s on (f).subject_actor_id = s.id 22 - ), 23 - -- Followed relationship (return rkey bigint for encoding in Rust) 24 - -- Read from subject's following[] array (subject follows viewer) 25 - followed_cte as ( 26 - select s.did as subject, (f).rkey as followed 27 - from subjects s 28 - cross join lateral unnest(COALESCE( 29 - (select following from actors where id = s.id), 30 - ARRAY[]::follow_record[] 31 - )) as f 32 - inner join viewer v on (f).subject_actor_id = v.id 33 - ), 34 - -- Blocking relationship (return rkey bigint for encoding in Rust) 35 - -- Read from viewer's blocks[] array 36 - blocking_cte as ( 37 - select s.did as subject, (b).rkey as blocking 38 - from viewer v 39 - cross join lateral unnest(COALESCE( 40 - (select blocks from actors where id = v.id), 41 - ARRAY[]::block_record[] 42 - )) as b 43 - inner join subjects s on (b).subject_actor_id = s.id 44 - ), 45 - -- Blocked relationship (subject has blocked viewer) 46 - -- Read from subject's blocks[] array 47 - blocked_cte as ( 48 - select s.did as subject, true as blocked 49 - from subjects s 50 - cross join lateral unnest(COALESCE( 51 - (select blocks from actors where id = s.id), 52 - ARRAY[]::block_record[] 53 - )) as b 54 - inner join viewer v on (b).subject_actor_id = v.id 55 - ), 56 - -- Muting relationship 57 - -- Read from viewer's mutes[] array 58 - muting_cte as ( 59 - select s.did as subject, true as muting 60 - from viewer v 61 - cross join lateral unnest(COALESCE( 62 - (select mutes from actors where id = v.id), 63 - ARRAY[]::mute_record[] 64 - )) as m 65 - inner join subjects s on (m).subject_actor_id = s.id 66 - ), 67 - -- List blocks (viewer has blocked subject via list) 68 - -- Viewer has blocked lists → check if subject is in those lists 69 - vlb as ( 70 - select s.did as subject, list_owner.did as list_owner_did, (lb).list_rkey as list_rkey 71 - from viewer v 72 - cross join lateral unnest(COALESCE( 73 - (select list_blocks from actors where id = v.id), 74 - ARRAY[]::list_block_record[] 75 - )) as lb 76 - inner join actors list_owner on list_owner.id = (lb).list_actor_id 77 - inner join list_items li on li.list_owner_actor_id = (lb).list_actor_id 78 - and li.list_rkey = (lb).list_rkey 79 - inner join subjects s on s.id = li.subject_actor_id 80 - ), 81 - -- List blocks reverse (subject has blocked viewer via list) 82 - -- Subject has blocked lists → check if viewer is in those lists 83 - vlb2 as ( 84 - select s.did as subject, true as blocked 85 - from subjects s 86 - cross join lateral unnest(COALESCE( 87 - (select list_blocks from actors where id = s.id), 88 - ARRAY[]::list_block_record[] 89 - )) as lb 90 - inner join list_items li on li.list_owner_actor_id = (lb).list_actor_id 91 - and li.list_rkey = (lb).list_rkey 92 - inner join viewer v on v.id = li.subject_actor_id 93 - ), 94 - -- List mutes (viewer has muted subject via list) 95 - -- Viewer has muted lists → check if subject is in those lists 96 - vlm as ( 97 - select s.did as subject, list_owner.did as list_owner_did, (lm).list_rkey as list_rkey 98 - from viewer v 99 - cross join lateral unnest(COALESCE( 100 - (select list_mutes from actors where id = v.id), 101 - ARRAY[]::list_mute_record[] 102 - )) as lm 103 - inner join actors list_owner on list_owner.id = (lm).list_actor_id 104 - inner join list_items li on li.list_owner_actor_id = (lm).list_actor_id 105 - and li.list_rkey = (lm).list_rkey 106 - inner join subjects s on s.id = li.subject_actor_id 107 - ) 108 - select 109 - $1 as did, 110 - coalesce(s.did, following.subject, followed.subject, blocking.subject, blocked.subject, muting.subject, vlb.subject, vlb2.subject, vlm.subject) as subject, 111 - muting.muting, 112 - coalesce(blocked.blocked, vlb2.blocked, false) as blocked, 113 - blocking.blocking, 114 - following.following, 115 - followed.followed, 116 - vlb.list_owner_did as list_block_owner_did, 117 - vlb.list_rkey as list_block_rkey, 118 - vlm.list_owner_did as list_mute_owner_did, 119 - vlm.list_rkey as list_mute_rkey 120 - from subjects s 121 - full join following_cte following on following.subject = s.did 122 - full join followed_cte followed on followed.subject = s.did 123 - full join blocking_cte blocking on blocking.subject = s.did 124 - full join blocked_cte blocked on blocked.subject = s.did 125 - full join muting_cte muting on muting.subject = s.did 126 - full join vlb on vlb.subject = s.did 127 - full join vlb2 on vlb2.subject = s.did 128 - full join vlm on vlm.subject = s.did;
-128
parakeet/src/sql/profile_state_by_ids.sql
··· 1 - -- Profile state query using actor_ids instead of DIDs (optimized for IdCache usage) 2 - -- Reconstructs profile state from actors.following, actors.blocks, actors.mutes arrays 3 - -- and actors.list_blocks/list_mutes arrays 4 - -- Parameters: $1 = viewer actor_id (integer), $2 = array of subject actor_ids (integer[]) 5 - -- 6 - -- This is an optimized version that avoids decompressing the actors table by accepting 7 - -- actor_ids directly. The caller should use IdCache to resolve DIDs to actor_ids first. 8 - 9 - with viewer as ( 10 - select $1::integer as id 11 - ), 12 - subjects as ( 13 - select unnest($2::integer[]) as id 14 - ), 15 - -- Following relationship (return rkey bigint for encoding in Rust) 16 - -- Read from viewer's following[] array 17 - following_cte as ( 18 - select s.id as subject_id, (f).rkey as following 19 - from viewer v 20 - cross join lateral unnest(COALESCE( 21 - (select following from actors where id = v.id), 22 - ARRAY[]::follow_record[] 23 - )) as f 24 - inner join subjects s on (f).subject_actor_id = s.id 25 - ), 26 - -- Followed relationship (return rkey bigint for encoding in Rust) 27 - -- Read from subject's following[] array (subject follows viewer) 28 - followed_cte as ( 29 - select s.id as subject_id, (f).rkey as followed 30 - from subjects s 31 - cross join lateral unnest(COALESCE( 32 - (select following from actors where id = s.id), 33 - ARRAY[]::follow_record[] 34 - )) as f 35 - inner join viewer v on (f).subject_actor_id = v.id 36 - ), 37 - -- Blocking relationship (return rkey bigint for encoding in Rust) 38 - -- Read from viewer's blocks[] array 39 - blocking_cte as ( 40 - select s.id as subject_id, (b).rkey as blocking 41 - from viewer v 42 - cross join lateral unnest(COALESCE( 43 - (select blocks from actors where id = v.id), 44 - ARRAY[]::block_record[] 45 - )) as b 46 - inner join subjects s on (b).subject_actor_id = s.id 47 - ), 48 - -- Blocked relationship (subject has blocked viewer) 49 - -- Read from subject's blocks[] array 50 - blocked_cte as ( 51 - select s.id as subject_id, true as blocked 52 - from subjects s 53 - cross join lateral unnest(COALESCE( 54 - (select blocks from actors where id = s.id), 55 - ARRAY[]::block_record[] 56 - )) as b 57 - inner join viewer v on (b).subject_actor_id = v.id 58 - ), 59 - -- Muting relationship 60 - -- Read from viewer's mutes[] array 61 - muting_cte as ( 62 - select s.id as subject_id, true as muting 63 - from viewer v 64 - cross join lateral unnest(COALESCE( 65 - (select mutes from actors where id = v.id), 66 - ARRAY[]::mute_record[] 67 - )) as m 68 - inner join subjects s on (m).subject_actor_id = s.id 69 - ), 70 - -- List blocks (viewer has blocked subject via list) 71 - -- Viewer has blocked lists → check if subject is in those lists 72 - vlb as ( 73 - select s.id as subject_id, (lb).list_actor_id as list_owner_actor_id, (lb).list_rkey as list_rkey 74 - from viewer v 75 - cross join lateral unnest(COALESCE( 76 - (select list_blocks from actors where id = v.id), 77 - ARRAY[]::list_block_record[] 78 - )) as lb 79 - inner join list_items li on li.list_owner_actor_id = (lb).list_actor_id 80 - and li.list_rkey = (lb).list_rkey 81 - inner join subjects s on s.id = li.subject_actor_id 82 - ), 83 - -- List blocks reverse (subject has blocked viewer via list) 84 - -- Subject has blocked lists → check if viewer is in those lists 85 - vlb2 as ( 86 - select s.id as subject_id, true as blocked 87 - from subjects s 88 - cross join lateral unnest(COALESCE( 89 - (select list_blocks from actors where id = s.id), 90 - ARRAY[]::list_block_record[] 91 - )) as lb 92 - inner join list_items li on li.list_owner_actor_id = (lb).list_actor_id 93 - and li.list_rkey = (lb).list_rkey 94 - inner join viewer v on v.id = li.subject_actor_id 95 - ), 96 - -- List mutes (viewer has muted subject via list) 97 - -- Viewer has muted lists → check if subject is in those lists 98 - vlm as ( 99 - select s.id as subject_id, (lm).list_actor_id as list_owner_actor_id, (lm).list_rkey as list_rkey 100 - from viewer v 101 - cross join lateral unnest(COALESCE( 102 - (select list_mutes from actors where id = v.id), 103 - ARRAY[]::list_mute_record[] 104 - )) as lm 105 - inner join list_items li on li.list_owner_actor_id = (lm).list_actor_id 106 - and li.list_rkey = (lm).list_rkey 107 - inner join subjects s on s.id = li.subject_actor_id 108 - ) 109 - select 110 - s.id as subject_id, 111 - muting.muting, 112 - coalesce(blocked.blocked, vlb2.blocked, false) as blocked, 113 - blocking.blocking, 114 - following.following, 115 - followed.followed, 116 - vlb.list_owner_actor_id as list_block_owner_actor_id, 117 - vlb.list_rkey as list_block_rkey, 118 - vlm.list_owner_actor_id as list_mute_owner_actor_id, 119 - vlm.list_rkey as list_mute_rkey 120 - from subjects s 121 - left join following_cte following on following.subject_id = s.id 122 - left join followed_cte followed on followed.subject_id = s.id 123 - left join blocking_cte blocking on blocking.subject_id = s.id 124 - left join blocked_cte blocked on blocked.subject_id = s.id 125 - left join muting_cte muting on muting.subject_id = s.id 126 - left join vlb on vlb.subject_id = s.id 127 - left join vlb2 on vlb2.subject_id = s.id 128 - left join vlm on vlm.subject_id = s.id;
-42
parakeet/src/sql/thread.sql
··· 1 - -- Recursive CTE to traverse thread replies 2 - -- $1 = target post actor_id (integer) 3 - -- $2 = target post rkey (bigint) 4 - -- $3 = max depth 5 - WITH RECURSIVE thread AS ( 6 - -- Base case: direct replies to the target post 7 - SELECT 8 - p.actor_id, 9 - p.rkey, 10 - p.parent_post_actor_id, 11 - p.parent_post_rkey, -- Already in posts table, no JOIN needed 12 - p.root_post_actor_id, 13 - p.root_post_rkey, -- Already in posts table, no JOIN needed 14 - 1 as depth 15 - FROM posts target_p 16 - -- Join replies using natural keys (NO actors table join!) 17 - INNER JOIN posts p ON p.parent_post_actor_id = target_p.actor_id 18 - AND p.parent_post_rkey = target_p.rkey 19 - WHERE target_p.actor_id = $1 20 - AND target_p.rkey = $2 21 - AND p.violates_threadgate = FALSE 22 - 23 - UNION ALL 24 - 25 - -- Recursive case: replies to replies (NO actors table join!) 26 - SELECT 27 - p.actor_id, 28 - p.rkey, 29 - p.parent_post_actor_id, 30 - p.parent_post_rkey, -- Already in posts table, no JOIN needed 31 - p.root_post_actor_id, 32 - p.root_post_rkey, -- Already in posts table, no JOIN needed 33 - thread.depth + 1 34 - FROM thread 35 - -- Join directly on actor_id from CTE (NO actors table join!) 36 - INNER JOIN posts p ON p.parent_post_actor_id = thread.actor_id 37 - AND p.parent_post_rkey = thread.rkey 38 - WHERE thread.depth < $3 39 - AND p.violates_threadgate = FALSE 40 - ) 41 - SELECT * FROM thread 42 - ORDER BY depth ASC;
-43
parakeet/src/sql/thread_branching_by_id.sql
··· 1 - -- Recursive CTE with branching factor limit 2 - -- $1 = target post actor_id (integer) 3 - -- $2 = target post rkey (bigint) 4 - -- $3 = max depth 5 - -- $4 = branching factor (max children per parent) 6 - WITH RECURSIVE thread AS ( 7 - -- Base case: direct replies to the target post 8 - SELECT 9 - p.actor_id, 10 - p.rkey, 11 - p.parent_post_actor_id, 12 - p.parent_post_rkey, -- Already in posts table, no JOIN needed 13 - p.root_post_actor_id, 14 - p.root_post_rkey, -- Already in posts table, no JOIN needed 15 - 1 as depth 16 - FROM posts target_p 17 - -- Join replies using natural keys (NO actors table join!) 18 - INNER JOIN posts p ON p.parent_post_actor_id = target_p.actor_id 19 - AND p.parent_post_rkey = target_p.rkey 20 - WHERE target_p.actor_id = $1 21 - AND target_p.rkey = $2 22 - AND p.violates_threadgate = FALSE 23 - 24 - UNION ALL 25 - 26 - -- Recursive case: replies to replies with branching limit 27 - (SELECT 28 - p.actor_id, 29 - p.rkey, 30 - p.parent_post_actor_id, 31 - p.parent_post_rkey, 32 - p.root_post_actor_id, 33 - p.root_post_rkey, 34 - thread.depth + 1 35 - FROM thread 36 - INNER JOIN posts p ON p.parent_post_actor_id = thread.actor_id 37 - AND p.parent_post_rkey = thread.rkey 38 - WHERE thread.depth < $3 39 - AND p.violates_threadgate = FALSE 40 - LIMIT $4) 41 - ) 42 - SELECT * FROM thread 43 - ORDER BY depth ASC;
-55
parakeet/src/sql/thread_parent_by_id.sql
··· 1 - -- Recursive CTE to walk up the thread parent chain using actor IDs. 2 - -- $1 = target post actor_id (integer) 3 - -- $2 = target post rkey (bigint) 4 - -- $3 = max depth 5 - -- $4 = root_post_actor_id (integer, from target post) 6 - -- $5 = root_post_rkey (bigint, from target post) 7 - -- 8 - -- Optimizations: 9 - -- - Uses actor_id lookups directly, avoiding actors table joins 10 - -- - Filters by root_post to use idx_posts_root index 11 - -- - Uses denormalized rkey columns instead of JOINs (270x faster!) 12 - WITH RECURSIVE parents AS ( 13 - -- Base case: parent of the target post 14 - SELECT 15 - p.actor_id, 16 - p.rkey, 17 - p.parent_post_actor_id, 18 - p.parent_post_rkey, -- Already in posts table, no JOIN needed 19 - p.root_post_actor_id, 20 - p.root_post_rkey, -- Already in posts table, no JOIN needed 21 - 0 as depth 22 - FROM posts target_p 23 - -- Join parent using natural keys (NO actors table join!) 24 - INNER JOIN posts p ON target_p.parent_post_actor_id = p.actor_id 25 - AND target_p.parent_post_rkey = p.rkey 26 - WHERE target_p.actor_id = $1 27 - AND target_p.rkey = $2 28 - AND p.violates_threadgate = FALSE 29 - -- Filter by root to use idx_posts_root and limit search space 30 - AND (p.root_post_actor_id = $4 AND p.root_post_rkey = $5 31 - OR (p.actor_id = $4 AND p.rkey = $5)) -- Parent might be the root itself 32 - 33 - UNION ALL 34 - 35 - -- Recursive case: parents of parents (NO actors table join!) 36 - SELECT 37 - p.actor_id, 38 - p.rkey, 39 - p.parent_post_actor_id, 40 - p.parent_post_rkey, -- Already in posts table, no JOIN needed 41 - p.root_post_actor_id, 42 - p.root_post_rkey, -- Already in posts table, no JOIN needed 43 - parents.depth + 1 44 - FROM parents 45 - -- Join directly on actor_id from CTE (NO actors table join!) 46 - INNER JOIN posts p ON parents.parent_post_actor_id = p.actor_id 47 - AND parents.parent_post_rkey = p.rkey 48 - WHERE parents.depth < $3 49 - AND p.violates_threadgate = FALSE 50 - -- Filter by root to use idx_posts_root and limit search space 51 - AND (p.root_post_actor_id = $4 AND p.root_post_rkey = $5 52 - OR (p.actor_id = $4 AND p.rkey = $5)) -- Parent might be the root itself 53 - ) 54 - SELECT * FROM parents 55 - ORDER BY depth DESC;
-21
parakeet/src/sql/thread_v2_hidden_children.sql
··· 1 - -- Find hidden replies to a post (DENORMALIZED: reads from posts.threadgate_hidden_* arrays) 2 - -- $1 = parent post actor_id (integer) 3 - -- $2 = parent post rkey (bigint) 4 - -- $3 = root post actor_id (integer) 5 - -- $4 = root post rkey (bigint) 6 - -- 7 - -- PERFORMANCE: Avoids ALL actors table joins by using actor_ids directly 8 - -- DENORMALIZED: Checks denormalized threadgate_hidden_actor_ids/rkeys arrays on root post 9 - SELECT p.actor_id, p.rkey 10 - FROM posts p 11 - -- Join to root post to access its threadgate arrays 12 - INNER JOIN posts root ON root.actor_id = $3 AND root.rkey = $4 13 - WHERE p.parent_post_actor_id = $1 14 - AND p.parent_post_rkey = $2 15 - -- Check if this reply is in the root post's hidden replies arrays 16 - AND EXISTS ( 17 - SELECT 1 18 - FROM unnest(root.threadgate_hidden_actor_ids, root.threadgate_hidden_rkeys) AS hidden(actor_id, rkey) 19 - WHERE hidden.actor_id = p.actor_id 20 - AND hidden.rkey = p.rkey 21 - )
-25
parakeet/src/sql/verification_by_ids.sql
··· 1 - -- Optimized verification query that uses actor_ids instead of DIDs 2 - -- This avoids decompressing the actors table by using IdCache to resolve DIDs beforehand 3 - -- 4 - -- Parameters: 5 - -- $1: subject_actor_ids (integer[]) - Array of subject actor IDs to fetch verifications for 6 - -- 7 - -- Performance: ~70-84ms → ~5-10ms (7-15x faster) 8 - -- Avoids: 2x actors table joins and DID-based filtering (which forces decompression) 9 - -- 10 - -- Note: Caller must use IdCache to: 11 - -- 1. Resolve subject DIDs → actor_ids before this query 12 - -- 2. Resolve subject_actor_id/verifier_actor_id → DIDs after this query 13 - 14 - SELECT 15 - v.id, 16 - v.actor_id, 17 - v.rkey, 18 - v.cid, 19 - tid_timestamp(v.rkey) as created_at, 20 - v.verifier_actor_id, 21 - v.subject_actor_id, 22 - v.handle, 23 - v.display_name 24 - FROM verification v 25 - WHERE v.subject_actor_id = ANY($1)