MediaWiki  master
DatabaseOracle.php
Go to the documentation of this file.
1 <?php
33 
37 class DatabaseOracle extends Database {
39  protected $mLastResult = null;
40 
42  protected $mAffectedRows;
43 
45  private $ignoreDupValOnIndex = false;
46 
48  private $sequenceData = null;
49 
51  private $defaultCharset = 'AL32UTF8';
52 
54  private $mFieldInfoCache = [];
55 
57  private $keywordTableMap = [];
58 
64  function __construct( array $params ) {
65  $this->keywordTableMap = $params['keywordTableMap'] ?? [];
66  $params['tablePrefix'] = strtoupper( $params['tablePrefix'] );
67  parent::__construct( $params );
68  }
69 
70  function __destruct() {
71  if ( $this->opened ) {
72  Wikimedia\suppressWarnings();
73  $this->close();
74  Wikimedia\restoreWarnings();
75  }
76  }
77 
78  function getType() {
79  return 'oracle';
80  }
81 
82  function implicitGroupby() {
83  return false;
84  }
85 
86  function implicitOrderby() {
87  return false;
88  }
89 
90  protected function open( $server, $user, $password, $dbName, $schema, $tablePrefix ) {
91  if ( !function_exists( 'oci_connect' ) ) {
92  throw new DBConnectionError(
93  $this,
94  "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n " .
95  "(Note: if you recently installed PHP, you may need to restart your webserver\n " .
96  "and database)\n" );
97  }
98 
99  $this->close();
100  $this->user = $user;
101  $this->password = $password;
102  if ( !$server ) {
103  // Backward compatibility (server used to be null and TNS was supplied in dbname)
104  $this->server = $dbName;
105  $realDatabase = $user;
106  } else {
107  // $server now holds the TNS endpoint
108  $this->server = $server;
109  // $dbName is schema name if different from username
110  $realDatabase = $dbName ?: $user;
111  }
112 
113  if ( !strlen( $user ) ) { # e.g. the class is being loaded
114  return null;
115  }
116 
117  $session_mode = ( $this->flags & DBO_SYSDBA ) ? OCI_SYSDBA : OCI_DEFAULT;
118 
119  Wikimedia\suppressWarnings();
120  if ( $this->flags & DBO_PERSISTENT ) {
121  $this->conn = oci_pconnect(
122  $this->user,
123  $this->password,
124  $this->server,
125  $this->defaultCharset,
126  $session_mode
127  );
128  } elseif ( $this->flags & DBO_DEFAULT ) {
129  $this->conn = oci_new_connect(
130  $this->user,
131  $this->password,
132  $this->server,
133  $this->defaultCharset,
134  $session_mode
135  );
136  } else {
137  $this->conn = oci_connect(
138  $this->user,
139  $this->password,
140  $this->server,
141  $this->defaultCharset,
142  $session_mode
143  );
144  }
145  Wikimedia\restoreWarnings();
146 
147  if ( $this->user != $realDatabase ) {
148  // change current schema in session
149  $this->selectDB( $realDatabase );
150  } else {
151  $this->currentDomain = new DatabaseDomain(
152  $realDatabase,
153  null,
154  $tablePrefix
155  );
156  }
157 
158  if ( !$this->conn ) {
159  throw new DBConnectionError( $this, $this->lastError() );
160  }
161 
162  $this->opened = true;
163 
164  # removed putenv calls because they interfere with the system globaly
165  $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
166  $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
167  $this->doQuery( 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=\'.,\'' );
168 
169  return (bool)$this->conn;
170  }
171 
177  protected function closeConnection() {
178  return oci_close( $this->conn );
179  }
180 
181  function execFlags() {
182  return $this->trxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
183  }
184 
189  protected function doQuery( $sql ) {
190  if ( !mb_check_encoding( (string)$sql, 'UTF-8' ) ) {
191  throw new DBUnexpectedError( $this, "SQL encoding is invalid\n$sql" );
192  }
193 
194  // handle some oracle specifics
195  // remove AS column/table/subquery namings
196  if ( !$this->getFlag( DBO_DDLMODE ) ) {
197  $sql = preg_replace( '/ as /i', ' ', $sql );
198  }
199 
200  // Oracle has issues with UNION clause if the statement includes LOB fields
201  // So we do a UNION ALL and then filter the results array with array_unique
202  $union_unique = ( preg_match( '/\/\* UNION_UNIQUE \*\/ /', $sql ) != 0 );
203  // EXPLAIN syntax in Oracle is EXPLAIN PLAN FOR and it return nothing
204  // you have to select data from plan table after explain
205  $explain_id = MWTimestamp::getLocalInstance()->format( 'dmYHis' );
206 
207  $sql = preg_replace(
208  '/^EXPLAIN /',
209  'EXPLAIN PLAN SET STATEMENT_ID = \'' . $explain_id . '\' FOR',
210  $sql,
211  1,
212  $explain_count
213  );
214 
215  Wikimedia\suppressWarnings();
216 
217  $this->mLastResult = $stmt = oci_parse( $this->conn, $sql );
218  if ( $stmt === false ) {
219  $e = oci_error( $this->conn );
220  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
221 
222  return false;
223  }
224 
225  if ( !oci_execute( $stmt, $this->execFlags() ) ) {
226  $e = oci_error( $stmt );
227  if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
228  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
229 
230  return false;
231  }
232  }
233 
234  Wikimedia\restoreWarnings();
235 
236  if ( $explain_count > 0 ) {
237  return $this->doQuery( 'SELECT id, cardinality "ROWS" FROM plan_table ' .
238  'WHERE statement_id = \'' . $explain_id . '\'' );
239  } elseif ( oci_statement_type( $stmt ) == 'SELECT' ) {
240  return new ORAResult( $this, $stmt, $union_unique );
241  } else {
242  $this->mAffectedRows = oci_num_rows( $stmt );
243 
244  return true;
245  }
246  }
247 
248  function queryIgnore( $sql, $fname = '' ) {
249  return $this->query( $sql, $fname, true );
250  }
251 
256  function freeResult( $res ) {
257  if ( $res instanceof ResultWrapper ) {
258  $res = $res->result;
259  }
260 
261  $res->free();
262  }
263 
268  function fetchObject( $res ) {
269  if ( $res instanceof ResultWrapper ) {
270  $res = $res->result;
271  }
272 
273  return $res->fetchObject();
274  }
275 
280  function fetchRow( $res ) {
281  if ( $res instanceof ResultWrapper ) {
282  $res = $res->result;
283  }
284 
285  return $res->fetchRow();
286  }
287 
292  function numRows( $res ) {
293  if ( $res instanceof ResultWrapper ) {
294  $res = $res->result;
295  }
296 
297  return $res->numRows();
298  }
299 
304  function numFields( $res ) {
305  if ( $res instanceof ResultWrapper ) {
306  $res = $res->result;
307  }
308 
309  return $res->numFields();
310  }
311 
312  function fieldName( $stmt, $n ) {
313  return oci_field_name( $stmt, $n );
314  }
315 
316  function insertId() {
317  $res = $this->query( "SELECT lastval_pkg.getLastval FROM dual" );
318  $row = $this->fetchRow( $res );
319  return is_null( $row[0] ) ? null : (int)$row[0];
320  }
321 
326  function dataSeek( $res, $row ) {
327  if ( $res instanceof ORAResult ) {
328  $res->seek( $row );
329  } else {
330  $res->result->seek( $row );
331  }
332  }
333 
334  function lastError() {
335  if ( $this->conn === false ) {
336  $e = oci_error();
337  } else {
338  $e = oci_error( $this->conn );
339  }
340 
341  return $e['message'];
342  }
343 
344  function lastErrno() {
345  if ( $this->conn === false ) {
346  $e = oci_error();
347  } else {
348  $e = oci_error( $this->conn );
349  }
350 
351  return $e['code'];
352  }
353 
354  protected function fetchAffectedRowCount() {
355  return $this->mAffectedRows;
356  }
357 
366  function indexInfo( $table, $index, $fname = __METHOD__ ) {
367  return false;
368  }
369 
370  function indexUnique( $table, $index, $fname = __METHOD__ ) {
371  return false;
372  }
373 
374  function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
375  if ( !count( $a ) ) {
376  return true;
377  }
378 
379  if ( !is_array( $options ) ) {
380  $options = [ $options ];
381  }
382 
383  if ( in_array( 'IGNORE', $options ) ) {
384  $this->ignoreDupValOnIndex = true;
385  }
386 
387  if ( !is_array( reset( $a ) ) ) {
388  $a = [ $a ];
389  }
390 
391  foreach ( $a as &$row ) {
392  $this->insertOneRow( $table, $row, $fname );
393  }
394 
395  if ( in_array( 'IGNORE', $options ) ) {
396  $this->ignoreDupValOnIndex = false;
397  }
398 
399  return true;
400  }
401 
402  private function fieldBindStatement( $table, $col, &$val, $includeCol = false ) {
403  $col_info = $this->fieldInfoMulti( $table, $col );
404  $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
405 
406  $bind = '';
407  if ( is_numeric( $col ) ) {
408  $bind = $val;
409  $val = null;
410 
411  return $bind;
412  } elseif ( $includeCol ) {
413  $bind = "$col = ";
414  }
415 
416  if ( $val == '' && $val !== 0 && $col_type != 'BLOB' && $col_type != 'CLOB' ) {
417  $val = null;
418  }
419 
420  if ( $val === 'NULL' ) {
421  $val = null;
422  }
423 
424  if ( $val === null ) {
425  if (
426  $col_info != false &&
427  $col_info->isNullable() == 0 &&
428  $col_info->defaultValue() != null
429  ) {
430  $bind .= 'DEFAULT';
431  } else {
432  $bind .= 'NULL';
433  }
434  } else {
435  $bind .= ':' . $col;
436  }
437 
438  return $bind;
439  }
440 
448  private function insertOneRow( $table, $row, $fname ) {
449  $table = $this->tableName( $table );
450  // "INSERT INTO tables (a, b, c)"
451  $sql = "INSERT INTO " . $table . " (" . implode( ',', array_keys( $row ) ) . ')';
452  $sql .= " VALUES (";
453 
454  // for each value, append ":key"
455  $first = true;
456  foreach ( $row as $col => &$val ) {
457  if ( !$first ) {
458  $sql .= ', ';
459  } else {
460  $first = false;
461  }
462  if ( $this->isQuotedIdentifier( $val ) ) {
463  $sql .= $this->removeIdentifierQuotes( $val );
464  unset( $row[$col] );
465  } else {
466  $sql .= $this->fieldBindStatement( $table, $col, $val );
467  }
468  }
469  $sql .= ')';
470 
471  $this->mLastResult = $stmt = oci_parse( $this->conn, $sql );
472  if ( $stmt === false ) {
473  $e = oci_error( $this->conn );
474  $this->reportQueryError( $e['message'], $e['code'], $sql, $fname );
475 
476  return false;
477  }
478  foreach ( $row as $col => &$val ) {
479  $col_info = $this->fieldInfoMulti( $table, $col );
480  $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
481 
482  if ( $val === null ) {
483  // do nothing ... null was inserted in statement creation
484  } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
485  if ( is_object( $val ) ) {
486  $val = $val->fetch();
487  }
488 
489  // backward compatibility
490  if (
491  preg_match( '/^timestamp.*/i', $col_type ) == 1 &&
492  strtolower( $val ) == 'infinity'
493  ) {
494  $val = $this->getInfinity();
495  }
496 
497  $val = $this->getVerifiedUTF8( $val );
498  if ( oci_bind_by_name( $stmt, ":$col", $val, -1, SQLT_CHR ) === false ) {
499  $e = oci_error( $stmt );
500  $this->reportQueryError( $e['message'], $e['code'], $sql, $fname );
501 
502  return false;
503  }
504  } else {
506  $lob[$col] = oci_new_descriptor( $this->conn, OCI_D_LOB );
507  if ( $lob[$col] === false ) {
508  $e = oci_error( $stmt );
509  throw new DBUnexpectedError(
510  $this,
511  "Cannot create LOB descriptor: " . $e['message']
512  );
513  }
514 
515  if ( is_object( $val ) ) {
516  $val = $val->fetch();
517  }
518 
519  if ( $col_type == 'BLOB' ) {
520  $lob[$col]->writeTemporary( $val, OCI_TEMP_BLOB );
521  oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_BLOB );
522  } else {
523  $lob[$col]->writeTemporary( $val, OCI_TEMP_CLOB );
524  oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB );
525  }
526  }
527  }
528 
529  Wikimedia\suppressWarnings();
530 
531  if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
532  $e = oci_error( $stmt );
533  if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
534  $this->reportQueryError( $e['message'], $e['code'], $sql, $fname );
535 
536  return false;
537  } else {
538  $this->mAffectedRows = oci_num_rows( $stmt );
539  }
540  } else {
541  $this->mAffectedRows = oci_num_rows( $stmt );
542  }
543 
544  Wikimedia\restoreWarnings();
545 
546  if ( isset( $lob ) ) {
547  foreach ( $lob as $lob_v ) {
548  $lob_v->free();
549  }
550  }
551 
552  if ( !$this->trxLevel ) {
553  oci_commit( $this->conn );
554  }
555 
556  return oci_free_statement( $stmt );
557  }
558 
559  function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
560  $insertOptions = [], $selectOptions = [], $selectJoinConds = []
561  ) {
562  $destTable = $this->tableName( $destTable );
563 
564  $sequenceData = $this->getSequenceData( $destTable );
565  if ( $sequenceData !== false &&
566  !isset( $varMap[$sequenceData['column']] )
567  ) {
568  $varMap[$sequenceData['column']] =
569  'GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')';
570  }
571 
572  // count-alias subselect fields to avoid abigious definition errors
573  $i = 0;
574  foreach ( $varMap as &$val ) {
575  $val .= ' field' . $i;
576  $i++;
577  }
578 
579  $selectSql = $this->selectSQLText(
580  $srcTable,
581  array_values( $varMap ),
582  $conds,
583  $fname,
584  $selectOptions,
585  $selectJoinConds
586  );
587 
588  $sql = "INSERT INTO $destTable (" .
589  implode( ',', array_keys( $varMap ) ) . ') ' . $selectSql;
590 
591  if ( in_array( 'IGNORE', $insertOptions ) ) {
592  $this->ignoreDupValOnIndex = true;
593  }
594 
595  $this->query( $sql, $fname );
596 
597  if ( in_array( 'IGNORE', $insertOptions ) ) {
598  $this->ignoreDupValOnIndex = false;
599  }
600  }
601 
602  public function upsert( $table, array $rows, $uniqueIndexes, array $set,
603  $fname = __METHOD__
604  ) {
605  if ( $rows === [] ) {
606  return true; // nothing to do
607  }
608 
609  if ( !is_array( reset( $rows ) ) ) {
610  $rows = [ $rows ];
611  }
612 
613  $sequenceData = $this->getSequenceData( $table );
614  if ( $sequenceData !== false ) {
615  // add sequence column to each list of columns, when not set
616  foreach ( $rows as &$row ) {
617  if ( !isset( $row[$sequenceData['column']] ) ) {
618  $row[$sequenceData['column']] =
619  $this->addIdentifierQuotes( 'GET_SEQUENCE_VALUE(\'' .
620  $sequenceData['sequence'] . '\')' );
621  }
622  }
623  }
624 
625  return parent::upsert( $table, $rows, $uniqueIndexes, $set, $fname );
626  }
627 
628  public function tableName( $name, $format = 'quoted' ) {
629  // Replace reserved words with better ones
630  $name = $this->remappedTableName( $name );
631 
632  return strtoupper( parent::tableName( $name, $format ) );
633  }
634 
639  public function remappedTableName( $name ) {
640  return $this->keywordTableMap[$name] ?? $name;
641  }
642 
643  function tableNameInternal( $name ) {
644  $name = $this->tableName( $name );
645 
646  return preg_replace( '/.*\.(.*)/', '$1', $name );
647  }
648 
655  private function getSequenceData( $table ) {
656  if ( $this->sequenceData == null ) {
657  $dbname = $this->currentDomain->getDatabase();
658  $prefix = $this->currentDomain->getTablePrefix();
659  // See https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions040.htm
660  $decodeArgs = [ 'atc.table_name' ]; // the switch
661  foreach ( $this->keywordTableMap as $reserved => $alternative ) {
662  $search = strtoupper( $prefix . $alternative ); // case
663  $replace = strtoupper( $prefix . $reserved ); // result
664  $decodeArgs[] = $this->addQuotes( $search );
665  $decodeArgs[] = $this->addQuotes( $replace );
666  }
667  $decodeArgs[] = [ 'atc.table_name' ]; // default
668  $decodeArgs = implode( ', ', $decodeArgs );
669 
670  $result = $this->doQuery(
671  "SELECT lower(asq.sequence_name), lower(atc.table_name), lower(atc.column_name)
672  FROM all_sequences asq, all_tab_columns atc
673  WHERE decode({$decodeArgs}) || '_' ||
674  atc.column_name || '_SEQ' = '{$prefix}' || asq.sequence_name
675  AND asq.sequence_owner = upper('{$dbname}')
676  AND atc.owner = upper('{$dbname}')"
677  );
678 
679  while ( ( $row = $result->fetchRow() ) !== false ) {
680  $this->sequenceData[$row[1]] = [
681  'sequence' => $row[0],
682  'column' => $row[2]
683  ];
684  }
685  }
686  $table = strtolower( $this->removeIdentifierQuotes( $this->tableName( $table ) ) );
687 
688  return $this->sequenceData[$table] ?? false;
689  }
690 
698  function textFieldSize( $table, $field ) {
699  $fieldInfoData = $this->fieldInfo( $table, $field );
700 
701  return $fieldInfoData->maxLength();
702  }
703 
704  function limitResult( $sql, $limit, $offset = false ) {
705  if ( $offset === false ) {
706  $offset = 0;
707  }
708 
709  return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < (1 + $limit + $offset)";
710  }
711 
712  function encodeBlob( $b ) {
713  return new Blob( $b );
714  }
715 
716  function unionQueries( $sqls, $all ) {
717  $glue = ' UNION ALL ';
718 
719  return 'SELECT * ' . ( $all ? '' : '/* UNION_UNIQUE */ ' ) .
720  'FROM (' . implode( $glue, $sqls ) . ')';
721  }
722 
723  function wasDeadlock() {
724  return $this->lastErrno() == 'OCI-00060';
725  }
726 
727  function duplicateTableStructure( $oldName, $newName, $temporary = false,
728  $fname = __METHOD__
729  ) {
730  $temporary = $temporary ? 'TRUE' : 'FALSE';
731  $tablePrefix = $this->currentDomain->getTablePrefix();
732 
733  $newName = strtoupper( $newName );
734  $oldName = strtoupper( $oldName );
735 
736  $tabName = substr( $newName, strlen( $tablePrefix ) );
737  $oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) );
738  $newPrefix = strtoupper( $tablePrefix );
739 
740  return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', " .
741  "'$oldPrefix', '$newPrefix', $temporary ); END;" );
742  }
743 
744  function listTables( $prefix = null, $fname = __METHOD__ ) {
745  $listWhere = '';
746  if ( !empty( $prefix ) ) {
747  $listWhere = ' AND table_name LIKE \'' . strtoupper( $prefix ) . '%\'';
748  }
749 
750  $owner = strtoupper( $this->getDBname() );
751  $result = $this->doQuery( "SELECT table_name FROM all_tables " .
752  "WHERE owner='$owner' AND table_name NOT LIKE '%!_IDX\$_' ESCAPE '!' $listWhere" );
753 
754  // dirty code ... i know
755  $endArray = [];
756  $endArray[] = strtoupper( $prefix . 'MWUSER' );
757  $endArray[] = strtoupper( $prefix . 'PAGE' );
758  $endArray[] = strtoupper( $prefix . 'IMAGE' );
759  $fixedOrderTabs = $endArray;
760  while ( ( $row = $result->fetchRow() ) !== false ) {
761  if ( !in_array( $row['table_name'], $fixedOrderTabs ) ) {
762  $endArray[] = $row['table_name'];
763  }
764  }
765 
766  return $endArray;
767  }
768 
769  public function dropTable( $tableName, $fName = __METHOD__ ) {
770  $tableName = $this->tableName( $tableName );
771  if ( !$this->tableExists( $tableName ) ) {
772  return false;
773  }
774 
775  return $this->doQuery( "DROP TABLE $tableName CASCADE CONSTRAINTS PURGE" );
776  }
777 
778  public function timestamp( $ts = 0 ) {
779  $t = new ConvertibleTimestamp( $ts );
780  // Let errors bubble up to avoid putting garbage in the DB
781  return $t->getTimestamp( TS_ORACLE );
782  }
783 
791  public function aggregateValue( $valuedata, $valuename = 'value' ) {
792  return $valuedata;
793  }
794 
798  public function getSoftwareLink() {
799  return '[{{int:version-db-oracle-url}} Oracle]';
800  }
801 
805  function getServerVersion() {
806  // better version number, fallback on driver
807  $rset = $this->doQuery(
808  'SELECT version FROM product_component_version ' .
809  'WHERE UPPER(product) LIKE \'ORACLE DATABASE%\''
810  );
811  $row = $rset->fetchRow();
812  if ( !$row ) {
813  return oci_server_version( $this->conn );
814  }
815 
816  return $row['version'];
817  }
818 
826  function indexExists( $table, $index, $fname = __METHOD__ ) {
827  $table = $this->tableName( $table );
828  $table = strtoupper( $this->removeIdentifierQuotes( $table ) );
829  $index = strtoupper( $index );
830  $owner = strtoupper( $this->getDBname() );
831  $sql = "SELECT 1 FROM all_indexes WHERE owner='$owner' AND index_name='{$table}_{$index}'";
832  $res = $this->doQuery( $sql );
833  if ( $res ) {
834  $count = $res->numRows();
835  $res->free();
836  } else {
837  $count = 0;
838  }
839 
840  return $count != 0;
841  }
842 
849  function tableExists( $table, $fname = __METHOD__ ) {
850  $table = $this->tableName( $table );
851  $table = $this->addQuotes( strtoupper( $this->removeIdentifierQuotes( $table ) ) );
852  $owner = $this->addQuotes( strtoupper( $this->getDBname() ) );
853  $sql = "SELECT 1 FROM all_tables WHERE owner=$owner AND table_name=$table";
854  $res = $this->doQuery( $sql );
855  if ( $res && $res->numRows() > 0 ) {
856  $exists = true;
857  } else {
858  $exists = false;
859  }
860 
861  $res->free();
862 
863  return $exists;
864  }
865 
876  private function fieldInfoMulti( $table, $field ) {
877  $field = strtoupper( $field );
878  if ( is_array( $table ) ) {
879  $table = array_map( [ $this, 'tableNameInternal' ], $table );
880  $tableWhere = 'IN (';
881  foreach ( $table as &$singleTable ) {
882  $singleTable = $this->removeIdentifierQuotes( $singleTable );
883  if ( isset( $this->mFieldInfoCache["$singleTable.$field"] ) ) {
884  return $this->mFieldInfoCache["$singleTable.$field"];
885  }
886  $tableWhere .= '\'' . $singleTable . '\',';
887  }
888  $tableWhere = rtrim( $tableWhere, ',' ) . ')';
889  } else {
890  $table = $this->removeIdentifierQuotes( $this->tableNameInternal( $table ) );
891  if ( isset( $this->mFieldInfoCache["$table.$field"] ) ) {
892  return $this->mFieldInfoCache["$table.$field"];
893  }
894  $tableWhere = '= \'' . $table . '\'';
895  }
896 
897  $fieldInfoStmt = oci_parse(
898  $this->conn,
899  'SELECT * FROM wiki_field_info_full WHERE table_name ' .
900  $tableWhere . ' and column_name = \'' . $field . '\''
901  );
902  if ( oci_execute( $fieldInfoStmt, $this->execFlags() ) === false ) {
903  $e = oci_error( $fieldInfoStmt );
904  $this->reportQueryError( $e['message'], $e['code'], 'fieldInfo QUERY', __METHOD__ );
905 
906  return false;
907  }
908  $res = new ORAResult( $this, $fieldInfoStmt );
909  if ( $res->numRows() == 0 ) {
910  if ( is_array( $table ) ) {
911  foreach ( $table as &$singleTable ) {
912  $this->mFieldInfoCache["$singleTable.$field"] = false;
913  }
914  } else {
915  $this->mFieldInfoCache["$table.$field"] = false;
916  }
917  $fieldInfoTemp = null;
918  } else {
919  $fieldInfoTemp = new ORAField( $res->fetchRow() );
920  $table = $fieldInfoTemp->tableName();
921  $this->mFieldInfoCache["$table.$field"] = $fieldInfoTemp;
922  }
923  $res->free();
924 
925  return $fieldInfoTemp;
926  }
927 
934  function fieldInfo( $table, $field ) {
935  if ( is_array( $table ) ) {
936  throw new DBUnexpectedError(
937  $this,
938  'DatabaseOracle::fieldInfo called with table array!'
939  );
940  }
941 
942  return $this->fieldInfoMulti( $table, $field );
943  }
944 
945  protected function doBegin( $fname = __METHOD__ ) {
946  $this->trxLevel = 1;
947  $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' );
948  }
949 
950  protected function doCommit( $fname = __METHOD__ ) {
951  if ( $this->trxLevel ) {
952  $ret = oci_commit( $this->conn );
953  if ( !$ret ) {
954  throw new DBUnexpectedError( $this, $this->lastError() );
955  }
956  $this->trxLevel = 0;
957  $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
958  }
959  }
960 
961  protected function doRollback( $fname = __METHOD__ ) {
962  if ( $this->trxLevel ) {
963  oci_rollback( $this->conn );
964  $this->trxLevel = 0;
965  $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
966  }
967  }
968 
969  function sourceStream(
970  $fp,
971  callable $lineCallback = null,
972  callable $resultCallback = null,
973  $fname = __METHOD__, callable $inputCallback = null
974  ) {
975  $cmd = '';
976  $done = false;
977  $dollarquote = false;
978 
979  $replacements = [];
980  // Defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}';
981  while ( !feof( $fp ) ) {
982  if ( $lineCallback ) {
983  $lineCallback();
984  }
985  $line = trim( fgets( $fp, 1024 ) );
986  $sl = strlen( $line ) - 1;
987 
988  if ( $sl < 0 ) {
989  continue;
990  }
991  if ( $line[0] == '-' && $line[1] == '-' ) {
992  continue;
993  }
994 
995  // Allow dollar quoting for function declarations
996  if ( substr( $line, 0, 8 ) == '/*$mw$*/' ) {
997  if ( $dollarquote ) {
998  $dollarquote = false;
999  $line = str_replace( '/*$mw$*/', '', $line ); // remove dollarquotes
1000  $done = true;
1001  } else {
1002  $dollarquote = true;
1003  }
1004  } elseif ( !$dollarquote ) {
1005  if ( $line[$sl] == ';' && ( $sl < 2 || $line[$sl - 1] != ';' ) ) {
1006  $done = true;
1007  $line = substr( $line, 0, $sl );
1008  }
1009  }
1010 
1011  if ( $cmd != '' ) {
1012  $cmd .= ' ';
1013  }
1014  $cmd .= "$line\n";
1015 
1016  if ( $done ) {
1017  $cmd = str_replace( ';;', ";", $cmd );
1018  if ( strtolower( substr( $cmd, 0, 6 ) ) == 'define' ) {
1019  if ( preg_match( '/^define\s*([^\s=]*)\s*=\s*\'\{\$([^\}]*)\}\'/', $cmd, $defines ) ) {
1020  $replacements[$defines[2]] = $defines[1];
1021  }
1022  } else {
1023  foreach ( $replacements as $mwVar => $scVar ) {
1024  $cmd = str_replace( '&' . $scVar . '.', '`{$' . $mwVar . '}`', $cmd );
1025  }
1026 
1027  $cmd = $this->replaceVars( $cmd );
1028  if ( $inputCallback ) {
1029  $inputCallback( $cmd );
1030  }
1031  $res = $this->doQuery( $cmd );
1032  if ( $resultCallback ) {
1033  call_user_func( $resultCallback, $res, $this );
1034  }
1035 
1036  if ( $res === false ) {
1037  $err = $this->lastError();
1038 
1039  return "Query \"{$cmd}\" failed with error code \"$err\".\n";
1040  }
1041  }
1042 
1043  $cmd = '';
1044  $done = false;
1045  }
1046  }
1047 
1048  return true;
1049  }
1050 
1051  protected function doSelectDomain( DatabaseDomain $domain ) {
1052  if ( $domain->getSchema() !== null ) {
1053  // We use the *database* aspect of $domain for schema, not the domain schema
1054  throw new DBExpectedError( $this, __CLASS__ . ": domain schemas are not supported." );
1055  }
1056 
1057  $database = $domain->getDatabase();
1058  if ( $database === null || $database === $this->user ) {
1059  // Backward compatibility
1060  $this->currentDomain = $domain;
1061 
1062  return true;
1063  }
1064 
1065  // https://docs.oracle.com/javadb/10.8.3.0/ref/rrefsqlj32268.html
1066  $encDatabase = $this->addIdentifierQuotes( strtoupper( $database ) );
1067  $sql = "ALTER SESSION SET CURRENT_SCHEMA=$encDatabase";
1068  $stmt = oci_parse( $this->conn, $sql );
1069  Wikimedia\suppressWarnings();
1070  $success = oci_execute( $stmt );
1071  Wikimedia\restoreWarnings();
1072  if ( $success ) {
1073  // Update that domain fields on success (no exception thrown)
1074  $this->currentDomain = $domain;
1075  } else {
1076  $e = oci_error( $stmt );
1077  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1078  }
1079 
1080  return true;
1081  }
1082 
1083  function strencode( $s ) {
1084  return str_replace( "'", "''", $s );
1085  }
1086 
1087  function addQuotes( $s ) {
1088  return "'" . $this->strencode( $this->getVerifiedUTF8( $s ) ) . "'";
1089  }
1090 
1091  public function addIdentifierQuotes( $s ) {
1092  if ( !$this->getFlag( DBO_DDLMODE ) ) {
1093  $s = '/*Q*/' . $s;
1094  }
1095 
1096  return $s;
1097  }
1098 
1099  public function removeIdentifierQuotes( $s ) {
1100  return strpos( $s, '/*Q*/' ) === false ? $s : substr( $s, 5 );
1101  }
1102 
1103  public function isQuotedIdentifier( $s ) {
1104  return strpos( $s, '/*Q*/' ) !== false;
1105  }
1106 
1107  private function wrapFieldForWhere( $table, &$col, &$val ) {
1108  $col_info = $this->fieldInfoMulti( $table, $col );
1109  $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
1110  if ( $col_type == 'CLOB' ) {
1111  $col = 'TO_CHAR(' . $col . ')';
1112  $val = $this->getVerifiedUTF8( $val );
1113  } elseif ( $col_type == 'VARCHAR2' ) {
1114  $val = $this->getVerifiedUTF8( $val );
1115  }
1116  }
1117 
1118  private function wrapConditionsForWhere( $table, $conds, $parentCol = null ) {
1119  $conds2 = [];
1120  foreach ( $conds as $col => $val ) {
1121  if ( is_array( $val ) ) {
1122  $conds2[$col] = $this->wrapConditionsForWhere( $table, $val, $col );
1123  } else {
1124  if ( is_numeric( $col ) && $parentCol != null ) {
1125  $this->wrapFieldForWhere( $table, $parentCol, $val );
1126  } else {
1127  $this->wrapFieldForWhere( $table, $col, $val );
1128  }
1129  $conds2[$col] = $val;
1130  }
1131  }
1132 
1133  return $conds2;
1134  }
1135 
1136  function selectRow( $table, $vars, $conds, $fname = __METHOD__,
1137  $options = [], $join_conds = []
1138  ) {
1139  if ( is_array( $conds ) ) {
1140  $conds = $this->wrapConditionsForWhere( $table, $conds );
1141  }
1142 
1143  return parent::selectRow( $table, $vars, $conds, $fname, $options, $join_conds );
1144  }
1145 
1155  $preLimitTail = $postLimitTail = '';
1156  $startOpts = '';
1157 
1158  $noKeyOptions = [];
1159  foreach ( $options as $key => $option ) {
1160  if ( is_numeric( $key ) ) {
1161  $noKeyOptions[$option] = true;
1162  }
1163  }
1164 
1165  $preLimitTail .= $this->makeGroupByWithHaving( $options );
1166 
1167  $preLimitTail .= $this->makeOrderBy( $options );
1168 
1169  if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
1170  $postLimitTail .= ' FOR UPDATE';
1171  }
1172 
1173  if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
1174  $startOpts .= 'DISTINCT';
1175  }
1176 
1177  if ( isset( $options['USE INDEX'] ) && !is_array( $options['USE INDEX'] ) ) {
1178  $useIndex = $this->useIndexClause( $options['USE INDEX'] );
1179  } else {
1180  $useIndex = '';
1181  }
1182 
1183  if ( isset( $options['IGNORE INDEX'] ) && !is_array( $options['IGNORE INDEX'] ) ) {
1184  $ignoreIndex = $this->ignoreIndexClause( $options['IGNORE INDEX'] );
1185  } else {
1186  $ignoreIndex = '';
1187  }
1188 
1189  return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ];
1190  }
1191 
1192  public function delete( $table, $conds, $fname = __METHOD__ ) {
1194 
1195  if ( is_array( $conds ) ) {
1196  $conds = $this->wrapConditionsForWhere( $table, $conds );
1197  }
1198  // a hack for deleting pages, users and images (which have non-nullable FKs)
1199  // all deletions on these tables have transactions so final failure rollbacks these updates
1200  // @todo: Normalize the schema to match MySQL, no special FKs and such
1201  $table = $this->tableName( $table );
1202  if ( $table == $this->tableName( 'user' ) &&
1203  ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD )
1204  ) {
1205  $this->update( 'archive', [ 'ar_user' => 0 ],
1206  [ 'ar_user' => $conds['user_id'] ], $fname );
1207  $this->update( 'ipblocks', [ 'ipb_user' => 0 ],
1208  [ 'ipb_user' => $conds['user_id'] ], $fname );
1209  $this->update( 'image', [ 'img_user' => 0 ],
1210  [ 'img_user' => $conds['user_id'] ], $fname );
1211  $this->update( 'oldimage', [ 'oi_user' => 0 ],
1212  [ 'oi_user' => $conds['user_id'] ], $fname );
1213  $this->update( 'filearchive', [ 'fa_deleted_user' => 0 ],
1214  [ 'fa_deleted_user' => $conds['user_id'] ], $fname );
1215  $this->update( 'filearchive', [ 'fa_user' => 0 ],
1216  [ 'fa_user' => $conds['user_id'] ], $fname );
1217  $this->update( 'uploadstash', [ 'us_user' => 0 ],
1218  [ 'us_user' => $conds['user_id'] ], $fname );
1219  $this->update( 'recentchanges', [ 'rc_user' => 0 ],
1220  [ 'rc_user' => $conds['user_id'] ], $fname );
1221  $this->update( 'logging', [ 'log_user' => 0 ],
1222  [ 'log_user' => $conds['user_id'] ], $fname );
1223  } elseif ( $table == $this->tableName( 'image' ) ) {
1224  $this->update( 'oldimage', [ 'oi_name' => 0 ],
1225  [ 'oi_name' => $conds['img_name'] ], $fname );
1226  }
1227 
1228  return parent::delete( $table, $conds, $fname );
1229  }
1230 
1240  function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
1241  $table = $this->tableName( $table );
1242  $opts = $this->makeUpdateOptions( $options );
1243  $sql = "UPDATE $opts $table SET ";
1244 
1245  $first = true;
1246  foreach ( $values as $col => &$val ) {
1247  $sqlSet = $this->fieldBindStatement( $table, $col, $val, true );
1248 
1249  if ( !$first ) {
1250  $sqlSet = ', ' . $sqlSet;
1251  } else {
1252  $first = false;
1253  }
1254  $sql .= $sqlSet;
1255  }
1256 
1257  if ( $conds !== [] && $conds !== '*' ) {
1258  $conds = $this->wrapConditionsForWhere( $table, $conds );
1259  $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
1260  }
1261 
1262  $this->mLastResult = $stmt = oci_parse( $this->conn, $sql );
1263  if ( $stmt === false ) {
1264  $e = oci_error( $this->conn );
1265  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1266 
1267  return false;
1268  }
1269  foreach ( $values as $col => &$val ) {
1270  $col_info = $this->fieldInfoMulti( $table, $col );
1271  $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
1272 
1273  if ( $val === null ) {
1274  // do nothing ... null was inserted in statement creation
1275  } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
1276  if ( is_object( $val ) ) {
1277  $val = $val->getData();
1278  }
1279 
1280  if (
1281  preg_match( '/^timestamp.*/i', $col_type ) == 1 &&
1282  strtolower( $val ) == 'infinity'
1283  ) {
1284  $val = '31-12-2030 12:00:00.000000';
1285  }
1286 
1287  $val = $this->getVerifiedUTF8( $val );
1288  if ( oci_bind_by_name( $stmt, ":$col", $val ) === false ) {
1289  $e = oci_error( $stmt );
1290  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1291 
1292  return false;
1293  }
1294  } else {
1296  $lob[$col] = oci_new_descriptor( $this->conn, OCI_D_LOB );
1297  if ( $lob[$col] === false ) {
1298  $e = oci_error( $stmt );
1299  throw new DBUnexpectedError(
1300  $this,
1301  "Cannot create LOB descriptor: " . $e['message']
1302  );
1303  }
1304 
1305  if ( is_object( $val ) ) {
1306  $val = $val->getData();
1307  }
1308 
1309  if ( $col_type == 'BLOB' ) {
1310  $lob[$col]->writeTemporary( $val );
1311  oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, SQLT_BLOB );
1312  } else {
1313  $lob[$col]->writeTemporary( $val );
1314  oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB );
1315  }
1316  }
1317  }
1318 
1319  Wikimedia\suppressWarnings();
1320 
1321  if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
1322  $e = oci_error( $stmt );
1323  if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
1324  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1325 
1326  return false;
1327  } else {
1328  $this->mAffectedRows = oci_num_rows( $stmt );
1329  }
1330  } else {
1331  $this->mAffectedRows = oci_num_rows( $stmt );
1332  }
1333 
1334  Wikimedia\restoreWarnings();
1335 
1336  if ( isset( $lob ) ) {
1337  foreach ( $lob as $lob_v ) {
1338  $lob_v->free();
1339  }
1340  }
1341 
1342  if ( !$this->trxLevel ) {
1343  oci_commit( $this->conn );
1344  }
1345 
1346  return oci_free_statement( $stmt );
1347  }
1348 
1349  function bitNot( $field ) {
1350  // expecting bit-fields smaller than 4bytes
1351  return 'BITNOT(' . $field . ')';
1352  }
1353 
1354  function bitAnd( $fieldLeft, $fieldRight ) {
1355  return 'BITAND(' . $fieldLeft . ', ' . $fieldRight . ')';
1356  }
1357 
1358  function bitOr( $fieldLeft, $fieldRight ) {
1359  return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')';
1360  }
1361 
1362  public function buildGroupConcatField(
1363  $delim, $table, $field, $conds = '', $join_conds = []
1364  ) {
1365  $fld = "LISTAGG($field," . $this->addQuotes( $delim ) . ") WITHIN GROUP (ORDER BY $field)";
1366 
1367  return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
1368  }
1369 
1370  public function buildSubstring( $input, $startPosition, $length = null ) {
1371  $this->assertBuildSubstringParams( $startPosition, $length );
1372  $params = [ $input, $startPosition ];
1373  if ( $length !== null ) {
1374  $params[] = $length;
1375  }
1376  return 'SUBSTR(' . implode( ',', $params ) . ')';
1377  }
1378 
1384  public function buildStringCast( $field ) {
1385  return 'CAST ( ' . $field . ' AS VARCHAR2 )';
1386  }
1387 
1388  public function getInfinity() {
1389  return '31-12-2030 12:00:00.000000';
1390  }
1391 
1396  private function getVerifiedUTF8( $s ) {
1397  if ( mb_check_encoding( (string)$s, 'UTF-8' ) ) {
1398  return $s; // valid
1399  }
1400 
1401  throw new DBUnexpectedError( $this, "Non BLOB/CLOB field must be UTF-8." );
1402  }
1403 }
__construct(array $params)
const SCHEMA_COMPAT_WRITE_OLD
Definition: Defines.php:280
wrapConditionsForWhere( $table, $conds, $parentCol=null)
fieldInfoMulti( $table, $field)
Function translates mysql_fetch_field() functionality on ORACLE.
buildSubstring( $input, $startPosition, $length=null)
addQuotes( $s)
Adds quotes and backslashes.
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
Definition: Database.php:762
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
if(is_array( $mode)) switch( $mode) $input
static getLocalInstance( $ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
$success
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1982
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Result wrapper for grabbing data queried from an IDatabase object.
implicitOrderby()
Returns true if this database does an implicit order by when the column has an index For example: SEL...
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2159
ignoreIndexClause( $index)
IGNORE INDEX clause.
Definition: Database.php:2784
aggregateValue( $valuedata, $valuename='value')
Return aggregated value function call.
insertId()
Get the inserted value of an auto-increment row.
insertOneRow( $table, $row, $fname)
int $wgActorTableSchemaMigrationStage
Actor table schema migration stage.
doBegin( $fname=__METHOD__)
assertBuildSubstringParams( $startPosition, $length)
Check type and bounds for parameters to self::buildSubstring()
Definition: Database.php:2291
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of or an object and a method hook function The function part of a third party developers and local administrators to define code that will be run at certain points in the mainline code
Definition: hooks.txt:23
bitAnd( $fieldLeft, $fieldRight)
selectDB( $db)
Change the current database.
Definition: Database.php:2327
indexUnique( $table, $index, $fname=__METHOD__)
string $defaultCharset
Character set for Oracle database.
trxLevel()
Gets the current transaction level.
Definition: Database.php:515
implicitGroupby()
Returns true if this database does an implicit sort when doing GROUP BY.
update( $table, $values, $conds, $fname=__METHOD__, $options=[])
dataSeek( $res, $row)
replaceVars( $ins)
Database independent variable replacement.
Definition: Database.php:4415
closeConnection()
Closes a database connection, if it is open Returns success, true if already closed.
resource $mLastResult
wrapFieldForWhere( $table, &$col, &$val)
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query and return the result.
Definition: Database.php:1130
string $server
Server that this instance is currently connected to.
Definition: Database.php:50
insert( $table, $a, $fname=__METHOD__, $options=[])
INSERT wrapper, inserts an array into a table.
in this case you re responsible for computing and outputting the entire conflict i e
Definition: hooks.txt:1420
const DBO_DDLMODE
Definition: defines.php:16
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 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name '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. '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 '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:Array with elements of the form "language:title" in the order that they will be output. & $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 since 1.28! 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:1980
This document provides an overview of the usage of PageUpdater and that is
Definition: pageupdater.txt:3
const DBO_PERSISTENT
Definition: defines.php:14
close()
Close the database connection.
Definition: Database.php:865
getDBname()
Get the current DB name.
Definition: Database.php:2345
bitOr( $fieldLeft, $fieldRight)
queryIgnore( $sql, $fname='')
doRollback( $fname=__METHOD__)
int $mAffectedRows
The number of rows affected as an integer.
const LIST_AND
Definition: Defines.php:39
freeResult( $res)
Frees resources associated with the LOB descriptor.
const DBO_SYSDBA
Definition: defines.php:15
reportQueryError( $error, $errno, $sql, $fname, $ignore=false)
Report a query error.
Definition: Database.php:1527
indexExists( $table, $index, $fname=__METHOD__)
Query whether a given index exists.
$res
Definition: database.txt:21
getSequenceData( $table)
Return sequence_name if table has a sequence.
dropTable( $tableName, $fName=__METHOD__)
Delete 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 and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same user
Wikitext formatted, in the key only.
Definition: distributors.txt:9
$params
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1982
buildStringCast( $field)
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
makeUpdateOptions( $options)
Make UPDATE options for the Database::update function.
Definition: Database.php:2123
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.
Definition: Database.php:1762
fieldInfo( $table, $field)
makeList( $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
Definition: Database.php:2143
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
doSelectDomain(DatabaseDomain $domain)
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings, LocalSettings).
Definition: Setup.php:123
string [] $keywordTableMap
Map of (reserved table name => alternate table name)
getInfinity()
Find out when &#39;infinity&#39; is.
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
const DBO_DEFAULT
Definition: defines.php:13
open( $server, $user, $password, $dbName, $schema, $tablePrefix)
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
Class to handle database/prefix specification for IDatabase domains.
lastError()
Get a description of the last error.
Relational database abstraction object.
Definition: Database.php:48
bool array $sequenceData
sourceStream( $fp, callable $lineCallback=null, callable $resultCallback=null, $fname=__METHOD__, callable $inputCallback=null)
Read and execute commands from an open file handle.
$line
Definition: cdb.php:59
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a message
Definition: hooks.txt:2151
makeSelectOptions( $options)
Returns an optional USE INDEX clause to go after the table, and a string to go at the end of the quer...
object resource null $conn
Database connection.
Definition: Database.php:100
string $user
User that this instance is currently connected under the name of.
Definition: Database.php:52
selectRow( $table, $vars, $conds, $fname=__METHOD__, $options=[], $join_conds=[])
Single row SELECT wrapper.
addIdentifierQuotes( $s)
Quotes an identifier, in order to make user controlled input safe.
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 and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same so they can t rely on Unix and must forbid reads to even standard directories like tmp lest users read each others files We cannot assume that the user has the ability to install or run any programs not written as web accessible PHP scripts Since anything that works on cheap shared hosting will work if you have shell or root access MediaWiki s design is based around catering to the lowest common denominator Although we support higher end setups as the way many things work by default is tailored toward shared hosting These defaults are unconventional from the point of view of and they certainly aren t ideal for someone who s installing MediaWiki as MediaWiki does not conform to normal Unix filesystem layout Hopefully we ll offer direct support for standard layouts in the but for now *any change to the location of files is unsupported *Moving things and leaving symlinks will *probably *not break but it is *strongly *advised not to try any more intrusive changes to get MediaWiki to conform more closely to your filesystem hierarchy Any such attempt will almost certainly result in unnecessary bugs The standard recommended location to install relative to the web is it should be possible to enable the appropriate rewrite rules by if you can reconfigure the web server
lastErrno()
Get the last error number.
fieldName( $stmt, $n)
Get a field name in a result object.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
makeOrderBy( $options)
Returns an optional ORDER BY.
Definition: Database.php:1742
The oci8 extension is fairly weak and doesn&#39;t support oci_num_rows, among other things.
Definition: ORAResult.php:11
indexInfo( $table, $index, $fname=__METHOD__)
Returns information about an index If errors are explicitly ignored, returns NULL on failure...
fieldBindStatement( $table, $col, &$val, $includeCol=false)
makeGroupByWithHaving( $options)
Returns an optional GROUP BY with an optional HAVING.
Definition: Database.php:1716
buildGroupConcatField( $delim, $table, $field, $conds='', $join_conds=[])
Build a GROUP_CONCAT or equivalent statement for a query.
doCommit( $fname=__METHOD__)
useIndexClause( $index)
USE INDEX clause.
Definition: Database.php:2770
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:2217
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists (in the given schema, or the default mw one if not given) ...
string $password
Password used to establish the current connection.
Definition: Database.php:54
Base class for the more common types of database errors.
nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname=__METHOD__, $insertOptions=[], $selectOptions=[], $selectJoinConds=[])
tableName()
Name of table this field belongs to.
Definition: ORAField.php:26
tableName( $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.