create table if not exists scan_jobs ( id text primary key, name text not null, status text not null check (status in ('queued','running','done','failed','canceled')), cidrs jsonb not null, exclude_ips jsonb not null default '[]'::jsonb, options jsonb not null, progress int not null default 0, stats jsonb not null default '{}'::jsonb, snmp_credentials_id text null, created_at timestamptz not null default now(), started_at timestamptz null, finished_at timestamptz null ); create index if not exists idx_scan_jobs_created_at on scan_jobs (created_at desc); alter table scan_jobs add column if not exists progress int not null default 0; alter table scan_jobs add column if not exists stats jsonb not null default '{}'::jsonb; create table if not exists scan_hosts ( id bigserial primary key, scan_id text not null references scan_jobs(id) on delete cascade, ip inet not null, is_up boolean not null, checked_at timestamptz not null ); create index if not exists idx_scan_hosts_scan_id_checked_at on scan_hosts (scan_id, checked_at desc); create table if not exists scan_ports ( id bigserial primary key, scan_id text not null references scan_jobs(id) on delete cascade, ip inet not null, port int not null, is_open boolean not null, checked_at timestamptz not null ); create index if not exists idx_scan_ports_scan_id_checked_at on scan_ports (scan_id, checked_at desc); create table if not exists scan_snmp ( id bigserial primary key, scan_id text not null references scan_jobs(id) on delete cascade, ip inet not null, success boolean not null, sys_name text null, sys_descr text null, sys_object_id text null, error_text text null, checked_at timestamptz not null ); create index if not exists idx_scan_snmp_scan_id_checked_at on scan_snmp (scan_id, checked_at desc);