3535
3636-- CreateIndex
3737-- Optimized: Only include owningUserId in index columns since isDeleted and hasApprovedVersion are in WHERE clause
38- CREATE INDEX "idx_store_listing_approved " ON " StoreListing" (" owningUserId" ) WHERE " isDeleted" = false AND " hasApprovedVersion" = true;
38+ CREATE INDEX IF NOT EXISTS " idx_store_listing_approved" ON " StoreListing" (" owningUserId" ) WHERE " isDeleted" = false AND " hasApprovedVersion" = true;
3939
4040-- CreateIndex
4141-- Optimized: Only include storeListingId since submissionStatus is in WHERE clause
42- CREATE INDEX "idx_store_listing_version_status " ON " StoreListingVersion" (" storeListingId" ) WHERE " submissionStatus" = ' APPROVED' ;
42+ CREATE INDEX IF NOT EXISTS " idx_store_listing_version_status" ON " StoreListingVersion" (" storeListingId" ) WHERE " submissionStatus" = ' APPROVED' ;
4343
4444-- CreateIndex
45- CREATE INDEX "idx_slv_categories_gin " ON " StoreListingVersion" USING GIN (" categories" ) WHERE " submissionStatus" = ' APPROVED' ;
45+ CREATE INDEX IF NOT EXISTS " idx_slv_categories_gin" ON " StoreListingVersion" USING GIN (" categories" ) WHERE " submissionStatus" = ' APPROVED' ;
4646
4747-- CreateIndex
48- CREATE INDEX "idx_slv_agent " ON " StoreListingVersion" (" agentGraphId" , " agentGraphVersion" ) WHERE " submissionStatus" = ' APPROVED' ;
48+ CREATE INDEX IF NOT EXISTS " idx_slv_agent" ON " StoreListingVersion" (" agentGraphId" , " agentGraphVersion" ) WHERE " submissionStatus" = ' APPROVED' ;
4949
5050-- CreateIndex
51- CREATE INDEX "idx_store_listing_review_version " ON " StoreListingReview" (" storeListingVersionId" );
51+ CREATE INDEX IF NOT EXISTS " idx_store_listing_review_version" ON " StoreListingReview" (" storeListingVersionId" );
5252
5353-- CreateIndex
54- CREATE INDEX "idx_agent_graph_execution_agent " ON " AgentGraphExecution" (" agentGraphId" );
54+ CREATE INDEX IF NOT EXISTS " idx_agent_graph_execution_agent" ON " AgentGraphExecution" (" agentGraphId" );
5555
5656-- CreateIndex
57- CREATE INDEX "idx_profile_user " ON " Profile" (" userId" );
57+ CREATE INDEX IF NOT EXISTS " idx_profile_user" ON " Profile" (" userId" );
5858
5959-- Additional performance indexes
60- CREATE INDEX "idx_store_listing_version_approved_listing " ON " StoreListingVersion" (" storeListingId" , " version" ) WHERE " submissionStatus" = ' APPROVED' ;
60+ CREATE INDEX IF NOT EXISTS " idx_store_listing_version_approved_listing" ON " StoreListingVersion" (" storeListingId" , " version" ) WHERE " submissionStatus" = ' APPROVED' ;
6161
6262-- Create materialized view for agent run counts
63- CREATE MATERIALIZED VIEW " mv_agent_run_counts" AS
63+ CREATE MATERIALIZED VIEW IF NOT EXISTS " mv_agent_run_counts" AS
6464SELECT
6565 " agentGraphId" ,
6666 COUNT (* ) AS run_count
6767FROM " AgentGraphExecution"
6868GROUP BY " agentGraphId" ;
6969
7070-- CreateIndex
71- CREATE UNIQUE INDEX ON " mv_agent_run_counts" (" agentGraphId" );
71+ CREATE UNIQUE INDEX IF NOT EXISTS " idx_mv_agent_run_counts " ON " mv_agent_run_counts" (" agentGraphId" );
7272
7373-- Create materialized view for review statistics
74- CREATE MATERIALIZED VIEW " mv_review_stats" AS
74+ CREATE MATERIALIZED VIEW IF NOT EXISTS " mv_review_stats" AS
7575SELECT
7676 sl .id AS " storeListingId" ,
7777 COUNT (sr .id ) AS review_count,
@@ -84,7 +84,7 @@ WHERE sl."isDeleted" = false
8484GROUP BY sl .id ;
8585
8686-- CreateIndex
87- CREATE UNIQUE INDEX ON " mv_review_stats" (" storeListingId" );
87+ CREATE UNIQUE INDEX IF NOT EXISTS " idx_mv_review_stats " ON " mv_review_stats" (" storeListingId" );
8888
8989-- DropForeignKey (if any exist on the views)
9090-- None needed as views don't have foreign keys
@@ -96,7 +96,7 @@ DROP VIEW IF EXISTS "Creator";
9696DROP VIEW IF EXISTS " StoreAgent" ;
9797
9898-- CreateView
99- CREATE VIEW "StoreAgent " AS
99+ CREATE OR REPLACE VIEW "StoreAgent " AS
100100WITH agent_versions AS (
101101 SELECT
102102 " storeListingId" ,
@@ -141,7 +141,7 @@ WHERE sl."isDeleted" = false
141141 AND sl." hasApprovedVersion" = true;
142142
143143-- CreateView
144- CREATE VIEW "Creator " AS
144+ CREATE OR REPLACE VIEW "Creator " AS
145145WITH creator_listings AS (
146146 SELECT
147147 sl." owningUserId" ,
@@ -186,22 +186,27 @@ SELECT
186186FROM " Profile" p
187187LEFT JOIN creator_stats cs ON cs." owningUserId" = p." userId" ;
188188
189- -- Create refresh function with better concurrency handling
189+ -- Create refresh function that works with the current schema
190190CREATE OR REPLACE FUNCTION refresh_store_materialized_views ()
191191RETURNS void
192192LANGUAGE plpgsql
193193AS $$
194+ DECLARE
195+ current_schema_name text ;
194196BEGIN
197+ -- Get the current schema
198+ current_schema_name := current_schema();
199+
195200 -- Use CONCURRENTLY for better performance during refresh
196- REFRESH MATERIALIZED VIEW CONCURRENTLY " mv_agent_run_counts" ;
197- REFRESH MATERIALIZED VIEW CONCURRENTLY " mv_review_stats" ;
198- RAISE NOTICE ' Materialized views refreshed at %' , NOW();
201+ EXECUTE format( ' REFRESH MATERIALIZED VIEW CONCURRENTLY %I. "mv_agent_run_counts"' , current_schema_name) ;
202+ EXECUTE format( ' REFRESH MATERIALIZED VIEW CONCURRENTLY %I. "mv_review_stats"' , current_schema_name) ;
203+ RAISE NOTICE ' Materialized views refreshed in schema % at %' , current_schema_name , NOW();
199204EXCEPTION
200205 WHEN OTHERS THEN
201206 -- Fallback to non-concurrent refresh if concurrent fails
202- REFRESH MATERIALIZED VIEW " mv_agent_run_counts" ;
203- REFRESH MATERIALIZED VIEW " mv_review_stats" ;
204- RAISE NOTICE ' Materialized views refreshed (non-concurrent) at % due to: %' , NOW(), SQLERRM;
207+ EXECUTE format( ' REFRESH MATERIALIZED VIEW %I. "mv_agent_run_counts"' , current_schema_name) ;
208+ EXECUTE format( ' REFRESH MATERIALIZED VIEW %I. "mv_review_stats"' , current_schema_name) ;
209+ RAISE NOTICE ' Materialized views refreshed (non-concurrent) in schema % at % due to: %' , current_schema_name , NOW(), SQLERRM;
205210END;
206211$$;
207212
@@ -212,29 +217,38 @@ SELECT refresh_store_materialized_views();
212217DO $$
213218DECLARE
214219 has_pg_cron BOOLEAN ;
220+ current_schema_name text ;
221+ job_name text ;
215222BEGIN
216223 -- Get the flag we set earlier
217224 has_pg_cron := current_setting(' migration.has_pg_cron' , true)::boolean ;
218225
226+ -- Get current schema name
227+ current_schema_name := current_schema();
228+
229+ -- Create a unique job name for this schema
230+ job_name := format(' refresh-store-views-%s' , current_schema_name);
231+
219232 IF has_pg_cron THEN
220- -- Check if the job already exists to avoid duplicates
221- IF NOT EXISTS (
222- SELECT 1 FROM cron .job
223- WHERE jobname = ' refresh-store-views'
224- ) THEN
225- PERFORM cron .schedule (
226- ' refresh-store-views' ,
227- ' */15 * * * *' ,
228- ' SELECT refresh_store_materialized_views();'
229- );
230- RAISE NOTICE ' Scheduled automatic refresh of materialized views every 15 minutes' ;
231- ELSE
232- RAISE NOTICE ' Materialized view refresh job already exists' ;
233- END IF;
233+ -- Try to unschedule existing job (ignore errors if it doesn't exist)
234+ BEGIN
235+ PERFORM cron .unschedule (job_name);
236+ EXCEPTION WHEN OTHERS THEN
237+ -- Job doesn't exist, that's fine
238+ NULL ;
239+ END;
240+
241+ -- Schedule the refresh job with schema-specific command
242+ PERFORM cron .schedule (
243+ job_name,
244+ ' */15 * * * *' ,
245+ format(' SELECT %I.refresh_store_materialized_views();' , current_schema_name)
246+ );
247+ RAISE NOTICE ' Scheduled automatic refresh of materialized views every 15 minutes for schema %' , current_schema_name;
234248 ELSE
235249 RAISE WARNING ' ⚠️ Automatic refresh NOT configured - pg_cron is not available' ;
236250 RAISE WARNING ' ⚠️ You must manually refresh views with: SELECT refresh_store_materialized_views();' ;
237251 RAISE WARNING ' ⚠️ Or install pg_cron for automatic refresh in production' ;
238252 END IF;
239253END;
240- $$;
254+ $$;
0 commit comments