phalcon/cphalcon · error · ReturningNotSupported
RETURNING clauses are not supported by this dialect
Error message
RETURNING clauses are not supported by this dialect
What it means
Dialect::returning() appends a `RETURNING` clause so INSERT/UPDATE/DELETE statements give back rows. It is a PostgreSQL and SQLite 3.35+ feature; the base dialect implements it as an unconditional throw, and the MySQL dialect inherits that because MySQL has no RETURNING construct. Use supportsReturning() (false on the base/MySQL dialects) to probe.
Source
Thrown at phalcon/Db/Dialect.zep:576
let assignments[] = this->escape((string) col)
. " = excluded." . this->escape((string) col);
}
return sqlQuery
. " ON CONFLICT (" . this->getColumnList(conflictColumns) . ")"
. " DO UPDATE SET " . implode(", ", assignments);
}
/**
* Returns a SQL statement extended with a `RETURNING` clause so the
* INSERT/UPDATE/DELETE returns rows. Supported by PostgreSQL and
* SQLite 3.35+. Pass `["*"]` for `RETURNING *`, or a list of column
* names. The base implementation throws - MySQL inherits it because
* MySQL has no RETURNING construct.
*/
public function returning( string sqlQuery, array columns) -> string
{
throw new ReturningNotSupported();
}
/**
* Generate SQL to release a savepoint
*/
public function releaseSavepoint( string name) -> string
{
return "RELEASE SAVEPOINT " . name;
}
/**
* Generate SQL to rollback a savepoint
*/
public function rollbackSavepoint( string name) -> string
{
return "ROLLBACK TO SAVEPOINT " . name;
}
View on GitHub (pinned to b7419de9cd)
Solutions
- Guard with if ($connection->getDialect()->supportsReturning()) and branch per adapter.
- On MySQL use $connection->lastInsertId() after plain INSERT instead of RETURNING.
- When portability matters, keep the RETURNING path but provide a MySQL fallback that re-selects by lastInsertId or by a client-generated unique key.
Example fix
// before
$sql = $connection->getDialect()->returning(
"INSERT INTO users (email) VALUES ('a@b.c')",
['id']
); // throws ReturningNotSupported on MySQL
// after
if ($connection->getDialect()->supportsReturning()) {
$sql = $connection->getDialect()->returning(
"INSERT INTO users (email) VALUES ('a@b.c')",
['id']
);
} else {
$connection->execute("INSERT INTO users (email) VALUES ('a@b.c')");
$id = $connection->lastInsertId();
} Defensive patterns
Strategy: validation
Validate before calling
if ($connection->getDialect()->supportsReturning()) {
$sql = $connection->getDialect()->returning($insert, ['id']);
} else {
$connection->execute($insert);
$id = $connection->lastInsertId();
} Try / catch
use Phalcon\Db\Exceptions\ReturningNotSupported;
try {
$sql = $connection->getDialect()->returning($insert, ['id']);
} catch (ReturningNotSupported $e) {
// MySQL path: plain insert + lastInsertId
$connection->execute($insert);
$id = $connection->lastInsertId();
} Prevention
- Probe the dialect with supportsReturning() before building RETURNING SQL.
- On MySQL, use lastInsertId() for auto-increment keys instead of RETURNING.
- Test adapter-specific write paths against every database your app supports.
When it happens
Trigger: Calling $dialect->returning($sql, ['id']) or an adapter helper built on it while connected through Pdo\Mysql; portable repository code that relies on RETURNING for last-insert-id and runs against a MySQL deployment.
Common situations: Code written and tested on PostgreSQL/SQLite then deployed on MySQL; multi-driver packages assuming RETURNING everywhere; migrating from Postgres to MySQL without auditing RETURNING usage.
Related errors
- Materialized views are not supported by this dialect
- ON CONFLICT requires at least one conflict-target column
- Operator '{}' is not supported by this SQL dialect
- RETURNING requires at least one column or '*'
- RETURNING requires at least one column or '*'
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/d090c48f7ca6dcdd.
Report an issue: GitHub.