summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>2021-12-13 20:33:22 +0000
committerGerrit Code Review <gerrit@wikimedia.org>2021-12-13 20:33:22 +0000
commitd81f7ee11086f2cc01bc1919db46fb416225f751 (patch)
tree126f850975f42ecb8719047d013b8dd8008cd113
parent9f303dc3988814db2d3f278f7a927fecf8d56017 (diff)
parent46425659faea5cb9efcf119ce9ae2739167e8b84 (diff)
Merge "rdbms: add query timeout support to Database::select()"
-rw-r--r--includes/libs/rdbms/database/DatabaseMysqlBase.php31
-rw-r--r--includes/libs/rdbms/database/IDatabase.php3
-rw-r--r--tests/phpunit/unit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php24
3 files changed, 57 insertions, 1 deletions
diff --git a/includes/libs/rdbms/database/DatabaseMysqlBase.php b/includes/libs/rdbms/database/DatabaseMysqlBase.php
index 2ae4dfe7c9f2..190e28878534 100644
--- a/includes/libs/rdbms/database/DatabaseMysqlBase.php
+++ b/includes/libs/rdbms/database/DatabaseMysqlBase.php
@@ -1380,7 +1380,36 @@ abstract class DatabaseMysqlBase extends Database {
return 'CAST( ' . $field . ' AS SIGNED )';
}
- /**
+ public function selectSQLText(
+ $table,
+ $vars,
+ $conds = '',
+ $fname = __METHOD__,
+ $options = [],
+ $join_conds = []
+ ) {
+ $sql = parent::selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
+ // https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html
+ // https://mariadb.com/kb/en/library/aborting-statements/
+ $timeoutMsec = intval( $options['MAX_EXECUTION_TIME'] ?? 0 );
+ if ( $timeoutMsec > 0 ) {
+ list( $vendor, $number ) = $this->getMySqlServerVariant();
+ if ( $vendor === 'MariaDB' && version_compare( $number, '10.1.2', '>=' ) ) {
+ $timeoutSec = $timeoutMsec / 1000;
+ $sql = "SET STATEMENT max_statement_time=$timeoutSec FOR $sql";
+ } elseif ( $vendor === 'MySQL' && version_compare( $number, '5.7.0', '>=' ) ) {
+ $sql = preg_replace(
+ '/^SELECT(?=\s)/',
+ "SELECT /*+ MAX_EXECUTION_TIME($timeoutMsec)*/",
+ $sql
+ );
+ }
+ }
+
+ return $sql;
+ }
+
+ /*
* @return bool Whether GTID support is used (mockable for testing)
*/
protected function useGTIDs() {
diff --git a/includes/libs/rdbms/database/IDatabase.php b/includes/libs/rdbms/database/IDatabase.php
index e19dd7b8b27b..94ce6d8b290b 100644
--- a/includes/libs/rdbms/database/IDatabase.php
+++ b/includes/libs/rdbms/database/IDatabase.php
@@ -767,6 +767,9 @@ interface IDatabase {
* - EXPLAIN: In MySQL, this causes an EXPLAIN SELECT query to be run,
* instead of SELECT.
*
+ * - MAX_EXECUTION_TIME: (only in MySQL/MariaDB) maximum allowed time to
+ * run the query in milliseconds (if database supports it).
+ *
* And also the following boolean MySQL extensions, see the MySQL manual
* for documentation:
*
diff --git a/tests/phpunit/unit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php b/tests/phpunit/unit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
index 4fdd4ab732c2..1493056d2790 100644
--- a/tests/phpunit/unit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
+++ b/tests/phpunit/unit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
@@ -745,6 +745,30 @@ class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
}
/**
+ * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::selectSQLText
+ */
+ public function testMaxExecutionTime() {
+ $db = $this->getMockBuilder( DatabaseMysqli::class )
+ ->disableOriginalConstructor()
+ ->onlyMethods( [ 'getMySqlServerVariant', 'dbSchema', 'tablePrefix' ] )
+ ->getMock();
+ $db->method( 'getMySqlServerVariant' )->willReturn( [ 'MariaDB', '10.4.21' ] );
+
+ /** @var IDatabase $db */
+ $sql = $db->selectSQLText( 'image',
+ 'img_metadata',
+ '*',
+ '',
+ [ 'MAX_EXECUTION_TIME' => 1 ]
+ );
+
+ $this->assertEquals(
+ "SET STATEMENT max_statement_time=0.001 FOR SELECT img_metadata FROM `image` ",
+ $sql
+ );
+ }
+
+ /**
* @covers \Wikimedia\Rdbms\Database::streamStatementEnd
* @covers \Wikimedia\Rdbms\DatabaseMysqlBase::streamStatementEnd
*/