Skip to content

Commit 6a9accb

Browse files
committed
Make WP_PDO_MySQL_On_SQLite extend PDO, update query() API
1 parent 7bfa0e4 commit 6a9accb

File tree

5 files changed

+160
-22
lines changed

5 files changed

+160
-22
lines changed

tests/WP_MySQL_On_SQLite_PDO_API_Tests.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,18 @@ class WP_PDO_MySQL_On_SQLite_PDO_API_Tests extends TestCase {
77
private $driver;
88

99
public function setUp(): void {
10-
$connection = new WP_SQLite_Connection( array( 'path' => ':memory:' ) );
11-
$this->driver = new WP_PDO_MySQL_On_SQLite( $connection, 'wp' );
10+
$this->driver = new WP_PDO_MySQL_On_SQLite( 'mysql-on-sqlite:path=:memory:;dbname=WordPress;' );
11+
}
12+
13+
public function test_connection(): void {
14+
$driver = new WP_PDO_MySQL_On_SQLite( 'mysql-on-sqlite:path=:memory:;dbname=WordPress;' );
15+
$this->assertInstanceOf( PDO::class, $driver );
16+
}
17+
18+
public function test_query(): void {
19+
$result = $this->driver->query( 'SELECT 1' );
20+
$this->assertInstanceOf( PDOStatement::class, $result );
21+
$this->assertEquals( 1, $result->fetchColumn() );
1222
}
1323

1424
public function test_begin_transaction(): void {

wp-includes/sqlite-ast/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
* The driver requires PDO with the SQLite driver, and the PCRE engine.
1616
*/
17-
class WP_PDO_MySQL_On_SQLite {
17+
class WP_PDO_MySQL_On_SQLite extends PDO {
1818
/**
1919
* The path to the MySQL SQL grammar file.
2020
*/
@@ -579,24 +579,51 @@ class WP_PDO_MySQL_On_SQLite {
579579
private $user_variables = array();
580580

581581
/**
582-
* Constructor.
582+
* PDO API: Constructor.
583583
*
584584
* Set up an SQLite connection and the MySQL-on-SQLite driver.
585585
*
586586
* @param WP_SQLite_Connection $connection A SQLite database connection.
587-
* @param string $database The database name.
587+
* @param string $db_name The database name.
588588
*
589589
* @throws WP_SQLite_Driver_Exception When the driver initialization fails.
590590
*/
591591
public function __construct(
592-
WP_SQLite_Connection $connection,
593-
string $database,
594-
int $mysql_version = 80038
592+
string $dsn,
593+
?string $username = null,
594+
?string $password = null,
595+
array $options = array()
595596
) {
596-
$this->mysql_version = $mysql_version;
597-
$this->connection = $connection;
598-
$this->main_db_name = $database;
599-
$this->db_name = $database;
597+
// Parse the DSN.
598+
$dsn_parts = explode( ':', $dsn, 2 );
599+
if ( count( $dsn_parts ) < 2 ) {
600+
throw new PDOException( 'invalid data source name' );
601+
}
602+
603+
$driver = $dsn_parts[0];
604+
if ( 'mysql-on-sqlite' !== $driver ) {
605+
throw new PDOException( 'could not find driver' );
606+
}
607+
608+
$args = array();
609+
foreach ( explode( ';', $dsn_parts[1] ) as $arg ) {
610+
$arg_parts = explode( '=', $arg, 2 );
611+
$args[ $arg_parts[0] ] = $arg_parts[1] ?? null;
612+
}
613+
614+
$path = $args['path'] ?? ':memory:';
615+
$db_name = $args['dbname'] ?? 'sqlite_database';
616+
617+
// Create a new SQLite connection.
618+
if ( isset( $options['pdo'] ) ) {
619+
$this->connection = new WP_SQLite_Connection( array( 'pdo' => $options['pdo'] ) );
620+
} else {
621+
$this->connection = new WP_SQLite_Connection( array( 'path' => $path ) );
622+
}
623+
624+
$this->mysql_version = $options['mysql_version'] ?? 80038;
625+
$this->main_db_name = $db_name;
626+
$this->db_name = $db_name;
600627

601628
// Check the database name.
602629
if ( '' === $this->db_name ) {
@@ -832,7 +859,7 @@ public function get_insert_id() {
832859
}
833860

834861
/**
835-
* Translate and execute a MySQL query in SQLite.
862+
* PDO API: Translate and execute a MySQL query in SQLite.
836863
*
837864
* A single MySQL query can be translated into zero or more SQLite queries.
838865
*
@@ -843,13 +870,9 @@ public function get_insert_id() {
843870
* @return mixed Return value, depending on the query type.
844871
*
845872
* @throws WP_SQLite_Driver_Exception When the query execution fails.
846-
*
847-
* TODO:
848-
* The API of this function is not final.
849-
* We should also add support for parametrized queries.
850-
* See: https://github.com/Automattic/sqlite-database-integration/issues/7
851873
*/
852-
public function query( string $query, $fetch_mode = PDO::FETCH_OBJ, ...$fetch_mode_args ) {
874+
#[ReturnTypeWillChange]
875+
public function query( string $query, ?int $fetch_mode = PDO::FETCH_COLUMN, ...$fetch_mode_args ) {
853876
$this->flush();
854877
$this->pdo_fetch_mode = $fetch_mode;
855878
$this->last_mysql_query = $query;
@@ -895,7 +918,11 @@ public function query( string $query, $fetch_mode = PDO::FETCH_OBJ, ...$fetch_mo
895918
if ( $wrap_in_transaction ) {
896919
$this->commit_wrapper_transaction();
897920
}
898-
return $this->last_return_value;
921+
922+
$affected_rows = is_int( $this->last_return_value ) ? $this->last_return_value : 0;
923+
$rows = is_array( $this->last_result ) ? $this->last_result : array();
924+
$column_meta = is_array( $this->last_column_meta ) ? $this->last_column_meta : array();
925+
return new WP_PDO_Synthetic_Statement( $rows, $column_meta, $affected_rows );
899926
} catch ( Throwable $e ) {
900927
try {
901928
$this->rollback_user_transaction();
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/*
4+
* The SQLite driver uses PDO. Enable PDO function calls:
5+
* phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDO
6+
* phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDOStatement
7+
*
8+
* PDO uses camel case naming, enable non-snake:
9+
* phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
10+
*
11+
* PDO uses $class as a variable name, enable it:
12+
* phpcs:disable Universal.NamingConventions.NoReservedKeywordParameterNames.classFound
13+
*/
14+
15+
class WP_PDO_Synthetic_Statement extends PDOStatement {
16+
private $rows = array();
17+
private $columns = array();
18+
private $row_offset = 0;
19+
20+
private $affected_rows;
21+
22+
public function __construct(
23+
array $rows,
24+
array $columns,
25+
int $affected_rows = 0
26+
) {
27+
$this->rows = $rows;
28+
$this->columns = $columns;
29+
$this->affected_rows = $affected_rows;
30+
}
31+
32+
public function execute( ?array $params = null ): bool {
33+
return true;
34+
}
35+
36+
public function columnCount(): int {
37+
return count( $this->columns );
38+
}
39+
40+
public function rowCount(): int {
41+
return count( $this->rows );
42+
}
43+
44+
#[ReturnTypeWillChange]
45+
public function fetch(
46+
int $mode = PDO::FETCH_DEFAULT,
47+
int $cursorOrientation = PDO::FETCH_ORI_NEXT,
48+
int $cursorOffset = 0
49+
) {
50+
// TODO: $cursorOffset
51+
return $this->rows[ $this->row_offset++ ];
52+
}
53+
54+
public function fetchAll( int $mode = PDO::FETCH_DEFAULT, ...$args ): array {
55+
return array_slice( $this->rows, $this->row_offset, count( $this->rows ) - $this->row_offset );
56+
}
57+
58+
#[ReturnTypeWillChange]
59+
public function fetchColumn( int $column = 0 ) {
60+
return $this->rows[ $this->row_offset ][ $column ] ?? null;
61+
}
62+
63+
#[ReturnTypeWillChange]
64+
public function fetchObject( ?string $class = 'stdClass', array $constructorArgs = array() ) {
65+
return new $class( $this->rows[ $this->row_offset ], $constructorArgs );
66+
}
67+
68+
public function getColumnMeta( int $column ): array {
69+
return $this->columns[ $column ] ?? array();
70+
}
71+
72+
public function errorCode(): ?string {
73+
return null;
74+
}
75+
76+
public function errorInfo(): array {
77+
return array( '00000', '00000', '00000' );
78+
}
79+
80+
// TODO:
81+
// public function bindColumn()
82+
// public function bindParam()
83+
// public function bindValue()
84+
// public function closeCursor()
85+
// public function debugDumpParams()
86+
// public function setFetchMode()
87+
// public function setAttribute()
88+
// public function getAttribute()
89+
// public function getIterator()
90+
// public function nextRowset()
91+
}

wp-includes/sqlite-ast/class-wp-sqlite-driver.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ public function __construct(
2323
string $database,
2424
int $mysql_version = 80038
2525
) {
26-
$this->mysql_on_sqlite_driver = new WP_PDO_MySQL_On_SQLite( $connection, $database, $mysql_version );
26+
$this->mysql_on_sqlite_driver = new WP_PDO_MySQL_On_SQLite(
27+
sprintf( 'mysql-on-sqlite:dbname=%s', $database ),
28+
null,
29+
null,
30+
array(
31+
'mysql_version' => $mysql_version,
32+
'pdo' => $connection->get_pdo(),
33+
)
34+
);
2735
$this->main_db_name = $database;
2836
$this->client_info = $this->mysql_on_sqlite_driver->client_info;
2937
}
@@ -67,7 +75,8 @@ public function get_insert_id() {
6775
* @throws WP_SQLite_Driver_Exception When the query execution fails.
6876
*/
6977
public function query( string $query, $fetch_mode = PDO::FETCH_OBJ, ...$fetch_mode_args ) {
70-
return $this->mysql_on_sqlite_driver->query( $query, $fetch_mode, ...$fetch_mode_args );
78+
$this->mysql_on_sqlite_driver->query( $query, $fetch_mode, ...$fetch_mode_args );
79+
return $this->mysql_on_sqlite_driver->get_query_results();
7180
}
7281

7382
public function create_parser( string $query ): WP_MySQL_Parser {

wp-pdo-mysql-on-sqlite.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@
2020
require_once __DIR__ . '/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-exception.php';
2121
require_once __DIR__ . '/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-reconstructor.php';
2222
require_once __DIR__ . '/wp-includes/sqlite-ast/class-wp-pdo-mysql-on-sqlite.php';
23+
require_once __DIR__ . '/wp-includes/sqlite-ast/class-wp-pdo-synthetic-statement.php';

0 commit comments

Comments
 (0)