From a5ffa1f6797c0e581a5dffea7562d66804387a11 Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Fri, 9 Jan 2026 00:26:24 -0600 Subject: [PATCH 1/2] fix: raise error when table declaration fails due to permissions Previously, AccessError during table declaration was silently swallowed, causing tables with cross-schema foreign keys to fail without any feedback when the user lacked REFERENCES privilege. Now: - If table already exists: suppress error (idempotent declaration) - If table doesn't exist: raise AccessError with helpful message about CREATE and REFERENCES privileges Closes #1161 Co-Authored-By: Claude Opus 4.5 --- src/datajoint/table.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/datajoint/table.py b/src/datajoint/table.py index 7543c385e..6b45ecf04 100644 --- a/src/datajoint/table.py +++ b/src/datajoint/table.py @@ -148,8 +148,15 @@ def declare(self, context=None): try: self.connection.query(sql) except AccessError: - # skip if no create privilege - return + # Only suppress if table already exists (idempotent declaration) + # Otherwise raise - user needs to know about permission issues + if self.is_declared: + return + raise AccessError( + f"Cannot declare table {self.full_table_name}. " + f"Check that you have CREATE privilege on schema `{self.database}` " + f"and REFERENCES privilege on any referenced parent tables." + ) from None # Populate lineage table for this table's attributes self._populate_lineage(primary_key, fk_attribute_map) From 4f4340aed6108d0f64f458aeb79a934157824f0b Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Fri, 9 Jan 2026 00:37:30 -0600 Subject: [PATCH 2/2] test: update test to expect AccessError at declaration time The test previously expected silent failure at declaration followed by error at insert time. Now we fail fast at declaration time (better UX). Co-Authored-By: Claude Opus 4.5 --- tests/integration/test_privileges.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/integration/test_privileges.py b/tests/integration/test_privileges.py index ff5fc0c7f..0939823a0 100644 --- a/tests/integration/test_privileges.py +++ b/tests/integration/test_privileges.py @@ -90,18 +90,19 @@ def test_insert_failure(self, connection_djview, schema_any): UnprivilegedLanguage().insert1(("Socrates", "Greek")) def test_failure_to_create_table(self, connection_djview, schema_any): + """Table declaration should raise AccessError when user lacks CREATE privilege.""" unprivileged = dj.Schema(schema_any.database, namespace, connection=connection_djview) - @unprivileged - class Try(dj.Manual): - definition = """ # should not matter really - id : int - --- - value : float - """ + # Should raise AccessError at declaration time, not silently fail + with pytest.raises(dj.errors.AccessError): - with pytest.raises(dj.DataJointError): - Try().insert1((1, 1.5)) + @unprivileged + class Try(dj.Manual): + definition = """ # should not matter really + id : int + --- + value : float + """ class TestSubset: