The PostgreSQL error "25007: schema_and_data_statement_mixing_not_supported" occurs when attempting to mix schema modification statements (DDL) with data manipulation statements (DML) in contexts where they cannot be combined. This typically happens in prepared statements, certain transaction blocks, or when using specific PostgreSQL extensions that have restrictions on statement mixing.
The "schema_and_data_statement_mixing_not_supported" error (SQLSTATE 25007) is a PostgreSQL error that prevents mixing Data Definition Language (DDL) statements with Data Manipulation Language (DML) statements in certain execution contexts. DDL statements include operations that modify the database schema, such as: - CREATE TABLE, ALTER TABLE, DROP TABLE - CREATE INDEX, DROP INDEX - CREATE VIEW, ALTER VIEW - GRANT, REVOKE permissions DML statements include operations that manipulate data within existing tables: - SELECT, INSERT, UPDATE, DELETE - COPY, TRUNCATE (though TRUNCATE has DDL characteristics) PostgreSQL restricts mixing these statement types in: 1. Prepared statements that will be executed multiple times 2. Certain PL/pgSQL function contexts 3. Cursors and some query execution plans 4. Specific replication or logical decoding scenarios This restriction exists because schema changes can invalidate execution plans and prepared statement parameter types, leading to runtime errors or data corruption.
The most straightforward fix is to separate DDL (schema) and DML (data) operations into separate statements or execution contexts:
-- This will cause 25007 error in prepared statements:
PREPARE myplan AS
CREATE TABLE temp_users (id SERIAL, name TEXT);
INSERT INTO temp_users (name) VALUES ('test');
-- Instead, use separate statements:
PREPARE create_table AS CREATE TABLE temp_users (id SERIAL, name TEXT);
PREPARE insert_data AS INSERT INTO temp_users (name) VALUES ($1);
-- Execute separately:
EXECUTE create_table;
EXECUTE insert_data('test');Always separate CREATE/ALTER/DROP statements from SELECT/INSERT/UPDATE/DELETE statements.
When you need both schema and data operations in a transaction, ensure they are in separate statements within the transaction block:
BEGIN;
-- Schema operation (DDL)
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
event_time TIMESTAMP,
message TEXT
);
-- Data operation (DML)
INSERT INTO audit_log (event_time, message)
VALUES (NOW(), 'Table created');
COMMIT;Note: Even in transactions, some contexts (like prepared statements) still restrict mixing. The key is having them as separate SQL statements, not combined in a single command string.
PL/pgSQL's EXECUTE command can have restrictions on statement mixing. Separate schema and data operations:
CREATE OR REPLACE FUNCTION setup_audit() RETURNS void AS $$
BEGIN
-- Schema operation
EXECUTE 'CREATE TABLE IF NOT EXISTS audit_trail (id SERIAL, data JSONB)';
-- Data operation (separate EXECUTE)
EXECUTE 'INSERT INTO audit_trail (data) VALUES ('"setup complete"'::jsonb)';
END;
$$ LANGUAGE plpgsql;Do not combine CREATE and INSERT in a single EXECUTE string, as this can trigger the 25007 error.
Prepared statements are optimized for repeated execution with different parameters. Schema changes break this model. Check your application code:
// Problematic - mixing DDL and DML in prepared statement
const badStatement = await pool.query(
`CREATE TEMP TABLE temp_data (id INT); INSERT INTO temp_data VALUES ($1)`,
[123]
);
// Correct - separate statements
await pool.query('CREATE TEMP TABLE temp_data (id INT)');
await pool.query('INSERT INTO temp_data VALUES ($1)', [123]);Most database drivers and ORMs will handle this automatically if you use separate method calls for different statement types.
The COPY command has specific restrictions when used with schema operations. COPY is considered a DML operation for this purpose:
-- This may cause 25007 in some contexts:
CREATE TABLE imported_data (col1 TEXT, col2 INT);
COPY imported_data FROM '/path/to/data.csv' CSV HEADER;
-- Ensure they are separate execution contexts or statements
-- First create table, then separately execute COPYIf using COPY within a function or prepared context, ensure no schema operations are mixed in the same execution unit.
Migration tools and batch scripts often combine operations. Review your migration files:
-- Problematic migration:
-- 001_create_and_populate.sql
CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT);
INSERT INTO settings VALUES ('version', '1.0');
-- Better approach:
-- 001_create_table.sql
CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT);
-- 002_initial_data.sql
INSERT INTO settings VALUES ('version', '1.0');Separate schema creation and data population into different migration files or distinct parts of your deployment process.
The 25007 error code is part of PostgreSQL's "Class 25 — Invalid Transaction State" error category. This specific error "schema_and_data_statement_mixing_not_supported" protects against several potential issues:
Execution Plan Invalidation: When a prepared statement includes DDL, subsequent executions could have different table structures, column types, or indexes, making cached execution plans invalid.
Parameter Type Resolution: Prepared statements resolve parameter types based on the statement context. DDL operations can change these types between executions.
Transaction Consistency: Some schema changes require exclusive locks or have different transactional semantics than DML operations.
Historical Context: This restriction has become more prominent with increased use of prepared statements for security (preventing SQL injection) and performance. Earlier PostgreSQL versions were more permissive but could produce subtle bugs.
Extension-Specific Behavior: Certain PostgreSQL extensions (like PostGIS, TimescaleDB) may have their own restrictions on statement mixing. Always check extension documentation.
Performance Considerations: While separating statements adds round-trips, it ensures correct execution. For bulk operations, consider using COPY for data loading after separate table creation.
Debugging Tips:
1. Use EXPLAIN to see how PostgreSQL parses and plans your statements
2. Check pg_prepared_statements view to see existing prepared statements
3. Review PostgreSQL logs for the full error context and stack traces
4. Test statements interactively with psql before embedding in application code
Related Errors:
- 25006: read_only_sql_transaction - When attempting writes in read-only mode
- 25001: active_sql_transaction - Transaction already in progress issues
- 26000: invalid_sql_statement_name - Problems with prepared statement names
- 0A000: feature_not_supported - General "not supported" error for various 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