MediaWiki  1.28.1
DatabasePostgres.php
Go to the documentation of this file.
1 <?php
24 
28 class DatabasePostgres extends Database {
30  protected $port;
31 
33  protected $mLastResult = null;
35  protected $mAffectedRows = null;
36 
38  private $mInsertId = null;
40  private $numericVersion = null;
42  private $connectString;
44  private $mCoreSchema;
45 
46  public function __construct( array $params ) {
47  $this->port = isset( $params['port'] ) ? $params['port'] : false;
48  parent::__construct( $params );
49  }
50 
51  function getType() {
52  return 'postgres';
53  }
54 
55  function implicitGroupby() {
56  return false;
57  }
58 
59  function implicitOrderby() {
60  return false;
61  }
62 
63  function hasConstraint( $name ) {
64  $conn = $this->getBindingHandle();
65 
66  $sql = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n " .
67  "WHERE c.connamespace = n.oid AND conname = '" .
68  pg_escape_string( $conn, $name ) . "' AND n.nspname = '" .
69  pg_escape_string( $conn, $this->getCoreSchema() ) . "'";
70  $res = $this->doQuery( $sql );
71 
72  return $this->numRows( $res );
73  }
74 
84  function open( $server, $user, $password, $dbName ) {
85  # Test for Postgres support, to avoid suppressed fatal error
86  if ( !function_exists( 'pg_connect' ) ) {
87  throw new DBConnectionError(
88  $this,
89  "Postgres functions missing, have you compiled PHP with the --with-pgsql\n" .
90  "option? (Note: if you recently installed PHP, you may need to restart your\n" .
91  "webserver and database)\n"
92  );
93  }
94 
95  $this->mServer = $server;
96  $this->mUser = $user;
97  $this->mPassword = $password;
98  $this->mDBname = $dbName;
99 
100  $connectVars = [
101  'dbname' => $dbName,
102  'user' => $user,
103  'password' => $password
104  ];
105  if ( $server != false && $server != '' ) {
106  $connectVars['host'] = $server;
107  }
108  if ( (int)$this->port > 0 ) {
109  $connectVars['port'] = (int)$this->port;
110  }
111  if ( $this->mFlags & self::DBO_SSL ) {
112  $connectVars['sslmode'] = 1;
113  }
114 
115  $this->connectString = $this->makeConnectionString( $connectVars );
116  $this->close();
117  $this->installErrorHandler();
118 
119  try {
120  // Use new connections to let LoadBalancer/LBFactory handle reuse
121  $this->mConn = pg_connect( $this->connectString, PGSQL_CONNECT_FORCE_NEW );
122  } catch ( Exception $ex ) {
123  $this->restoreErrorHandler();
124  throw $ex;
125  }
126 
127  $phpError = $this->restoreErrorHandler();
128 
129  if ( !$this->mConn ) {
130  $this->queryLogger->debug(
131  "DB connection error\n" .
132  "Server: $server, Database: $dbName, User: $user, Password: " .
133  substr( $password, 0, 3 ) . "...\n"
134  );
135  $this->queryLogger->debug( $this->lastError() . "\n" );
136  throw new DBConnectionError( $this, str_replace( "\n", ' ', $phpError ) );
137  }
138 
139  $this->mOpened = true;
140 
141  # If called from the command-line (e.g. importDump), only show errors
142  if ( $this->cliMode ) {
143  $this->doQuery( "SET client_min_messages = 'ERROR'" );
144  }
145 
146  $this->query( "SET client_encoding='UTF8'", __METHOD__ );
147  $this->query( "SET datestyle = 'ISO, YMD'", __METHOD__ );
148  $this->query( "SET timezone = 'GMT'", __METHOD__ );
149  $this->query( "SET standard_conforming_strings = on", __METHOD__ );
150  if ( $this->getServerVersion() >= 9.0 ) {
151  $this->query( "SET bytea_output = 'escape'", __METHOD__ ); // PHP bug 53127
152  }
153 
154  $this->determineCoreSchema( $this->mSchema );
155  // The schema to be used is now in the search path; no need for explicit qualification
156  $this->mSchema = null;
157 
158  return $this->mConn;
159  }
160 
167  function selectDB( $db ) {
168  if ( $this->mDBname !== $db ) {
169  return (bool)$this->open( $this->mServer, $this->mUser, $this->mPassword, $db );
170  } else {
171  return true;
172  }
173  }
174 
176  $s = '';
177  foreach ( $vars as $name => $value ) {
178  $s .= "$name='" . str_replace( "'", "\\'", $value ) . "' ";
179  }
180 
181  return $s;
182  }
183 
189  protected function closeConnection() {
190  return $this->mConn ? pg_close( $this->mConn ) : true;
191  }
192 
193  public function doQuery( $sql ) {
194  $conn = $this->getBindingHandle();
195 
196  $sql = mb_convert_encoding( $sql, 'UTF-8' );
197  // Clear previously left over PQresult
198  while ( $res = pg_get_result( $conn ) ) {
199  pg_free_result( $res );
200  }
201  if ( pg_send_query( $conn, $sql ) === false ) {
202  throw new DBUnexpectedError( $this, "Unable to post new query to PostgreSQL\n" );
203  }
204  $this->mLastResult = pg_get_result( $conn );
205  $this->mAffectedRows = null;
206  if ( pg_result_error( $this->mLastResult ) ) {
207  return false;
208  }
209 
210  return $this->mLastResult;
211  }
212 
213  protected function dumpError() {
214  $diags = [
215  PGSQL_DIAG_SEVERITY,
216  PGSQL_DIAG_SQLSTATE,
217  PGSQL_DIAG_MESSAGE_PRIMARY,
218  PGSQL_DIAG_MESSAGE_DETAIL,
219  PGSQL_DIAG_MESSAGE_HINT,
220  PGSQL_DIAG_STATEMENT_POSITION,
221  PGSQL_DIAG_INTERNAL_POSITION,
222  PGSQL_DIAG_INTERNAL_QUERY,
223  PGSQL_DIAG_CONTEXT,
224  PGSQL_DIAG_SOURCE_FILE,
225  PGSQL_DIAG_SOURCE_LINE,
226  PGSQL_DIAG_SOURCE_FUNCTION
227  ];
228  foreach ( $diags as $d ) {
229  $this->queryLogger->debug( sprintf( "PgSQL ERROR(%d): %s\n",
230  $d, pg_result_error_field( $this->mLastResult, $d ) ) );
231  }
232  }
233 
234  function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
235  if ( $tempIgnore ) {
236  /* Check for constraint violation */
237  if ( $errno === '23505' ) {
238  parent::reportQueryError( $error, $errno, $sql, $fname, $tempIgnore );
239 
240  return;
241  }
242  }
243  /* Transaction stays in the ERROR state until rolled back */
244  if ( $this->mTrxLevel ) {
245  $ignore = $this->ignoreErrors( true );
246  $this->rollback( __METHOD__ );
247  $this->ignoreErrors( $ignore );
248  }
249  parent::reportQueryError( $error, $errno, $sql, $fname, false );
250  }
251 
252  function queryIgnore( $sql, $fname = __METHOD__ ) {
253  return $this->query( $sql, $fname, true );
254  }
255 
260  function freeResult( $res ) {
261  if ( $res instanceof ResultWrapper ) {
262  $res = $res->result;
263  }
264  MediaWiki\suppressWarnings();
265  $ok = pg_free_result( $res );
266  MediaWiki\restoreWarnings();
267  if ( !$ok ) {
268  throw new DBUnexpectedError( $this, "Unable to free Postgres result\n" );
269  }
270  }
271 
277  function fetchObject( $res ) {
278  if ( $res instanceof ResultWrapper ) {
279  $res = $res->result;
280  }
281  MediaWiki\suppressWarnings();
282  $row = pg_fetch_object( $res );
283  MediaWiki\restoreWarnings();
284  # @todo FIXME: HACK HACK HACK HACK debug
285 
286  # @todo hashar: not sure if the following test really trigger if the object
287  # fetching failed.
288  $conn = $this->getBindingHandle();
289  if ( pg_last_error( $conn ) ) {
290  throw new DBUnexpectedError(
291  $this,
292  'SQL error: ' . htmlspecialchars( pg_last_error( $conn ) )
293  );
294  }
295 
296  return $row;
297  }
298 
299  function fetchRow( $res ) {
300  if ( $res instanceof ResultWrapper ) {
301  $res = $res->result;
302  }
303  MediaWiki\suppressWarnings();
304  $row = pg_fetch_array( $res );
305  MediaWiki\restoreWarnings();
306 
307  $conn = $this->getBindingHandle();
308  if ( pg_last_error( $conn ) ) {
309  throw new DBUnexpectedError(
310  $this,
311  'SQL error: ' . htmlspecialchars( pg_last_error( $conn ) )
312  );
313  }
314 
315  return $row;
316  }
317 
318  function numRows( $res ) {
319  if ( $res instanceof ResultWrapper ) {
320  $res = $res->result;
321  }
322  MediaWiki\suppressWarnings();
323  $n = pg_num_rows( $res );
324  MediaWiki\restoreWarnings();
325 
326  $conn = $this->getBindingHandle();
327  if ( pg_last_error( $conn ) ) {
328  throw new DBUnexpectedError(
329  $this,
330  'SQL error: ' . htmlspecialchars( pg_last_error( $conn ) )
331  );
332  }
333 
334  return $n;
335  }
336 
337  function numFields( $res ) {
338  if ( $res instanceof ResultWrapper ) {
339  $res = $res->result;
340  }
341 
342  return pg_num_fields( $res );
343  }
344 
345  function fieldName( $res, $n ) {
346  if ( $res instanceof ResultWrapper ) {
347  $res = $res->result;
348  }
349 
350  return pg_field_name( $res, $n );
351  }
352 
359  function insertId() {
360  return $this->mInsertId;
361  }
362 
368  function dataSeek( $res, $row ) {
369  if ( $res instanceof ResultWrapper ) {
370  $res = $res->result;
371  }
372 
373  return pg_result_seek( $res, $row );
374  }
375 
376  function lastError() {
377  if ( $this->mConn ) {
378  if ( $this->mLastResult ) {
379  return pg_result_error( $this->mLastResult );
380  } else {
381  return pg_last_error();
382  }
383  }
384 
385  return $this->getLastPHPError() ?: 'No database connection';
386  }
387 
388  function lastErrno() {
389  if ( $this->mLastResult ) {
390  return pg_result_error_field( $this->mLastResult, PGSQL_DIAG_SQLSTATE );
391  } else {
392  return false;
393  }
394  }
395 
396  function affectedRows() {
397  if ( !is_null( $this->mAffectedRows ) ) {
398  // Forced result for simulated queries
399  return $this->mAffectedRows;
400  }
401  if ( empty( $this->mLastResult ) ) {
402  return 0;
403  }
404 
405  return pg_affected_rows( $this->mLastResult );
406  }
407 
422  function estimateRowCount( $table, $vars = '*', $conds = '',
423  $fname = __METHOD__, $options = []
424  ) {
425  $options['EXPLAIN'] = true;
426  $res = $this->select( $table, $vars, $conds, $fname, $options );
427  $rows = -1;
428  if ( $res ) {
429  $row = $this->fetchRow( $res );
430  $count = [];
431  if ( preg_match( '/rows=(\d+)/', $row[0], $count ) ) {
432  $rows = (int)$count[1];
433  }
434  }
435 
436  return $rows;
437  }
438 
448  function indexInfo( $table, $index, $fname = __METHOD__ ) {
449  $sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'";
450  $res = $this->query( $sql, $fname );
451  if ( !$res ) {
452  return null;
453  }
454  foreach ( $res as $row ) {
455  if ( $row->indexname == $this->indexName( $index ) ) {
456  return $row;
457  }
458  }
459 
460  return false;
461  }
462 
471  function indexAttributes( $index, $schema = false ) {
472  if ( $schema === false ) {
473  $schema = $this->getCoreSchema();
474  }
475  /*
476  * A subquery would be not needed if we didn't care about the order
477  * of attributes, but we do
478  */
479  $sql = <<<__INDEXATTR__
480 
481  SELECT opcname,
482  attname,
483  i.indoption[s.g] as option,
484  pg_am.amname
485  FROM
486  (SELECT generate_series(array_lower(isub.indkey,1), array_upper(isub.indkey,1)) AS g
487  FROM
488  pg_index isub
489  JOIN pg_class cis
490  ON cis.oid=isub.indexrelid
491  JOIN pg_namespace ns
492  ON cis.relnamespace = ns.oid
493  WHERE cis.relname='$index' AND ns.nspname='$schema') AS s,
494  pg_attribute,
495  pg_opclass opcls,
496  pg_am,
497  pg_class ci
498  JOIN pg_index i
499  ON ci.oid=i.indexrelid
500  JOIN pg_class ct
501  ON ct.oid = i.indrelid
502  JOIN pg_namespace n
503  ON ci.relnamespace = n.oid
504  WHERE
505  ci.relname='$index' AND n.nspname='$schema'
506  AND attrelid = ct.oid
507  AND i.indkey[s.g] = attnum
508  AND i.indclass[s.g] = opcls.oid
509  AND pg_am.oid = opcls.opcmethod
510 __INDEXATTR__;
511  $res = $this->query( $sql, __METHOD__ );
512  $a = [];
513  if ( $res ) {
514  foreach ( $res as $row ) {
515  $a[] = [
516  $row->attname,
517  $row->opcname,
518  $row->amname,
519  $row->option ];
520  }
521  } else {
522  return null;
523  }
524 
525  return $a;
526  }
527 
528  function indexUnique( $table, $index, $fname = __METHOD__ ) {
529  $sql = "SELECT indexname FROM pg_indexes WHERE tablename='{$table}'" .
530  " AND indexdef LIKE 'CREATE UNIQUE%(" .
531  $this->strencode( $this->indexName( $index ) ) .
532  ")'";
533  $res = $this->query( $sql, $fname );
534  if ( !$res ) {
535  return null;
536  }
537 
538  return $res->numRows() > 0;
539  }
540 
541  function selectSQLText(
542  $table, $vars, $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
543  ) {
544  // Change the FOR UPDATE option as necessary based on the join conditions. Then pass
545  // to the parent function to get the actual SQL text.
546  // In Postgres when using FOR UPDATE, only the main table and tables that are inner joined
547  // can be locked. That means tables in an outer join cannot be FOR UPDATE locked. Trying to
548  // do so causes a DB error. This wrapper checks which tables can be locked and adjusts it
549  // accordingly.
550  // MySQL uses "ORDER BY NULL" as an optimization hint, but that is illegal in PostgreSQL.
551  if ( is_array( $options ) ) {
552  $forUpdateKey = array_search( 'FOR UPDATE', $options, true );
553  if ( $forUpdateKey !== false && $join_conds ) {
554  unset( $options[$forUpdateKey] );
555 
556  foreach ( $join_conds as $table_cond => $join_cond ) {
557  if ( 0 === preg_match( '/^(?:LEFT|RIGHT|FULL)(?: OUTER)? JOIN$/i', $join_cond[0] ) ) {
558  $options['FOR UPDATE'][] = $table_cond;
559  }
560  }
561  }
562 
563  if ( isset( $options['ORDER BY'] ) && $options['ORDER BY'] == 'NULL' ) {
564  unset( $options['ORDER BY'] );
565  }
566  }
567 
568  return parent::selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
569  }
570 
583  function insert( $table, $args, $fname = __METHOD__, $options = [] ) {
584  if ( !count( $args ) ) {
585  return true;
586  }
587 
588  $table = $this->tableName( $table );
589  if ( !isset( $this->numericVersion ) ) {
590  $this->getServerVersion();
591  }
592 
593  if ( !is_array( $options ) ) {
594  $options = [ $options ];
595  }
596 
597  if ( isset( $args[0] ) && is_array( $args[0] ) ) {
598  $multi = true;
599  $keys = array_keys( $args[0] );
600  } else {
601  $multi = false;
602  $keys = array_keys( $args );
603  }
604 
605  // If IGNORE is set, we use savepoints to emulate mysql's behavior
606  $savepoint = $olde = null;
607  $numrowsinserted = 0;
608  if ( in_array( 'IGNORE', $options ) ) {
609  $savepoint = new SavepointPostgres( $this, 'mw', $this->queryLogger );
610  $olde = error_reporting( 0 );
611  // For future use, we may want to track the number of actual inserts
612  // Right now, insert (all writes) simply return true/false
613  }
614 
615  $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
616 
617  if ( $multi ) {
618  if ( $this->numericVersion >= 8.2 && !$savepoint ) {
619  $first = true;
620  foreach ( $args as $row ) {
621  if ( $first ) {
622  $first = false;
623  } else {
624  $sql .= ',';
625  }
626  $sql .= '(' . $this->makeList( $row ) . ')';
627  }
628  $res = (bool)$this->query( $sql, $fname, $savepoint );
629  } else {
630  $res = true;
631  $origsql = $sql;
632  foreach ( $args as $row ) {
633  $tempsql = $origsql;
634  $tempsql .= '(' . $this->makeList( $row ) . ')';
635 
636  if ( $savepoint ) {
637  $savepoint->savepoint();
638  }
639 
640  $tempres = (bool)$this->query( $tempsql, $fname, $savepoint );
641 
642  if ( $savepoint ) {
643  $bar = pg_result_error( $this->mLastResult );
644  if ( $bar != false ) {
645  $savepoint->rollback();
646  } else {
647  $savepoint->release();
648  $numrowsinserted++;
649  }
650  }
651 
652  // If any of them fail, we fail overall for this function call
653  // Note that this will be ignored if IGNORE is set
654  if ( !$tempres ) {
655  $res = false;
656  }
657  }
658  }
659  } else {
660  // Not multi, just a lone insert
661  if ( $savepoint ) {
662  $savepoint->savepoint();
663  }
664 
665  $sql .= '(' . $this->makeList( $args ) . ')';
666  $res = (bool)$this->query( $sql, $fname, $savepoint );
667  if ( $savepoint ) {
668  $bar = pg_result_error( $this->mLastResult );
669  if ( $bar != false ) {
670  $savepoint->rollback();
671  } else {
672  $savepoint->release();
673  $numrowsinserted++;
674  }
675  }
676  }
677  if ( $savepoint ) {
678  error_reporting( $olde );
679  $savepoint->commit();
680 
681  // Set the affected row count for the whole operation
682  $this->mAffectedRows = $numrowsinserted;
683 
684  // IGNORE always returns true
685  return true;
686  }
687 
688  return $res;
689  }
690 
709  function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
710  $insertOptions = [], $selectOptions = [] ) {
711  $destTable = $this->tableName( $destTable );
712 
713  if ( !is_array( $insertOptions ) ) {
714  $insertOptions = [ $insertOptions ];
715  }
716 
717  /*
718  * If IGNORE is set, we use savepoints to emulate mysql's behavior
719  * Ignore LOW PRIORITY option, since it is MySQL-specific
720  */
721  $savepoint = $olde = null;
722  $numrowsinserted = 0;
723  if ( in_array( 'IGNORE', $insertOptions ) ) {
724  $savepoint = new SavepointPostgres( $this, 'mw', $this->queryLogger );
725  $olde = error_reporting( 0 );
726  $savepoint->savepoint();
727  }
728 
729  if ( !is_array( $selectOptions ) ) {
730  $selectOptions = [ $selectOptions ];
731  }
732  list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) =
733  $this->makeSelectOptions( $selectOptions );
734  if ( is_array( $srcTable ) ) {
735  $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) );
736  } else {
737  $srcTable = $this->tableName( $srcTable );
738  }
739 
740  $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
741  " SELECT $startOpts " . implode( ',', $varMap ) .
742  " FROM $srcTable $useIndex $ignoreIndex ";
743 
744  if ( $conds != '*' ) {
745  $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
746  }
747 
748  $sql .= " $tailOpts";
749 
750  $res = (bool)$this->query( $sql, $fname, $savepoint );
751  if ( $savepoint ) {
752  $bar = pg_result_error( $this->mLastResult );
753  if ( $bar != false ) {
754  $savepoint->rollback();
755  } else {
756  $savepoint->release();
757  $numrowsinserted++;
758  }
759  error_reporting( $olde );
760  $savepoint->commit();
761 
762  // Set the affected row count for the whole operation
763  $this->mAffectedRows = $numrowsinserted;
764 
765  // IGNORE always returns true
766  return true;
767  }
768 
769  return $res;
770  }
771 
772  function tableName( $name, $format = 'quoted' ) {
773  // Replace reserved words with better ones
774  $name = $this->remappedTableName( $name );
775 
776  return parent::tableName( $name, $format );
777  }
778 
784  public function remappedTableName( $name ) {
785  if ( $name === 'user' ) {
786  return 'mwuser';
787  } elseif ( $name === 'text' ) {
788  return 'pagecontent';
789  }
790 
791  return $name;
792  }
793 
799  public function realTableName( $name, $format = 'quoted' ) {
800  return parent::tableName( $name, $format );
801  }
802 
809  function nextSequenceValue( $seqName ) {
810  $safeseq = str_replace( "'", "''", $seqName );
811  $res = $this->query( "SELECT nextval('$safeseq')" );
812  $row = $this->fetchRow( $res );
813  $this->mInsertId = $row[0];
814 
815  return $this->mInsertId;
816  }
817 
824  function currentSequenceValue( $seqName ) {
825  $safeseq = str_replace( "'", "''", $seqName );
826  $res = $this->query( "SELECT currval('$safeseq')" );
827  $row = $this->fetchRow( $res );
828  $currval = $row[0];
829 
830  return $currval;
831  }
832 
833  # Returns the size of a text field, or -1 for "unlimited"
834  function textFieldSize( $table, $field ) {
835  $table = $this->tableName( $table );
836  $sql = "SELECT t.typname as ftype,a.atttypmod as size
837  FROM pg_class c, pg_attribute a, pg_type t
838  WHERE relname='$table' AND a.attrelid=c.oid AND
839  a.atttypid=t.oid and a.attname='$field'";
840  $res = $this->query( $sql );
841  $row = $this->fetchObject( $res );
842  if ( $row->ftype == 'varchar' ) {
843  $size = $row->size - 4;
844  } else {
845  $size = $row->size;
846  }
847 
848  return $size;
849  }
850 
851  function limitResult( $sql, $limit, $offset = false ) {
852  return "$sql LIMIT $limit " . ( is_numeric( $offset ) ? " OFFSET {$offset} " : '' );
853  }
854 
855  function wasDeadlock() {
856  return $this->lastErrno() == '40P01';
857  }
858 
860  $oldName, $newName, $temporary = false, $fname = __METHOD__
861  ) {
862  $newName = $this->addIdentifierQuotes( $newName );
863  $oldName = $this->addIdentifierQuotes( $oldName );
864 
865  return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName " .
866  "(LIKE $oldName INCLUDING DEFAULTS)", $fname );
867  }
868 
869  function listTables( $prefix = null, $fname = __METHOD__ ) {
870  $eschema = $this->addQuotes( $this->getCoreSchema() );
871  $result = $this->query(
872  "SELECT tablename FROM pg_tables WHERE schemaname = $eschema", $fname );
873  $endArray = [];
874 
875  foreach ( $result as $table ) {
876  $vars = get_object_vars( $table );
877  $table = array_pop( $vars );
878  if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
879  $endArray[] = $table;
880  }
881  }
882 
883  return $endArray;
884  }
885 
886  function timestamp( $ts = 0 ) {
887  $ct = new ConvertibleTimestamp( $ts );
888 
889  return $ct->getTimestamp( TS_POSTGRES );
890  }
891 
910  function pg_array_parse( $text, &$output, $limit = false, $offset = 1 ) {
911  if ( false === $limit ) {
912  $limit = strlen( $text ) - 1;
913  $output = [];
914  }
915  if ( '{}' == $text ) {
916  return $output;
917  }
918  do {
919  if ( '{' != $text[$offset] ) {
920  preg_match( "/(\\{?\"([^\"\\\\]|\\\\.)*\"|[^,{}]+)+([,}]+)/",
921  $text, $match, 0, $offset );
922  $offset += strlen( $match[0] );
923  $output[] = ( '"' != $match[1][0]
924  ? $match[1]
925  : stripcslashes( substr( $match[1], 1, -1 ) ) );
926  if ( '},' == $match[3] ) {
927  return $output;
928  }
929  } else {
930  $offset = $this->pg_array_parse( $text, $output, $limit, $offset + 1 );
931  }
932  } while ( $limit > $offset );
933 
934  return $output;
935  }
936 
943  public function aggregateValue( $valuedata, $valuename = 'value' ) {
944  return $valuedata;
945  }
946 
950  public function getSoftwareLink() {
951  return '[{{int:version-db-postgres-url}} PostgreSQL]';
952  }
953 
961  function getCurrentSchema() {
962  $res = $this->query( "SELECT current_schema()", __METHOD__ );
963  $row = $this->fetchRow( $res );
964 
965  return $row[0];
966  }
967 
978  function getSchemas() {
979  $res = $this->query( "SELECT current_schemas(false)", __METHOD__ );
980  $row = $this->fetchRow( $res );
981  $schemas = [];
982 
983  /* PHP pgsql support does not support array type, "{a,b}" string is returned */
984 
985  return $this->pg_array_parse( $row[0], $schemas );
986  }
987 
997  function getSearchPath() {
998  $res = $this->query( "SHOW search_path", __METHOD__ );
999  $row = $this->fetchRow( $res );
1000 
1001  /* PostgreSQL returns SHOW values as strings */
1002 
1003  return explode( ",", $row[0] );
1004  }
1005 
1013  function setSearchPath( $search_path ) {
1014  $this->query( "SET search_path = " . implode( ", ", $search_path ) );
1015  }
1016 
1031  function determineCoreSchema( $desiredSchema ) {
1032  $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
1033  if ( $this->schemaExists( $desiredSchema ) ) {
1034  if ( in_array( $desiredSchema, $this->getSchemas() ) ) {
1035  $this->mCoreSchema = $desiredSchema;
1036  $this->queryLogger->debug(
1037  "Schema \"" . $desiredSchema . "\" already in the search path\n" );
1038  } else {
1044  $search_path = $this->getSearchPath();
1045  array_unshift( $search_path,
1046  $this->addIdentifierQuotes( $desiredSchema ) );
1047  $this->setSearchPath( $search_path );
1048  $this->mCoreSchema = $desiredSchema;
1049  $this->queryLogger->debug(
1050  "Schema \"" . $desiredSchema . "\" added to the search path\n" );
1051  }
1052  } else {
1053  $this->mCoreSchema = $this->getCurrentSchema();
1054  $this->queryLogger->debug(
1055  "Schema \"" . $desiredSchema . "\" not found, using current \"" .
1056  $this->mCoreSchema . "\"\n" );
1057  }
1058  /* Commit SET otherwise it will be rollbacked on error or IGNORE SELECT */
1059  $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
1060  }
1061 
1068  function getCoreSchema() {
1069  return $this->mCoreSchema;
1070  }
1071 
1075  function getServerVersion() {
1076  if ( !isset( $this->numericVersion ) ) {
1077  $conn = $this->getBindingHandle();
1078  $versionInfo = pg_version( $conn );
1079  if ( version_compare( $versionInfo['client'], '7.4.0', 'lt' ) ) {
1080  // Old client, abort install
1081  $this->numericVersion = '7.3 or earlier';
1082  } elseif ( isset( $versionInfo['server'] ) ) {
1083  // Normal client
1084  $this->numericVersion = $versionInfo['server'];
1085  } else {
1086  // Bug 16937: broken pgsql extension from PHP<5.3
1087  $this->numericVersion = pg_parameter_status( $conn, 'server_version' );
1088  }
1089  }
1090 
1091  return $this->numericVersion;
1092  }
1093 
1102  function relationExists( $table, $types, $schema = false ) {
1103  if ( !is_array( $types ) ) {
1104  $types = [ $types ];
1105  }
1106  if ( $schema === false ) {
1107  $schema = $this->getCoreSchema();
1108  }
1109  $etable = $this->addQuotes( $table );
1110  $eschema = $this->addQuotes( $schema );
1111  $sql = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
1112  . "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
1113  . "AND c.relkind IN ('" . implode( "','", $types ) . "')";
1114  $res = $this->query( $sql );
1115  $count = $res ? $res->numRows() : 0;
1116 
1117  return (bool)$count;
1118  }
1119 
1128  function tableExists( $table, $fname = __METHOD__, $schema = false ) {
1129  return $this->relationExists( $table, [ 'r', 'v' ], $schema );
1130  }
1131 
1132  function sequenceExists( $sequence, $schema = false ) {
1133  return $this->relationExists( $sequence, 'S', $schema );
1134  }
1135 
1136  function triggerExists( $table, $trigger ) {
1137  $q = <<<SQL
1138  SELECT 1 FROM pg_class, pg_namespace, pg_trigger
1139  WHERE relnamespace=pg_namespace.oid AND relkind='r'
1140  AND tgrelid=pg_class.oid
1141  AND nspname=%s AND relname=%s AND tgname=%s
1142 SQL;
1143  $res = $this->query(
1144  sprintf(
1145  $q,
1146  $this->addQuotes( $this->getCoreSchema() ),
1147  $this->addQuotes( $table ),
1148  $this->addQuotes( $trigger )
1149  )
1150  );
1151  if ( !$res ) {
1152  return null;
1153  }
1154  $rows = $res->numRows();
1155 
1156  return $rows;
1157  }
1158 
1159  function ruleExists( $table, $rule ) {
1160  $exists = $this->selectField( 'pg_rules', 'rulename',
1161  [
1162  'rulename' => $rule,
1163  'tablename' => $table,
1164  'schemaname' => $this->getCoreSchema()
1165  ]
1166  );
1167 
1168  return $exists === $rule;
1169  }
1170 
1171  function constraintExists( $table, $constraint ) {
1172  $sql = sprintf( "SELECT 1 FROM information_schema.table_constraints " .
1173  "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s",
1174  $this->addQuotes( $this->getCoreSchema() ),
1175  $this->addQuotes( $table ),
1176  $this->addQuotes( $constraint )
1177  );
1178  $res = $this->query( $sql );
1179  if ( !$res ) {
1180  return null;
1181  }
1182  $rows = $res->numRows();
1183 
1184  return $rows;
1185  }
1186 
1192  function schemaExists( $schema ) {
1193  $exists = $this->selectField( '"pg_catalog"."pg_namespace"', 1,
1194  [ 'nspname' => $schema ], __METHOD__ );
1195 
1196  return (bool)$exists;
1197  }
1198 
1204  function roleExists( $roleName ) {
1205  $exists = $this->selectField( '"pg_catalog"."pg_roles"', 1,
1206  [ 'rolname' => $roleName ], __METHOD__ );
1207 
1208  return (bool)$exists;
1209  }
1210 
1216  function fieldInfo( $table, $field ) {
1217  return PostgresField::fromText( $this, $table, $field );
1218  }
1219 
1226  function fieldType( $res, $index ) {
1227  if ( $res instanceof ResultWrapper ) {
1228  $res = $res->result;
1229  }
1230 
1231  return pg_field_type( $res, $index );
1232  }
1233 
1238  function encodeBlob( $b ) {
1239  return new PostgresBlob( pg_escape_bytea( $b ) );
1240  }
1241 
1242  function decodeBlob( $b ) {
1243  if ( $b instanceof PostgresBlob ) {
1244  $b = $b->fetch();
1245  } elseif ( $b instanceof Blob ) {
1246  return $b->fetch();
1247  }
1248 
1249  return pg_unescape_bytea( $b );
1250  }
1251 
1252  function strencode( $s ) {
1253  // Should not be called by us
1254  return pg_escape_string( $this->getBindingHandle(), $s );
1255  }
1256 
1261  function addQuotes( $s ) {
1262  $conn = $this->getBindingHandle();
1263 
1264  if ( is_null( $s ) ) {
1265  return 'NULL';
1266  } elseif ( is_bool( $s ) ) {
1267  return intval( $s );
1268  } elseif ( $s instanceof Blob ) {
1269  if ( $s instanceof PostgresBlob ) {
1270  $s = $s->fetch();
1271  } else {
1272  $s = pg_escape_bytea( $conn, $s->fetch() );
1273  }
1274  return "'$s'";
1275  }
1276 
1277  return "'" . pg_escape_string( $conn, $s ) . "'";
1278  }
1279 
1287  protected function replaceVars( $ins ) {
1288  $ins = parent::replaceVars( $ins );
1289 
1290  if ( $this->numericVersion >= 8.3 ) {
1291  // Thanks for not providing backwards-compatibility, 8.3
1292  $ins = preg_replace( "/to_tsvector\s*\(\s*'default'\s*,/", 'to_tsvector(', $ins );
1293  }
1294 
1295  if ( $this->numericVersion <= 8.1 ) { // Our minimum version
1296  $ins = str_replace( 'USING gin', 'USING gist', $ins );
1297  }
1298 
1299  return $ins;
1300  }
1301 
1310  $preLimitTail = $postLimitTail = '';
1311  $startOpts = $useIndex = $ignoreIndex = '';
1312 
1313  $noKeyOptions = [];
1314  foreach ( $options as $key => $option ) {
1315  if ( is_numeric( $key ) ) {
1316  $noKeyOptions[$option] = true;
1317  }
1318  }
1319 
1320  $preLimitTail .= $this->makeGroupByWithHaving( $options );
1321 
1322  $preLimitTail .= $this->makeOrderBy( $options );
1323 
1324  // if ( isset( $options['LIMIT'] ) ) {
1325  // $tailOpts .= $this->limitResult( '', $options['LIMIT'],
1326  // isset( $options['OFFSET'] ) ? $options['OFFSET']
1327  // : false );
1328  // }
1329 
1330  if ( isset( $options['FOR UPDATE'] ) ) {
1331  $postLimitTail .= ' FOR UPDATE OF ' .
1332  implode( ', ', array_map( [ &$this, 'tableName' ], $options['FOR UPDATE'] ) );
1333  } elseif ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
1334  $postLimitTail .= ' FOR UPDATE';
1335  }
1336 
1337  if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
1338  $startOpts .= 'DISTINCT';
1339  }
1340 
1341  return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ];
1342  }
1343 
1344  function getDBname() {
1345  return $this->mDBname;
1346  }
1347 
1348  function getServer() {
1349  return $this->mServer;
1350  }
1351 
1352  function buildConcat( $stringList ) {
1353  return implode( ' || ', $stringList );
1354  }
1355 
1356  public function buildGroupConcatField(
1357  $delimiter, $table, $field, $conds = '', $options = [], $join_conds = []
1358  ) {
1359  $fld = "array_to_string(array_agg($field)," . $this->addQuotes( $delimiter ) . ')';
1360 
1361  return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
1362  }
1363 
1369  public function buildStringCast( $field ) {
1370  return $field . '::text';
1371  }
1372 
1373  public function streamStatementEnd( &$sql, &$newLine ) {
1374  # Allow dollar quoting for function declarations
1375  if ( substr( $newLine, 0, 4 ) == '$mw$' ) {
1376  if ( $this->delimiter ) {
1377  $this->delimiter = false;
1378  } else {
1379  $this->delimiter = ';';
1380  }
1381  }
1382 
1383  return parent::streamStatementEnd( $sql, $newLine );
1384  }
1385 
1395  public function lockIsFree( $lockName, $method ) {
1396  $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
1397  $result = $this->query( "SELECT (CASE(pg_try_advisory_lock($key))
1398  WHEN 'f' THEN 'f' ELSE pg_advisory_unlock($key) END) AS lockstatus", $method );
1399  $row = $this->fetchObject( $result );
1400 
1401  return ( $row->lockstatus === 't' );
1402  }
1403 
1411  public function lock( $lockName, $method, $timeout = 5 ) {
1412  $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
1413  $loop = new WaitConditionLoop(
1414  function () use ( $lockName, $key, $timeout, $method ) {
1415  $res = $this->query( "SELECT pg_try_advisory_lock($key) AS lockstatus", $method );
1416  $row = $this->fetchObject( $res );
1417  if ( $row->lockstatus === 't' ) {
1418  parent::lock( $lockName, $method, $timeout ); // record
1419  return true;
1420  }
1421 
1422  return WaitConditionLoop::CONDITION_CONTINUE;
1423  },
1424  $timeout
1425  );
1426 
1427  return ( $loop->invoke() === $loop::CONDITION_REACHED );
1428  }
1429 
1437  public function unlock( $lockName, $method ) {
1438  $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
1439  $result = $this->query( "SELECT pg_advisory_unlock($key) as lockstatus", $method );
1440  $row = $this->fetchObject( $result );
1441 
1442  if ( $row->lockstatus === 't' ) {
1443  parent::unlock( $lockName, $method ); // record
1444  return true;
1445  }
1446 
1447  $this->queryLogger->debug( __METHOD__ . " failed to release lock\n" );
1448 
1449  return false;
1450  }
1451 
1456  private function bigintFromLockName( $lockName ) {
1457  return Wikimedia\base_convert( substr( sha1( $lockName ), 0, 15 ), 16, 10 );
1458  }
1459 }
rollback($fname=__METHOD__, $flush= '')
Rollback a transaction previously started using begin().
Definition: Database.php:2813
indexAttributes($index, $schema=false)
Returns is of attributes used in index.
fetchRow($res)
Fetch the next row from the given result object, in associative array form.
lock($lockName, $method, $timeout=5)
See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS.
Library for creating, parsing, and converting timestamps.
nextSequenceValue($seqName)
Return the next in a sequence, save the value for retrieval via insertId()
constraintExists($table, $constraint)
timestamp($ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
commit($fname=__METHOD__, $flush= '')
Commits a transaction previously started using begin().
Definition: Database.php:2752
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
string $connectString
Connect string to open a PostgreSQL connection.
the array() calling protocol came about after MediaWiki 1.4rc1.
select($table, $vars, $conds= '', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
Definition: Database.php:1250
Utility classThis allows us to distinguish a blob from a normal string and an array of strings...
Definition: Blob.php:8
addIdentifierQuotes($s)
Quotes an identifier using backticks or "double quotes" depending on the database type...
Definition: Database.php:1998
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to and or sell copies of the and to permit persons to whom the Software is furnished to do subject to the following WITHOUT WARRANTY OF ANY EXPRESS OR INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DAMAGES OR OTHER WHETHER IN AN ACTION OF TORT OR ARISING FROM
Definition: LICENSE.txt:10
streamStatementEnd(&$sql, &$newLine)
Called by sourceStream() to check if we've reached a statement end.
string $mDBname
Definition: Database.php:65
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
implicitOrderby()
Returns true if this database does an implicit order by when the column has an index For example: SEL...
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
replaceVars($ins)
Postgres specific version of replaceVars.
aggregateValue($valuedata, $valuename= 'value')
Return aggregated value function call.
__construct(array $params)
getSearchPath()
Return search patch for schemas This is different from getSchemas() since it contain magic keywords (...
$value
unlock($lockName, $method)
See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKSFROM PG DO...
Manage savepoints within a transaction.
reportQueryError($error, $errno, $sql, $fname, $tempIgnore=false)
Report a query error.
indexUnique($table, $index, $fname=__METHOD__)
decodeBlob($b)
Some DBMSs return a special placeholder object representing blob fields in result objects...
relationExists($table, $types, $schema=false)
Query whether a given relation exists (in the given schema, or the default mw one if not given) ...
triggerExists($table, $trigger)
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for tableName() and addQuotes().You will need both of them.------------------------------------------------------------------------Basic query optimisation------------------------------------------------------------------------MediaWiki developers who need to write DB queries should have some understanding of databases and the performance issues associated with them.Patches containing unacceptably slow features will not be accepted.Unindexed queries are generally not welcome in MediaWiki
getCoreSchema()
Return schema name for core application tables.
lastError()
Get a description of the last error.
currentSequenceValue($seqName)
Return the current value of a sequence.
selectDB($db)
Postgres doesn't support selectDB in the same way MySQL does.
makeOrderBy($options)
Returns an optional ORDER BY.
Definition: Database.php:1238
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message.Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item.Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page.Return false to stop further processing of the tag $reader:XMLReader object &$pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision.Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag.Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload.Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports.&$fullInterwikiPrefix:Interwiki prefix, may contain colons.&$pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable.Can be used to lazy-load the import sources list.&$importSources:The value of $wgImportSources.Modify as necessary.See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page.$context:IContextSource object &$pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect.&$title:Title object for the current page &$request:WebRequest &$ignoreRedirect:boolean to skip redirect check &$target:Title/string of redirect target &$article:Article object 'InternalParseBeforeLinks':during Parser's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InternalParseBeforeSanitize':during Parser's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings.Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not.Return true without providing an interwiki to continue interwiki search.$prefix:interwiki prefix we are looking for.&$iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user's email has been invalidated successfully.$user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification.Callee may modify $url and $query, URL will be constructed as $url.$query &$url:URL to index.php &$query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) &$article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() &$ip:IP being check &$result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from &$allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn't match your organization.$addr:The e-mail address entered by the user &$result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user &$result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we're looking for a messages file for &$file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED!Use $magicWords in a file listed in $wgExtensionMessagesFiles instead.Use this to define synonyms of magic words depending of the language &$magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces.Do not use this hook to add namespaces.Use CanonicalNamespaces for that.&$namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED!Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead.Use to define aliases of special pages names depending of the language &$specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names.&$names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page's language links.This is called in various places to allow extensions to define the effective language links for a page.$title:The page's Title.&$links:Associative array mapping language codes to prefixed links of the form"language:title".&$linkFlags:Associative array mapping prefixed links to arrays of flags.Currently unused, but planned to provide support for marking individual language links in the UI, e.g.for featured articles. 'LanguageSelector':Hook to change the language selector available on a page.$out:The output page.$cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED!Use HtmlPageLinkRendererBegin instead.Used when generating internal and interwiki links in Linker::link(), before processing starts.Return false to skip default processing and return $ret.See documentation for Linker::link() for details on the expected meanings of parameters.$skin:the Skin object $target:the Title that the link is pointing to &$html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1934
if($line===false) $args
Definition: cdb.php:64
string $mServer
Definition: Database.php:59
indexName($index)
Get the name of an index in a given table.
Definition: Database.php:1957
limitResult($sql, $limit, $offset=false)
getDBname()
Get the current DB name.
numFields($res)
Get the number of fields in a result object.
begin($fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
Definition: Database.php:2692
lastErrno()
Get the last error number.
const LIST_AND
Definition: Defines.php:35
makeGroupByWithHaving($options)
Returns an optional GROUP BY with an optional HAVING.
Definition: Database.php:1212
fieldName($res, $n)
Get a field name in a result object.
close()
Closes a database connection.
Definition: Database.php:705
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1046
queryIgnore($sql, $fname=__METHOD__)
sequenceExists($sequence, $schema=false)
$res
Definition: database.txt:21
buildGroupConcatField($delimiter, $table, $field, $conds= '', $options=[], $join_conds=[])
getSchemas()
Return list of schemas which are accessible without schema name This is list does not contain magic k...
buildConcat($stringList)
Build a concatenation list to feed into a SQL query.
duplicateTableStructure($oldName, $newName, $temporary=false, $fname=__METHOD__)
$params
determineCoreSchema($desiredSchema)
Determine default schema for the current application Adjust this session schema search path if desire...
makeList($a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
Definition: Database.php:1548
getLastPHPError()
Definition: Database.php:667
implicitGroupby()
Returns true if this database does an implicit sort when doing GROUP BY.
while(($__line=Maintenance::readconsole())!==false) print n
Definition: eval.php:64
setSearchPath($search_path)
Update search_path, values should already be sanitized Values may contain magic keywords like "$user"...
ignoreErrors($ignoreErrors=null)
Turns on (false) or off (true) the automatic generation and sending of a "we're sorry, but there has been a database error" page on database errors.
Definition: Database.php:429
selectSQLText($table, $vars, $conds= '', $fname=__METHOD__, $options=[], $join_conds=[])
The equivalent of IDatabase::select() except that the constructed SQL is returned, instead of being immediately executed.
roleExists($roleName)
Returns true if a given role (i.e.
insert($table, $args, $fname=__METHOD__, $options=[])
INSERT wrapper, inserts an array into a table.
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
affectedRows()
Get the number of rows affected by the last write query.
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition: hooks.txt:242
bigintFromLockName($lockName)
wasDeadlock()
Determines if the last failure was due to a deadlock.
float string $numericVersion
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object & $output
Definition: hooks.txt:1046
getBindingHandle()
Get the underlying binding handle, mConn.
Definition: Database.php:3419
getCurrentSchema()
Return current schema (executes SELECT current_schema()) Needs transaction.
ruleExists($table, $rule)
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined...
Definition: Setup.php:36
listTables($prefix=null, $fname=__METHOD__)
List all tables on the database.
static fromText(DatabasePostgres $db, $table, $field)
tableName($name, $format= 'quoted')
Format a table name ready for use in constructing an SQL query.
pg_array_parse($text, &$output, $limit=false, $offset=1)
Posted by cc[plus]php[at]c2se[dot]com on 25-Mar-2009 09:12 to http://www.php.net/manual/en/ref.pgsql.php.
textFieldSize($table, $field)
Returns the size of a text field, or -1 for "unlimited".
tableExists($table, $fname=__METHOD__, $schema=false)
For backward compatibility, this function checks both tables and views.
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1046
string $delimiter
Definition: Database.php:117
resource null $mConn
Database connection.
Definition: Database.php:83
open($server, $user, $password, $dbName)
Usually aborts on failure.
const TS_POSTGRES
Postgres format time.
Definition: defines.php:47
fieldType($res, $index)
pg_field_type() wrapper
$count
indexInfo($table, $index, $fname=__METHOD__)
Returns information about an index If errors are explicitly ignored, returns NULL on failure...
fieldInfo($table, $field)
mysql_fetch_field() wrapper Returns false if the field doesn't exist
selectField($table, $var, $cond= '', $fname=__METHOD__, $options=[])
A SELECT wrapper which returns a single field from a single result row.
Definition: Database.php:1061
Result wrapper for grabbing data queried from an IDatabase object.
int $mAffectedRows
The number of rows affected as an integer.
nativeInsertSelect($destTable, $srcTable, $varMap, $conds, $fname=__METHOD__, $insertOptions=[], $selectOptions=[])
INSERT SELECT wrapper $varMap must be an associative array of the form [ 'dest1' => 'source1'...
storage can be distributed across multiple and multiple web servers can use the same cache cluster *********************W A R N I N G ***********************Memcached has no security or authentication Please ensure that your server is appropriately and that the port(s) used for memcached servers are not publicly accessible.Otherwise
const DBO_SSL
Definition: defines.php:14
getServer()
Get the server hostname or IP address.
closeConnection()
Closes a database connection, if it is open Returns success, true if already closed.
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:2159
schemaExists($schema)
Query whether a given schema exists.
makeSelectOptions($options)
Various select options.
realTableName($name, $format= 'quoted')
estimateRowCount($table, $vars= '*', $conds= '', $fname=__METHOD__, $options=[])
Estimate rows in dataset Returns estimated count, based on EXPLAIN output This is not necessarily an ...
restoreErrorHandler()
Definition: Database.php:655
lockIsFree($lockName, $method)
Check to see if a named lock is available.
insertId()
Return the result of the last call to nextSequenceValue(); This must be called after nextSequenceValu...
numRows($res)
Get the number of rows in a result object.
query($sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition: Database.php:829
installErrorHandler()
Definition: Database.php:646
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:300