Repository: Archmonger/django-dbbackup

Issue:
pg_restore fails because cannot drop IDENTITY COLUMN 
### Current Situation

When I run dbrestore command on postgres i get this error pg_restore: error: could not execute query: ERROR: column "id" of relation ... is an identity column HINT: Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.

### Proposed Actions

Probably the the restore --clean is using the drop maybe it's just a problem with pg_restore version 

### System Information

_No response_


Discussion:

Patch:
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 53c72346..a9157f45 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -37,9 +37,9 @@ Run tests in multiple configurations:
 
 - `hatch test` – unit test runner for all matrix environments
 - `hatch test --python 3.12` – isolate matrix to Python 3.12 set
-- `hatch run functional:all` – functional backup/restore (SQLite + PostgreSQL)
-- `hatch run functional:sqlite --all` – functional (SQLite only)
-- `hatch run functional:postgres --all` – functional (PostgreSQL only)
+- `hatch run functional:all -v` – functional backup/restore (SQLite + PostgreSQL)
+- `hatch run functional:sqlite --all -v` – functional (SQLite only)
+- `hatch run functional:postgres --all -v` – functional (PostgreSQL only)
 - `hatch run lint:check` – linting
 - `hatch run lint:format` – auto-format (Ruff)
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee645bf0..d942630d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -47,6 +47,7 @@ Don't forget to remove deprecated code on each major release!
 - Fix SQLite `no such table` errors.
 - Fix SQLite `UNIQUE constraint` errors.
 - Fix SQLite `index`/`trigger`/`view` `<NAME> already exists` errors.
+- Fix PostgreSQL restore errors with identity columns by automatically enabling `--if-exists` when using `--clean` in `PgDumpBinaryConnector`.
 
 ### Security
 
diff --git a/dbbackup/db/postgresql.py b/dbbackup/db/postgresql.py
index 8721ed56..e781a35e 100644
--- a/dbbackup/db/postgresql.py
+++ b/dbbackup/db/postgresql.py
@@ -166,7 +166,7 @@ def _restore_dump(self, dump: str):
             for schema in self.schemas:
                 cmd.extend(["-n", schema])
 
-        if self.if_exists:
+        if self.if_exists or self.drop:
             cmd.extend(["--if-exists"])
 
         if self.restore_suffix:
diff --git a/docs/src/databases.md b/docs/src/databases.md
index a73395b6..44decb49 100644
--- a/docs/src/databases.md
+++ b/docs/src/databases.md
@@ -113,7 +113,7 @@ All PostgreSQL connectors have the following settings:
 | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ------- |
 | SINGLE_TRANSACTION | Wrap restore in a single transaction so errors cause full rollback (`--single-transaction` for `psql` / `pg_restore`).                  | `True`  |
 | DROP               | Include / execute drop statements when restoring (`--clean` with `pg_dump` / `pg_restore`). In binary mode drops happen during restore. | `True`  |
-| IF_EXISTS          | Add `IF EXISTS` to destructive statements in clean mode.                                                                                | `False` |
+| IF_EXISTS          | Add `IF EXISTS` to destructive statements in clean mode. Automatically enabled when `DROP=True` to prevent identity column errors.      | `False` |
 
 #### PgDumpConnector
 
diff --git a/scripts/_utils.py b/scripts/_utils.py
index c89ec666..cf66ae29 100644
--- a/scripts/_utils.py
+++ b/scripts/_utils.py
@@ -30,11 +30,11 @@
 }
 
 _ASCII_SYMBOLS = {
-    "PASS": "",
-    "FAIL": "",
-    "SUMMARY": "",
-    "TEST": "",
-    "PG": "",
+    "PASS": "PASS:",
+    "FAIL": "FAIL:",
+    "SUMMARY": "SUMMARY:",
+    "TEST": "TEST:",
+    "PG": ">>",
 }
 
 


Test patch:
diff --git a/tests/test_connectors/test_postgresql.py b/tests/test_connectors/test_postgresql.py
index e2d979ce..ed3f912a 100644
--- a/tests/test_connectors/test_postgresql.py
+++ b/tests/test_connectors/test_postgresql.py
@@ -220,14 +220,40 @@ def test_create_dump_drop(self, mock_dump_cmd):
 
     def test_create_dump_if_exists(self, mock_run_command):
         dump = self.connector.create_dump()
-        # Without
+        # Test explicit if_exists=True behavior
+        self.connector.if_exists = True
+        self.connector.drop = False  # Disable automatic --if-exists from --clean
+        self.connector.restore_dump(dump)
+        cmd_args = mock_run_command.call_args[0][0]
+        self.assertNotIn(" --clean", cmd_args)
+        self.assertIn(" --if-exists", cmd_args)
+        
+        # Test that if_exists=False with drop=False means no --if-exists
         self.connector.if_exists = False
+        self.connector.drop = False
         self.connector.restore_dump(dump)
-        self.assertNotIn(" --if-exists", mock_run_command.call_args[0][0])
-        # With
-        self.connector.if_exists = True
+        cmd_args = mock_run_command.call_args[0][0]
+        self.assertNotIn(" --clean", cmd_args)
+        self.assertNotIn(" --if-exists", cmd_args)
+    
+    def test_clean_automatically_enables_if_exists(self, mock_run_command):
+        """Test that --if-exists is automatically added when using --clean to prevent identity column errors."""
+        dump = self.connector.create_dump()
+        # When drop=True (which adds --clean), --if-exists should be automatically added
+        self.connector.drop = True
+        self.connector.if_exists = False  # Explicitly set to False
         self.connector.restore_dump(dump)
-        self.assertIn(" --if-exists", mock_run_command.call_args[0][0])
+        cmd_args = mock_run_command.call_args[0][0]
+        self.assertIn(" --clean", cmd_args)
+        self.assertIn(" --if-exists", cmd_args)
+        
+        # When drop=False, --if-exists should not be added automatically
+        self.connector.drop = False
+        self.connector.if_exists = False
+        self.connector.restore_dump(dump)
+        cmd_args = mock_run_command.call_args[0][0]
+        self.assertNotIn(" --clean", cmd_args)
+        self.assertNotIn(" --if-exists", cmd_args)
 
     def test_pg_options(self, mock_run_command):
         dump = self.connector.create_dump()