phalcon/cphalcon · error · ReturningRequiresColumn

RETURNING requires at least one column or '*'

Error message

RETURNING requires at least one column or '*'

What it means

The PostgreSQL dialect's returning() helper appends a RETURNING clause to an INSERT/UPDATE/DELETE and throws ReturningRequiresColumn when the columns array is empty. RETURNING with no column list is not valid PostgreSQL (there is no bare 'RETURNING' without a projection), so the helper demands at least one column name or the literal '*' as the single element.

Source

Thrown at phalcon/Db/Dialect/Postgresql.zep:910

            if column->hasDefault() {
                let defaultValue = this->castDefault(column);
                let sql .= sqlAlterTable . " ALTER COLUMN \"" . column->getName() . "\" SET DEFAULT " . defaultValue;
            }
        }

        return sql;
    }

    /**
     * Appends a `RETURNING` clause to the supplied INSERT/UPDATE/DELETE
     * statement. Pass `["*"]` for `RETURNING *`, or a list of column names.
     */
    public function returning( string sqlQuery,  array columns) -> string
    {
        var first;

        if unlikely empty columns {
            throw new ReturningRequiresColumn();
        }

        if count(columns) == 1 {
            let first = (string) columns[0];

            if first == "*" {
                return sqlQuery . " RETURNING *";
            }
        }

        return sqlQuery . " RETURNING " . this->getColumnList(columns);
    }

    /**
     * PostgreSQL supports materialized views (`CREATE MATERIALIZED VIEW`).
     */
    public function supportsMaterializedViews() -> bool
    {

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Pass a non-empty list: returning($sql, ['*']) for all columns, or returning($sql, ['id', 'created_at'])
  2. Skip the RETURNING clause entirely when you do not need returned columns instead of calling with an empty array
  3. Default the list: $columns = $columns ?: ['*'] before the call

Example fix

// before
$sql = $dialect->returning($insertSql, []);

// after
$sql = $dialect->returning($insertSql, ['*']);
Defensive patterns

Strategy: validation

Validate before calling

if (empty($columns)) {
    throw new InvalidArgumentException('RETURNING requires at least one column or "*"');
}
// or default: $columns = $columns ?: ['*'];

Type guard

function isNonEmptyColumnList(array $columns): bool
{
    return $columns !== [] && array_reduce($columns, fn($ok, $c) => $ok && is_string($c) && $c !== '', true);
}

Try / catch

try {
    $sql = $dialect->returning($sql, $columns);
} catch (\Phalcon\Db\Exceptions\ReturningRequiresColumn $e) {
    $sql = $dialect->returning($sql, ['*']); // or skip RETURNING
}

Prevention

When it happens

Trigger: Calling $dialect->returning($sql, []) or returning($sql, array_filter($cols)) where the filter removed everything; building the column list from a variable that is null/empty, e.g. when no auto-generated columns were requested.

Common situations: Generic DAO layers that add RETURNING only when generated columns are configured and pass the empty list anyway; refactors that changed the default from ['*'] to []; request-driven column lists that arrive empty.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/ebd36590dd3036ad. Report an issue: GitHub.