Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions tests/WP_SQLite_Driver_Tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -4125,11 +4125,62 @@ public function testTimestampColumnNamedTimestamp() {
);
}

public function testCompoundPrimaryKeyAndAutoincrementNotSupported(): void {
$this->expectException( WP_SQLite_Driver_Exception::class );
$this->expectExceptionMessage( 'Cannot combine AUTOINCREMENT and multiple primary keys in SQLite' );
public function testCompoundPrimaryKeyWithAutoincrement(): void {
$this->assertQuery(
'CREATE TABLE t1 (id1 INT AUTO_INCREMENT, id2 INT, PRIMARY KEY(id1, id2))'
'CREATE TABLE t1 (id INT AUTO_INCREMENT, name VARCHAR(32), PRIMARY KEY(id, name))'
);

// Ensure auto-increment is working.
$this->assertQuery( "INSERT INTO t1 (name) VALUES ('A'), ('B'), ('C')" );
$results = $this->assertQuery( 'SELECT * FROM t1' );
$this->assertEquals(
array(
(object) array(
'id' => 1,
'name' => 'A',
),
(object) array(
'id' => 2,
'name' => 'B',
),
(object) array(
'id' => 3,
'name' => 'C',
),
),
$results
);

// Verify the table schema.
$results = $this->assertQuery( 'SHOW CREATE TABLE t1' );
$this->assertEquals(
implode(
"\n",
array(
'CREATE TABLE `t1` (',
' `id` int NOT NULL AUTO_INCREMENT,',
' `name` varchar(32) NOT NULL,',
' PRIMARY KEY (`id`, `name`)',
') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci',
)
),
$results[0]->{'Create Table'}
);

// Ensure an SQLite index was created for the compound key columns.
$result = $this->engine
->execute_sqlite_query( "SELECT * FROM pragma_index_list('t1')" )
->fetchAll( PDO::FETCH_ASSOC );
$this->assertCount( 1, $result );
$this->assertEquals(
array(
'seq' => '0',
'name' => '_wp_sqlite_t1__primary',
'unique' => '1',
'origin' => 'c',
'partial' => '0',
),
$result[0]
);
}

Expand Down
73 changes: 40 additions & 33 deletions wp-includes/sqlite-ast/class-wp-pdo-mysql-on-sqlite.php
Original file line number Diff line number Diff line change
Expand Up @@ -5605,56 +5605,63 @@ private function get_sqlite_create_table_statement(
ksort( $constraint );
$info = $constraint[1];

$column_list = array_map(
function ( $column ) {
$fragment = $this->quote_sqlite_identifier( $column['COLUMN_NAME'] );
if ( 'D' === $column['COLLATION'] ) {
$fragment .= ' DESC';
}
return $fragment;
},
$constraint
);

if ( 'PRIMARY' === $info['INDEX_NAME'] ) {
if ( $has_autoincrement ) {
/*
* In MySQL, a compound PRIMARY KEY can have an AUTO_INCREMENT
* column, when it is the first column in the key.
*
* SQLite doesn't support this, but we can emulate it as follows:
* 1. Keep only the first column as a PRIMARY KEY.
* Since this is the column that also has AUTO_INCREMENT,
* it reasonable to assume that its values are unique.
* 2. Create a UNIQUE key for all the PRIMARY KEY columns.
* This is to preserve the index of the compound key.
*/
if ( count( $constraint ) > 1 ) {
throw $this->new_driver_exception(
'Cannot combine AUTOINCREMENT and multiple primary keys in SQLite'
$sqlite_index_name = $this->get_sqlite_index_name( $table_name, 'primary' );
$create_index_queries[] = sprintf(
'CREATE UNIQUE INDEX %s ON %s (%s)',
self::RESERVED_PREFIX . $sqlite_index_name,
$this->quote_sqlite_identifier( $table_name ),
implode( ', ', $column_list )
);
}

/*
* The PRIMARY KEY was already generated with AUTOINCREMENT,
* as required by SQLite column constraint syntax.
*
* @see https://www.sqlite.org/syntax/column-constraint.html
*/
continue;
}
$query = ' PRIMARY KEY (';
$query .= implode(
', ',
array_map(
function ( $column ) {
return $this->quote_sqlite_identifier( $column['COLUMN_NAME'] );
},
$constraint
)
);
$query .= ')';
$rows[] = $query;
$rows[] = sprintf( ' PRIMARY KEY (%s)', implode( ', ', $column_list ) );
} else {
$is_unique = '0' === $info['NON_UNIQUE'];

// Prefix the original index name with the table name.
// This is to avoid conflicting index names in SQLite.
$sqlite_index_name = $this->get_sqlite_index_name( $table_name, $info['INDEX_NAME'] );

$query = sprintf(
'CREATE %sINDEX %s ON %s (',
$create_index_queries[] = sprintf(
'CREATE %sINDEX %s ON %s (%s)',
$is_unique ? 'UNIQUE ' : '',
$this->quote_sqlite_identifier( $sqlite_index_name ),
$this->quote_sqlite_identifier( $table_name )
$this->quote_sqlite_identifier( $table_name ),
implode( ', ', $column_list )
);
$query .= implode(
', ',
array_map(
function ( $column ) {
$fragment = $this->quote_sqlite_identifier( $column['COLUMN_NAME'] );
if ( 'D' === $column['COLLATION'] ) {
$fragment .= ' DESC';
}
return $fragment;
},
$constraint
)
);
$query .= ')';

$create_index_queries[] = $query;
}
}

Expand Down
Loading