dd8afd683fe0e60e556eac5c2115b2c415314fd5
[lhc/web/wiklou.git] / includes / db / DatabaseMysql.php
1 <?php
2 /**
3 * This is the MySQL database abstraction layer.
4 *
5 * @file
6 * @ingroup Database
7 */
8
9 /**
10 * Database abstraction object for mySQL
11 * Inherit all methods and properties of Database::Database()
12 *
13 * @ingroup Database
14 * @see Database
15 */
16 class DatabaseMysql extends DatabaseBase {
17
18 /**
19 * @return string
20 */
21 function getType() {
22 return 'mysql';
23 }
24
25 protected function doQuery( $sql ) {
26 if( $this->bufferResults() ) {
27 $ret = mysql_query( $sql, $this->mConn );
28 } else {
29 $ret = mysql_unbuffered_query( $sql, $this->mConn );
30 }
31 return $ret;
32 }
33
34 function open( $server, $user, $password, $dbName ) {
35 global $wgAllDBsAreLocalhost;
36 wfProfileIn( __METHOD__ );
37
38 # Load mysql.so if we don't have it
39 wfDl( 'mysql' );
40
41 # Fail now
42 # Otherwise we get a suppressed fatal error, which is very hard to track down
43 if ( !function_exists( 'mysql_connect' ) ) {
44 throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" );
45 }
46
47 # Debugging hack -- fake cluster
48 if ( $wgAllDBsAreLocalhost ) {
49 $realServer = 'localhost';
50 } else {
51 $realServer = $server;
52 }
53 $this->close();
54 $this->mServer = $server;
55 $this->mUser = $user;
56 $this->mPassword = $password;
57 $this->mDBname = $dbName;
58
59 wfProfileIn("dbconnect-$server");
60
61 # The kernel's default SYN retransmission period is far too slow for us,
62 # so we use a short timeout plus a manual retry. Retrying means that a small
63 # but finite rate of SYN packet loss won't cause user-visible errors.
64 $this->mConn = false;
65 if ( ini_get( 'mysql.connect_timeout' ) <= 3 ) {
66 $numAttempts = 2;
67 } else {
68 $numAttempts = 1;
69 }
70 $this->installErrorHandler();
71 for ( $i = 0; $i < $numAttempts && !$this->mConn; $i++ ) {
72 if ( $i > 1 ) {
73 usleep( 1000 );
74 }
75 if ( $this->mFlags & DBO_PERSISTENT ) {
76 $this->mConn = mysql_pconnect( $realServer, $user, $password );
77 } else {
78 # Create a new connection...
79 $this->mConn = mysql_connect( $realServer, $user, $password, true );
80 }
81 #if ( $this->mConn === false ) {
82 #$iplus = $i + 1;
83 #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
84 #}
85 }
86 $phpError = $this->restoreErrorHandler();
87 # Always log connection errors
88 if ( !$this->mConn ) {
89 $error = $this->lastError();
90 if ( !$error ) {
91 $error = $phpError;
92 }
93 wfLogDBError( "Error connecting to {$this->mServer}: $error\n" );
94 wfDebug( "DB connection error\n" );
95 wfDebug( "Server: $server, User: $user, Password: " .
96 substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" );
97 }
98
99 wfProfileOut("dbconnect-$server");
100
101 if ( $dbName != '' && $this->mConn !== false ) {
102 wfSuppressWarnings();
103 $success = mysql_select_db( $dbName, $this->mConn );
104 wfRestoreWarnings();
105 if ( !$success ) {
106 $error = "Error selecting database $dbName on server {$this->mServer} " .
107 "from client host " . wfHostname() . "\n";
108 wfLogDBError(" Error selecting database $dbName on server {$this->mServer} \n");
109 wfDebug( $error );
110 }
111 } else {
112 # Delay USE query
113 $success = (bool)$this->mConn;
114 }
115
116 if ( $success ) {
117 // Tell the server we're communicating with it in UTF-8.
118 // This may engage various charset conversions.
119 global $wgDBmysql5;
120 if( $wgDBmysql5 ) {
121 $this->query( 'SET NAMES utf8', __METHOD__ );
122 } else {
123 $this->query( 'SET NAMES binary', __METHOD__ );
124 }
125 // Set SQL mode, default is turning them all off, can be overridden or skipped with null
126 global $wgSQLMode;
127 if ( is_string( $wgSQLMode ) ) {
128 $mode = $this->addQuotes( $wgSQLMode );
129 $this->query( "SET sql_mode = $mode", __METHOD__ );
130 }
131
132 // Turn off strict mode if it is on
133 } else {
134 $this->reportConnectionError( $phpError );
135 }
136
137 $this->mOpened = $success;
138 wfProfileOut( __METHOD__ );
139 return $success;
140 }
141
142 /**
143 * @return bool
144 */
145 function close() {
146 $this->mOpened = false;
147 if ( $this->mConn ) {
148 if ( $this->trxLevel() ) {
149 $this->commit();
150 }
151 return mysql_close( $this->mConn );
152 } else {
153 return true;
154 }
155 }
156
157 function freeResult( $res ) {
158 if ( $res instanceof ResultWrapper ) {
159 $res = $res->result;
160 }
161 wfSuppressWarnings();
162 $ok = mysql_free_result( $res );
163 wfRestoreWarnings();
164 if ( !$ok ) {
165 throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
166 }
167 }
168
169 function fetchObject( $res ) {
170 if ( $res instanceof ResultWrapper ) {
171 $res = $res->result;
172 }
173 wfSuppressWarnings();
174 $row = mysql_fetch_object( $res );
175 wfRestoreWarnings();
176 if( $this->lastErrno() ) {
177 throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
178 }
179 return $row;
180 }
181
182 function fetchRow( $res ) {
183 if ( $res instanceof ResultWrapper ) {
184 $res = $res->result;
185 }
186 wfSuppressWarnings();
187 $row = mysql_fetch_array( $res );
188 wfRestoreWarnings();
189 if ( $this->lastErrno() ) {
190 throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
191 }
192 return $row;
193 }
194
195 /**
196 * @throws DBUnexpectedError
197 * @param $res
198 * @return int
199 */
200 function numRows( $res ) {
201 if ( $res instanceof ResultWrapper ) {
202 $res = $res->result;
203 }
204 wfSuppressWarnings();
205 $n = mysql_num_rows( $res );
206 wfRestoreWarnings();
207 if( $this->lastErrno() ) {
208 throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) );
209 }
210 return $n;
211 }
212
213 /**
214 * @param $res
215 * @return int
216 */
217 function numFields( $res ) {
218 if ( $res instanceof ResultWrapper ) {
219 $res = $res->result;
220 }
221 return mysql_num_fields( $res );
222 }
223
224 /**
225 * @param $res
226 * @param $n
227 * @return string
228 */
229 function fieldName( $res, $n ) {
230 if ( $res instanceof ResultWrapper ) {
231 $res = $res->result;
232 }
233 return mysql_field_name( $res, $n );
234 }
235
236 /**
237 * @return int
238 */
239 function insertId() {
240 return mysql_insert_id( $this->mConn );
241 }
242
243 function dataSeek( $res, $row ) {
244 if ( $res instanceof ResultWrapper ) {
245 $res = $res->result;
246 }
247 return mysql_data_seek( $res, $row );
248 }
249
250 function lastErrno() {
251 if ( $this->mConn ) {
252 return mysql_errno( $this->mConn );
253 } else {
254 return mysql_errno();
255 }
256 }
257
258 function lastError() {
259 if ( $this->mConn ) {
260 # Even if it's non-zero, it can still be invalid
261 wfSuppressWarnings();
262 $error = mysql_error( $this->mConn );
263 if ( !$error ) {
264 $error = mysql_error();
265 }
266 wfRestoreWarnings();
267 } else {
268 $error = mysql_error();
269 }
270 if( $error ) {
271 $error .= ' (' . $this->mServer . ')';
272 }
273 return $error;
274 }
275
276 /**
277 * @return int
278 */
279 function affectedRows() {
280 return mysql_affected_rows( $this->mConn );
281 }
282
283 function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseMysql::replace' ) {
284 return $this->nativeReplace( $table, $rows, $fname );
285 }
286
287 /**
288 * Estimate rows in dataset
289 * Returns estimated count, based on EXPLAIN output
290 * Takes same arguments as Database::select()
291 *
292 * @return int
293 */
294 public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabaseMysql::estimateRowCount', $options = array() ) {
295 $options['EXPLAIN'] = true;
296 $res = $this->select( $table, $vars, $conds, $fname, $options );
297 if ( $res === false ) {
298 return false;
299 }
300 if ( !$this->numRows( $res ) ) {
301 return 0;
302 }
303
304 $rows = 1;
305 foreach ( $res as $plan ) {
306 $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero
307 }
308 return $rows;
309 }
310
311 /**
312 * @param $table string
313 * @param $field string
314 * @return bool|MySQLField
315 */
316 function fieldInfo( $table, $field ) {
317 $table = $this->tableName( $table );
318 $res = $this->query( "SELECT * FROM $table LIMIT 1", __METHOD__, true );
319 if ( !$res ) {
320 return false;
321 }
322 $n = mysql_num_fields( $res->result );
323 for( $i = 0; $i < $n; $i++ ) {
324 $meta = mysql_fetch_field( $res->result, $i );
325 if( $field == $meta->name ) {
326 return new MySQLField($meta);
327 }
328 }
329 return false;
330 }
331
332 /**
333 * Get information about an index into an object
334 * Returns false if the index does not exist
335 *
336 * @return false|array
337 */
338 function indexInfo( $table, $index, $fname = 'DatabaseMysql::indexInfo' ) {
339 # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not.
340 # SHOW INDEX should work for 3.x and up:
341 # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
342 $table = $this->tableName( $table );
343 $index = $this->indexName( $index );
344 $sql = 'SHOW INDEX FROM ' . $table;
345 $res = $this->query( $sql, $fname );
346
347 if ( !$res ) {
348 return null;
349 }
350
351 $result = array();
352
353 foreach ( $res as $row ) {
354 if ( $row->Key_name == $index ) {
355 $result[] = $row;
356 }
357 }
358
359 return empty( $result ) ? false : $result;
360 }
361
362 /**
363 * @param $db
364 * @return bool
365 */
366 function selectDB( $db ) {
367 $this->mDBname = $db;
368 return mysql_select_db( $db, $this->mConn );
369 }
370
371 /**
372 * @param $s string
373 *
374 * @return string
375 */
376 function strencode( $s ) {
377 $sQuoted = mysql_real_escape_string( $s, $this->mConn );
378
379 if($sQuoted === false) {
380 $this->ping();
381 $sQuoted = mysql_real_escape_string( $s, $this->mConn );
382 }
383 return $sQuoted;
384 }
385
386 /**
387 * MySQL uses `backticks` for identifier quoting instead of the sql standard "double quotes".
388 *
389 * @param $s string
390 *
391 * @return string
392 */
393 public function addIdentifierQuotes( $s ) {
394 return "`" . $this->strencode( $s ) . "`";
395 }
396
397 /**
398 * @param $name string
399 * @return bool
400 */
401 public function isQuotedIdentifier( $name ) {
402 return strlen( $name ) && $name[0] == '`' && substr( $name, -1, 1 ) == '`';
403 }
404
405 /**
406 * @return bool
407 */
408 function ping() {
409 $ping = mysql_ping( $this->mConn );
410 if ( $ping ) {
411 return true;
412 }
413
414 mysql_close( $this->mConn );
415 $this->mOpened = false;
416 $this->mConn = false;
417 $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
418 return true;
419 }
420
421 /**
422 * Returns slave lag.
423 *
424 * On MySQL 4.1.9 and later, this will do a SHOW SLAVE STATUS
425 *
426 * @return int
427 */
428 function getLag() {
429 if ( !is_null( $this->mFakeSlaveLag ) ) {
430 wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" );
431 return $this->mFakeSlaveLag;
432 }
433
434 return $this->getLagFromSlaveStatus();
435 }
436
437 /**
438 * @return bool|int
439 */
440 function getLagFromSlaveStatus() {
441 $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
442 if ( !$res ) {
443 return false;
444 }
445 $row = $res->fetchObject();
446 if ( !$row ) {
447 return false;
448 }
449 if ( strval( $row->Seconds_Behind_Master ) === '' ) {
450 return false;
451 } else {
452 return intval( $row->Seconds_Behind_Master );
453 }
454 }
455
456 /**
457 * @deprecated in 1.19, use getLagFromSlaveStatus
458 *
459 * @return bool|int
460 */
461 function getLagFromProcesslist() {
462 $res = $this->query( 'SHOW PROCESSLIST', __METHOD__ );
463 if( !$res ) {
464 return false;
465 }
466 # Find slave SQL thread
467 foreach( $res as $row ) {
468 /* This should work for most situations - when default db
469 * for thread is not specified, it had no events executed,
470 * and therefore it doesn't know yet how lagged it is.
471 *
472 * Relay log I/O thread does not select databases.
473 */
474 if ( $row->User == 'system user' &&
475 $row->State != 'Waiting for master to send event' &&
476 $row->State != 'Connecting to master' &&
477 $row->State != 'Queueing master event to the relay log' &&
478 $row->State != 'Waiting for master update' &&
479 $row->State != 'Requesting binlog dump' &&
480 $row->State != 'Waiting to reconnect after a failed master event read' &&
481 $row->State != 'Reconnecting after a failed master event read' &&
482 $row->State != 'Registering slave on master'
483 ) {
484 # This is it, return the time (except -ve)
485 if ( $row->Time > 0x7fffffff ) {
486 return false;
487 } else {
488 return $row->Time;
489 }
490 }
491 }
492 return false;
493 }
494
495 /**
496 * Wait for the slave to catch up to a given master position.
497 *
498 * @param $pos DBMasterPos object
499 * @param $timeout Integer: the maximum number of seconds to wait for synchronisation
500 */
501 function masterPosWait( DBMasterPos $pos, $timeout ) {
502 $fname = 'DatabaseBase::masterPosWait';
503 wfProfileIn( $fname );
504
505 # Commit any open transactions
506 if ( $this->mTrxLevel ) {
507 $this->commit();
508 }
509
510 if ( !is_null( $this->mFakeSlaveLag ) ) {
511 $status = parent::masterPosWait( $pos, $timeout );
512 wfProfileOut( $fname );
513 return $status;
514 }
515
516 # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
517 $encFile = $this->addQuotes( $pos->file );
518 $encPos = intval( $pos->pos );
519 $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)";
520 $res = $this->doQuery( $sql );
521
522 if ( $res && $row = $this->fetchRow( $res ) ) {
523 wfProfileOut( $fname );
524 return $row[0];
525 } else {
526 wfProfileOut( $fname );
527 return false;
528 }
529 }
530
531 /**
532 * Get the position of the master from SHOW SLAVE STATUS
533 *
534 * @return MySQLMasterPos|false
535 */
536 function getSlavePos() {
537 if ( !is_null( $this->mFakeSlaveLag ) ) {
538 return parent::getSlavePos();
539 }
540
541 $res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' );
542 $row = $this->fetchObject( $res );
543
544 if ( $row ) {
545 $pos = isset( $row->Exec_master_log_pos ) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos;
546 return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos );
547 } else {
548 return false;
549 }
550 }
551
552 /**
553 * Get the position of the master from SHOW MASTER STATUS
554 *
555 * @return MySQLMasterPos|false
556 */
557 function getMasterPos() {
558 if ( $this->mFakeMaster ) {
559 return parent::getMasterPos();
560 }
561
562 $res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' );
563 $row = $this->fetchObject( $res );
564
565 if ( $row ) {
566 return new MySQLMasterPos( $row->File, $row->Position );
567 } else {
568 return false;
569 }
570 }
571
572 /**
573 * @return string
574 */
575 function getServerVersion() {
576 return mysql_get_server_info( $this->mConn );
577 }
578
579 /**
580 * @param $index
581 * @return string
582 */
583 function useIndexClause( $index ) {
584 return "FORCE INDEX (" . $this->indexName( $index ) . ")";
585 }
586
587 /**
588 * @return string
589 */
590 function lowPriorityOption() {
591 return 'LOW_PRIORITY';
592 }
593
594 /**
595 * @return string
596 */
597 public static function getSoftwareLink() {
598 return '[http://www.mysql.com/ MySQL]';
599 }
600
601 function standardSelectDistinct() {
602 return false;
603 }
604
605 public function setSessionOptions( array $options ) {
606 if ( isset( $options['connTimeout'] ) ) {
607 $timeout = (int)$options['connTimeout'];
608 $this->query( "SET net_read_timeout=$timeout" );
609 $this->query( "SET net_write_timeout=$timeout" );
610 }
611 }
612
613 public function lock( $lockName, $method, $timeout = 5 ) {
614 $lockName = $this->addQuotes( $lockName );
615 $result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method );
616 $row = $this->fetchObject( $result );
617
618 if( $row->lockstatus == 1 ) {
619 return true;
620 } else {
621 wfDebug( __METHOD__." failed to acquire lock\n" );
622 return false;
623 }
624 }
625
626 /**
627 * FROM MYSQL DOCS: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock
628 */
629 public function unlock( $lockName, $method ) {
630 $lockName = $this->addQuotes( $lockName );
631 $result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method );
632 $row = $this->fetchObject( $result );
633 return $row->lockstatus;
634 }
635
636 public function lockTables( $read, $write, $method, $lowPriority = true ) {
637 $items = array();
638
639 foreach( $write as $table ) {
640 $tbl = $this->tableName( $table ) .
641 ( $lowPriority ? ' LOW_PRIORITY' : '' ) .
642 ' WRITE';
643 $items[] = $tbl;
644 }
645 foreach( $read as $table ) {
646 $items[] = $this->tableName( $table ) . ' READ';
647 }
648 $sql = "LOCK TABLES " . implode( ',', $items );
649 $this->query( $sql, $method );
650 }
651
652 public function unlockTables( $method ) {
653 $this->query( "UNLOCK TABLES", $method );
654 }
655
656 /**
657 * Get search engine class. All subclasses of this
658 * need to implement this if they wish to use searching.
659 *
660 * @return String
661 */
662 public function getSearchEngine() {
663 return 'SearchMySQL';
664 }
665
666 public function setBigSelects( $value = true ) {
667 if ( $value === 'default' ) {
668 if ( $this->mDefaultBigSelects === null ) {
669 # Function hasn't been called before so it must already be set to the default
670 return;
671 } else {
672 $value = $this->mDefaultBigSelects;
673 }
674 } elseif ( $this->mDefaultBigSelects === null ) {
675 $this->mDefaultBigSelects = (bool)$this->selectField( false, '@@sql_big_selects' );
676 }
677 $encValue = $value ? '1' : '0';
678 $this->query( "SET sql_big_selects=$encValue", __METHOD__ );
679 }
680
681 /**
682 * DELETE where the condition is a join. MySql uses multi-table deletes.
683 */
684 function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'DatabaseBase::deleteJoin' ) {
685 if ( !$conds ) {
686 throw new DBUnexpectedError( $this, 'DatabaseBase::deleteJoin() called with empty $conds' );
687 }
688
689 $delTable = $this->tableName( $delTable );
690 $joinTable = $this->tableName( $joinTable );
691 $sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar ";
692
693 if ( $conds != '*' ) {
694 $sql .= ' AND ' . $this->makeList( $conds, LIST_AND );
695 }
696
697 return $this->query( $sql, $fname );
698 }
699
700 /**
701 * Determines if the last failure was due to a deadlock
702 *
703 * @return bool
704 */
705 function wasDeadlock() {
706 return $this->lastErrno() == 1213;
707 }
708
709 /**
710 * Determines if the last failure was due to a lock timeout
711 *
712 * @return bool
713 */
714 function wasLockTimeout() {
715 return $this->lastErrno() == 1205;
716 }
717
718 /**
719 * Determines if the last query error was something that should be dealt
720 * with by pinging the connection and reissuing the query
721 *
722 * @return bool
723 */
724 function wasErrorReissuable() {
725 return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
726 }
727
728 /**
729 * Determines if the last failure was due to the database being read-only.
730 *
731 * @return bool
732 */
733 function wasReadOnlyError() {
734 return $this->lastErrno() == 1223 ||
735 ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
736 }
737
738 function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseMysql::duplicateTableStructure' ) {
739 $tmp = $temporary ? 'TEMPORARY ' : '';
740 $newName = $this->addIdentifierQuotes( $newName );
741 $oldName = $this->addIdentifierQuotes( $oldName );
742 $query = "CREATE $tmp TABLE $newName (LIKE $oldName)";
743 $this->query( $query, $fname );
744 }
745
746 /**
747 * List all tables on the database
748 *
749 * @param $prefix Only show tables with this prefix, e.g. mw_
750 * @param $fname String: calling function name
751 */
752 function listTables( $prefix = null, $fname = 'DatabaseMysql::listTables' ) {
753 $result = $this->query( "SHOW TABLES", $fname);
754
755 $endArray = array();
756
757 foreach( $result as $table ) {
758 $vars = get_object_vars($table);
759 $table = array_pop( $vars );
760
761 if( !$prefix || strpos( $table, $prefix ) === 0 ) {
762 $endArray[] = $table;
763 }
764 }
765
766 return $endArray;
767 }
768
769 /**
770 * @param $tableName
771 * @param $fName string
772 * @return bool|ResultWrapper
773 */
774 public function dropTable( $tableName, $fName = 'DatabaseMysql::dropTable' ) {
775 if( !$this->tableExists( $tableName, $fName ) ) {
776 return false;
777 }
778 return $this->query( "DROP TABLE IF EXISTS " . $this->tableName( $tableName ), $fName );
779 }
780
781 /**
782 * @return array
783 */
784 protected function getDefaultSchemaVars() {
785 $vars = parent::getDefaultSchemaVars();
786 $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $GLOBALS['wgDBTableOptions'] );
787 $vars['wgDBTableOptions'] = str_replace( 'CHARSET=mysql4', 'CHARSET=binary', $GLOBALS['wgDBTableOptions'] );
788 return $vars;
789 }
790
791 /**
792 * Get status information from SHOW STATUS in an associative array
793 *
794 * @return array
795 */
796 function getMysqlStatus( $which = "%" ) {
797 $res = $this->query( "SHOW STATUS LIKE '{$which}'" );
798 $status = array();
799
800 foreach ( $res as $row ) {
801 $status[$row->Variable_name] = $row->Value;
802 }
803
804 return $status;
805 }
806
807 }
808
809 /**
810 * Legacy support: Database == DatabaseMysql
811 *
812 * @deprecated in 1.16
813 */
814 class Database extends DatabaseMysql {}
815
816 /**
817 * Utility class.
818 * @ingroup Database
819 */
820 class MySQLField implements Field {
821 private $name, $tablename, $default, $max_length, $nullable,
822 $is_pk, $is_unique, $is_multiple, $is_key, $type;
823
824 function __construct ( $info ) {
825 $this->name = $info->name;
826 $this->tablename = $info->table;
827 $this->default = $info->def;
828 $this->max_length = $info->max_length;
829 $this->nullable = !$info->not_null;
830 $this->is_pk = $info->primary_key;
831 $this->is_unique = $info->unique_key;
832 $this->is_multiple = $info->multiple_key;
833 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
834 $this->type = $info->type;
835 }
836
837 /**
838 * @return string
839 */
840 function name() {
841 return $this->name;
842 }
843
844 /**
845 * @return string
846 */
847 function tableName() {
848 return $this->tableName;
849 }
850
851 function type() {
852 return $this->type;
853 }
854
855 /**
856 * @return bool
857 */
858 function isNullable() {
859 return $this->nullable;
860 }
861
862 function defaultValue() {
863 return $this->default;
864 }
865
866 /**
867 * @return bool
868 */
869 function isKey() {
870 return $this->is_key;
871 }
872
873 /**
874 * @return bool
875 */
876 function isMultipleKey() {
877 return $this->is_multiple;
878 }
879 }
880
881 class MySQLMasterPos implements DBMasterPos {
882 var $file, $pos;
883
884 function __construct( $file, $pos ) {
885 $this->file = $file;
886 $this->pos = $pos;
887 }
888
889 function __toString() {
890 return "{$this->file}/{$this->pos}";
891 }
892 }