use Wikimedia\AtEase\AtEase;
26use Wikimedia\IPUtils;
66 private$sessionLastAutoRowId;
88 foreach( [
'KeyPath',
'CertPath',
'CAFile',
'CAPath',
'Ciphers'] as $name ) {
90 if( isset( $params[$var] ) ) {
91$this->$var = $params[$var];
94$this->utf8Mode = !empty( $params[
'utf8Mode'] );
95parent::__construct( $params );
103$params[
'topologyRole'],
105$params[
'srvCache'],
106$params[
'lagDetectionMethod'] ??
'Seconds_Behind_Master',
107$params[
'lagDetectionOptions'] ?? [],
108!empty( $params[
'useGTIDs'] )
120 protected function open( $server, $user, $password, $db, $schema, $tablePrefix ) {
121$this->
close( __METHOD__ );
123 if( $schema !==
null) {
129$this->conn = $this->mysqlConnect( $server, $user, $password, $db );
130}
catch( RuntimeException $e ) {
136 if( !$this->conn ) {
142( $db !==
'') ? $db :
null,
149 if( !$this->
flagsHolder->getFlag( self::DBO_GAUGE ) ) {
151$set[] =
'group_concat_max_len = 262144';
156 if( !is_int( $val ) && !is_float( $val ) ) {
159$set[] = $this->
platform->addIdentifierQuotes( $var ) .
' = '. $val;
164$sql =
'SET '. implode(
', ', $set );
170 if( $qs->res ===
false) {
174}
catch( RuntimeException $e ) {
184__CLASS__ .
": domain '{$domain->getId()}' has a schema component" 190 if( $database ===
null) {
201 if( $database !== $this->
getDBname() ) {
203$query =
new Query( $sql, self::QUERY_CHANGE_TRX,
'USE');
204$qs = $this->
executeQuery( $query, __METHOD__, self::QUERY_CHANGE_TRX );
205 if( $qs->res ===
false) {
213$this->
platform->setCurrentDomain( $domain );
224$error = $this->mysqlError( $this->conn );
226$error = $this->mysqlError();
229$error = $this->mysqlError() ?: $this->lastConnectError;
237$row = $this->replicationReporter->getReplicationSafetyInfo( $this, $fname );
239 if( $row->binlog_format ===
'ROW') {
243 if( isset( $selectOptions[
'LIMIT'] ) ) {
254in_array(
'NO_AUTO_COLUMNS', $insertOptions ) ||
255(
int)$row->innodb_autoinc_lock_mode === 0
261 if( $this->conn && $this->conn->warning_count ) {
263$warnings = $this->conn->get_warnings();
264$done = $warnings ===
false;
266 if( in_array( $warnings->errno, [
278 'Insert returned unacceptable warning: '. $warnings->message,
284$done = !$warnings->next();
298$conds = $this->
platform->normalizeConditions( $conds, $fname );
299$column = $this->
platform->extractSingleFieldFromList( $var );
300 if( is_string( $column ) && !in_array( $column, [
'*',
'1'] ) ) {
301$conds[] =
"$column IS NOT NULL";
304$options[
'EXPLAIN'] =
true;
305$res = $this->
select( $tables, $var, $conds, $fname, $options, $join_conds );
306 if( $res ===
false) {
309 if( !$res->numRows() ) {
314 foreach( $res as $plan ) {
315$rows *= $plan->rows > 0 ? $plan->rows : 1;
323[ $db, $pt ] = $this->platform->getDatabaseAndTableIdentifier( $table );
324 if( isset( $this->sessionTempTables[$db][$pt] ) ) {
328 return(
bool)$this->newSelectQueryBuilder()
330->from(
'information_schema.tables')
332 'table_schema'=> $db,
346 "SELECT * FROM ". $this->tableName( $table ) .
" LIMIT 1",
347self::QUERY_SILENCE_ERRORS | self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE,
350$res = $this->query( $query, __METHOD__ );
355 '@phan-var MysqliResultWrapper $res';
356 return$res->getInternalFieldInfo( $field );
360 public function indexInfo( $table, $index, $fname = __METHOD__ ) {
363 'SHOW INDEX FROM '. $this->tableName( $table ),
364self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE,
367$res = $this->query( $query, $fname );
369 foreach( $res as $row ) {
370 if( $row->Key_name === $index ) {
371 return[
'unique'=> !$row->Non_unique ];
383 return$this->mysqlRealEscapeString( $s );
389$flags = self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE;
390$query =
new Query(
"SELECT @@GLOBAL.read_only AS Value", $flags,
'SELECT');
391$res = $this->query( $query, __METHOD__ );
392$row = $res->fetchObject();
394 return$row && $row->Value && $row->Value !==
'OFF';
401[ $variant ] = $this->getMySqlServerVariant();
402 if( $variant ===
'MariaDB') {
403 return '[{{int:version-db-mariadb-url}} MariaDB]';
406 return '[{{int:version-db-mysql-url}} MySQL]';
412 private functiongetMySqlServerVariant() {
413$version = $this->getServerVersion();
419$parts = explode(
'-', $version, 2 );
421$suffix = $parts[1] ??
'';
422 if( str_contains( $suffix,
'MariaDB') || str_contains( $suffix,
'-maria-') ) {
423$vendor =
'MariaDB';
428 return[ $vendor, $number ];
437$version = $this->conn->server_info;
439str_starts_with( $version,
'5.5.5-') &&
440( str_contains( $version,
'MariaDB') || str_contains( $version,
'-maria-') )
442$version = substr( $version, strlen(
'5.5.5-') );
448$sqlAssignments = [];
450 if( isset( $options[
'connTimeout'] ) ) {
451$encTimeout = (int)$options[
'connTimeout'];
452$sqlAssignments[] =
"net_read_timeout=$encTimeout";
453$sqlAssignments[] =
"net_write_timeout=$encTimeout";
455 if( isset( $options[
'groupConcatMaxLen'] ) ) {
456$maxLength = (int)$options[
'groupConcatMaxLen'];
457$sqlAssignments[] =
"group_concat_max_len=$maxLength";
460 if( $sqlAssignments ) {
462 'SET '. implode(
', ', $sqlAssignments ),
463self::QUERY_CHANGE_TRX | self::QUERY_CHANGE_NONE,
466$this->query( $query, __METHOD__ );
476 if( preg_match(
'/^DELIMITER\s+(\S+)/i', $newLine, $m ) ) {
477$this->delimiter = $m[1];
481 returnparent::streamStatementEnd( $sql, $newLine );
486$query =
new Query( $this->platform->lockIsFreeSQLText( $lockName ), self::QUERY_CHANGE_LOCKS,
'SELECT');
487$res = $this->query( $query, $method );
488$row = $res->fetchObject();
490 return( $row->unlocked == 1 );
494 public function doLock(
string$lockName,
string$method,
int$timeout ) {
495$query =
new Query( $this->platform->lockSQLText( $lockName, $timeout ), self::QUERY_CHANGE_LOCKS,
'SELECT');
496$res = $this->query( $query, $method );
497$row = $res->fetchObject();
499 return( $row->acquired !==
null) ? (float)$row->acquired :
null;
503 public function doUnlock(
string$lockName,
string$method ) {
504$query =
new Query( $this->platform->unlockSQLText( $lockName ), self::QUERY_CHANGE_LOCKS,
'SELECT');
505$res = $this->query( $query, $method );
506$row = $res->fetchObject();
508 return( $row->released == 1 );
515$releaseLockFields = [];
516 foreach( $this->sessionNamedLocks as $name => $info ) {
517$encName = $this->addQuotes( $this->platform->makeLockName( $name ) );
518$releaseLockFields[] =
"RELEASE_LOCK($encName)";
520 if( $releaseLockFields ) {
521$sql =
'SELECT '. implode(
',', $releaseLockFields );
522$flags = self::QUERY_CHANGE_LOCKS | self::QUERY_NO_RETRY;
523$query =
new Query( $sql, $flags,
'SELECT');
524$qs = $this->executeQuery( $query, __METHOD__, $flags );
525 if( $qs->res ===
false) {
526$this->reportQueryError( $qs->message, $qs->code, $sql, $fname,
true);
532 public function upsert( $table, array $rows, $uniqueKeys, array $set, $fname = __METHOD__ ) {
533$identityKey = $this->platform->normalizeUpsertParams( $uniqueKeys, $rows );
537$this->platform->assertValidUpsertSetArray( $set, $identityKey, $rows );
539$encTable = $this->tableName( $table );
540[ $sqlColumns, $sqlTuples ] = $this->platform->makeInsertLists( $rows );
541$sqlColumnAssignments = $this->makeList( $set, self::LIST_SET );
547 "INSERT INTO $encTable ".
548 "($sqlColumns) VALUES $sqlTuples ".
549 "ON DUPLICATE KEY UPDATE $sqlColumnAssignments";
550$query =
new Query( $sql, self::QUERY_CHANGE_ROWS,
'INSERT', $table );
551$this->query( $query, $fname );
553$this->lastQueryAffectedRows = min( $this->lastQueryAffectedRows, count( $rows ) );
557 public function replace( $table, $uniqueKeys, $rows, $fname = __METHOD__ ) {
558$this->platform->normalizeUpsertParams( $uniqueKeys, $rows );
562$encTable = $this->tableName( $table );
563[ $sqlColumns, $sqlTuples ] = $this->platform->makeInsertLists( $rows );
565$sql =
"REPLACE INTO $encTable ($sqlColumns) VALUES $sqlTuples";
568$query =
new Query( $sql, self::QUERY_CHANGE_ROWS,
'REPLACE', $table );
569$this->query( $query, $fname );
571$this->lastQueryAffectedRows = min( $this->lastQueryAffectedRows, count( $rows ) );
579 returnin_array( $errno, [ 2013, 2006, 2003, 1927, 1053 ],
true);
587 returnin_array( $errno, [ 3024, 2062, 1969, 1028 ],
true);
596[ 3024, 1969, 1022, 1062, 1216, 1217, 1137, 1146, 1051, 1054 ],
609$oldName, $newName, $temporary =
false, $fname = __METHOD__
611$tmp = $temporary ?
'TEMPORARY ':
'';
612$newNameQuoted = $this->addIdentifierQuotes( $newName );
613$oldNameQuoted = $this->addIdentifierQuotes( $oldName );
616 "CREATE $tmp TABLE $newNameQuoted (LIKE $oldNameQuoted)",
617self::QUERY_PSEUDO_PERMANENT | self::QUERY_CHANGE_SCHEMA,
618$temporary ?
'CREATE TEMPORARY':
'CREATE',
622 return$this->query( $query, $fname );
632 public function listTables( $prefix =
null, $fname = __METHOD__ ) {
633$qb = $this->newSelectQueryBuilder()
634->select(
'table_name')
635->from(
'information_schema.tables')
637 'table_schema'=> $this->currentDomain->getDatabase(),
638 'table_type'=>
'BASE TABLE' 641 if( $prefix !==
null&& $prefix !==
'') {
642$qb->andWhere( $this->expr(
646 return$qb->fetchFieldValues();
658$sql = parent::selectSQLText( $tables, $vars, $conds, $fname, $options, $join_conds );
661$timeoutMsec = intval( $options[
'MAX_EXECUTION_TIME'] ?? 0 );
662 if( $timeoutMsec > 0 ) {
663[ $vendor, $number ] = $this->getMySqlServerVariant();
664 if( $vendor ===
'MariaDB'&& version_compare( $number,
'10.1.2',
'>=') ) {
665$timeoutSec = $timeoutMsec / 1000;
666$sql =
"SET STATEMENT max_statement_time=$timeoutSec FOR $sql";
667} elseif ( $vendor ===
'MySQL'&& version_compare( $number,
'5.7.0',
'>=') ) {
670 "SELECT /*+ MAX_EXECUTION_TIME($timeoutMsec)*/",
680$conn = $this->getBindingHandle();
683AtEase::suppressWarnings();
684$res = $conn->query( $sql );
685AtEase::restoreWarnings();
687$insertId = (int)$conn->insert_id;
688$this->lastQueryInsertId = $insertId;
689$this->sessionLastAutoRowId = $insertId ?: $this->sessionLastAutoRowId;
693$conn->affected_rows,
707 private functionmysqlConnect( $server, $user, $password, $db ) {
708 if( !function_exists(
'mysqli_init') ) {
709 throw$this->newExceptionAfterConnectError(
710 "MySQLi functions missing, have you compiled PHP with the --with-mysqli option?" 715mysqli_report( MYSQLI_REPORT_OFF );
727$hostAndPort = IPUtils::splitHostAndPort( $server );
728 if( $hostAndPort ) {
729$realServer = $hostAndPort[0];
730 if( $hostAndPort[1] ) {
731$port = $hostAndPort[1];
733} elseif ( substr_count( $server,
':/') == 1 ) {
736[ $realServer, $socket ] = explode(
':', $server, 2 );
738$realServer = $server;
741$mysqli = mysqli_init();
745$flags = MYSQLI_CLIENT_FOUND_ROWS;
747$flags |= MYSQLI_CLIENT_SSL;
756 if( $this->getFlag( self::DBO_COMPRESS ) ) {
757$flags |= MYSQLI_CLIENT_COMPRESS;
759 if( $this->getFlag( self::DBO_PERSISTENT ) ) {
760$realServer =
'p:'. $realServer;
763 if( $this->utf8Mode ) {
766$mysqli->options( MYSQLI_SET_CHARSET_NAME,
'utf8');
768$mysqli->options( MYSQLI_SET_CHARSET_NAME,
'binary');
771$mysqli->options( MYSQLI_OPT_CONNECT_TIMEOUT, $this->connectTimeout ?: 3 );
772 if( $this->receiveTimeout ) {
773$mysqli->options( MYSQLI_OPT_READ_TIMEOUT, $this->receiveTimeout );
777$ok = $mysqli->real_connect( $realServer, $user, $password, $db, $port, $socket, $flags );
779 return$ok ? $mysqli :
null;
784 return( $this->conn instanceof mysqli ) ? mysqli_close( $this->conn ) :
true;
789 return$this->sessionLastAutoRowId;
794$this->sessionLastAutoRowId = 0;
799 if( $this->lastEmulatedInsertId ===
null) {
800$conn = $this->getBindingHandle();
802$this->lastEmulatedInsertId = (int)$conn->insert_id;
805 return$this->lastEmulatedInsertId;
812 if( $this->conn instanceof mysqli ) {
813 return$this->conn->errno;
815 returnmysqli_connect_errno();
823 private functionmysqlError( $conn =
null) {
824 if( $conn ===
null) {
825 return(
string)mysqli_connect_error();
834 private functionmysqlRealEscapeString( $s ): string {
835$conn = $this->getBindingHandle();
837 return$conn->real_escape_string( (
string)$s );
Class to handle database/schema/prefix specifications for IDatabase.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4