create extension if not exists pgcrypto; create or replace function public.set_updated_at() returns trigger language plpgsql as $$ begin new.updated_at = now(); return new; end; $$; create table if not exists public.search_jobs ( id uuid primary key default gen_random_uuid(), user_id uuid not null references auth.users (id) on delete cascade, name text not null, city text, address text, postal_code text, radius_km numeric not null, business_type text not null, keywords text, status text not null check (status in ('pending', 'running', 'completed', 'failed', 'stopped')), total_results integer not null default 0, cancel_requested boolean not null default false, started_at timestamptz, completed_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table if not exists public.businesses ( id uuid primary key default gen_random_uuid(), user_id uuid not null references auth.users (id) on delete cascade, external_source_id text, source text not null, name text not null, address text, city text, state_province text, postal_code text, country text, phone text, website text, rating numeric, review_count integer, category text, hours_json jsonb, latitude double precision, longitude double precision, general_info text, metadata_json jsonb, first_seen_at timestamptz, last_seen_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint businesses_user_source_external_source_key unique (user_id, source, external_source_id) ); create table if not exists public.search_job_results ( id uuid primary key default gen_random_uuid(), user_id uuid not null references auth.users (id) on delete cascade, search_job_id uuid not null references public.search_jobs (id) on delete cascade, business_id uuid not null references public.businesses (id) on delete cascade, matched_keywords text[], rank integer, captured_at timestamptz not null default now(), constraint search_job_results_job_business_key unique (search_job_id, business_id) ); create index if not exists search_jobs_user_created_at_idx on public.search_jobs (user_id, created_at desc); create index if not exists businesses_user_created_at_idx on public.businesses (user_id, created_at desc); create index if not exists search_job_results_user_job_idx on public.search_job_results (user_id, search_job_id); drop trigger if exists set_search_jobs_updated_at on public.search_jobs; create trigger set_search_jobs_updated_at before update on public.search_jobs for each row execute function public.set_updated_at(); drop trigger if exists set_businesses_updated_at on public.businesses; create trigger set_businesses_updated_at before update on public.businesses for each row execute function public.set_updated_at(); alter table public.search_jobs enable row level security; alter table public.businesses enable row level security; alter table public.search_job_results enable row level security; drop policy if exists "Users can read their search jobs" on public.search_jobs; drop policy if exists "Users can insert their search jobs" on public.search_jobs; drop policy if exists "Users can update their search jobs" on public.search_jobs; drop policy if exists "Users can delete their search jobs" on public.search_jobs; drop policy if exists "Users can read their businesses" on public.businesses; drop policy if exists "Users can insert their businesses" on public.businesses; drop policy if exists "Users can update their businesses" on public.businesses; drop policy if exists "Users can delete their businesses" on public.businesses; drop policy if exists "Users can read their search job results" on public.search_job_results; drop policy if exists "Users can insert their search job results" on public.search_job_results; drop policy if exists "Users can update their search job results" on public.search_job_results; drop policy if exists "Users can delete their search job results" on public.search_job_results; create policy "Users can read their search jobs" on public.search_jobs for select using (auth.uid() = user_id); create policy "Users can insert their search jobs" on public.search_jobs for insert with check (auth.uid() = user_id); create policy "Users can update their search jobs" on public.search_jobs for update using (auth.uid() = user_id) with check (auth.uid() = user_id); create policy "Users can delete their search jobs" on public.search_jobs for delete using (auth.uid() = user_id); create policy "Users can read their businesses" on public.businesses for select using (auth.uid() = user_id); create policy "Users can insert their businesses" on public.businesses for insert with check (auth.uid() = user_id); create policy "Users can update their businesses" on public.businesses for update using (auth.uid() = user_id) with check (auth.uid() = user_id); create policy "Users can delete their businesses" on public.businesses for delete using (auth.uid() = user_id); create policy "Users can read their search job results" on public.search_job_results for select using (auth.uid() = user_id); create policy "Users can insert their search job results" on public.search_job_results for insert with check (auth.uid() = user_id); create policy "Users can update their search job results" on public.search_job_results for update using (auth.uid() = user_id) with check (auth.uid() = user_id); create policy "Users can delete their search job results" on public.search_job_results for delete using (auth.uid() = user_id);