rdmbs: Start of SQLPlatform to split out of Database

This is the first step to split parts of Database that doesn't require a
connection and are used for query parts.

Bug: T299691
Change-Id: I140aa4328865994499926f898233867ce383908c
This commit is contained in:
Amir Sarabadani 2021-12-08 13:08:11 +01:00 committed by Ladsgroup
parent f0938b5acc
commit 236a0941c0
14 changed files with 402 additions and 121 deletions

View file

@ -1807,6 +1807,7 @@ $wgAutoloadLocalClasses = [
'Wikimedia\\Rdbms\\DBConnectionError' => __DIR__ . '/includes/libs/rdbms/exception/DBConnectionError.php',
'Wikimedia\\Rdbms\\DBError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
'Wikimedia\\Rdbms\\DBExpectedError' => __DIR__ . '/includes/libs/rdbms/exception/DBExpectedError.php',
'Wikimedia\\Rdbms\\DBLanguageError' => __DIR__ . '/includes/libs/rdbms/exception/DBLanguageError.php',
'Wikimedia\\Rdbms\\DBPrimaryPos' => __DIR__ . '/includes/libs/rdbms/database/position/DBPrimaryPos.php',
'Wikimedia\\Rdbms\\DBQueryDisconnectedError' => __DIR__ . '/includes/libs/rdbms/exception/DBQueryDisconnectedError.php',
'Wikimedia\\Rdbms\\DBQueryError' => __DIR__ . '/includes/libs/rdbms/exception/DBQueryError.php',

View file

@ -179,6 +179,7 @@ class AutoLoader {
'MediaWiki\\Widget\\' => __DIR__ . '/widget/',
'Wikimedia\\' => __DIR__ . '/libs/',
'Wikimedia\\Http\\' => __DIR__ . '/libs/http/',
'Wikimedia\\Rdbms\\Platform\\' => __DIR__ . '/libs/rdbms/platform/',
'Wikimedia\\UUID\\' => __DIR__ . '/libs/uuid/',
];
}

View file

