Files
2026-07-02 15:54:39 -06:00

128 lines
4.1 KiB
PHP

<?php
class WP_SQLite_Crosscheck_DB_ extends WP_SQLite_DB {
public function __construct( string $dbname ) {
parent::__construct( $dbname );
$GLOBALS['sqlite'] = $this;
$GLOBALS['mysql'] = new wpdb(
DB_USER,
DB_PASSWORD,
DB_NAME,
DB_HOST
);
// $this->resetDatabases();
}
private function resetDatabases() {
if ( file_exists( FQDB ) ) {
unlink( FQDB );
}
$GLOBALS['mysql']->query( 'DROP DATABASE IF EXISTS ' . DB_NAME );
$GLOBALS['mysql']->query( 'CREATE DATABASE ' . DB_NAME );
$GLOBALS['mysql']->query( 'USE ' . DB_NAME );
}
public function query( $query ) {
/**
* In MySQL, AUTO_INCREMENT columns don't reuse IDs assigned in rollback transactions
* In SQLite, AUTOINCREMENT columns do reuse IDs assigned in rollback transactions
*
* Let's store the current AUTOINCREMENT value for each table, and restore it afterwards.
*/
if ( preg_match( '/^\s*rollback/i', $query ) ) {
$autoincrements = array();
$tables = $GLOBALS['@pdo']->query( "SELECT name as `table` FROM sqlite_master WHERE type='table' ORDER BY name" )->fetchAll();
foreach ( $tables as $table ) {
$table = $table['table'];
$autoincrement = $GLOBALS['@pdo']->query( "SELECT seq FROM sqlite_sequence WHERE name = '$table'" )->fetchColumn();
$autoincrements[ $table ] = $autoincrement ?: 1;
}
}
$sqlite_retval = parent::query( $query );
if ( preg_match( '/^\s*rollback/i', $query ) ) {
foreach ( $autoincrements as $table => $autoincrement ) {
$GLOBALS['@pdo']->query( "UPDATE sqlite_sequence SET seq = $autoincrement WHERE name = '$table'" );
}
}
$this->crosscheck( $query, $sqlite_retval );
return $sqlite_retval;
}
private function crosscheck( $query, $sqlite_retval ) {
// echo $query."\n\n";
// Be lenient on cross-checking some query types
if ( preg_match( '/^\s*SET storage_engine/i', $query ) ) {
return;
}
$this->show_errors = false;
$this->suppress_errors = true;
$GLOBALS['mysql']->show_errors = false;
$GLOBALS['mysql']->suppress_errors = true;
ob_start();
$mysql_retval = $GLOBALS['mysql']->query( $query );
ob_end_clean();
$tests = array(
array( 'retval', $mysql_retval, $sqlite_retval ),
array( 'num_rows', $GLOBALS['mysql']->num_rows, $GLOBALS['sqlite']->num_rows ),
array( 'insert_id', $GLOBALS['mysql']->insert_id, $GLOBALS['sqlite']->insert_id ),
array( 'rows_affected', $GLOBALS['mysql']->rows_affected, $GLOBALS['sqlite']->rows_affected ),
);
foreach ( $tests as $test ) {
list($factor, $mysql, $sqlite) = $test;
if ( $mysql !== $sqlite ) {
if ( 'insert_id' === $factor ) {
// On multi-inserts MySQL returns the first inserted ID
// while SQLite returns the last one. The cached insert_id
// value stays the same for a number of subsequent queries.
// Let's forgive this for now.
continue;
}
if ( 'rows_affected' === $factor && $mysql_retval === $mysql ) {
// SQLite doesn't provide the rowcount() functionality
continue;
}
if ( 'retval' === $factor && $GLOBALS['mysql']->rows_affected === $mysql ) {
// SQLite doesn't provide the rowcount() functionality
continue;
}
echo "======================================================\n";
echo "======== *** $factor *** differed for query ========= \n";
echo "======================================================\n";
echo "MySQL query: \n";
echo "$query\n\n";
echo "SQLite queries: \n";
foreach ( $this->dbh->last_translation->queries as $query ) {
echo $query->sql . "\n";
}
echo "\n";
$this->report_factor(
'error',
$GLOBALS['mysql']->last_error,
$GLOBALS['sqlite']->last_error
);
foreach ( $tests as $test ) {
$this->report_factor(
$test[0],
$test[1],
$test[2]
);
}
// throw new Exception();
break;
}
}
}
private function report_factor( $factor, $mysql, $sqlite ) {
echo "$factor: \n";
echo ' MySQL: ' . var_export( $mysql, true ) . "\n";
echo ' SQLite: ' . var_export( $sqlite, true ) . "\n\n";
}
}