From 94bfc60fc49b029b492c627cfe3e00216e14a14a Mon Sep 17 00:00:00 2001 From: Dominik Dachs Date: Tue, 14 Jul 2026 08:58:46 +0000 Subject: [PATCH] Fix db_init for non-owner DB users ALTER TABLE, CREATE INDEX and CREATE OR REPLACE FUNCTION all require table/function ownership in PostgreSQL. Replace IF NOT EXISTS DDL with explicit existence checks so db_init works for unprivileged application users where the schema was created by a different role (e.g. postgres). Co-Authored-By: Claude Sonnet 4.6 --- main.py | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/main.py b/main.py index 0486df2..d28ba0a 100644 --- a/main.py +++ b/main.py @@ -411,28 +411,37 @@ def db_init(cur): updated_ts timestamptz NOT NULL DEFAULT now() ); """) - cur.execute(""" - ALTER TABLE remote_cam.import_job - ADD COLUMN IF NOT EXISTS needs_ocr_backfill boolean NOT NULL DEFAULT false; - """) - cur.execute(""" - ALTER TABLE remote_cam.import_job - ADD COLUMN IF NOT EXISTS needs_exif_backfill boolean NOT NULL DEFAULT false; - """) - cur.execute("CREATE INDEX IF NOT EXISTS import_job_needs_ocr_idx ON remote_cam.import_job(needs_ocr_backfill);") - cur.execute("CREATE INDEX IF NOT EXISTS import_job_needs_exif_idx ON remote_cam.import_job(needs_exif_backfill);") - cur.execute("CREATE INDEX IF NOT EXISTS import_job_status_idx ON remote_cam.import_job(status);") - cur.execute("CREATE INDEX IF NOT EXISTS import_job_updated_idx ON remote_cam.import_job(updated_ts);") + for col, typedef in [ + ("needs_ocr_backfill", "boolean NOT NULL DEFAULT false"), + ("needs_exif_backfill", "boolean NOT NULL DEFAULT false"), + ]: + cur.execute(""" + SELECT 1 FROM information_schema.columns + WHERE table_schema='remote_cam' AND table_name='import_job' AND column_name=%s + """, (col,)) + if not cur.fetchone(): + cur.execute(f"ALTER TABLE remote_cam.import_job ADD COLUMN {col} {typedef};") + for idx, col in [ + ("import_job_needs_ocr_idx", "needs_ocr_backfill"), + ("import_job_needs_exif_idx", "needs_exif_backfill"), + ("import_job_status_idx", "status"), + ("import_job_updated_idx", "updated_ts"), + ]: + cur.execute("SELECT 1 FROM pg_indexes WHERE schemaname='remote_cam' AND indexname=%s", (idx,)) + if not cur.fetchone(): + cur.execute(f"CREATE INDEX {idx} ON remote_cam.import_job({col});") - cur.execute(""" - CREATE OR REPLACE FUNCTION remote_cam.set_updated_ts() - RETURNS trigger AS $$ - BEGIN - NEW.updated_ts = now(); - RETURN NEW; - END; - $$ LANGUAGE plpgsql; - """) + cur.execute("SELECT 1 FROM pg_proc JOIN pg_namespace ON pg_proc.pronamespace=pg_namespace.oid WHERE nspname='remote_cam' AND proname='set_updated_ts'") + if not cur.fetchone(): + cur.execute(""" + CREATE FUNCTION remote_cam.set_updated_ts() + RETURNS trigger AS $$ + BEGIN + NEW.updated_ts = now(); + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + """) cur.execute(""" DO $$ BEGIN