@ -26,6 +26,8 @@ use InvalidArgumentException;
use RuntimeException;
use stdClass;
use Wikimedia\AtEase\AtEase;
use Wikimedia\Rdbms\Platform\ISQLPlatform;
use Wikimedia\Rdbms\Platform\MySQLPlatform;
/**
* Database abstraction object for MySQL.
@ -81,6 +83,9 @@ abstract class DatabaseMysqlBase extends Database {
/** @var float Warn if lag estimates are made for transactions older than this many seconds */
private const LAG_STALE_WARN_THRESHOLD = 0.100;
/** @var ISQLPlatform */
protected $platform;
/**
* Additional $params include:
* - lagDetectionMethod : set to one of (Seconds_Behind_Master,pt-heartbeat).
@ -114,6 +119,7 @@ abstract class DatabaseMysqlBase extends Database {
$this->utf8Mode = !empty( $params['utf8Mode'] );
$this->insertSelectIsSafe = isset( $params['insertSelectIsSafe'] )
? (bool)$params['insertSelectIsSafe'] : null;
$this->platform = new MySQLPlatform();
parent::__construct( $params );
}
@ -164,7 +170,7 @@ abstract class DatabaseMysqlBase extends Database {
if ( !is_int( $val ) && !is_float( $val ) ) {
$val = $this->addQuotes( $val );
}
$set[] = $this->addIdentifierQuotes( $var ) . ' = ' . $val;
$set[] = $this->platform->addIdentifierQuotes( $var ) . ' = ' . $val;
}
// @phan-suppress-next-next-line PhanRedundantCondition
@ -205,7 +211,7 @@ abstract class DatabaseMysqlBase extends Database {
}
if ( $database !== $this->getDBname() ) {
$sql = 'USE ' . $this->addIdentifierQuotes( $database );
$sql = 'USE ' . $this->platform->addIdentifierQuotes( $database );
list( $res, $err, $errno ) =
$this->executeQuery( $sql, __METHOD__, self::QUERY_IGNORE_DBO_TRX );
@ -365,7 +371,7 @@ abstract class DatabaseMysqlBase extends Database {
// If the database has been specified (such as for shared tables), use "FROM"
if ( $database !== '' ) {
$encDatabase = $this->addIdentifierQuotes( $database );
$encDatabase = $this->platform->addIdentifierQuotes( $database );
$sql = "SHOW TABLES FROM $encDatabase LIKE '$encLike'";
} else {
$sql = "SHOW TABLES LIKE '$encLike'";
@ -459,15 +465,15 @@ abstract class DatabaseMysqlBase extends Database {
abstract protected function mysqlRealEscapeString( $s );
/**
* MySQL uses `backticks` for identifier quoting instead of the sql standard "double quotes".
*
* @param string $s
* @return string
*/
public function addIdentifierQuotes( $s ) {
// Characters in the range \u0001-\uFFFF are valid in a quoted identifier
// Remove NUL bytes and escape backticks by doubling
return '`' . str_replace( [ "\0", '`' ], [ '', '``' ], $s ) . '`';
// Tests disabling constructor
if ( !$this->platform ) {
$this->platform = new MySQLPlatform();
}
return $this->platform->addIdentifierQuotes( $s );
}
/**

View file

@ -23,6 +23,8 @@
namespace Wikimedia\Rdbms;
use RuntimeException;
use Wikimedia\Rdbms\Platform\ISQLPlatform;
use Wikimedia\Rdbms\Platform\PostgresPlatform;
use Wikimedia\Timestamp\ConvertibleTimestamp;
use Wikimedia\WaitConditionLoop;
@ -44,6 +46,9 @@ class DatabasePostgres extends Database {
/** @var resource|null */
private $lastResultHandle;
/** @var ISQLPlatform */
private $platform;
/**
* @see Database::__construct()
* @param array $params Additional parameters include:
@ -63,6 +68,7 @@ class DatabasePostgres extends Database {
}
parent::__construct( $params );
$this->platform = new PostgresPlatform();
}
public function getType() {
@ -129,7 +135,7 @@ class DatabasePostgres extends Database {
];
foreach ( $variables as $var => $val ) {
$this->query(
'SET ' . $this->addIdentifierQuotes( $var ) . ' = ' . $this->addQuotes( $val ),
'SET ' . $this->platform->addIdentifierQuotes( $var ) . ' = ' . $this->addQuotes( $val ),
__METHOD__,
self::QUERY_NO_RETRY | self::QUERY_CHANGE_TRX
);
@ -456,7 +462,9 @@ __INDEXATTR__;
$toCheck = array_merge( $toCheck, $name );
} else {
// Quote alias names so $this->tableName() won't mangle them
$options['FOR UPDATE'][] = $hasAlias ? $this->addIdentifierQuotes( $alias ) : $alias;
$options['FOR UPDATE'][] = $hasAlias ?
// @phan-suppress-next-line PhanTypeMismatchArgumentNullable
$this->platform->addIdentifierQuotes( $alias ) : $alias;
}
}
}
@ -674,8 +682,8 @@ __INDEXATTR__;
public function duplicateTableStructure(
$oldName, $newName, $temporary = false, $fname = __METHOD__
) {
$newNameE = $this->addIdentifierQuotes( $newName );
$oldNameE = $this->addIdentifierQuotes( $oldName );
$newNameE = $this->platform->addIdentifierQuotes( $newName );
$oldNameE = $this->platform->addIdentifierQuotes( $oldName );
$temporary = $temporary ? 'TEMPORARY' : '';
@ -705,8 +713,8 @@ __INDEXATTR__;
if ( $row ) {
$field = $row->attname;
$newSeq = "{$newName}_{$field}_seq";
$fieldE = $this->addIdentifierQuotes( $field );
$newSeqE = $this->addIdentifierQuotes( $newSeq );
$fieldE = $this->platform->addIdentifierQuotes( $field );
$newSeqE = $this->platform->addIdentifierQuotes( $newSeq );
$newSeqQ = $this->addQuotes( $newSeq );
$this->query(
"CREATE $temporary SEQUENCE $newSeqE OWNED BY $newNameE.$fieldE",
@ -921,7 +929,7 @@ __INDEXATTR__;
} else {
// Prepend the desired schema to the search path (T17816)
$search_path = $this->getSearchPath();
array_unshift( $search_path, $this->addIdentifierQuotes( $desiredSchema ) );
array_unshift( $search_path, $this->platform->addIdentifierQuotes( $desiredSchema ) );
$this->setSearchPath( $search_path );
$this->coreSchema = $desiredSchema;
$this->queryLogger->debug(

View file

@ -29,6 +29,8 @@ use NullLockManager;
use PDO;
use PDOException;
use RuntimeException;
use Wikimedia\Rdbms\Platform\ISQLPlatform;
use Wikimedia\Rdbms\Platform\SqlitePlatform;
/**
* @ingroup Database
@ -67,6 +69,9 @@ class DatabaseSqlite extends Database {
'temp_store' => [ 'FILE', 'MEMORY' ]
];
/** @var ISQLPlatform */
private $platform;
/**
* Additional params include:
* - dbDirectory : directory containing the DB and the lock file directory
@ -89,6 +94,7 @@ class DatabaseSqlite extends Database {
$this->trxMode = strtoupper( $params['trxMode'] ?? '' );
$this->lockMgr = $this->makeLockManager();
$this->platform = new SqlitePlatform();
}
protected static function getAttributes() {
@ -922,9 +928,9 @@ class DatabaseSqlite extends Database {
$sqlCreateTable = $obj->sql;
$sqlCreateTable = preg_replace(
'/(?<=\W)"?' .
preg_quote( trim( $this->addIdentifierQuotes( $oldName ), '"' ), '/' ) .
preg_quote( trim( $this->platform->addIdentifierQuotes( $oldName ), '"' ), '/' ) .
'"?(?=\W)/',
$this->addIdentifierQuotes( $newName ),
$this->platform->addIdentifierQuotes( $newName ),
$sqlCreateTable,
1
);
@ -966,8 +972,8 @@ class DatabaseSqlite extends Database {
}
// Try to come up with a new index name, given indexes have database scope in SQLite
$indexName = $newName . '_' . $index->name;
$sqlIndex .= ' ' . $this->addIdentifierQuotes( $indexName ) .
' ON ' . $this->addIdentifierQuotes( $newName );
$sqlIndex .= ' ' . $this->platform->addIdentifierQuotes( $indexName ) .
' ON ' . $this->platform->addIdentifierQuotes( $newName );
$indexInfo = $this->query(
'PRAGMA INDEX_INFO(' . $this->addQuotes( $index->name ) . ')',
@ -1046,7 +1052,7 @@ class DatabaseSqlite extends Database {
$encSeqNames[] = $this->addQuotes( $this->tableName( $table, 'raw' ) );
}
$encMasterTable = $this->addIdentifierQuotes( 'sqlite_sequence' );
$encMasterTable = $this->platform->addIdentifierQuotes( 'sqlite_sequence' );
$this->query(
"DELETE FROM $encMasterTable WHERE name IN(" . implode( ',', $encSeqNames ) . ")",
$fname,

View file

@ -22,6 +22,7 @@ namespace Wikimedia\Rdbms;
use Exception;
use InvalidArgumentException;
use stdClass;
use Wikimedia\Rdbms\Platform\ISQLPlatform;
use Wikimedia\ScopedCallback;
/**
@ -35,7 +36,7 @@ use Wikimedia\ScopedCallback;
* @note IDatabase and DBConnRef should be updated to reflect any changes
* @ingroup Database
*/
interface IDatabase {
interface IDatabase extends ISQLPlatform {
/** @var int Callback triggered immediately due to no active transaction */
public const TRIGGER_IDLE = 1;
/** @var int Callback triggered by COMMIT */
@ -990,26 +991,6 @@ interface IDatabase {
*/
public function factorConds( $condsArray );
/**
* @param string|int $field
* @return string
*/
public function bitNot( $field );
/**
* @param string|int $fieldLeft
* @param string|int $fieldRight
* @return string
*/
public function bitAnd( $fieldLeft, $fieldRight );
/**
* @param string|int $fieldLeft
* @param string|int $fieldRight
* @return string
*/
public function bitOr( $fieldLeft, $fieldRight );
/**
* Build a concatenation list to feed into a SQL query
* @param string[] $stringList Raw SQL expression list; caller is responsible for escaping
@ -1210,17 +1191,6 @@ interface IDatabase {
*/
public function addQuotes( $s );
/**
* Escape a SQL identifier (e.g. table, column, database) for use in a SQL query
*
* Depending on the database this will either be `backticks` or "double quotes"
*
* @param string $s
* @return string
* @since 1.33
*/
public function addIdentifierQuotes( $s );
/**
* LIKE statement wrapper
*

View file

@ -0,0 +1,36 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Database
*/
namespace Wikimedia\Rdbms;
use Throwable;
/**
* @since 1.39
* @newable
* @stable to extend
* @ingroup Database
*/
class DBLanguageError extends DBUnexpectedError {
public function __construct( $error, Throwable $prev = null ) {
parent::__construct( null, $error, $prev );
}
}

View file

@ -0,0 +1,60 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace Wikimedia\Rdbms\Platform;
/**
* Interface for query language.
* Note: This is for simple SQL operations, use QueryBuilder for building
* full queries.
* @since 1.39
*/
interface ISQLPlatform {
/**
* @param string|int $field
* @return string
*/
public function bitNot( $field );
/**
* @param string|int $fieldLeft
* @param string|int $fieldRight
* @return string
*/
public function bitAnd( $fieldLeft, $fieldRight );
/**
* @param string|int $fieldLeft
* @param string|int $fieldRight
* @return string
*/
public function bitOr( $fieldLeft, $fieldRight );
/**
* Escape a SQL identifier (e.g. table, column, database) for use in a SQL query
*
* Depending on the database this will either be `backticks` or "double quotes"
*
* @param string $s
* @return string
* @since 1.33
*/
public function addIdentifierQuotes( $s );
}

View file

@ -0,0 +1,38 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace Wikimedia\Rdbms\Platform;
/**
* @since 1.39
* @see ISQLPlatform
*/
class MySQLPlatform extends SQLPlatform {
/**
* MySQL uses `backticks` for identifier quoting instead of the sql standard "double quotes".
*
* @param string $s
* @return string
*/
public function addIdentifierQuotes( $s ) {
// Characters in the range \u0001-\uFFFF are valid in a quoted identifier
// Remove NUL bytes and escape backticks by doubling
return '`' . str_replace( [ "\0", '`' ], [ '', '``' ], $s ) . '`';
}
}

View file

@ -0,0 +1,31 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace Wikimedia\Rdbms\Platform;
/**
* @since 1.39
* @see ISQLPlatform
*/
class PostgresPlatform extends SQLPlatform {
/**
* Empty for now
*/
}

View file

@ -0,0 +1,61 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace Wikimedia\Rdbms\Platform;
/**
* Sql abstraction object.
* This class nor any of its subclasses should create or use a db connection.
* It also should not become stateful. Preferably the constructor should stay empty.
* @since 1.39
*/
class SQLPlatform implements ISQLPlatform {
/**
* @inheritDoc
* @stable to override
*/
public function bitNot( $field ) {
return "(~$field)";
}
/**
* @inheritDoc
* @stable to override
*/
public function bitAnd( $fieldLeft, $fieldRight ) {
return "($fieldLeft & $fieldRight)";
}
/**
* @inheritDoc
* @stable to override
*/
public function bitOr( $fieldLeft, $fieldRight ) {
return "($fieldLeft | $fieldRight)";
}
/**
* @inheritDoc
* @stable to override
*/
public function addIdentifierQuotes( $s ) {
return '"' . str_replace( '"', '""', $s ) . '"';
}
}

View file

@ -0,0 +1,31 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace Wikimedia\Rdbms\Platform;
/**
* @since 1.38
* @see ISQLPlatform
*/
class SqlitePlatform extends SQLPlatform {
/**
* Empty for now
*/
}

View file

@ -28,79 +28,13 @@ use Wikimedia\Rdbms\DatabaseMysqli;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\IMaintainableDatabase;
use Wikimedia\Rdbms\MySQLPrimaryPos;
use Wikimedia\Rdbms\Platform\MySQLPlatform;
use Wikimedia\TestingAccessWrapper;
class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
use MediaWikiCoversValidator;
/**
* @dataProvider provideDiapers
* @covers \Wikimedia\Rdbms\DatabaseMysqlBase::addIdentifierQuotes
*/
public function testAddIdentifierQuotes( $expected, $in ) {
$db = $this->getMockBuilder( DatabaseMysqli::class )
->disableOriginalConstructor()
->onlyMethods( [] )
->getMock();
/** @var IDatabase $db */
$quoted = $db->addIdentifierQuotes( $in );
$this->assertEquals( $expected, $quoted );
}
/**
* Feeds testAddIdentifierQuotes
*
* Named per T22281 convention.
*/
public static function provideDiapers() {
return [
// Format: expected, input
[ '``', '' ],
// Yeah I really hate loosely typed PHP idiocies nowadays
[ '``', null ],
// Dear codereviewer, guess what addIdentifierQuotes()
// will return with thoses:
[ '``', false ],
[ '`1`', true ],
// We never know what could happen
[ '`0`', 0 ],
[ '`1`', 1 ],
// Whatchout! Should probably use something more meaningful
[ "`'`", "'" ], # single quote
[ '`"`', '"' ], # double quote
[ '````', '`' ], # backtick
[ '``', '' ], # apostrophe (look at your encyclopedia)
// sneaky NUL bytes are lurking everywhere
[ '``', "\0" ],
[ '`xyzzy`', "\0x\0y\0z\0z\0y\0" ],
// unicode chars
[
"`\u{0001}a\u{FFFF}b`",
"\u{0001}a\u{FFFF}b"
],
[
"`\u{0001}\u{FFFF}`",
"\u{0001}\u{0000}\u{FFFF}\u{0000}"
],
[ '`☃`', '☃' ],
[ '`メインページ`', 'メインページ' ],
[ '`Басты_бет`', асты_бет' ],
// Real world:
[ '`Alix`', 'Alix' ], # while( ! $recovered ) { sleep(); }
[ '`Backtick: ```', 'Backtick: `' ],
[ '`This is a test`', 'This is a test' ],
];
}
private function getMockForViews(): IMaintainableDatabase {
$db = $this->getMockBuilder( DatabaseMysqli::class )
->disableOriginalConstructor()
@ -734,13 +668,18 @@ class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
public function testIndexAliases() {
$db = $this->getMockBuilder( DatabaseMysqli::class )
->disableOriginalConstructor()
->onlyMethods( [ 'mysqlRealEscapeString', 'dbSchema', 'tablePrefix' ] )
->onlyMethods( [ 'mysqlRealEscapeString', 'dbSchema', 'tablePrefix', 'addIdentifierQuotes' ] )
->getMock();
$db->method( 'mysqlRealEscapeString' )->willReturnCallback(
static function ( $s ) {
return str_replace( "'", "\\'", $s );
}
);
$db->method( 'addIdentifierQuotes' )->willReturnCallback(
static function ( $s ) {
return ( new MySQLPlatform() )->addIdentifierQuotes( $s );
}
);
/** @var IDatabase $db */
$db->setIndexAliases( [ 'a_b_idx' => 'a_c_idx' ] );
@ -768,13 +707,18 @@ class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
public function testTableAliases() {
$db = $this->getMockBuilder( DatabaseMysqli::class )
->disableOriginalConstructor()
->onlyMethods( [ 'mysqlRealEscapeString', 'dbSchema', 'tablePrefix' ] )
->onlyMethods( [ 'mysqlRealEscapeString', 'dbSchema', 'tablePrefix', 'addIdentifierQuotes' ] )
->getMock();
$db->method( 'mysqlRealEscapeString' )->willReturnCallback(
static function ( $s ) {
return str_replace( "'", "\\'", $s );
}
);
$db->method( 'addIdentifierQuotes' )->willReturnCallback(
static function ( $s ) {
return ( new MySQLPlatform() )->addIdentifierQuotes( $s );
}
);
/** @var IDatabase $db */
$db->setTableAliases( [

View file

@ -0,0 +1,88 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
use Wikimedia\Rdbms\Platform\MySQLPlatform;
class MySQLPlatformTest extends PHPUnit\Framework\TestCase {
use MediaWikiCoversValidator;
/**
* @dataProvider provideDiapers
* @covers \Wikimedia\Rdbms\Platform\MySQLPlatform::addIdentifierQuotes
*/
public function testAddIdentifierQuotes( $expected, $in ) {
$platform = new MySQLPlatform();
$quoted = $platform->addIdentifierQuotes( $in );
$this->assertEquals( $expected, $quoted );
}
/**
* Feeds testAddIdentifierQuotes
*
* Named per T22281 convention.
*/
public static function provideDiapers() {
return [
// Format: expected, input
[ '``', '' ],
// Yeah I really hate loosely typed PHP idiocies nowadays
[ '``', null ],
// Dear codereviewer, guess what addIdentifierQuotes()
// will return with thoses:
[ '``', false ],
[ '`1`', true ],
// We never know what could happen
[ '`0`', 0 ],
[ '`1`', 1 ],
// Whatchout! Should probably use something more meaningful
[ "`'`", "'" ], # single quote
[ '`"`', '"' ], # double quote
[ '````', '`' ], # backtick
[ '``', '' ], # apostrophe (look at your encyclopedia)
// sneaky NUL bytes are lurking everywhere
[ '``', "\0" ],
[ '`xyzzy`', "\0x\0y\0z\0z\0y\0" ],
// unicode chars
[
"`\u{0001}a\u{FFFF}b`",
"\u{0001}a\u{FFFF}b"
],
[
"`\u{0001}\u{FFFF}`",
"\u{0001}\u{0000}\u{FFFF}\u{0000}"
],
[ '`☃`', '☃' ],
[ '`メインページ`', 'メインページ' ],
[ '`Басты_бет`', асты_бет' ],
// Real world:
[ '`Alix`', 'Alix' ], # while( ! $recovered ) { sleep(); }
[ '`Backtick: ```', 'Backtick: `' ],
[ '`This is a test`', 'This is a test' ],
];
}
}