PostgreSQL raises 2F004 reading_sql_data_not_permitted when a function or procedure marked NO SQL or with lower access tries to run a query that reads table data.
reading_sql_data_not_permitted (SQLSTATE 2F004) means the routine was declared without permission to read tables, yet it attempted a SELECT or similar. Alter the function with SQL DATA ACCESS READS SQL DATA (or MODIFIES SQL DATA) or remove the NO SQL clause to resolve the error.
reading_sql_data_not_permitted
PostgreSQL returns SQLSTATE 2F004 when a function, procedure, or trigger routine attempts to access table data but its definition forbids that level of access.
The SQL Standard defines data-access modifiers such as NO SQL, CONTAINS SQL, READS SQL DATA, and MODIFIES SQL DATA. PostgreSQL enforces those flags from version 14 onward for SQL-language routines.
A mismatch triggers reading_sql_data_not_permitted.
The routine is declared NO SQL or CONTAINS SQL yet executes a SELECT, cursor, or other read operation. PostgreSQL blocks the call and raises 2F004.
Using READS SQL DATA but then calling INSERT, UPDATE, or DELETE also fails because writes are considered more powerful than reads.
Grant the routine the correct data-access level.
Use ALTER FUNCTION / PROCEDURE to switch to READS SQL DATA for reading queries or MODIFIES SQL DATA when writes are required.
Alternatively, rewrite the routine so it no longer issues SQL that violates its declared restrictions.
Language SQL functions migrated from older PostgreSQL versions often default to NO SQL. Updating their definitions usually restores functionality.
Trigger functions created by ORMs sometimes inherit restrictive flags.
Adjusting the generated DDL or adding a post-migration script resolves the problem.
Always align the data-access flag with the most powerful operation inside the routine at creation time.
Include routine linting in CI pipelines.
Galaxy’s AI copilot can scan CREATE FUNCTION statements and suggest the correct access modifier before deployment.
2F003 - modifies_sql_data_not_permitted: raised when a routine marked READS SQL DATA performs a write.
38001 - invalid_sql_statement_name: often appears when dynamic SQL resolves to a forbidden statement inside a restricted routine.
.
Older code or generated DDL frequently adds NO SQL automatically. Any SELECT inside those bodies conflicts with the declaration and triggers 2F004.
Write operations exceed the permission implied by READS SQL DATA, causing PostgreSQL to reject the call.
Some tools set CONTAINS SQL for every function.
If the body later evolves to include table queries, the mismatch surfaces at runtime.
Databases upgraded to PostgreSQL 14+ see this error on first call because earlier versions ignored the flag.
.
No. Use ALTER FUNCTION or ALTER PROCEDURE to modify the flag in place without losing dependent objects.
Performance is unaffected. The flag controls permissible operations, not execution speed.
Versions 14 and later enforce the SQL Standard flags. Older versions silently ignored mismatches.
Yes. Galaxy’s AI copilot inspects CREATE FUNCTION statements and suggests the correct flag before you run them, reducing runtime failures.