Skip to content

Commit f14f3eb

Browse files
committed
wip
1 parent 4aac70a commit f14f3eb

3 files changed

Lines changed: 72 additions & 108 deletions

File tree

tests/WP_PDO_MySQL_On_SQLite_PDO_API_Tests.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ public function test_query_with_fetch_mode( $query, $mode, $expected ): void {
6363
if ( is_object( $expected ) ) {
6464
$this->assertInstanceOf( get_class( $expected ), $result );
6565
$this->assertEquals( $expected, $result );
66+
} elseif ( PDO::FETCH_NAMED === $mode ) {
67+
$this->assertSame( array_map( 'strval', array_keys( $expected ) ), array_keys( $result ) );
68+
$this->assertSame( array_values( $expected ), array_values( $result ) );
6669
} else {
6770
$this->assertSame( $expected, $result );
6871
}
@@ -249,6 +252,9 @@ public function test_fetch( $query, $mode, $expected ): void {
249252
if ( is_object( $expected ) ) {
250253
$this->assertInstanceOf( get_class( $expected ), $result );
251254
$this->assertEquals( $expected, $result );
255+
} elseif ( PDO::FETCH_NAMED === $mode ) {
256+
$this->assertSame( array_map( 'strval', array_keys( $expected ) ), array_keys( $result ) );
257+
$this->assertSame( array_values( $expected ), array_values( $result ) );
252258
} else {
253259
$this->assertSame( $expected, $result );
254260
}

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

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,13 @@ class WP_PDO_MySQL_On_SQLite extends PDO {
469469
*/
470470
private $last_sqlite_queries = array();
471471

472+
/**
473+
* TODO
474+
*
475+
* @var PDOStatement
476+
*/
477+
private $last_result_statement;
478+
472479
/**
473480
* Results of the last emulated query.
474481
*
@@ -823,6 +830,8 @@ public function query( string $query, ?int $fetch_mode = null, ...$fetch_mode_ar
823830
);
824831
}
825832
$arg_into = $fetch_mode_args[0];
833+
} elseif ( PDO::FETCH_FUNC === $fetch_mode ) {
834+
throw new ValueError( 'Can only use PDO::FETCH_FUNC in PDOStatement::fetchAll()' );
826835
} elseif ( $arg_count > 2 ) {
827836
throw new ArgumentCountError(
828837
sprintf( 'PDO::query() expects exactly 2 arguments for the fetch mode provided, %d given', $arg_count )
@@ -887,7 +896,9 @@ public function query( string $query, ?int $fetch_mode = null, ...$fetch_mode_ar
887896
$rows = is_array( $this->last_result ) ? $this->last_result : array();
888897
$affected_rows = is_int( $this->last_return_value ) ? $this->last_return_value : 0;
889898

890-
$stmt = new WP_PDO_Synthetic_Statement( $this, $columns, $rows, $affected_rows );
899+
$this->set_result_statement_from_data( $columns, $rows );
900+
901+
$stmt = new WP_PDO_Synthetic_Statement( $this->last_result_statement, $affected_rows );
891902
$stmt->setFetchMode( $fetch_mode, ...$fetch_mode_args );
892903
return $stmt;
893904
} catch ( Throwable $e ) {
@@ -6348,6 +6359,49 @@ private function flush(): void {
63486359
$this->wrapper_transaction_type = null;
63496360
}
63506361

6362+
private function set_result_statement_from_data( array $columns, array $rows ): void {
6363+
$pdo = $this->connection->get_pdo();
6364+
if ( 0 === count( $columns ) ) {
6365+
// Statement that returns no columns.
6366+
$this->last_result_statement = $pdo->query( 'PRAGMA noop;' );
6367+
return;
6368+
}
6369+
6370+
// SELECT NULL AS col1, NULL AS col2, ... WHERE FALSE
6371+
$query = 'SELECT ';
6372+
foreach ( $columns as $i => $column ) {
6373+
$query .= $i > 0 ? ', ' : '';
6374+
$query .= 'NULL AS ' . $pdo->quote( $column['name'] );
6375+
}
6376+
$query .= ' WHERE FALSE';
6377+
6378+
// UNION ALL VALUES (val1, val2, ...), (val1, val2, ...), ...
6379+
if ( count( $rows ) > 0 ) {
6380+
$query .= ' UNION ALL VALUES ';
6381+
}
6382+
foreach ( $rows as $i => $row ) {
6383+
$query .= $i > 0 ? ', ' : '';
6384+
$query .= '(';
6385+
foreach ( array_values( $row ) as $j => $value ) {
6386+
$query .= $j > 0 ? ', ' : '';
6387+
if ( null === $value ) {
6388+
$query .= 'NULL';
6389+
} elseif ( is_string( $value ) ) {
6390+
if ( strpos( $value, "\0" ) !== false ) {
6391+
$query .= sprintf( "CAST(x'%s' AS TEXT)", bin2hex( $value ) );
6392+
} else {
6393+
$query .= $pdo->quote( $value );
6394+
}
6395+
} else {
6396+
$query .= $value;
6397+
}
6398+
}
6399+
$query .= ')';
6400+
}
6401+
$this->last_result_statement = $pdo->prepare( $query );
6402+
$this->last_result_statement->execute();
6403+
}
6404+
63516405
/**
63526406
* Set results of a query() call using fetched data.
63536407
*

wp-includes/sqlite-ast/class-wp-pdo-synthetic-statement.php

Lines changed: 11 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ trait WP_PDO_Synthetic_Statement_PHP_Compat {
6161
*/
6262
#[ReturnTypeWillChange]
6363
public function setFetchMode( $mode, ...$args ): bool {
64-
return $this->setDefaultFetchMode( $mode, $args );
64+
return $this->setDefaultFetchMode( $mode, ...$args );
6565
}
6666

6767
/**
@@ -159,23 +159,19 @@ class WP_PDO_Synthetic_Statement extends PDOStatement {
159159
*/
160160
private $attributes = array();
161161

162+
private $stmt;
163+
162164
/**
163165
* Constructor.
164166
*
165-
* @param PDO $pdo The PDO connection.
166-
* @param array $columns Basic column metadata (containing at least name, table name, and native type).
167-
* @param array $rows Rows of the result set.
168-
* @param int $affected_rows The number of affected rows.
167+
* @param PDOStatement $stmt The original PDO statement.
168+
* @param int $affected_rows The number of affected rows.
169169
*/
170170
public function __construct(
171-
PDO $pdo,
172-
array $columns,
173-
array $rows,
171+
PDOStatement $stmt,
174172
int $affected_rows
175173
) {
176-
$this->pdo = $pdo;
177-
$this->columns = $columns;
178-
$this->rows = $rows;
174+
$this->stmt = $stmt;
179175
$this->affected_rows = $affected_rows;
180176
}
181177

@@ -186,7 +182,7 @@ public function __construct(
186182
* @return bool True on success, false on failure.
187183
*/
188184
public function execute( $params = null ): bool {
189-
throw new RuntimeException( 'Not implemented' );
185+
return $this->stmt->execute( $params );
190186
}
191187

192188
/**
@@ -195,7 +191,7 @@ public function execute( $params = null ): bool {
195191
* @return int The number of columns in the result set.
196192
*/
197193
public function columnCount(): int {
198-
return count( $this->columns );
194+
return $this->stmt->columnCount();
199195
}
200196

201197
/**
@@ -225,91 +221,7 @@ public function fetch(
225221
$cursorOrientation = 0,
226222
$cursorOffset = 0
227223
) {
228-
if ( 0 === $mode || null === $mode ) {
229-
$mode = $this->fetch_mode;
230-
}
231-
if ( null === $cursorOrientation ) {
232-
$cursorOrientation = PDO::FETCH_ORI_NEXT;
233-
}
234-
if ( null === $cursorOffset ) {
235-
$cursorOffset = 0;
236-
}
237-
238-
if ( ! array_key_exists( $this->cursor_offset, $this->rows ) ) {
239-
return false;
240-
}
241-
242-
// Get current row data and column names.
243-
$row = $this->rows[ $this->cursor_offset ];
244-
$column_names = array_column( $this->columns, 'name' );
245-
246-
// Advance the cursor to the next row.
247-
$this->cursor_offset += 1;
248-
249-
/*
250-
* TODO: Support scrollable cursor ($cursorOrientation and $cursorOffset).
251-
* This only has works for with statements that were prepared with
252-
* the PDO::ATTR_CURSOR attribute set to PDO::CURSOR_SCROLL value.
253-
* Without it, these parameters have no effect.
254-
*/
255-
256-
/**
257-
* With PHP < 8.1, the "PDO::ATTR_STRINGIFY_FETCHES" value of "false"
258-
* is not working correctly with the PDO SQLite driver. In such case,
259-
* we need to manually convert the row values to the correct types.
260-
*/
261-
if ( PHP_VERSION_ID < 80100 && ! $this->getAttribute( PDO::ATTR_STRINGIFY_FETCHES ) ) {
262-
foreach ( $row as $i => $value ) {
263-
$type = $this->columns[ $i ]['native_type'];
264-
if ( 'integer' === $type ) {
265-
$row[ $i ] = (int) $value;
266-
} elseif ( 'float' === $type ) {
267-
$row[ $i ] = (float) $value;
268-
}
269-
}
270-
}
271-
272-
switch ( $mode ) {
273-
case PDO::FETCH_BOTH:
274-
$values = array();
275-
foreach ( $row as $i => $value ) {
276-
$name = $column_names[ $i ];
277-
$values[ $name ] = $value;
278-
if ( ! array_key_exists( $i, $values ) ) {
279-
$values[ $i ] = $value;
280-
}
281-
}
282-
return $values;
283-
case PDO::FETCH_NUM:
284-
return $row;
285-
case PDO::FETCH_ASSOC:
286-
return array_combine( $column_names, $row );
287-
case PDO::FETCH_NAMED:
288-
$values = array();
289-
foreach ( $row as $i => $value ) {
290-
$name = $column_names[ $i ];
291-
if ( is_array( $values[ $name ] ?? null ) ) {
292-
$values[ $name ][] = $value;
293-
} elseif ( array_key_exists( $name, $values ) ) {
294-
$values[ $name ] = array( $values[ $name ], $value );
295-
} else {
296-
$values[ $name ] = $value;
297-
}
298-
}
299-
return $values;
300-
case PDO::FETCH_OBJ:
301-
return (object) array_combine( $column_names, $row );
302-
case PDO::FETCH_CLASS:
303-
throw new RuntimeException( "'PDO::FETCH_CLASS' mode is not supported" );
304-
case PDO::FETCH_INTO:
305-
throw new RuntimeException( "'PDO::FETCH_INTO' mode is not supported" );
306-
case PDO::FETCH_LAZY:
307-
throw new RuntimeException( "'PDO::FETCH_LAZY' mode is not supported" );
308-
case PDO::FETCH_BOUND:
309-
throw new RuntimeException( "'PDO::FETCH_BOUND' mode is not supported" );
310-
default:
311-
throw new ValueError( sprintf( 'PDOStatement::fetch(): Argument #1 ($mode) must be a bitmask of PDO::FETCH_* constants', $mode ) );
312-
}
224+
return $this->stmt->fetch( $mode, $cursorOrientation, $cursorOffset );
313225
}
314226

315227
/**
@@ -480,15 +392,7 @@ public function debugDumpParams(): ?bool {
480392
* @return array The result set as an array of rows.
481393
*/
482394
private function fetchAllRows( $mode = null, ...$args ): array {
483-
if ( null === $mode || 0 === $mode ) {
484-
$mode = $this->fetch_mode;
485-
}
486-
487-
$rows = array();
488-
while ( $row = $this->fetch( $mode, ...$args ) ) {
489-
$rows[] = $row;
490-
}
491-
return $rows;
395+
return $this->stmt->fetchAll( $mode, ...$args );
492396
}
493397

494398
/**

0 commit comments

Comments
 (0)