Skip to content

Commit ffbd81a

Browse files
committed
wip
1 parent 017c3c1 commit ffbd81a

File tree

2 files changed

+578
-76
lines changed

2 files changed

+578
-76
lines changed
+395
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
<?php
2+
3+
require_once __DIR__ . '/../wp-includes/sqlite-ast/class-wp-sqlite-driver.php';
4+
require_once __DIR__ . '/../wp-includes/sqlite-ast/class-wp-sqlite-driver-exception.php';
5+
require_once __DIR__ . '/../wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php';
6+
7+
use PHPUnit\Framework\TestCase;
8+
9+
class WP_SQLite_Driver_Metadata_Tests extends TestCase {
10+
11+
private $engine;
12+
private $sqlite;
13+
14+
public static function setUpBeforeClass(): void {
15+
// if ( ! defined( 'PDO_DEBUG' )) {
16+
// define( 'PDO_DEBUG', true );
17+
// }
18+
if ( ! defined( 'FQDB' ) ) {
19+
define( 'FQDB', ':memory:' );
20+
define( 'FQDBDIR', __DIR__ . '/../testdb' );
21+
}
22+
error_reporting( E_ALL & ~E_DEPRECATED );
23+
if ( ! isset( $GLOBALS['table_prefix'] ) ) {
24+
$GLOBALS['table_prefix'] = 'wptests_';
25+
}
26+
if ( ! isset( $GLOBALS['wpdb'] ) ) {
27+
$GLOBALS['wpdb'] = new stdClass();
28+
$GLOBALS['wpdb']->suppress_errors = false;
29+
$GLOBALS['wpdb']->show_errors = true;
30+
}
31+
return;
32+
}
33+
34+
// Before each test, we create a new database
35+
public function setUp(): void {
36+
global $blog_tables;
37+
$queries = explode( ';', $blog_tables );
38+
39+
$this->sqlite = new PDO( 'sqlite::memory:' );
40+
$this->engine = new WP_SQLite_Driver(
41+
array(
42+
'connection' => $this->sqlite,
43+
'database' => 'wp',
44+
)
45+
);
46+
47+
$translator = $this->engine;
48+
49+
try {
50+
$translator->begin_transaction();
51+
foreach ( $queries as $query ) {
52+
$query = trim( $query );
53+
if ( empty( $query ) ) {
54+
continue;
55+
}
56+
57+
$translator->execute_sqlite_query( $query );
58+
}
59+
$translator->commit();
60+
} catch ( PDOException $err ) {
61+
$err_data =
62+
$err->errorInfo; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
63+
$err_code = $err_data[1];
64+
$translator->rollback();
65+
$message = sprintf(
66+
'Error occurred while creating tables or indexes...<br />Query was: %s<br />',
67+
var_export( $query, true )
68+
);
69+
$message .= sprintf( 'Error message is: %s', $err_data[2] );
70+
wp_die( $message, 'Database Error!' );
71+
}
72+
}
73+
74+
public function testCountTables() {
75+
$this->assertQuery( 'CREATE TABLE t1 (id INT)' );
76+
$this->assertQuery( 'CREATE TABLE t2 (id INT)' );
77+
78+
$result = $this->assertQuery( "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'wp'" );
79+
$this->assertEquals( array( (object) array( 'COUNT ( * )' => '2' ) ), $result );
80+
81+
$result = $this->assertQuery( "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'other'" );
82+
$this->assertEquals( array( (object) array( 'COUNT ( * )' => '0' ) ), $result );
83+
84+
// @TODO: The result key should be "COUNT(*)" instead of "COUNT ( * )".
85+
// The spacing was probably inserted by the translator.
86+
}
87+
88+
public function testInformationSchemaTables() {
89+
$this->assertQuery(
90+
'
91+
CREATE TABLE t (id INT PRIMARY KEY, name TEXT, age INT)
92+
'
93+
);
94+
95+
$result = $this->assertQuery( "SELECT * FROM information_schema.tables WHERE TABLE_NAME = 't'" );
96+
$this->assertCount( 1, $result );
97+
$this->assertRegExp( '/\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/', $result[0]->CREATE_TIME );
98+
$this->assertEquals(
99+
array(
100+
'TABLE_CATALOG' => 'def',
101+
'TABLE_SCHEMA' => 'wp',
102+
'TABLE_NAME' => 't',
103+
'TABLE_TYPE' => 'BASE TABLE',
104+
'ENGINE' => 'InnoDB',
105+
'VERSION' => '10',
106+
'ROW_FORMAT' => 'Dynamic',
107+
'TABLE_ROWS' => '0',
108+
'AVG_ROW_LENGTH' => '0',
109+
'DATA_LENGTH' => '0',
110+
'MAX_DATA_LENGTH' => '0',
111+
'INDEX_LENGTH' => '0',
112+
'DATA_FREE' => '0',
113+
'AUTO_INCREMENT' => null,
114+
'CREATE_TIME' => $result[0]->CREATE_TIME,
115+
'UPDATE_TIME' => null,
116+
'CHECK_TIME' => null,
117+
'TABLE_COLLATION' => 'utf8mb4_general_ci',
118+
'CHECKSUM' => null,
119+
'CREATE_OPTIONS' => '',
120+
'TABLE_COMMENT' => '',
121+
),
122+
(array) $result[0]
123+
);
124+
125+
$result = $this->assertQuery(
126+
"SELECT
127+
table_name as 'name',
128+
engine AS 'engine',
129+
FLOOR( data_length / 1024 / 1024 ) 'data'
130+
FROM INFORMATION_SCHEMA.TABLES
131+
WHERE TABLE_NAME = 't'
132+
ORDER BY name ASC"
133+
);
134+
135+
$this->assertEquals(
136+
array(
137+
'name' => 't',
138+
'engine' => 'InnoDB',
139+
'data' => '0',
140+
),
141+
(array) $result[0]
142+
);
143+
}
144+
145+
public function testInformationSchemaQueryHidesSqliteSystemTables() {
146+
/**
147+
* By default, system tables are not returned.
148+
*/
149+
$result = $this->assertQuery( "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sqlite_sequence'" );
150+
$this->assertEquals( 0, count( $result ) );
151+
}
152+
153+
public function testUseStatement() {
154+
$this->assertQuery( 'CREATE TABLE tables (ENGINE TEXT)' );
155+
$this->assertQuery( "INSERT INTO tables (ENGINE) VALUES ('test')" );
156+
157+
$this->assertQuery( 'USE wp' );
158+
$result = $this->assertQuery( 'SELECT * FROM tables' );
159+
$this->assertSame( 'test', $result[0]->ENGINE );
160+
161+
$this->assertQuery( 'USE information_schema' );
162+
$result = $this->assertQuery( 'SELECT * FROM tables' );
163+
$this->assertSame( 'InnoDB', $result[0]->ENGINE );
164+
}
165+
166+
private function assertQuery( $sql ) {
167+
$retval = $this->engine->query( $sql );
168+
$this->assertNotFalse( $retval );
169+
return $retval;
170+
}
171+
172+
public function testCheckTable() {
173+
174+
/* a good table */
175+
$table_name = 'wp_options';
176+
$expected_result = array(
177+
(object) array(
178+
'Table' => 'wp.' . $table_name,
179+
'Op' => 'check',
180+
'Msg_type' => 'status',
181+
'Msg_text' => 'OK',
182+
),
183+
);
184+
185+
$this->assertQuery(
186+
"CHECK TABLE $table_name;"
187+
);
188+
189+
$this->assertEquals(
190+
$expected_result,
191+
$this->engine->get_query_results()
192+
);
193+
194+
/* a different good table */
195+
$table_name = 'wp_postmeta';
196+
$expected_result = array(
197+
(object) array(
198+
'Table' => 'wp.' . $table_name,
199+
'Op' => 'check',
200+
'Msg_type' => 'status',
201+
'Msg_text' => 'OK',
202+
),
203+
);
204+
205+
$this->assertQuery(
206+
"CHECK TABLE $table_name;"
207+
);
208+
$this->assertEquals(
209+
$expected_result,
210+
$this->engine->get_query_results()
211+
);
212+
213+
/* a bogus, missing, table */
214+
$table_name = 'wp_sqlite_rocks';
215+
$expected_result = array(
216+
(object) array(
217+
'Table' => 'wp.' . $table_name,
218+
'Op' => 'check',
219+
'Msg_type' => 'Error',
220+
'Msg_text' => "Table '$table_name' doesn't exist",
221+
),
222+
(object) array(
223+
'Table' => 'wp.' . $table_name,
224+
'Op' => 'check',
225+
'Msg_type' => 'status',
226+
'Msg_text' => 'Operation failed',
227+
),
228+
);
229+
230+
$this->assertQuery(
231+
"CHECK TABLE $table_name;"
232+
);
233+
234+
$this->assertEquals(
235+
$expected_result,
236+
$this->engine->get_query_results()
237+
);
238+
}
239+
240+
public function testOptimizeTable() {
241+
242+
/* a good table */
243+
$table_name = 'wp_options';
244+
245+
$this->assertQuery(
246+
"OPTIMIZE TABLE $table_name;"
247+
);
248+
249+
$actual = $this->engine->get_query_results();
250+
251+
array_map(
252+
function ( $row ) {
253+
$this->assertIsObject( $row );
254+
$row = (array) $row;
255+
$this->assertIsString( $row['Table'] );
256+
$this->assertIsString( $row['Op'] );
257+
$this->assertIsString( $row['Msg_type'] );
258+
$this->assertIsString( $row['Msg_text'] );
259+
},
260+
$actual
261+
);
262+
263+
$ok = array_filter(
264+
$actual,
265+
function ( $row ) {
266+
$row = (array) $row;
267+
268+
return strtolower( $row['Msg_type'] ) === 'status' && strtolower( $row['Msg_text'] ) === 'ok';
269+
}
270+
);
271+
$this->assertIsArray( $ok );
272+
$this->assertGreaterThan( 0, count( $ok ) );
273+
}
274+
275+
public function testRepairTable() {
276+
277+
/* a good table */
278+
$table_name = 'wp_options';
279+
280+
$this->assertQuery(
281+
"REPAIR TABLE $table_name;"
282+
);
283+
284+
$actual = $this->engine->get_query_results();
285+
286+
array_map(
287+
function ( $r ) {
288+
$this->assertIsObject( $r );
289+
$row = $r;
290+
$row = (array) $row;
291+
$this->assertIsString( $row['Table'] );
292+
$this->assertIsString( $row['Op'] );
293+
$this->assertIsString( $row['Msg_type'] );
294+
$this->assertIsString( $row['Msg_text'] );
295+
},
296+
$actual
297+
);
298+
299+
$ok = array_filter(
300+
$actual,
301+
function ( $row ) {
302+
return strtolower( $row->Msg_type ) === 'status' && strtolower( $row->Msg_text ) === 'ok';
303+
}
304+
);
305+
$this->assertIsArray( $ok );
306+
$this->assertGreaterThan( 0, count( $ok ) );
307+
}
308+
309+
// this tests for successful rejection of a bad query
310+
311+
public function testShowTableStatus() {
312+
313+
$this->assertQuery(
314+
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit', 'Testing' )"
315+
);
316+
317+
$this->assertQuery(
318+
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit0', 'Testing0' ), ( 'PhpUnit1', 'Testing1' ), ( 'PhpUnit2', 'Testing2' )"
319+
);
320+
321+
$this->assertTableEmpty( 'wp_comments', false );
322+
323+
$this->assertQuery(
324+
'SHOW TABLE STATUS FROM wp;'
325+
);
326+
327+
$actual = $this->engine->get_query_results();
328+
329+
$this->assertIsArray( $actual );
330+
$this->assertGreaterThanOrEqual(
331+
1,
332+
count( $actual )
333+
);
334+
$this->assertIsObject( $actual[0] );
335+
336+
$rows = array_values(
337+
array_filter(
338+
$actual,
339+
function ( $row ) {
340+
$this->assertIsObject( $row );
341+
$this->assertIsString( $row->Name );
342+
$this->assertIsNumeric( $row->Rows );
343+
344+
return str_ends_with( $row->Name, 'comments' );
345+
}
346+
)
347+
);
348+
$this->assertEquals( 'wp_comments', $rows[0]->Name );
349+
$this->assertEquals( 4, $rows[0]->Rows );
350+
}
351+
352+
private function assertTableEmpty( $table_name, $empty_var ) {
353+
354+
$this->assertQuery(
355+
"SELECT COUNT(*) num FROM $table_name"
356+
);
357+
358+
$actual = $this->engine->get_query_results();
359+
if ( $empty_var ) {
360+
$this->assertEquals( 0, $actual[0]->num, "$table_name is not empty" );
361+
} else {
362+
$this->assertGreaterThan( 0, $actual[0]->num, "$table_name is empty" );
363+
}
364+
}
365+
366+
public function testTruncateTable() {
367+
368+
$this->assertQuery(
369+
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit', 'Testing' )"
370+
);
371+
372+
$this->assertQuery(
373+
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit0', 'Testing0' ), ( 'PhpUnit1', 'Testing1' ), ( 'PhpUnit2', 'Testing2' )"
374+
);
375+
376+
$this->assertTableEmpty( 'wp_comments', false );
377+
378+
$this->assertQuery(
379+
'TRUNCATE TABLE wp_comments;'
380+
);
381+
$actual = $this->engine->get_query_results();
382+
$this->assertEquals(
383+
true,
384+
$actual
385+
);
386+
$this->assertTableEmpty( 'wp_comments', true );
387+
}
388+
389+
public function testBogusQuery() {
390+
$this->expectExceptionMessage( 'no such table: bogus' );
391+
$this->assertQuery(
392+
'SELECT 1, BOGUS(1) FROM bogus;',
393+
);
394+
}
395+
}

0 commit comments

Comments
 (0)