The PostgreSQL error "42P11: invalid_cursor_definition" occurs when a cursor declaration contains invalid syntax, references non-existent tables or columns, or violates cursor definition rules. Cursors in PostgreSQL allow iterative processing of query results, but improper definition prevents their creation and requires correcting the cursor declaration.
The "invalid_cursor_definition" error (SQLSTATE 42P11) is a PostgreSQL-specific error that falls under Class 42 - Syntax Error or Access Rule Violation. This error occurs when attempting to declare a cursor with invalid syntax or semantic errors in the cursor definition. Cursors in PostgreSQL provide a way to retrieve query results row-by-row, which is useful for processing large result sets in PL/pgSQL functions or for implementing server-side pagination. The DECLARE statement creates a cursor that can later be opened, fetched from, and closed. The "invalid_cursor_definition" error specifically targets problems in the cursor declaration phase, before any data retrieval occurs. This error helps maintain SQL correctness by ensuring that cursor definitions are syntactically valid and reference existing database objects. Common triggers include referencing non-existent tables or columns in the cursor query, using invalid SQL syntax in the cursor declaration, attempting to declare a cursor with parameters incorrectly, or violating cursor-specific constraints like using FOR UPDATE with inappropriate query types.
PostgreSQL error messages usually include details about what specifically is invalid in the cursor definition. Look for the exact line and position of the error.
-- Example error message:
ERROR: 42P11: invalid_cursor_definition
DETAIL: Table "nonexistent_table" does not exist.
CONTEXT: PL/pgSQL function my_function() line 5 at DECLARECheck the DECLARE statement in your code and note any referenced tables, columns, or parameters.
Ensure all tables, views, and columns referenced in the cursor query actually exist in the current schema and database.
```sql
-- Check if table exists:
SELECT EXISTS (SELECT FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = 'your_table');
-- Check if columns exist:
SELECT column_name FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'your_table';
-- Check schema search path:
SHOW search_path;
-- Tables might be in a different schema than expected
-- For temporary tables, ensure they're created before cursor declaration:
CREATE TEMP TABLE temp_data (id INT, value TEXT);
-- Then declare cursor on temp_data
DECLARE my_cursor CURSOR FOR SELECT * FROM temp_data;
Ensure the cursor declaration follows correct PostgreSQL syntax. Common issues include missing keywords, incorrect parameter syntax, or invalid query constructs.
```sql
-- Basic cursor declaration syntax:
DECLARE cursor_name [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
CURSOR [ { WITH | WITHOUT } HOLD ] FOR query;
-- Parameterized cursor syntax:
DECLARE cursor_name CURSOR (param_name param_type, ...) FOR query;
-- Examples of correct declarations:
DECLARE user_cursor CURSOR FOR SELECT * FROM users;
DECLARE active_users CURSOR FOR
SELECT * FROM users WHERE active = true ORDER BY created_at;
DECLARE param_cursor CURSOR (min_id INT) FOR
SELECT * FROM users WHERE id >= min_id;
-- Common syntax errors to avoid:
-- Missing FOR keyword
-- Incorrect parameter type declarations
-- Invalid use of SCROLL or INSENSITIVE options
If using parameterized cursors, ensure parameters are declared with correct types and referenced properly in the query.
```sql
-- Incorrect: parameter type mismatch or missing type
DECLARE my_cursor CURSOR (user_id) FOR -- Missing type
SELECT * FROM users WHERE id = user_id;
-- Correct: specify parameter type
DECLARE my_cursor CURSOR (user_id INT) FOR
SELECT * FROM users WHERE id = user_id;
-- Incorrect: parameter not used in query
DECLARE my_cursor CURSOR (filter TEXT) FOR
SELECT * FROM users; -- Parameter 'filter' not used
-- Correct: use parameter in WHERE clause
DECLARE my_cursor CURSOR (filter TEXT) FOR
SELECT * FROM users WHERE username LIKE filter || '%';
Cursors with FOR UPDATE or FOR SHARE clauses have specific requirements. The underlying query must be updatable (typically a simple table query, not a join or aggregation).
```sql
-- Problematic: FOR UPDATE on join query
DECLARE update_cursor CURSOR FOR
SELECT u.*, o.total FROM users u JOIN orders o ON u.id = o.user_id
FOR UPDATE; -- Cannot lock rows from join
-- Solution 1: Use FOR UPDATE on primary table only
DECLARE update_cursor CURSOR FOR
SELECT u.* FROM users u WHERE u.active = true
FOR UPDATE;
-- Solution 2: Use multiple cursors or different approach
DECLARE user_cursor CURSOR FOR
SELECT * FROM users WHERE active = true FOR UPDATE;
-- Then process orders separately
-- Note: Some queries cannot use FOR UPDATE:
-- Aggregates (GROUP BY, window functions)
-- DISTINCT queries
-- Queries with UNION/INTERSECT/EXCEPT
-- Queries on views without INSTEAD OF triggers
If the error persists, test the cursor declaration separately from the rest of your code to isolate the issue.
```sql
-- Test in psql or separate session:
BEGIN;
-- Try simple cursor first:
DECLARE test_cursor CURSOR FOR SELECT 1 as test;
OPEN test_cursor;
FETCH test_cursor;
CLOSE test_cursor;
-- If that works, gradually add complexity:
DECLARE test2 CURSOR FOR SELECT * FROM pg_tables LIMIT 5;
-- Then add your actual query piece by piece
ROLLBACK; -- Or COMMIT if successful
-- In PL/pgSQL, you might need to check function compilation:
CREATE OR REPLACE FUNCTION test_cursor() RETURNS void AS $$
DECLARE
cur CURSOR FOR SELECT * FROM existing_table;
BEGIN
-- function body
END;
$$ LANGUAGE plpgsql;
-- If this compiles, the cursor definition is valid
The "invalid_cursor_definition" error is specific to PostgreSQL's cursor implementation. Other database systems may have different cursor semantics or error codes for similar issues.
In PL/pgSQL, cursor declarations are checked at function compilation time (CREATE FUNCTION), not at execution time. This means schema changes can break existing functions even if they're not currently being executed.
For dynamic cursor declarations using EXECUTE, the error occurs at runtime when the dynamic SQL is executed:
DECLARE
query_text TEXT := 'SELECT * FROM ' || table_name;
BEGIN
EXECUTE 'DECLARE dyn_cursor CURSOR FOR ' || query_text;
-- Error occurs here if table_name references non-existent table
END;Consider using REFCURSOR variables for more flexible cursor handling, especially when dealing with dynamic queries:
DECLARE
my_cursor REFCURSOR;
query_text TEXT;
BEGIN
query_text := 'SELECT * FROM ' || table_name;
OPEN my_cursor FOR EXECUTE query_text;
-- Error occurs at OPEN, not DECLARE
END;Performance consideration: While cursors are useful for row-by-row processing, they can be less efficient than set-based operations. Consider whether a cursor is truly necessary or if the operation can be rewritten using standard SQL operations.
ERROR: syntax error at end of input
Syntax error at end of input in PostgreSQL
Bind message supplies N parameters but prepared statement requires M
Bind message supplies N parameters but prepared statement requires M in PostgreSQL
Multidimensional arrays must have sub-arrays with matching dimensions
Multidimensional arrays must have sub-arrays with matching dimensions
ERROR: value too long for type character varying
Value too long for type character varying
insufficient columns in unique constraint for partition key
How to fix "insufficient columns in unique constraint for partition key" in PostgreSQL