phalcon/cphalcon · error · SqliteAlterPrimaryKeyNotSupported

Adding a primary key after table has been created is not sup

Error message

Adding a primary key after table has been created is not supported by SQLite

What it means

The SQLite dialect's addPrimaryKey() always throws SqliteAlterPrimaryKeyNotSupported. SQLite's ALTER TABLE cannot add a PRIMARY KEY after a table exists; the key must be declared in the original CREATE TABLE column/table definition. The dialect throws immediately instead of producing SQL the engine would reject.

Source

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

            let sql .= "\"" . index->getName() . "\"";
        }

        let sql .= " ON \"" . tableName . "\" ("
            . this->getIndexColumnList(index, false) . ")";

        if index->getWhere() !== "" {
            let sql .= " WHERE " . index->getWhere();
        }

        return sql;
    }

    /**
     * Generates SQL to add the primary key to a table
     */
    public function addPrimaryKey( string tableName,  string schemaName, <IndexInterface> index) -> string
    {
        throw new SqliteAlterPrimaryKeyNotSupported();
    }

    /**
     * Generates SQL to create a table
     */
    public function createTable( string tableName,  string schemaName,  array definition) -> string
    {
        var columns, table, temporary, options, createLines, columnLine,
            column, indexes, index, indexName, indexType, references, reference,
            defaultValue, referenceSql, onDelete, onUpdate, checks, check;
        bool hasPrimary;
        string sql;

        let table = this->prepareTable(tableName, schemaName);

        let temporary = false;
        if fetch options, definition["options"] {
            fetch temporary, options["temporary"];

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Declare the primary key at creation: new Column('id', ['type' => Column::TYPE_INTEGER, 'primary' => true]) or the 'primary' key in the table definition's indexes
  2. For an existing table use the SQLite table-rebuild procedure (new table with PK, copy data, drop, rename)
  3. Skip the call on SQLite: if (!($adapter->getDialect() instanceof Sqlite)) { $adapter->addPrimaryKey(...); }

Example fix

// before
$connection->addPrimaryKey('robots', null, new Index('PRIMARY', ['id']));

// after — PK at CREATE TABLE time on SQLite
$connection->createTable('robots', null, [
    'columns' => [
        new Column('id', ['type' => Column::TYPE_INTEGER, 'primary' => true]),
    ],
]);
Defensive patterns

Strategy: type-guard

Validate before calling

if ($adapter->getDialect() instanceof \Phalcon\Db\Dialect\Sqlite) {
    throw new RuntimeException('SQLite: declare the primary key in createTable(), not addPrimaryKey()');
}
$adapter->addPrimaryKey($table, $schema, $index);

Type guard

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

Try / catch

try {
    $adapter->addPrimaryKey($table, $schema, $index);
} catch (\Phalcon\Db\Exceptions\SqliteAlterPrimaryKeyNotSupported $e) {
    rebuildSqliteTableWithPrimaryKey($adapter, $table, $index);
}

Prevention

When it happens

Trigger: Calling $sqliteAdapter->addPrimaryKey('robots', null, $index) — typically from generated migrations that create the table first and add its primary key in a later step; running those migrations against SQLite in tests or local dev.

Common situations: Migration generators that split primary-key creation into a separate ALTER step (valid on MySQL/PostgreSQL); adapting production migrations to SQLite-backed test environments.

Related errors


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