From 3a97d987ad7c0ba52d73bfb58323b6c3e3935854 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:43:12 -0700 Subject: [PATCH 1/2] chore(db): sync schema dump with merged migrations Regenerate sql/01_schema.sql and sql/03_migration_tracker.sql via `make test-schema`. The committed dump had drifted behind several already-merged migrations (~0218-0228); this brings the test-template schema back in sync. No functional change. Co-Authored-By: Claude Opus 4.8 --- sql/01_schema.sql | 666 ++++++++++++++++++----------------- sql/03_migration_tracker.sql | 51 ++- 2 files changed, 392 insertions(+), 325 deletions(-) diff --git a/sql/01_schema.sql b/sql/01_schema.sql index 15da93ad..3e656dc9 100644 --- a/sql/01_schema.sql +++ b/sql/01_schema.sql @@ -1894,31 +1894,7 @@ $$; CREATE FUNCTION public.get_user_score(target_user_id integer) RETURNS TABLE(user_id integer, handle_lc text, play_count bigint, distinct_tracks_played bigint, challenge_count bigint, following_count bigint, follower_count bigint, chat_block_count bigint, is_audius_impersonator boolean, has_profile_picture boolean, karma bigint, score bigint) LANGUAGE sql - AS $$ with play_activity as ( - select p.user_id, - count(distinct date_trunc('day', p.created_at)) as play_count, - count(distinct p.play_item_id) as distinct_tracks_played - from plays p - where p.user_id = target_user_id - group by p.user_id - ), - fast_challenge_completion as ( - select u.user_id, - u.handle_lc, - u.created_at, - count(*) as challenge_count, - array_agg(uc.challenge_id) as challenge_ids - from users u - left join user_challenges uc on u.user_id = uc.user_id - where u.user_id = target_user_id - and uc.is_complete - and uc.completed_at - u.created_at <= interval '3 minutes' - and uc.challenge_id not in ('m', 'b') - group by u.user_id, - u.handle_lc, - u.created_at - ), - chat_blocks as ( + AS $$ with chat_blocks as ( select c.blockee_user_id as user_id, count(*) as block_count from chat_blocked_users c @@ -1928,9 +1904,9 @@ CREATE FUNCTION public.get_user_score(target_user_id integer) RETURNS TABLE(user aggregate_scores as ( select u.user_id, u.handle_lc, - coalesce(p.play_count, 0) as play_count, - coalesce(p.distinct_tracks_played, 0) as distinct_tracks_played, - coalesce(c.challenge_count, 0) as challenge_count, + coalesce(udph.hours_with_play, 0)::bigint as play_count, + coalesce(udpt.track_count, 0)::bigint as distinct_tracks_played, + coalesce(usf.challenge_count, 0)::bigint as challenge_count, coalesce(au.following_count, 0) as following_count, coalesce(au.follower_count, 0) as follower_count, coalesce(cb.block_count, 0) as chat_block_count, @@ -1965,11 +1941,13 @@ CREATE FUNCTION public.get_user_score(target_user_id integer) RETURNS TABLE(user ) end as karma from users u - left join play_activity p on u.user_id = p.user_id - left join fast_challenge_completion c on u.user_id = c.user_id + left join user_distinct_play_hours udph on u.user_id = udph.user_id + left join user_distinct_play_tracks udpt on u.user_id = udpt.user_id + left join user_score_features usf on u.user_id = usf.user_id left join chat_blocks cb on u.user_id = cb.user_id left join aggregate_user au on u.user_id = au.user_id where u.user_id = target_user_id + and u.is_current and u.handle_lc is not null ) select a.*, @@ -1994,36 +1972,7 @@ $$; CREATE FUNCTION public.get_user_scores(target_user_ids integer[] DEFAULT NULL::integer[]) RETURNS TABLE(user_id integer, handle_lc text, play_count bigint, distinct_tracks_played bigint, follower_count bigint, following_count bigint, challenge_count bigint, chat_block_count bigint, is_audius_impersonator boolean, has_profile_picture boolean, karma bigint, score bigint) LANGUAGE sql - AS $$ with play_activity as ( - select plays.user_id, - count(distinct (date_trunc('hour', plays.created_at))) as play_count, - count(distinct(plays.play_item_id)) as distinct_tracks_played - from plays - join users on plays.user_id = users.user_id - where target_user_ids is null - or plays.user_id = any(target_user_ids) - group by plays.user_id - ), - fast_challenge_completion as ( - select users.user_id, - handle_lc, - users.created_at, - count(*) as challenge_count, - array_agg(user_challenges.challenge_id) as challenge_ids - from users - left join user_challenges on users.user_id = user_challenges.user_id - where user_challenges.is_complete - and user_challenges.completed_at - users.created_at <= interval '3 minutes' - and user_challenges.challenge_id not in ('m', 'b') - and ( - target_user_ids is null - or users.user_id = any(target_user_ids) - ) - group by users.user_id, - users.handle_lc, - users.created_at - ), - chat_blocks as ( + AS $$ with chat_blocks as ( select chat_blocked_users.blockee_user_id as user_id, count(*) as block_count from chat_blocked_users @@ -2035,11 +1984,11 @@ CREATE FUNCTION public.get_user_scores(target_user_ids integer[] DEFAULT NULL::i aggregate_scores as ( select users.user_id, users.handle_lc, - coalesce(play_activity.play_count, 0) as play_count, - coalesce(play_activity.distinct_tracks_played, 0) as distinct_tracks_played, + coalesce(user_distinct_play_hours.hours_with_play, 0)::bigint as play_count, + coalesce(user_distinct_play_tracks.track_count, 0)::bigint as distinct_tracks_played, coalesce(aggregate_user.following_count, 0) as following_count, coalesce(aggregate_user.follower_count, 0) as follower_count, - coalesce(fast_challenge_completion.challenge_count, 0) as challenge_count, + coalesce(user_score_features.challenge_count, 0)::bigint as challenge_count, coalesce(chat_blocks.block_count, 0) as chat_block_count, case when ( @@ -2072,11 +2021,13 @@ CREATE FUNCTION public.get_user_scores(target_user_ids integer[] DEFAULT NULL::i ) end as karma from users - left join play_activity on users.user_id = play_activity.user_id - left join fast_challenge_completion on users.user_id = fast_challenge_completion.user_id + left join user_distinct_play_hours on users.user_id = user_distinct_play_hours.user_id + left join user_distinct_play_tracks on users.user_id = user_distinct_play_tracks.user_id + left join user_score_features on users.user_id = user_score_features.user_id left join chat_blocks on users.user_id = chat_blocks.user_id left join aggregate_user on aggregate_user.user_id = users.user_id where users.handle_lc is not null + and users.is_current and ( target_user_ids is null or users.user_id = any(target_user_ids) @@ -3187,12 +3138,9 @@ begin insert into aggregate_user (user_id) values (new.followee_user_id) on conflict do nothing; insert into aggregate_user (user_id) values (new.follower_user_id) on conflict do nothing; - -- increment or decrement? - if new.is_delete then - delta := -1; - else - delta := 1; - end if; + -- transition-aware delta (active = not is_delete); 0 on no-op re-delivery + delta := (case when new.is_delete then 0 else 1 end) + - (case when tg_op = 'UPDATE' and old.is_delete is false then 1 else 0 end); update aggregate_user set following_count = following_count + delta @@ -3796,37 +3744,20 @@ begin where ap.playlist_id = new.repost_item_id; end if; - -- increment or decrement? - if new.is_delete then - delta := -1; - else - delta := 1; - end if; + -- transition-aware delta (active = not is_delete); 0 on no-op re-delivery + delta := (case when new.is_delete then 0 else 1 end) + - (case when tg_op = 'UPDATE' and old.is_delete is false then 1 else 0 end); -- update agg user - update aggregate_user - set repost_count = ( - select count(*) - from reposts r - where r.is_current is true - and r.is_delete is false - and r.user_id = new.user_id - ) + update aggregate_user + set repost_count = repost_count + delta where user_id = new.user_id; -- update agg track or playlist if new.repost_type = 'track' then milestone_name := 'TRACK_REPOST_COUNT'; - update aggregate_track - set repost_count = ( - select count(*) - from reposts r - where - r.is_current is true - and r.is_delete is false - and r.repost_type = new.repost_type - and r.repost_item_id = new.repost_item_id - ) + update aggregate_track + set repost_count = repost_count + delta where track_id = new.repost_item_id returning repost_count into new_val; if new.is_delete IS FALSE then @@ -3835,15 +3766,7 @@ begin else milestone_name := 'PLAYLIST_REPOST_COUNT'; update aggregate_playlist - set repost_count = ( - select count(*) - from reposts r - where - r.is_current is true - and r.is_delete is false - and r.repost_type = new.repost_type - and r.repost_item_id = new.repost_item_id - ) + set repost_count = repost_count + delta where playlist_id = new.repost_item_id returning repost_count into new_val; @@ -4076,42 +3999,24 @@ begin ) into is_purchased; end if; - -- increment or decrement? - if new.is_delete then - delta := -1; - else - delta := 1; - end if; + -- transition-aware delta (active = not is_delete); 0 on no-op re-delivery + delta := (case when new.is_delete then 0 else 1 end) + - (case when tg_op = 'UPDATE' and old.is_delete is false then 1 else 0 end); -- update agg track or playlist if new.save_type = 'track' then milestone_name := 'TRACK_SAVE_COUNT'; - update aggregate_track - set save_count = ( - select count(*) - from saves r - where - r.is_current is true - and r.is_delete is false - and r.save_type = new.save_type - and r.save_item_id = new.save_item_id - ) + update aggregate_track + set save_count = save_count + delta where track_id = new.save_item_id returning save_count into new_val; -- update agg user - update aggregate_user - set track_save_count = ( - select count(*) - from saves r - where r.is_current is true - and r.is_delete is false - and r.user_id = new.user_id - and r.save_type = new.save_type - ) + update aggregate_user + set track_save_count = track_save_count + delta where user_id = new.user_id; - + if new.is_delete IS FALSE then select tracks.owner_id, tracks.remix_of into owner_user_id, track_remix_of from tracks where is_current and track_id = new.save_item_id; end if; @@ -4119,15 +4024,7 @@ begin milestone_name := 'PLAYLIST_SAVE_COUNT'; update aggregate_playlist - set save_count = ( - select count(*) - from saves r - where - r.is_current is true - and r.is_delete is false - and r.save_type = new.save_type - and r.save_item_id = new.save_item_id - ) + set save_count = save_count + delta where playlist_id = new.save_item_id returning save_count into new_val; @@ -5980,6 +5877,20 @@ begin PERFORM pg_notify(TG_TABLE_NAME, json_build_object('track_id', new.track_id, 'updated_at', new.updated_at, 'created_at', new.created_at, 'blocknumber', new.blocknumber)::text); when 'users' then PERFORM pg_notify(TG_TABLE_NAME, json_build_object('user_id', new.user_id, 'blocknumber', new.blocknumber)::text); + -- Dedicated verification-transition channel. + -- + -- The Go ETL writes `users` in place (single is_current row per user; it + -- bumps blocknumber rather than appending a versioned row), so consumers + -- can no longer reconstruct the "previous" verification state from the + -- `users` table or from revert_blocks. But because the update is in place, + -- this AFTER trigger's OLD row holds the true pre-update state. Emit on a + -- separate channel only for the genuine false -> true transition (or a + -- brand-new row that arrives already verified) so downstream listeners + -- (e.g. the verified-notifications Slack bot) fire exactly once instead of + -- on every profile edit by an already-verified user. + if new.is_verified and (TG_OP = 'INSERT' or not coalesce(old.is_verified, false)) then + PERFORM pg_notify('user_verified', json_build_object('user_id', new.user_id, 'blocknumber', new.blocknumber)::text); + end if; when 'playlists' then PERFORM pg_notify(TG_TABLE_NAME, json_build_object('playlist_id', new.playlist_id)::text); else @@ -6073,6 +5984,72 @@ end; $$; +-- +-- Name: process_track_collaborator_change(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.process_track_collaborator_change() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + -- A newly created pending invite (created_at = updated_at distinguishes a + -- fresh insert from a reconciled re-write), or a row resurrected back to + -- pending: notify the invited collaborator. + if (TG_OP = 'INSERT' and NEW.status = 'pending' and NEW.created_at = NEW.updated_at) or + (TG_OP = 'UPDATE' and NEW.status = 'pending' and OLD.status is distinct from 'pending') + then + insert into notification + (blocknumber, user_ids, timestamp, type, specifier, group_id, data) + values + ( + new.blocknumber, + array [new.collaborator_user_id], + new.updated_at, + 'track_collaborator_invite', + new.invited_by, + 'track_collaborator_invite:' || 'track_id:' || new.track_id || + ':collaborator_user_id:' || new.collaborator_user_id || + ':inviter_user_id:' || new.invited_by, + json_build_object( + 'track_id', new.track_id, + 'collaborator_user_id', new.collaborator_user_id, + 'inviter_user_id', new.invited_by + ) + ) + on conflict do nothing; + -- Invite accepted: notify the inviter (track owner). + elsif (TG_OP = 'UPDATE' and NEW.status = 'accepted' and OLD.status is distinct from 'accepted') or + (TG_OP = 'INSERT' and NEW.status = 'accepted') + then + insert into notification + (blocknumber, user_ids, timestamp, type, specifier, group_id, data) + values + ( + new.blocknumber, + array [new.invited_by], + new.updated_at, + 'track_collaborator_accept', + new.collaborator_user_id, + 'track_collaborator_accept:' || 'track_id:' || new.track_id || + ':collaborator_user_id:' || new.collaborator_user_id || + ':inviter_user_id:' || new.invited_by, + json_build_object( + 'track_id', new.track_id, + 'collaborator_user_id', new.collaborator_user_id, + 'inviter_user_id', new.invited_by + ) + ) + on conflict do nothing; + end if; + return null; +exception + when others then + raise warning 'An error occurred in %: %', tg_name, sqlerrm; + return null; +end; +$$; + + -- -- Name: recreate_trending_params(); Type: FUNCTION; Schema: public; Owner: - -- @@ -7193,7 +7170,8 @@ CREATE TABLE public.api_access_keys ( api_key character varying(255) NOT NULL, api_access_key character varying(255) NOT NULL, created_at timestamp without time zone DEFAULT now() NOT NULL, - is_active boolean DEFAULT true NOT NULL + is_active boolean DEFAULT true NOT NULL, + CONSTRAINT api_access_keys_api_key_lowercase_check CHECK (((api_key)::text = lower((api_key)::text))) ); @@ -7206,7 +7184,8 @@ CREATE TABLE public.api_keys ( api_secret character varying(255), rps integer DEFAULT 10 NOT NULL, rpm integer DEFAULT 500000 NOT NULL, - created_at timestamp without time zone DEFAULT now() NOT NULL + created_at timestamp without time zone DEFAULT now() NOT NULL, + CONSTRAINT api_keys_api_key_lowercase_check CHECK (((api_key)::text = lower((api_key)::text))) ); @@ -8404,6 +8383,21 @@ CREATE TABLE public.eth_wallet_balances ( COMMENT ON TABLE public.eth_wallet_balances IS 'AUDIO ERC-20 balances (in wei) for tracked Ethereum wallets — primary users.wallet and chain=eth associated_wallets. Maintained event-driven by the eth-indexer (WebSocket subscription to the AUDIO Transfer topic, targeted balanceOf reads).'; +-- +-- Name: event_routes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.event_routes ( + slug character varying NOT NULL, + owner_id integer NOT NULL, + event_id integer NOT NULL, + is_current boolean NOT NULL, + blockhash character varying NOT NULL, + blocknumber integer NOT NULL, + txhash character varying NOT NULL +); + + -- -- Name: events; Type: TABLE; Schema: public; Owner: - -- @@ -8425,21 +8419,6 @@ CREATE TABLE public.events ( ); --- --- Name: event_routes; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.event_routes ( - slug character varying NOT NULL, - owner_id integer NOT NULL, - event_id integer NOT NULL, - is_current boolean NOT NULL, - blockhash character varying NOT NULL, - blocknumber integer NOT NULL, - txhash character varying NOT NULL -); - - -- -- Name: follows; Type: TABLE; Schema: public; Owner: - -- @@ -8475,95 +8454,6 @@ CREATE TABLE public.grants ( ); --- --- Name: track_collaborators; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.track_collaborators ( - track_id integer NOT NULL, - collaborator_user_id integer NOT NULL, - invited_by integer NOT NULL, - status text DEFAULT 'pending'::text NOT NULL, - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL, - txhash character varying NOT NULL, - blocknumber integer, - CONSTRAINT track_collaborators_pkey PRIMARY KEY (track_id, collaborator_user_id), - CONSTRAINT track_collaborators_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'accepted'::text, 'rejected'::text]))) -); - - -CREATE INDEX IF NOT EXISTS idx_track_collaborators_collaborator ON public.track_collaborators USING btree (collaborator_user_id, status, track_id); - - --- Notification trigger for collaborative tracks (see --- ddl/functions/handle_track_collaborator.sql). Defined inline here so fresh / --- test databases loaded from this schema have it; `make test-schema` will --- canonicalize the placement. -create or replace function public.process_track_collaborator_change() returns trigger as $$ -begin - if (TG_OP = 'INSERT' and NEW.status = 'pending' and NEW.created_at = NEW.updated_at) or - (TG_OP = 'UPDATE' and NEW.status = 'pending' and OLD.status is distinct from 'pending') - then - insert into notification - (blocknumber, user_ids, timestamp, type, specifier, group_id, data) - values - ( - new.blocknumber, - array [new.collaborator_user_id], - new.updated_at, - 'track_collaborator_invite', - new.invited_by, - 'track_collaborator_invite:' || 'track_id:' || new.track_id || - ':collaborator_user_id:' || new.collaborator_user_id || - ':inviter_user_id:' || new.invited_by, - json_build_object( - 'track_id', new.track_id, - 'collaborator_user_id', new.collaborator_user_id, - 'inviter_user_id', new.invited_by - ) - ) - on conflict do nothing; - elsif (TG_OP = 'UPDATE' and NEW.status = 'accepted' and OLD.status is distinct from 'accepted') or - (TG_OP = 'INSERT' and NEW.status = 'accepted') - then - insert into notification - (blocknumber, user_ids, timestamp, type, specifier, group_id, data) - values - ( - new.blocknumber, - array [new.invited_by], - new.updated_at, - 'track_collaborator_accept', - new.collaborator_user_id, - 'track_collaborator_accept:' || 'track_id:' || new.track_id || - ':collaborator_user_id:' || new.collaborator_user_id || - ':inviter_user_id:' || new.invited_by, - json_build_object( - 'track_id', new.track_id, - 'collaborator_user_id', new.collaborator_user_id, - 'inviter_user_id', new.invited_by - ) - ) - on conflict do nothing; - end if; - return null; -exception - when others then - raise warning 'An error occurred in %: %', tg_name, sqlerrm; - return null; -end; -$$ language plpgsql; - -do $$ begin - create trigger trigger_track_collaborator_change - after insert or update on public.track_collaborators - for each row execute procedure public.process_track_collaborator_change(); -exception - when others then null; -end $$; - - -- -- Name: hourly_play_counts; Type: TABLE; Schema: public; Owner: - -- @@ -10160,6 +10050,30 @@ CREATE MATERIALIZED VIEW public.tag_track_user AS WITH NO DATA; +-- +-- Name: track_collaborators; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.track_collaborators ( + track_id integer NOT NULL, + collaborator_user_id integer NOT NULL, + invited_by integer NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + txhash character varying NOT NULL, + blocknumber integer, + CONSTRAINT track_collaborators_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'accepted'::text, 'rejected'::text]))) +); + + +-- +-- Name: TABLE track_collaborators; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.track_collaborators IS 'Collaborator credits on a track. Owner invites via track metadata (status=pending); the collaborator accepts/declines on-chain (accepted/rejected). Indexed by ETL (go-openaudio).'; + + -- -- Name: track_delist_statuses; Type: TABLE; Schema: public; Owner: - -- @@ -10298,6 +10212,13 @@ CREATE TABLE public.users ( COMMENT ON COLUMN public.users.coin_flair_mint IS 'The mint of the coin which the user has selected as their preferred flair. NULL for auto, empty string for none.'; +-- +-- Name: COLUMN users.last_active_at; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.users.last_active_at IS 'Timestamp of the user''s most recent app-open event, updated by POST /v1/users/me/ping.'; + + -- -- Name: trending_params; Type: MATERIALIZED VIEW; Schema: public; Owner: - -- @@ -12243,6 +12164,14 @@ ALTER TABLE ONLY public.supporter_rank_ups ADD CONSTRAINT supporter_rank_ups_pkey PRIMARY KEY (slot, sender_user_id, receiver_user_id); +-- +-- Name: track_collaborators track_collaborators_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.track_collaborators + ADD CONSTRAINT track_collaborators_pkey PRIMARY KEY (track_id, collaborator_user_id); + + -- -- Name: track_delist_statuses track_delist_statuses_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -12526,17 +12455,31 @@ CREATE INDEX challenge_disbursements_user_id ON public.challenge_disbursements U -- --- Name: chat_chat_id_idx; Type: INDEX; Schema: public; Owner: - +-- Name: chat_member_user_idx; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX chat_chat_id_idx ON public.chat USING btree (chat_id); +CREATE INDEX chat_member_user_idx ON public.chat_member USING btree (user_id); -- --- Name: chat_member_user_idx; Type: INDEX; Schema: public; Owner: - +-- Name: chat_message_chat_created_non_blast_idx; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX chat_member_user_idx ON public.chat_member USING btree (user_id); +CREATE INDEX chat_message_chat_created_non_blast_idx ON public.chat_message USING btree (chat_id, created_at, user_id) WHERE (blast_id IS NULL); + + +-- +-- Name: chat_message_reactions_updated_at_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX chat_message_reactions_updated_at_idx ON public.chat_message_reactions USING btree (updated_at, message_id, user_id); + + +-- +-- Name: INDEX chat_message_reactions_updated_at_idx; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON INDEX public.chat_message_reactions_updated_at_idx IS 'Supports DM reaction notification polling by updated_at cursor.'; -- @@ -12581,6 +12524,34 @@ CREATE INDEX claimed_prizes_wallet_idx ON public.claimed_prizes USING btree (wal COMMENT ON INDEX public.claimed_prizes_wallet_idx IS 'Used for getting claimed prizes by wallet.'; +-- +-- Name: comment_threads_comment_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX comment_threads_comment_id_idx ON public.comment_threads USING btree (comment_id); + + +-- +-- Name: comments_blocknumber_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX comments_blocknumber_idx ON public.comments USING btree (blocknumber); + + +-- +-- Name: INDEX comments_blocknumber_idx; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON INDEX public.comments_blocknumber_idx IS 'Range scans by blocknumber for the incremental FirstWeeklyComment challenge processor (c).'; + + +-- +-- Name: comments_user_track_created_at_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX comments_user_track_created_at_idx ON public.comments USING btree (user_id, created_at DESC) INCLUDE (comment_id) WHERE ((entity_type = 'Track'::text) AND (is_delete = false)); + + -- -- Name: eth_wallet_balances_updated_at_idx; Type: INDEX; Schema: public; Owner: - -- @@ -12595,6 +12566,13 @@ CREATE INDEX eth_wallet_balances_updated_at_idx ON public.eth_wallet_balances US COMMENT ON INDEX public.eth_wallet_balances_updated_at_idx IS 'Supports staleness queries / catch-up sweeps.'; +-- +-- Name: event_routes_event_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX event_routes_event_id_idx ON public.event_routes USING btree (event_id); + + -- -- Name: fix_tracks_top_genre_users_idx; Type: INDEX; Schema: public; Owner: - -- @@ -12707,13 +12685,6 @@ CREATE INDEX idx_api_metrics_routes_route_pattern ON public.api_metrics_routes U CREATE INDEX idx_chain_blockhash ON public.core_indexed_blocks USING btree (blockhash); --- --- Name: idx_chain_id_height; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX idx_chain_id_height ON public.core_indexed_blocks USING btree (chain_id, height); - - -- -- Name: idx_challenge_disbursements_created_at; Type: INDEX; Schema: public; Owner: - -- @@ -12924,6 +12895,20 @@ CREATE INDEX idx_playlist_status ON public.playlists USING btree (playlist_id, i CREATE INDEX idx_playlist_tracks_track_id ON public.playlist_tracks USING btree (track_id, created_at); +-- +-- Name: idx_playlist_trending_scores_ordered; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_playlist_trending_scores_ordered ON public.playlist_trending_scores USING btree (type, version, time_range, score DESC, playlist_id DESC); + + +-- +-- Name: INDEX idx_playlist_trending_scores_ordered; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON INDEX public.idx_playlist_trending_scores_ordered IS 'Covers playlist trending endpoints ordered by score desc, playlist_id desc.'; + + -- -- Name: idx_playlists_albums_published; Type: INDEX; Schema: public; Owner: - -- @@ -12966,6 +12951,13 @@ CREATE INDEX idx_sol_reward_manager_inits_mint ON public.sol_reward_manager_init COMMENT ON INDEX public.idx_sol_reward_manager_inits_mint IS 'Index to quickly find reward manager init instructions by mint of its token rewards'; +-- +-- Name: idx_track_collaborators_collaborator; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_track_collaborators_collaborator ON public.track_collaborators USING btree (collaborator_user_id, status, track_id); + + -- -- Name: idx_track_status; Type: INDEX; Schema: public; Owner: - -- @@ -12974,24 +12966,31 @@ CREATE INDEX idx_track_status ON public.tracks USING btree (track_id, is_unliste -- --- Name: idx_track_trending_scores_for_you; Type: INDEX; Schema: public; Owner: - +-- Name: idx_track_trending_scores_for_you_desc_tiebreak; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX idx_track_trending_scores_for_you ON public.track_trending_scores USING btree (score DESC, track_id) WHERE (((type)::text = 'TRACKS'::text) AND ((version)::text = 'pnagD'::text) AND ((time_range)::text = 'week'::text) AND ((genre IS NULL) OR ((genre)::text = ''::text))); +CREATE INDEX idx_track_trending_scores_for_you_desc_tiebreak ON public.track_trending_scores USING btree (score DESC, track_id DESC) WHERE (((type)::text = 'TRACKS'::text) AND ((version)::text = 'pnagD'::text) AND ((time_range)::text = 'week'::text) AND ((genre IS NULL) OR ((genre)::text = ''::text))); -- --- Name: INDEX idx_track_trending_scores_for_you; Type: COMMENT; Schema: public; Owner: - +-- Name: INDEX idx_track_trending_scores_for_you_desc_tiebreak; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON INDEX public.idx_track_trending_scores_for_you IS 'Partial index for the For You feed trending/underground candidate sources; replaces a ~12s full-table scan with a small index seek.'; +COMMENT ON INDEX public.idx_track_trending_scores_for_you_desc_tiebreak IS 'Partial index matching For You feed weekly track candidate ordering by score desc, track_id desc.'; -- --- Name: idx_tts_genre_time_score; Type: INDEX; Schema: public; Owner: - +-- Name: idx_tts_tracks_pnagd_genre_time_score_desc; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX idx_tts_genre_time_score ON public.track_trending_scores USING btree (genre, time_range, score DESC, track_id); +CREATE INDEX idx_tts_tracks_pnagd_genre_time_score_desc ON public.track_trending_scores USING btree (genre, time_range, score DESC, track_id DESC) WHERE (((type)::text = 'TRACKS'::text) AND ((version)::text = 'pnagD'::text)); + + +-- +-- Name: INDEX idx_tts_tracks_pnagd_genre_time_score_desc; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON INDEX public.idx_tts_tracks_pnagd_genre_time_score_desc IS 'Covers current TRACKS/pnagD genre-filtered trending reads with score desc, track_id desc ordering.'; -- @@ -13057,25 +13056,11 @@ CREATE INDEX idx_user_bank_txs_slot ON public.user_bank_txs USING btree (slot); CREATE INDEX idx_user_status ON public.users USING btree (user_id, is_deactivated, is_available, is_current); --- --- Name: interval_play_month_count_idx; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX interval_play_month_count_idx ON public.aggregate_interval_plays USING btree (month_listen_counts); - - -- -- Name: interval_play_track_id_idx; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX interval_play_track_id_idx ON public.aggregate_interval_plays USING btree (track_id); - - --- --- Name: interval_play_week_count_idx; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX interval_play_week_count_idx ON public.aggregate_interval_plays USING btree (week_listen_counts); +CREATE UNIQUE INDEX interval_play_track_id_idx ON public.aggregate_interval_plays USING btree (track_id); -- @@ -13261,24 +13246,10 @@ CREATE INDEX ix_supporter_rank_ups_slot ON public.supporter_rank_ups USING btree -- --- Name: ix_track_trending_scores_genre; Type: INDEX; Schema: public; Owner: - +-- Name: ix_trending_scores_desc_tiebreak; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX ix_track_trending_scores_genre ON public.track_trending_scores USING btree (genre); - - --- --- Name: ix_track_trending_scores_track_id; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX ix_track_trending_scores_track_id ON public.track_trending_scores USING btree (track_id); - - --- --- Name: ix_trending_scores; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX ix_trending_scores ON public.track_trending_scores USING btree (type, version, time_range, score DESC, track_id); +CREATE INDEX ix_trending_scores_desc_tiebreak ON public.track_trending_scores USING btree (type, version, time_range, score DESC, track_id DESC); -- @@ -13330,6 +13301,20 @@ CREATE INDEX ix_user_tips_slot ON public.user_tips USING btree (slot); CREATE INDEX milestones_name_idx ON public.milestones USING btree (name, id); +-- +-- Name: notification_multi_recipient_user_ids_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notification_multi_recipient_user_ids_idx ON public.notification USING gin (user_ids) WHERE (COALESCE(array_length(user_ids, 1), 0) <> 1); + + +-- +-- Name: INDEX notification_multi_recipient_user_ids_idx; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON INDEX public.notification_multi_recipient_user_ids_idx IS 'Covers notification reads for the uncommon multi-recipient user_ids array path.'; + + -- -- Name: notification_seen_blocknumber_idx; Type: INDEX; Schema: public; Owner: - -- @@ -13337,6 +13322,20 @@ CREATE INDEX milestones_name_idx ON public.milestones USING btree (name, id); CREATE INDEX notification_seen_blocknumber_idx ON public.notification_seen USING btree (blocknumber); +-- +-- Name: notification_single_recipient_user_timestamp_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notification_single_recipient_user_timestamp_idx ON public.notification USING btree ((user_ids[1]), "timestamp" DESC, group_id DESC, type) WHERE (array_length(user_ids, 1) = 1); + + +-- +-- Name: INDEX notification_single_recipient_user_timestamp_idx; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON INDEX public.notification_single_recipient_user_timestamp_idx IS 'Covers notification reads for the common single-recipient user_ids array path.'; + + -- -- Name: playlist_created_at_idx; Type: INDEX; Schema: public; Owner: - -- @@ -13365,6 +13364,20 @@ CREATE INDEX playlist_routes_playlist_id_idx ON public.playlist_routes USING btr CREATE INDEX playlists_blocknumber_idx ON public.playlists USING btree (blocknumber); +-- +-- Name: playlists_scheduled_release_due_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX playlists_scheduled_release_due_idx ON public.playlists USING btree (release_date) WHERE ((is_private = true) AND (is_album = true) AND (is_scheduled_release = true) AND (release_date IS NOT NULL) AND (is_current = true) AND (is_delete = false)); + + +-- +-- Name: INDEX playlists_scheduled_release_due_idx; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON INDEX public.playlists_scheduled_release_due_idx IS 'Covers the scheduled-release publisher album update by release_date for due private scheduled albums.'; + + -- -- Name: prizes_active_idx; Type: INDEX; Schema: public; Owner: - -- @@ -13414,6 +13427,13 @@ CREATE INDEX reposts_new_blocknumber_idx ON public.reposts USING btree (blocknum CREATE INDEX reposts_new_created_at_idx ON public.reposts USING btree (created_at); +-- +-- Name: reposts_user_created_at_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX reposts_user_created_at_active_idx ON public.reposts USING btree (user_id, created_at DESC) INCLUDE (repost_type, repost_item_id) WHERE (is_delete = false); + + -- -- Name: reposts_user_idx; Type: INDEX; Schema: public; Owner: - -- @@ -13477,6 +13497,13 @@ CREATE INDEX saves_new_blocknumber_idx ON public.saves USING btree (blocknumber) CREATE INDEX saves_user_idx ON public.saves USING btree (user_id, save_type, save_item_id, is_delete); +-- +-- Name: saves_user_track_current_blocknumber_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saves_user_track_current_blocknumber_idx ON public.saves USING btree (user_id, blocknumber, save_item_id DESC) INCLUDE (created_at) WHERE ((save_type = 'track'::public.savetype) AND (is_current = true) AND (is_delete = false)); + + -- -- Name: shares_item_idx; Type: INDEX; Schema: public; Owner: - -- @@ -13708,13 +13735,6 @@ CREATE INDEX sol_reward_disbursements_challenge_idx ON public.sol_reward_disburs COMMENT ON INDEX public.sol_reward_disbursements_challenge_idx IS 'Used for getting reward disbursements for a specific challenge type or claim.'; --- --- Name: sol_reward_disbursements_challenge_specifier_idx; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX sol_reward_disbursements_challenge_specifier_idx ON public.sol_reward_disbursements USING btree (challenge_id, specifier); - - -- -- Name: sol_reward_disbursements_created_at_idx; Type: INDEX; Schema: public; Owner: - -- @@ -13925,6 +13945,13 @@ CREATE INDEX track_owner_id_idx ON public.tracks USING btree (owner_id); CREATE INDEX track_owner_idx ON public.tracks USING btree (owner_id, created_at); +-- +-- Name: track_routes_owner_title_slug_collision_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX track_routes_owner_title_slug_collision_idx ON public.track_routes USING btree (owner_id, title_slug, collision_id DESC); + + -- -- Name: track_routes_track_id_idx; Type: INDEX; Schema: public; Owner: - -- @@ -13961,45 +13988,45 @@ COMMENT ON INDEX public.tracks_isrc_normalized_idx IS 'Functional index supporti -- --- Name: tracks_track_cid_idx; Type: INDEX; Schema: public; Owner: - +-- Name: tracks_scheduled_release_due_idx; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX tracks_track_cid_idx ON public.tracks USING btree (track_cid, is_delete); +CREATE INDEX tracks_scheduled_release_due_idx ON public.tracks USING btree (release_date) WHERE ((is_unlisted = true) AND (is_scheduled_release = true) AND (release_date IS NOT NULL) AND (is_current = true) AND (is_delete = false)); -- --- Name: trending_params_track_id_idx; Type: INDEX; Schema: public; Owner: - +-- Name: INDEX tracks_scheduled_release_due_idx; Type: COMMENT; Schema: public; Owner: - -- -CREATE INDEX trending_params_track_id_idx ON public.trending_params USING btree (track_id); +COMMENT ON INDEX public.tracks_scheduled_release_due_idx IS 'Covers the scheduled-release publisher tracks update by release_date for due unlisted scheduled tracks.'; -- --- Name: user_balance_history_timestamp_idx; Type: INDEX; Schema: public; Owner: - +-- Name: tracks_track_cid_idx; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX user_balance_history_timestamp_idx ON public.user_balance_history USING btree ("timestamp" DESC); +CREATE INDEX tracks_track_cid_idx ON public.tracks USING btree (track_cid, is_delete); -- --- Name: INDEX user_balance_history_timestamp_idx; Type: COMMENT; Schema: public; Owner: - +-- Name: trending_params_track_id_idx; Type: INDEX; Schema: public; Owner: - -- -COMMENT ON INDEX public.user_balance_history_timestamp_idx IS 'Optimizes queries finding recent balances across all users'; +CREATE UNIQUE INDEX trending_params_track_id_idx ON public.trending_params USING btree (track_id); -- --- Name: user_balance_history_user_mint_timestamp_idx; Type: INDEX; Schema: public; Owner: - +-- Name: user_balance_history_timestamp_idx; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX user_balance_history_user_mint_timestamp_idx ON public.user_balance_history USING btree (user_id, mint, "timestamp"); +CREATE INDEX user_balance_history_timestamp_idx ON public.user_balance_history USING btree ("timestamp" DESC); -- --- Name: INDEX user_balance_history_user_mint_timestamp_idx; Type: COMMENT; Schema: public; Owner: - +-- Name: INDEX user_balance_history_timestamp_idx; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON INDEX public.user_balance_history_user_mint_timestamp_idx IS 'Optimizes queries filtering by specific mint(s) and time range (e.g., "show USDC balance history")'; +COMMENT ON INDEX public.user_balance_history_timestamp_idx IS 'Optimizes queries finding recent balances across all users'; -- @@ -14223,7 +14250,7 @@ CREATE CONSTRAINT TRIGGER on_fan_club_text_post AFTER INSERT ON public.comments -- Name: follows on_follow; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER on_follow AFTER INSERT ON public.follows FOR EACH ROW EXECUTE FUNCTION public.handle_follow(); +CREATE TRIGGER on_follow AFTER INSERT OR UPDATE ON public.follows FOR EACH ROW EXECUTE FUNCTION public.handle_follow(); -- @@ -14265,7 +14292,7 @@ CREATE TRIGGER on_reaction AFTER INSERT ON public.reactions FOR EACH ROW EXECUTE -- Name: reposts on_repost; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER on_repost AFTER INSERT ON public.reposts FOR EACH ROW EXECUTE FUNCTION public.handle_repost(); +CREATE TRIGGER on_repost AFTER INSERT OR UPDATE ON public.reposts FOR EACH ROW EXECUTE FUNCTION public.handle_repost(); -- @@ -14279,7 +14306,7 @@ CREATE TRIGGER on_rpc_log AFTER INSERT ON public.rpc_log FOR EACH ROW EXECUTE FU -- Name: saves on_save; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER on_save AFTER INSERT ON public.saves FOR EACH ROW EXECUTE FUNCTION public.handle_save(); +CREATE TRIGGER on_save AFTER INSERT OR UPDATE ON public.saves FOR EACH ROW EXECUTE FUNCTION public.handle_save(); -- @@ -14499,6 +14526,13 @@ CREATE TRIGGER trg_users AFTER INSERT OR UPDATE ON public.users FOR EACH ROW EXE CREATE TRIGGER trigger_grant_change AFTER INSERT OR UPDATE ON public.grants FOR EACH ROW EXECUTE FUNCTION public.process_grant_change(); +-- +-- Name: track_collaborators trigger_track_collaborator_change; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trigger_track_collaborator_change AFTER INSERT OR UPDATE ON public.track_collaborators FOR EACH ROW EXECUTE FUNCTION public.process_track_collaborator_change(); + + -- -- Name: album_price_history album_price_history_blocknumber_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -14782,3 +14816,5 @@ ALTER TABLE ONLY public.users -- -- PostgreSQL database dump complete -- + + diff --git a/sql/03_migration_tracker.sql b/sql/03_migration_tracker.sql index e16adc9a..358035df 100644 --- a/sql/03_migration_tracker.sql +++ b/sql/03_migration_tracker.sql @@ -105,38 +105,29 @@ functions/chat_blast_audience.sql 3202f26a9bdf02f6d0e967e275aea7ee 2026-05-27 00 functions/compute_user_score.sql 814b5fa3d1383d3e216943b4ff8c4877 2026-05-27 00:22:35.517899+00 functions/country_to_iso_alpha2.sql 218832f0607aeca4fce99815a07f7a85 2026-05-27 00:22:35.592939+00 functions/find_track.sql f09431015118f31aa7b4ac49d14e7639 2026-05-27 00:22:35.667572+00 -functions/get_user_score.sql 2d5e3003ae1074cbe22ee780dd0ff901 2026-05-27 00:22:35.742438+00 -functions/get_user_scores.sql b22e36599e6503649d7cabff0609bc05 2026-05-27 00:22:35.829695+00 functions/handle_artist_coins.sql 21ed1610d80cceca2262efb1338060ed 2026-05-27 00:22:35.905536+00 functions/handle_challenge_disbursements.sql 1bbf82cc035971d828ed96f3ea1a23d6 2026-05-29 22:00:00+00 functions/handle_chat_blast.sql ccd276957c1b68bfc82fb5a11bac09ee 2026-05-27 00:22:36.155215+00 functions/handle_chat_message.sql 31bebee3d0437133f1f53c2eadb7b391 2026-05-27 00:22:36.229275+00 functions/handle_chat_message_reaction.sql b898313aa8f31c61df7f1e6dd791ef31 2026-05-27 00:22:36.304728+00 functions/handle_comment.sql 619233e3cda476da5695d22ff995ace4 2026-05-27 00:22:36.384986+00 -functions/handle_comment_remix_contest_update.sql 1f5209f511e03a3f4d8e01a0a300b678 2026-05-27 00:22:36.469378+00 functions/handle_comms_rpc_log.sql bca8170b77a97b521b050f49873d43d4 2026-05-27 00:22:36.545119+00 functions/handle_dbc_pools.sql 5d8727fa203bb5f204868f4209a2022a 2026-05-27 00:22:36.618827+00 -functions/handle_event.sql 85ff1a58bb6da94b20a9ba4335fdabc5 2026-05-27 00:22:36.697044+00 -functions/handle_follow.sql f552cb2453bb86161f40cc7a0b23b0aa 2026-05-27 00:22:36.771042+00 functions/handle_manager_request.sql 426004c1b9ac2be9e5721afb580679be 2026-05-27 00:22:36.848838+00 functions/handle_play.sql c710d1ef4805d7f99817baffc6ed8e25 2026-05-27 00:22:36.924069+00 functions/handle_playlist.sql 4b338726d86db94fb3339e8407968cdf 2026-05-27 00:22:36.987976+00 functions/handle_playlist_track.sql bbb4dce9244617aa2d6580aae05d154a 2026-05-27 00:22:37.057245+00 functions/handle_reaction.sql 679796675e687b45288c517c568692d4 2026-05-27 00:22:37.13432+00 -functions/handle_repost.sql fdb05891752cb029b097925ad2d5367e 2026-05-27 00:22:37.218515+00 -functions/handle_save.sql 4893d5ca7730b055a3a20cb0eb546d97 2026-05-27 00:22:37.295378+00 functions/handle_share.sql 84efa350bfd7f5501ccd34a93f6207a6 2026-05-27 00:22:37.371581+00 functions/handle_sol_claimable_accounts.sql de881daabbfb8eb5222e3ae10c6b72c7 2026-05-27 00:22:37.462649+00 functions/handle_sol_token_balance_change.sql 212ea3ca708570c506051a167e1e9117 2026-05-27 00:22:37.555359+00 functions/handle_supporter_rank_ups.sql dea497f1859ade282b4f7d33687e6fe7 2026-05-27 00:22:37.633644+00 -functions/handle_track.sql 89ef5a957b3662310fb30f914aab9ed4 2026-05-27 00:22:37.715524+00 functions/handle_usdc_purchase.sql c35bccc2789ae0641d413d28a131ef8d 2026-05-27 00:22:37.800109+00 functions/handle_user.sql 169778112e5362ee20af56d9b8f274c5 2026-05-27 00:22:37.955619+00 functions/handle_user_balance_changes.sql 1ae7f99f4f37194cdf27dc3189ebc858 2026-05-27 00:22:38.02983+00 functions/handle_user_challenges.sql 8a1287bc971c83e7440b88da7c86e93d 2026-05-29 22:00:00.1+00 functions/handle_user_tip.sql e4bde4e7e04b0ed8254690959513bd69 2026-05-27 00:22:38.167097+00 functions/is_country_eur.sql c5641d570edb9cd47cd4e38d883e941a 2026-05-27 00:22:38.234372+00 -functions/notify_on_row.sql 8b0232ed60eb108aa45f68c6dfe05d59 2026-05-27 00:22:38.304698+00 functions/notify_pending_purchase_revalidation.sql beb9eebc6bd34ab069c7b90a51bb8bb3 2026-05-27 00:22:38.378429+00 functions/price_from_sqrt_price.sql 1b217f211adba88e3f12d1d6c81fc97d 2026-05-27 00:22:38.451965+00 functions/refresh_all_user_scores.sql 04935173f102e5e28b5c384e312102c1 2026-05-27 00:22:38.539364+00 @@ -145,7 +136,6 @@ functions/user_mint_balance_at.sql 6d227781d4d97500e0ec2bb76e8fa295 2026-05-27 0 views/artist_coin_prices.sql 10a65b64b7d13aaabe18ad1055e9fd7b 2026-05-27 00:22:38.820919+00 views/v_challenge_disbursements.sql 74a0a05af6a02f82af3695d5c3ade45c 2026-05-27 00:22:38.899214+00 views/v_usdc_purchases.sql 322a527e132aed647c39b70d63aa6b51 2026-05-27 00:22:39.061667+00 -preflight/0001_initial_block.sql 1e59cca66b2208eda67a87ac0d09b67d 2026-05-27 00:22:39.249328+00 migrations/0204_backfill_eth_wallet_balances_tracked.sql d8b752794c42edb94f0d43633e14208c 2026-05-28 00:56:10.178339+00 migrations/0205_sol_transfer_memo_tables.sql 49aa6bdf68df733710e7820153a78393 2026-05-28 19:54:42.181969+00 migrations/0206_backfill_sol_transfer_memo_types.sql 8b63b2e420c94b740a15e7f1fbd02ae7 2026-05-28 19:54:42.266989+00 @@ -166,6 +156,15 @@ functions/handle_associated_wallet.sql c069cb48c574cc01689ab45f6e036443 2026-05- functions/handle_comment_mention.sql 518e297c3053c240ec1cb804ff4228ee 2026-05-30 01:37:22.69354+00 views/v_user_balances.sql 9ee4060617250285c419e5caf6cb5fab 2026-05-30 01:37:23.77109+00 functions/handle_comment_notification.sql f9ee033ab1fe8ee8dc3437e328873242 2026-05-30 01:37:22.773681+00 +functions/get_user_scores.sql f3d9423e7433e24624f7ea6e7fa0c60c 2026-07-28 05:42:28.398467+00 +functions/handle_comment_remix_contest_update.sql 6c1ef3e0a033b95a7825819ac169ec96 2026-07-28 05:42:28.575556+00 +functions/handle_event.sql f15d9cc1838fa327b5df7e6b10c5ad7c 2026-07-28 05:42:28.698884+00 +functions/handle_follow.sql cf954862ef38daf93740ca1a88f24863 2026-07-28 05:42:28.792892+00 +functions/handle_repost.sql e7cfd188b5aec2b01584dc8db1e9bc00 2026-07-28 05:42:28.962664+00 +functions/handle_save.sql 05422848c57572704f78f27171e94058 2026-07-28 05:42:29.039008+00 +functions/handle_track.sql c2d4c5674b0cb1db907ad625fd957c91 2026-07-28 05:42:29.145055+00 +functions/notify_on_row.sql a326d476636de01dd939047526b0cb92 2026-07-28 05:42:29.345724+00 +preflight/0001_initial_block.sql 6cc3c0833c195a1104bed5bf849c0266 2026-07-28 05:42:29.588508+00 functions/handle_comment_reaction.sql 8153e3cdb922265857b6beaf20d29733 2026-05-30 01:37:22.856663+00 functions/handle_comment_thread.sql 6eb74eb92cf3a01421498df96c6832f3 2026-05-30 01:37:22.955997+00 functions/handle_eth_wallet_balance_change.sql 3e31160b4bc55e951d9dfa4d994c180b 2026-05-30 01:37:23.054573+00 @@ -173,6 +172,38 @@ functions/handle_fan_club_text_post.sql 531bf682bcfd67c6866faf8ccdf7603b 2026-05 functions/handle_tastemaker.sql 04690b53bd094a59717ef3a5b5d2c0a0 2026-05-30 01:37:23.34542+00 functions/handle_trending.sql b754ca4670313bbd331102244f6182e5 2026-05-30 01:37:23.437507+00 functions/update_eth_user_balance.sql 52d3ab427477d0a49ce68bc1ee270aec 2026-05-30 01:37:23.595691+00 +migrations/0214_trending_matview_unique_indexes.sql cdc353a06fb8457c8142919786b2f828 2026-07-28 05:42:25.85407+00 +migrations/0215_drop_blocks_is_current.sql b3afc984072fd2ab9baf286cb7a4d7c4 2026-07-28 05:42:25.924625+00 +migrations/0215_seed_play_count_milestones_checkpoint.sql 82a3be141cc09564a6ad2629c78e0baf 2026-07-28 05:42:25.993786+00 +migrations/0216_comments_blocknumber_idx.sql b8099bdb95245a0d9893dd958a3a18d3 2026-07-28 05:42:26.063232+00 +migrations/0217_seed_first_weekly_comment_checkpoint.sql ab9878725ae608ddafc4046b13afd37b 2026-07-28 05:42:26.134501+00 +migrations/0218_canonicalize_api_keys.sql 7485765447e132f54faaea9c572f9398 2026-07-28 05:42:26.214628+00 +migrations/0218_create_event_routes.sql ae18211413b3ab7cd4c414904deeb8ba 2026-07-28 05:42:26.281543+00 +migrations/0218_drop_duplicate_reward_disbursement_challenge_index.sql e67800044d6314a2da9f310581e29a4d 2026-07-28 05:42:26.351654+00 +migrations/0218_drop_unused_interval_play_count_indexes.sql 0c3c76350b6796ba695cd188f8d7f198 2026-07-28 05:42:26.421192+00 +migrations/0218_drop_user_balance_history_user_mint_timestamp_idx.sql f5a1732a0e6dc42cf0f11c3a26e0b31a 2026-07-28 05:42:26.489438+00 +migrations/0218_playlist_trending_scores_ordered_idx.sql d09a926b8d86eeae12573de759e49d33 2026-07-28 05:42:26.558753+00 +migrations/0218_track_trending_scores_desc_tiebreak_idx.sql ea0b540c7335af939bcecc8bc129674d 2026-07-28 05:42:26.627386+00 +migrations/0218_track_trending_scores_for_you_desc_tiebreak_idx.sql b4575a838f03faedc6b6a305864d9154 2026-07-28 05:42:26.697173+00 +migrations/0219_chat_notification_timestamp_indexes.sql 24e0521bc4034246a11a75c19646d9bc 2026-07-28 05:42:26.766798+00 +migrations/0219_drop_duplicate_chat_id_idx.sql 80f9a50c6112a6900bb2b277d111d915 2026-07-28 05:42:26.836159+00 +migrations/0219_drop_duplicate_core_indexed_blocks_chain_height_idx.sql d49e5534d23a88bd87cb70aae314bddd 2026-07-28 05:42:26.905582+00 +migrations/0219_drop_superseded_playlist_trending_scores_idx.sql 8c4217b00426c9f3170f5396f9217212 2026-07-28 05:42:26.971742+00 +migrations/0219_scheduled_release_publisher_indexes.sql bd4d9591161dcbd66e74c29b6dcc9bb5 2026-07-28 05:42:27.039836+00 +migrations/0220_chat_message_chat_created_idx.sql 16c713d77a29ccd3ab1fa54df79996b7 2026-07-28 05:42:27.108358+00 +migrations/0220_saves_user_track_current_blocknumber_idx.sql 00220ba4dd9bb13a623ad928ed6f4cb0 2026-07-28 05:42:27.17729+00 +migrations/0220_track_collaborators.sql 20408d36d93a7adfda1ee6c2a9391bad 2026-07-28 05:42:27.24615+00 +migrations/0221_add_users_last_active_at.sql 5a9657e0544e51a802446b9957af2ccd 2026-07-28 05:42:27.354321+00 +migrations/0222_drop_superseded_track_trending_indexes.sql 4db5a21a23d05a7ffa086baf943f362b 2026-07-28 05:42:27.579414+00 +migrations/0223_reposts_user_created_at_idx.sql fca2f7a512c1f876a0c00d5675af9474 2026-07-28 05:42:27.653333+00 +migrations/0224_comments_user_track_created_at_idx.sql ab3a1b2132ca707a33804bb4c8719849 2026-07-28 05:42:27.726959+00 +migrations/0225_track_routes_slug_allocation_idx.sql deb0bd40333ef257bf0a31a1f6237371 2026-07-28 05:42:27.809428+00 +migrations/0225_track_trending_scores_index_cleanup.sql e9908abd1b9fd135aad0591d7eac68c8 2026-07-28 05:42:27.898087+00 +migrations/0226_comment_threads_comment_id_idx.sql c059f5ca5f6cf04e12b4b79ff93e033b 2026-07-28 05:42:27.969696+00 +migrations/0227_notification_single_recipient_user_timestamp_idx.sql f50ed8c909f4c515691011ac175ea03d 2026-07-28 05:42:28.053257+00 +migrations/0228_notification_multi_recipient_user_ids_idx.sql 43c58f2b7875cd09428fc8a9b01e1677 2026-07-28 05:42:28.131348+00 +functions/get_user_score.sql 20f79f8ffe7b3b2582c5a7e29a9db45a 2026-07-28 05:42:28.312865+00 +functions/handle_track_collaborator.sql 781dd365ffdf7619e234f5aff2544ba6 2026-07-28 05:42:29.217485+00 \. From 3d32a9c0774d69c751d353418f95a9d9d2b2bf04 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Mon, 27 Jul 2026 23:08:12 -0700 Subject: [PATCH 2/2] test: fix aggregate_user save-count fixture for delta-based triggers Regenerating the schema dump activates the delta-based social-aggregate triggers introduced in #898 (on_save/on_follow/on_repost now fire on INSERT OR UPDATE and adjust counts by a delta instead of recomputing). The shared aggregate_user fixture seeded user 1's track_save_count to 20, which the old recompute trigger overwrote but the delta trigger instead treats as a baseline (20 + 3 seeded saves = 23), breaking TestGetUsersAccount. Seed the pre-save baseline of 0 so the trigger builds the intended count of 3. Co-Authored-By: Claude Opus 4.8 --- api/testdata/aggregate_user_fixtures.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/api/testdata/aggregate_user_fixtures.go b/api/testdata/aggregate_user_fixtures.go index 44cd8008..f78d4a20 100644 --- a/api/testdata/aggregate_user_fixtures.go +++ b/api/testdata/aggregate_user_fixtures.go @@ -2,11 +2,14 @@ package testdata var AggregateUser = []map[string]any{ { - "user_id": 1, - "follower_count": 100, - "following_count": 25, - "dominant_genre": "Electronic", - "track_save_count": 20, + "user_id": 1, + "follower_count": 100, + "following_count": 25, + "dominant_genre": "Electronic", + // Seed the pre-save baseline (0): the on_save trigger is delta-based + // (since #898) and increments this as the 3 track saves in SaveFixtures + // are inserted, yielding a final track_save_count of 3. + "track_save_count": 0, }, { "user_id": 2,