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