phalcon/cphalcon · error · SqliteAlterCheckNotSupported

Adding a CHECK constraint to an existing table is not suppor

Error message

Adding a CHECK constraint to an existing table is not supported by SQLite

What it means

The SQLite dialect's addCheck() always throws SqliteAlterCheckNotSupported. SQLite has no ALTER TABLE ... ADD CONSTRAINT CHECK statement: CHECK constraints must be part of the original CREATE TABLE statement. Phalcon surfaces this engine limitation as an exception instead of emitting SQL SQLite would reject.

Source

Thrown at phalcon/Db/Dialect/Sqlite.zep:93

            let sql .= " NOT NULL";
        } else {
            let sql .= " NULL";
        }

        if !column->isGenerated() && column->isAutoincrement() {
            let sql .= " PRIMARY KEY AUTOINCREMENT";
        }

        return sql;
    }

    /**
     * SQLite cannot ALTER an existing table to add a CHECK constraint;
     * the constraint must be declared at CREATE TABLE time.
     */
    public function addCheck( string tableName,  string schemaName, <CheckInterface> check) -> string
    {
        throw new SqliteAlterCheckNotSupported();
    }

    /**
     * Generates SQL to add an index to a table
     */
    public function addForeignKey( string tableName,  string schemaName, <ReferenceInterface> reference) -> string
    {
        throw new SqliteAlterForeignKeyNotSupported();
    }

    /**
     * Generates SQL to add an index to a table
     */
    public function addIndex( string tableName,  string schemaName, <IndexInterface> index) -> string
    {
        var indexType;
        string sql;

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Declare the CHECK in createTable() using a Phalcon\Db\Check constraint in the definition array, so it lands in the CREATE TABLE statement
  2. If the constraint must be added later, use SQLite's 12-step procedure: create a new table with the constraint, copy data, drop the old table, rename
  3. Skip addCheck() on SQLite via dialect check: if (!($adapter->getDialect() instanceof Sqlite)) { $adapter->addCheck(...); }

Example fix

// before
$connection->addCheck('robots', null, new Check('chk_price', ['price > 0']));

// after — declare at CREATE TABLE time on SQLite
$connection->createTable('robots', null, [
    'columns' => [new Column('price', ['type' => Column::TYPE_DECIMAL, 'size' => 10, 'scale' => 2])],
    'checks'  => [new Check('chk_price', ['price > 0'])],
]);
Defensive patterns

Strategy: type-guard

Validate before calling

if ($adapter->getDialect() instanceof \Phalcon\Db\Dialect\Sqlite) {
    // fold the CHECK into CREATE TABLE instead of addCheck()
    $definition['checks'] = $definition['checks'] ?? [];
    $definition['checks'][] = $check;
} else {
    $adapter->addCheck($table, $schema, $check);
}

Type guard

function supportsAddCheck(\Phalcon\Db\Adapter\AdapterInterface $adapter): bool
{
    return !($adapter->getDialect() instanceof \Phalcon\Db\Dialect\Sqlite);
}

Try / catch

try {
    $adapter->addCheck($table, $schema, $check);
} catch (\Phalcon\Db\Exceptions\SqliteAlterCheckNotSupported $e) {
    // rebuild the table with the CHECK declared inline
    rebuildSqliteTableWithChecks($adapter, $table, [$check]);
}

Prevention

When it happens

Trigger: Calling $sqliteAdapter->addCheck('robots', null, $check) or any migration/DDL pipeline that invokes addCheck() on an SQLite connection; running cross-database migration code written for MySQL/PostgreSQL against SQLite tests.

Common situations: Test suites running migrations on SQLite while production runs MySQL/Postgres; shared migration files that add CHECK constraints after table creation; CI pipelines switching the adapter to SQLite for speed.

Related errors


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