MediaWiki  1.29.1
DatabaseMssql.php
Go to the documentation of this file.
1 <?php
28 namespace Wikimedia\Rdbms;
29 
31 use Exception;
32 use stdClass;
33 
37 class DatabaseMssql extends Database {
38  protected $mPort;
39  protected $mUseWindowsAuth = false;
40 
41  protected $mInsertId = null;
42  protected $mLastResult = null;
43  protected $mAffectedRows = null;
44  protected $mSubqueryId = 0;
45  protected $mScrollableCursor = true;
46  protected $mPrepareStatements = true;
47  protected $mBinaryColumnCache = null;
48  protected $mBitColumnCache = null;
49  protected $mIgnoreDupKeyErrors = false;
50  protected $mIgnoreErrors = [];
51 
52  public function implicitGroupby() {
53  return false;
54  }
55 
56  public function implicitOrderby() {
57  return false;
58  }
59 
60  public function unionSupportsOrderAndLimit() {
61  return false;
62  }
63 
64  public function __construct( array $params ) {
65  $this->mPort = $params['port'];
66  $this->mUseWindowsAuth = $params['UseWindowsAuth'];
67 
68  parent::__construct( $params );
69  }
70 
80  public function open( $server, $user, $password, $dbName ) {
81  # Test for driver support, to avoid suppressed fatal error
82  if ( !function_exists( 'sqlsrv_connect' ) ) {
83  throw new DBConnectionError(
84  $this,
85  "Microsoft SQL Server Native (sqlsrv) functions missing.
86  You can download the driver from: http://go.microsoft.com/fwlink/?LinkId=123470\n"
87  );
88  }
89 
90  # e.g. the class is being loaded
91  if ( !strlen( $user ) ) {
92  return null;
93  }
94 
95  $this->close();
96  $this->mServer = $server;
97  $this->mUser = $user;
98  $this->mPassword = $password;
99  $this->mDBname = $dbName;
100 
101  $connectionInfo = [];
102 
103  if ( $dbName ) {
104  $connectionInfo['Database'] = $dbName;
105  }
106 
107  // Decide which auth scenerio to use
108  // if we are using Windows auth, then don't add credentials to $connectionInfo
109  if ( !$this->mUseWindowsAuth ) {
110  $connectionInfo['UID'] = $user;
111  $connectionInfo['PWD'] = $password;
112  }
113 
114  MediaWiki\suppressWarnings();
115  $this->mConn = sqlsrv_connect( $server, $connectionInfo );
116  MediaWiki\restoreWarnings();
117 
118  if ( $this->mConn === false ) {
119  throw new DBConnectionError( $this, $this->lastError() );
120  }
121 
122  $this->mOpened = true;
123 
124  return $this->mConn;
125  }
126 
132  protected function closeConnection() {
133  return sqlsrv_close( $this->mConn );
134  }
135 
140  protected function resultObject( $result ) {
141  if ( !$result ) {
142  return false;
143  } elseif ( $result instanceof MssqlResultWrapper ) {
144  return $result;
145  } elseif ( $result === true ) {
146  // Successful write query
147  return $result;
148  } else {
149  return new MssqlResultWrapper( $this, $result );
150  }
151  }
152 
158  protected function doQuery( $sql ) {
159  // several extensions seem to think that all databases support limits
160  // via LIMIT N after the WHERE clause, but MSSQL uses SELECT TOP N,
161  // so to catch any of those extensions we'll do a quick check for a
162  // LIMIT clause and pass $sql through $this->LimitToTopN() which parses
163  // the LIMIT clause and passes the result to $this->limitResult();
164  if ( preg_match( '/\bLIMIT\s*/i', $sql ) ) {
165  // massage LIMIT -> TopN
166  $sql = $this->LimitToTopN( $sql );
167  }
168 
169  // MSSQL doesn't have EXTRACT(epoch FROM XXX)
170  if ( preg_match( '#\bEXTRACT\s*?\(\s*?EPOCH\s+FROM\b#i', $sql, $matches ) ) {
171  // This is same as UNIX_TIMESTAMP, we need to calc # of seconds from 1970
172  $sql = str_replace( $matches[0], "DATEDIFF(s,CONVERT(datetime,'1/1/1970'),", $sql );
173  }
174 
175  // perform query
176 
177  // SQLSRV_CURSOR_STATIC is slower than SQLSRV_CURSOR_CLIENT_BUFFERED (one of the two is
178  // needed if we want to be able to seek around the result set), however CLIENT_BUFFERED
179  // has a bug in the sqlsrv driver where wchar_t types (such as nvarchar) that are empty
180  // strings make php throw a fatal error "Severe error translating Unicode"
181  if ( $this->mScrollableCursor ) {
182  $scrollArr = [ 'Scrollable' => SQLSRV_CURSOR_STATIC ];
183  } else {
184  $scrollArr = [];
185  }
186 
187  if ( $this->mPrepareStatements ) {
188  // we do prepare + execute so we can get its field metadata for later usage if desired
189  $stmt = sqlsrv_prepare( $this->mConn, $sql, [], $scrollArr );
190  $success = sqlsrv_execute( $stmt );
191  } else {
192  $stmt = sqlsrv_query( $this->mConn, $sql, [], $scrollArr );
193  $success = (bool)$stmt;
194  }
195 
196  // Make a copy to ensure what we add below does not get reflected in future queries
197  $ignoreErrors = $this->mIgnoreErrors;
198 
199  if ( $this->mIgnoreDupKeyErrors ) {
200  // ignore duplicate key errors
201  // this emulates INSERT IGNORE in MySQL
202  $ignoreErrors[] = '2601'; // duplicate key error caused by unique index
203  $ignoreErrors[] = '2627'; // duplicate key error caused by primary key
204  $ignoreErrors[] = '3621'; // generic "the statement has been terminated" error
205  }
206 
207  if ( $success === false ) {
208  $errors = sqlsrv_errors();
209  $success = true;
210 
211  foreach ( $errors as $err ) {
212  if ( !in_array( $err['code'], $ignoreErrors ) ) {
213  $success = false;
214  break;
215  }
216  }
217 
218  if ( $success === false ) {
219  return false;
220  }
221  }
222  // remember number of rows affected
223  $this->mAffectedRows = sqlsrv_rows_affected( $stmt );
224 
225  return $stmt;
226  }
227 
228  public function freeResult( $res ) {
229  if ( $res instanceof ResultWrapper ) {
230  $res = $res->result;
231  }
232 
233  sqlsrv_free_stmt( $res );
234  }
235 
240  public function fetchObject( $res ) {
241  // $res is expected to be an instance of MssqlResultWrapper here
242  return $res->fetchObject();
243  }
244 
249  public function fetchRow( $res ) {
250  return $res->fetchRow();
251  }
252 
257  public function numRows( $res ) {
258  if ( $res instanceof ResultWrapper ) {
259  $res = $res->result;
260  }
261 
262  $ret = sqlsrv_num_rows( $res );
263 
264  if ( $ret === false ) {
265  // we cannot get an amount of rows from this cursor type
266  // has_rows returns bool true/false if the result has rows
267  $ret = (int)sqlsrv_has_rows( $res );
268  }
269 
270  return $ret;
271  }
272 
277  public function numFields( $res ) {
278  if ( $res instanceof ResultWrapper ) {
279  $res = $res->result;
280  }
281 
282  return sqlsrv_num_fields( $res );
283  }
284 
290  public function fieldName( $res, $n ) {
291  if ( $res instanceof ResultWrapper ) {
292  $res = $res->result;
293  }
294 
295  return sqlsrv_field_metadata( $res )[$n]['Name'];
296  }
297 
302  public function insertId() {
303  return $this->mInsertId;
304  }
305 
311  public function dataSeek( $res, $row ) {
312  return $res->seek( $row );
313  }
314 
318  public function lastError() {
319  $strRet = '';
320  $retErrors = sqlsrv_errors( SQLSRV_ERR_ALL );
321  if ( $retErrors != null ) {
322  foreach ( $retErrors as $arrError ) {
323  $strRet .= $this->formatError( $arrError ) . "\n";
324  }
325  } else {
326  $strRet = "No errors found";
327  }
328 
329  return $strRet;
330  }
331 
336  private function formatError( $err ) {
337  return '[SQLSTATE ' .
338  $err['SQLSTATE'] . '][Error Code ' . $err['code'] . ']' . $err['message'];
339  }
340 
344  public function lastErrno() {
345  $err = sqlsrv_errors( SQLSRV_ERR_ALL );
346  if ( $err !== null && isset( $err[0] ) ) {
347  return $err[0]['code'];
348  } else {
349  return 0;
350  }
351  }
352 
356  public function affectedRows() {
357  return $this->mAffectedRows;
358  }
359 
378  public function select( $table, $vars, $conds = '', $fname = __METHOD__,
379  $options = [], $join_conds = []
380  ) {
381  $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
382  if ( isset( $options['EXPLAIN'] ) ) {
383  try {
384  $this->mScrollableCursor = false;
385  $this->mPrepareStatements = false;
386  $this->query( "SET SHOWPLAN_ALL ON" );
387  $ret = $this->query( $sql, $fname );
388  $this->query( "SET SHOWPLAN_ALL OFF" );
389  } catch ( DBQueryError $dqe ) {
390  if ( isset( $options['FOR COUNT'] ) ) {
391  // likely don't have privs for SHOWPLAN, so run a select count instead
392  $this->query( "SET SHOWPLAN_ALL OFF" );
393  unset( $options['EXPLAIN'] );
394  $ret = $this->select(
395  $table,
396  'COUNT(*) AS EstimateRows',
397  $conds,
398  $fname,
399  $options,
400  $join_conds
401  );
402  } else {
403  // someone actually wanted the query plan instead of an est row count
404  // let them know of the error
405  $this->mScrollableCursor = true;
406  $this->mPrepareStatements = true;
407  throw $dqe;
408  }
409  }
410  $this->mScrollableCursor = true;
411  $this->mPrepareStatements = true;
412  return $ret;
413  }
414  return $this->query( $sql, $fname );
415  }
416 
430  public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
431  $options = [], $join_conds = []
432  ) {
433  if ( isset( $options['EXPLAIN'] ) ) {
434  unset( $options['EXPLAIN'] );
435  }
436 
437  $sql = parent::selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
438 
439  // try to rewrite aggregations of bit columns (currently MAX and MIN)
440  if ( strpos( $sql, 'MAX(' ) !== false || strpos( $sql, 'MIN(' ) !== false ) {
441  $bitColumns = [];
442  if ( is_array( $table ) ) {
443  foreach ( $table as $t ) {
444  $bitColumns += $this->getBitColumns( $this->tableName( $t ) );
445  }
446  } else {
447  $bitColumns = $this->getBitColumns( $this->tableName( $table ) );
448  }
449 
450  foreach ( $bitColumns as $col => $info ) {
451  $replace = [
452  "MAX({$col})" => "MAX(CAST({$col} AS tinyint))",
453  "MIN({$col})" => "MIN(CAST({$col} AS tinyint))",
454  ];
455  $sql = str_replace( array_keys( $replace ), array_values( $replace ), $sql );
456  }
457  }
458 
459  return $sql;
460  }
461 
462  public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
463  $fname = __METHOD__
464  ) {
465  $this->mScrollableCursor = false;
466  try {
467  parent::deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname );
468  } catch ( Exception $e ) {
469  $this->mScrollableCursor = true;
470  throw $e;
471  }
472  $this->mScrollableCursor = true;
473  }
474 
475  public function delete( $table, $conds, $fname = __METHOD__ ) {
476  $this->mScrollableCursor = false;
477  try {
478  parent::delete( $table, $conds, $fname );
479  } catch ( Exception $e ) {
480  $this->mScrollableCursor = true;
481  throw $e;
482  }
483  $this->mScrollableCursor = true;
484  }
485 
499  public function estimateRowCount( $table, $vars = '*', $conds = '',
500  $fname = __METHOD__, $options = []
501  ) {
502  // http://msdn2.microsoft.com/en-us/library/aa259203.aspx
503  $options['EXPLAIN'] = true;
504  $options['FOR COUNT'] = true;
505  $res = $this->select( $table, $vars, $conds, $fname, $options );
506 
507  $rows = -1;
508  if ( $res ) {
509  $row = $this->fetchRow( $res );
510 
511  if ( isset( $row['EstimateRows'] ) ) {
512  $rows = (int)$row['EstimateRows'];
513  }
514  }
515 
516  return $rows;
517  }
518 
527  public function indexInfo( $table, $index, $fname = __METHOD__ ) {
528  # This does not return the same info as MYSQL would, but that's OK
529  # because MediaWiki never uses the returned value except to check for
530  # the existence of indexes.
531  $sql = "sp_helpindex '" . $this->tableName( $table ) . "'";
532  $res = $this->query( $sql, $fname );
533 
534  if ( !$res ) {
535  return null;
536  }
537 
538  $result = [];
539  foreach ( $res as $row ) {
540  if ( $row->index_name == $index ) {
541  $row->Non_unique = !stristr( $row->index_description, "unique" );
542  $cols = explode( ", ", $row->index_keys );
543  foreach ( $cols as $col ) {
544  $row->Column_name = trim( $col );
545  $result[] = clone $row;
546  }
547  } elseif ( $index == 'PRIMARY' && stristr( $row->index_description, 'PRIMARY' ) ) {
548  $row->Non_unique = 0;
549  $cols = explode( ", ", $row->index_keys );
550  foreach ( $cols as $col ) {
551  $row->Column_name = trim( $col );
552  $result[] = clone $row;
553  }
554  }
555  }
556 
557  return empty( $result ) ? false : $result;
558  }
559 
575  public function insert( $table, $arrToInsert, $fname = __METHOD__, $options = [] ) {
576  # No rows to insert, easy just return now
577  if ( !count( $arrToInsert ) ) {
578  return true;
579  }
580 
581  if ( !is_array( $options ) ) {
582  $options = [ $options ];
583  }
584 
585  $table = $this->tableName( $table );
586 
587  if ( !( isset( $arrToInsert[0] ) && is_array( $arrToInsert[0] ) ) ) { // Not multi row
588  $arrToInsert = [ 0 => $arrToInsert ]; // make everything multi row compatible
589  }
590 
591  // We know the table we're inserting into, get its identity column
592  $identity = null;
593  // strip matching square brackets and the db/schema from table name
594  $tableRawArr = explode( '.', preg_replace( '#\[([^\]]*)\]#', '$1', $table ) );
595  $tableRaw = array_pop( $tableRawArr );
596  $res = $this->doQuery(
597  "SELECT NAME AS idColumn FROM SYS.IDENTITY_COLUMNS " .
598  "WHERE OBJECT_NAME(OBJECT_ID)='{$tableRaw}'"
599  );
600  if ( $res && sqlsrv_has_rows( $res ) ) {
601  // There is an identity for this table.
602  $identityArr = sqlsrv_fetch_array( $res, SQLSRV_FETCH_ASSOC );
603  $identity = array_pop( $identityArr );
604  }
605  sqlsrv_free_stmt( $res );
606 
607  // Determine binary/varbinary fields so we can encode data as a hex string like 0xABCDEF
608  $binaryColumns = $this->getBinaryColumns( $table );
609 
610  // INSERT IGNORE is not supported by SQL Server
611  // remove IGNORE from options list and set ignore flag to true
612  if ( in_array( 'IGNORE', $options ) ) {
613  $options = array_diff( $options, [ 'IGNORE' ] );
614  $this->mIgnoreDupKeyErrors = true;
615  }
616 
617  $ret = null;
618  foreach ( $arrToInsert as $a ) {
619  // start out with empty identity column, this is so we can return
620  // it as a result of the INSERT logic
621  $sqlPre = '';
622  $sqlPost = '';
623  $identityClause = '';
624 
625  // if we have an identity column
626  if ( $identity ) {
627  // iterate through
628  foreach ( $a as $k => $v ) {
629  if ( $k == $identity ) {
630  if ( !is_null( $v ) ) {
631  // there is a value being passed to us,
632  // we need to turn on and off inserted identity
633  $sqlPre = "SET IDENTITY_INSERT $table ON;";
634  $sqlPost = ";SET IDENTITY_INSERT $table OFF;";
635  } else {
636  // we can't insert NULL into an identity column,
637  // so remove the column from the insert.
638  unset( $a[$k] );
639  }
640  }
641  }
642 
643  // we want to output an identity column as result
644  $identityClause = "OUTPUT INSERTED.$identity ";
645  }
646 
647  $keys = array_keys( $a );
648 
649  // Build the actual query
650  $sql = $sqlPre . 'INSERT ' . implode( ' ', $options ) .
651  " INTO $table (" . implode( ',', $keys ) . ") $identityClause VALUES (";
652 
653  $first = true;
654  foreach ( $a as $key => $value ) {
655  if ( isset( $binaryColumns[$key] ) ) {
656  $value = new MssqlBlob( $value );
657  }
658  if ( $first ) {
659  $first = false;
660  } else {
661  $sql .= ',';
662  }
663  if ( is_null( $value ) ) {
664  $sql .= 'null';
665  } elseif ( is_array( $value ) || is_object( $value ) ) {
666  if ( is_object( $value ) && $value instanceof Blob ) {
667  $sql .= $this->addQuotes( $value );
668  } else {
669  $sql .= $this->addQuotes( serialize( $value ) );
670  }
671  } else {
672  $sql .= $this->addQuotes( $value );
673  }
674  }
675  $sql .= ')' . $sqlPost;
676 
677  // Run the query
678  $this->mScrollableCursor = false;
679  try {
680  $ret = $this->query( $sql );
681  } catch ( Exception $e ) {
682  $this->mScrollableCursor = true;
683  $this->mIgnoreDupKeyErrors = false;
684  throw $e;
685  }
686  $this->mScrollableCursor = true;
687 
688  if ( $ret instanceof ResultWrapper && !is_null( $identity ) ) {
689  // Then we want to get the identity column value we were assigned and save it off
690  $row = $ret->fetchObject();
691  if ( is_object( $row ) ) {
692  $this->mInsertId = $row->$identity;
693  // It seems that mAffectedRows is -1 sometimes when OUTPUT INSERTED.identity is
694  // used if we got an identity back, we know for sure a row was affected, so
695  // adjust that here
696  if ( $this->mAffectedRows == -1 ) {
697  $this->mAffectedRows = 1;
698  }
699  }
700  }
701  }
702 
703  $this->mIgnoreDupKeyErrors = false;
704 
705  return $ret;
706  }
707 
723  public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
724  $insertOptions = [], $selectOptions = []
725  ) {
726  $this->mScrollableCursor = false;
727  try {
728  $ret = parent::nativeInsertSelect(
729  $destTable,
730  $srcTable,
731  $varMap,
732  $conds,
733  $fname,
734  $insertOptions,
735  $selectOptions
736  );
737  } catch ( Exception $e ) {
738  $this->mScrollableCursor = true;
739  throw $e;
740  }
741  $this->mScrollableCursor = true;
742 
743  return $ret;
744  }
745 
771  function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
772  $table = $this->tableName( $table );
773  $binaryColumns = $this->getBinaryColumns( $table );
774 
775  $opts = $this->makeUpdateOptions( $options );
776  $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET, $binaryColumns );
777 
778  if ( $conds !== [] && $conds !== '*' ) {
779  $sql .= " WHERE " . $this->makeList( $conds, LIST_AND, $binaryColumns );
780  }
781 
782  $this->mScrollableCursor = false;
783  try {
784  $this->query( $sql );
785  } catch ( Exception $e ) {
786  $this->mScrollableCursor = true;
787  throw $e;
788  }
789  $this->mScrollableCursor = true;
790  return true;
791  }
792 
809  public function makeList( $a, $mode = LIST_COMMA, $binaryColumns = [] ) {
810  if ( !is_array( $a ) ) {
811  throw new DBUnexpectedError( $this, __METHOD__ . ' called with incorrect parameters' );
812  }
813 
814  if ( $mode != LIST_NAMES ) {
815  // In MS SQL, values need to be specially encoded when they are
816  // inserted into binary fields. Perform this necessary encoding
817  // for the specified set of columns.
818  foreach ( array_keys( $a ) as $field ) {
819  if ( !isset( $binaryColumns[$field] ) ) {
820  continue;
821  }
822 
823  if ( is_array( $a[$field] ) ) {
824  foreach ( $a[$field] as &$v ) {
825  $v = new MssqlBlob( $v );
826  }
827  unset( $v );
828  } else {
829  $a[$field] = new MssqlBlob( $a[$field] );
830  }
831  }
832  }
833 
834  return parent::makeList( $a, $mode );
835  }
836 
842  public function textFieldSize( $table, $field ) {
843  $table = $this->tableName( $table );
844  $sql = "SELECT CHARACTER_MAXIMUM_LENGTH,DATA_TYPE FROM INFORMATION_SCHEMA.Columns
845  WHERE TABLE_NAME = '$table' AND COLUMN_NAME = '$field'";
846  $res = $this->query( $sql );
847  $row = $this->fetchRow( $res );
848  $size = -1;
849  if ( strtolower( $row['DATA_TYPE'] ) != 'text' ) {
850  $size = $row['CHARACTER_MAXIMUM_LENGTH'];
851  }
852 
853  return $size;
854  }
855 
866  public function limitResult( $sql, $limit, $offset = false ) {
867  if ( $offset === false || $offset == 0 ) {
868  if ( strpos( $sql, "SELECT" ) === false ) {
869  return "TOP {$limit} " . $sql;
870  } else {
871  return preg_replace( '/\bSELECT(\s+DISTINCT)?\b/Dsi',
872  'SELECT$1 TOP ' . $limit, $sql, 1 );
873  }
874  } else {
875  // This one is fun, we need to pull out the select list as well as any ORDER BY clause
876  $select = $orderby = [];
877  $s1 = preg_match( '#SELECT\s+(.+?)\s+FROM#Dis', $sql, $select );
878  $s2 = preg_match( '#(ORDER BY\s+.+?)(\s*FOR XML .*)?$#Dis', $sql, $orderby );
879  $postOrder = '';
880  $first = $offset + 1;
881  $last = $offset + $limit;
882  $sub1 = 'sub_' . $this->mSubqueryId;
883  $sub2 = 'sub_' . ( $this->mSubqueryId + 1 );
884  $this->mSubqueryId += 2;
885  if ( !$s1 ) {
886  // wat
887  throw new DBUnexpectedError( $this, "Attempting to LIMIT a non-SELECT query\n" );
888  }
889  if ( !$s2 ) {
890  // no ORDER BY
891  $overOrder = 'ORDER BY (SELECT 1)';
892  } else {
893  if ( !isset( $orderby[2] ) || !$orderby[2] ) {
894  // don't need to strip it out if we're using a FOR XML clause
895  $sql = str_replace( $orderby[1], '', $sql );
896  }
897  $overOrder = $orderby[1];
898  $postOrder = ' ' . $overOrder;
899  }
900  $sql = "SELECT {$select[1]}
901  FROM (
902  SELECT ROW_NUMBER() OVER({$overOrder}) AS rowNumber, *
903  FROM ({$sql}) {$sub1}
904  ) {$sub2}
905  WHERE rowNumber BETWEEN {$first} AND {$last}{$postOrder}";
906 
907  return $sql;
908  }
909  }
910 
921  public function LimitToTopN( $sql ) {
922  // Matches: LIMIT {[offset,] row_count | row_count OFFSET offset}
923  $pattern = '/\bLIMIT\s+((([0-9]+)\s*,\s*)?([0-9]+)(\s+OFFSET\s+([0-9]+))?)/i';
924  if ( preg_match( $pattern, $sql, $matches ) ) {
925  $row_count = $matches[4];
926  $offset = $matches[3] ?: $matches[6] ?: false;
927 
928  // strip the matching LIMIT clause out
929  $sql = str_replace( $matches[0], '', $sql );
930 
931  return $this->limitResult( $sql, $row_count, $offset );
932  }
933 
934  return $sql;
935  }
936 
940  public function getSoftwareLink() {
941  return "[{{int:version-db-mssql-url}} MS SQL Server]";
942  }
943 
947  public function getServerVersion() {
948  $server_info = sqlsrv_server_info( $this->mConn );
949  $version = 'Error';
950  if ( isset( $server_info['SQLServerVersion'] ) ) {
951  $version = $server_info['SQLServerVersion'];
952  }
953 
954  return $version;
955  }
956 
962  public function tableExists( $table, $fname = __METHOD__ ) {
963  list( $db, $schema, $table ) = $this->tableName( $table, 'split' );
964 
965  if ( $db !== false ) {
966  // remote database
967  $this->queryLogger->error( "Attempting to call tableExists on a remote table" );
968  return false;
969  }
970 
971  if ( $schema === false ) {
972  $schema = $this->mSchema;
973  }
974 
975  $res = $this->query( "SELECT 1 FROM INFORMATION_SCHEMA.TABLES
976  WHERE TABLE_TYPE = 'BASE TABLE'
977  AND TABLE_SCHEMA = '$schema' AND TABLE_NAME = '$table'" );
978 
979  if ( $res->numRows() ) {
980  return true;
981  } else {
982  return false;
983  }
984  }
985 
993  public function fieldExists( $table, $field, $fname = __METHOD__ ) {
994  list( $db, $schema, $table ) = $this->tableName( $table, 'split' );
995 
996  if ( $db !== false ) {
997  // remote database
998  $this->queryLogger->error( "Attempting to call fieldExists on a remote table" );
999  return false;
1000  }
1001 
1002  $res = $this->query( "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
1003  WHERE TABLE_SCHEMA = '$schema' AND TABLE_NAME = '$table' AND COLUMN_NAME = '$field'" );
1004 
1005  if ( $res->numRows() ) {
1006  return true;
1007  } else {
1008  return false;
1009  }
1010  }
1011 
1012  public function fieldInfo( $table, $field ) {
1013  list( $db, $schema, $table ) = $this->tableName( $table, 'split' );
1014 
1015  if ( $db !== false ) {
1016  // remote database
1017  $this->queryLogger->error( "Attempting to call fieldInfo on a remote table" );
1018  return false;
1019  }
1020 
1021  $res = $this->query( "SELECT * FROM INFORMATION_SCHEMA.COLUMNS
1022  WHERE TABLE_SCHEMA = '$schema' AND TABLE_NAME = '$table' AND COLUMN_NAME = '$field'" );
1023 
1024  $meta = $res->fetchRow();
1025  if ( $meta ) {
1026  return new MssqlField( $meta );
1027  }
1028 
1029  return false;
1030  }
1031 
1036  protected function doBegin( $fname = __METHOD__ ) {
1037  sqlsrv_begin_transaction( $this->mConn );
1038  $this->mTrxLevel = 1;
1039  }
1040 
1045  protected function doCommit( $fname = __METHOD__ ) {
1046  sqlsrv_commit( $this->mConn );
1047  $this->mTrxLevel = 0;
1048  }
1049 
1055  protected function doRollback( $fname = __METHOD__ ) {
1056  sqlsrv_rollback( $this->mConn );
1057  $this->mTrxLevel = 0;
1058  }
1059 
1064  public function strencode( $s ) {
1065  // Should not be called by us
1066 
1067  return str_replace( "'", "''", $s );
1068  }
1069 
1074  public function addQuotes( $s ) {
1075  if ( $s instanceof MssqlBlob ) {
1076  return $s->fetch();
1077  } elseif ( $s instanceof Blob ) {
1078  // this shouldn't really ever be called, but it's here if needed
1079  // (and will quite possibly make the SQL error out)
1080  $blob = new MssqlBlob( $s->fetch() );
1081  return $blob->fetch();
1082  } else {
1083  if ( is_bool( $s ) ) {
1084  $s = $s ? 1 : 0;
1085  }
1086  return parent::addQuotes( $s );
1087  }
1088  }
1089 
1094  public function addIdentifierQuotes( $s ) {
1095  // http://msdn.microsoft.com/en-us/library/aa223962.aspx
1096  return '[' . $s . ']';
1097  }
1098 
1103  public function isQuotedIdentifier( $name ) {
1104  return strlen( $name ) && $name[0] == '[' && substr( $name, -1, 1 ) == ']';
1105  }
1106 
1113  protected function escapeLikeInternal( $s, $escapeChar = '`' ) {
1114  return str_replace( [ $escapeChar, '%', '_', '[', ']', '^' ],
1115  [ "{$escapeChar}{$escapeChar}", "{$escapeChar}%", "{$escapeChar}_",
1116  "{$escapeChar}[", "{$escapeChar}]", "{$escapeChar}^" ],
1117  $s );
1118  }
1119 
1124  public function selectDB( $db ) {
1125  try {
1126  $this->mDBname = $db;
1127  $this->query( "USE $db" );
1128  return true;
1129  } catch ( Exception $e ) {
1130  return false;
1131  }
1132  }
1133 
1139  public function makeSelectOptions( $options ) {
1140  $tailOpts = '';
1141  $startOpts = '';
1142 
1143  $noKeyOptions = [];
1144  foreach ( $options as $key => $option ) {
1145  if ( is_numeric( $key ) ) {
1146  $noKeyOptions[$option] = true;
1147  }
1148  }
1149 
1150  $tailOpts .= $this->makeGroupByWithHaving( $options );
1151 
1152  $tailOpts .= $this->makeOrderBy( $options );
1153 
1154  if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
1155  $startOpts .= 'DISTINCT';
1156  }
1157 
1158  if ( isset( $noKeyOptions['FOR XML'] ) ) {
1159  // used in group concat field emulation
1160  $tailOpts .= " FOR XML PATH('')";
1161  }
1162 
1163  // we want this to be compatible with the output of parent::makeSelectOptions()
1164  return [ $startOpts, '', $tailOpts, '', '' ];
1165  }
1166 
1167  public function getType() {
1168  return 'mssql';
1169  }
1170 
1175  public function buildConcat( $stringList ) {
1176  return implode( ' + ', $stringList );
1177  }
1178 
1196  public function buildGroupConcatField( $delim, $table, $field, $conds = '',
1197  $join_conds = []
1198  ) {
1199  $gcsq = 'gcsq_' . $this->mSubqueryId;
1200  $this->mSubqueryId++;
1201 
1202  $delimLen = strlen( $delim );
1203  $fld = "{$field} + {$this->addQuotes( $delim )}";
1204  $sql = "(SELECT LEFT({$field}, LEN({$field}) - {$delimLen}) FROM ("
1205  . $this->selectSQLText( $table, $fld, $conds, null, [ 'FOR XML' ], $join_conds )
1206  . ") {$gcsq} ({$field}))";
1207 
1208  return $sql;
1209  }
1210 
1217  private function getBinaryColumns( $table ) {
1218  $tableRawArr = explode( '.', preg_replace( '#\[([^\]]*)\]#', '$1', $table ) );
1219  $tableRaw = array_pop( $tableRawArr );
1220 
1221  if ( $this->mBinaryColumnCache === null ) {
1222  $this->populateColumnCaches();
1223  }
1224 
1225  return isset( $this->mBinaryColumnCache[$tableRaw] )
1226  ? $this->mBinaryColumnCache[$tableRaw]
1227  : [];
1228  }
1229 
1234  private function getBitColumns( $table ) {
1235  $tableRawArr = explode( '.', preg_replace( '#\[([^\]]*)\]#', '$1', $table ) );
1236  $tableRaw = array_pop( $tableRawArr );
1237 
1238  if ( $this->mBitColumnCache === null ) {
1239  $this->populateColumnCaches();
1240  }
1241 
1242  return isset( $this->mBitColumnCache[$tableRaw] )
1243  ? $this->mBitColumnCache[$tableRaw]
1244  : [];
1245  }
1246 
1247  private function populateColumnCaches() {
1248  $res = $this->select( 'INFORMATION_SCHEMA.COLUMNS', '*',
1249  [
1250  'TABLE_CATALOG' => $this->mDBname,
1251  'TABLE_SCHEMA' => $this->mSchema,
1252  'DATA_TYPE' => [ 'varbinary', 'binary', 'image', 'bit' ]
1253  ] );
1254 
1255  $this->mBinaryColumnCache = [];
1256  $this->mBitColumnCache = [];
1257  foreach ( $res as $row ) {
1258  if ( $row->DATA_TYPE == 'bit' ) {
1259  $this->mBitColumnCache[$row->TABLE_NAME][$row->COLUMN_NAME] = $row;
1260  } else {
1261  $this->mBinaryColumnCache[$row->TABLE_NAME][$row->COLUMN_NAME] = $row;
1262  }
1263  }
1264  }
1265 
1271  function tableName( $name, $format = 'quoted' ) {
1272  # Replace reserved words with better ones
1273  switch ( $name ) {
1274  case 'user':
1275  return $this->realTableName( 'mwuser', $format );
1276  default:
1277  return $this->realTableName( $name, $format );
1278  }
1279  }
1280 
1287  function realTableName( $name, $format = 'quoted' ) {
1288  $table = parent::tableName( $name, $format );
1289  if ( $format == 'split' ) {
1290  // Used internally, we want the schema split off from the table name and returned
1291  // as a list with 3 elements (database, schema, table)
1292  $table = explode( '.', $table );
1293  while ( count( $table ) < 3 ) {
1294  array_unshift( $table, false );
1295  }
1296  }
1297  return $table;
1298  }
1299 
1307  public function dropTable( $tableName, $fName = __METHOD__ ) {
1308  if ( !$this->tableExists( $tableName, $fName ) ) {
1309  return false;
1310  }
1311 
1312  // parent function incorrectly appends CASCADE, which we don't want
1313  $sql = "DROP TABLE " . $this->tableName( $tableName );
1314 
1315  return $this->query( $sql, $fName );
1316  }
1317 
1324  public function prepareStatements( $value = null ) {
1326  if ( $value !== null ) {
1327  $this->mPrepareStatements = $value;
1328  }
1329 
1330  return $old;
1331  }
1332 
1339  public function scrollableCursor( $value = null ) {
1340  $old = $this->mScrollableCursor;
1341  if ( $value !== null ) {
1342  $this->mScrollableCursor = $value;
1343  }
1344 
1345  return $old;
1346  }
1347 }
1348 
1349 class_alias( DatabaseMssql::class, 'DatabaseMssql' );
Wikimedia\Rdbms\DatabaseMssql\indexInfo
indexInfo( $table, $index, $fname=__METHOD__)
Returns information about an index If errors are explicitly ignored, returns NULL on failure.
Definition: DatabaseMssql.php:527
Wikimedia\Rdbms\DatabaseMssql\update
update( $table, $values, $conds, $fname=__METHOD__, $options=[])
UPDATE wrapper.
Definition: DatabaseMssql.php:771
Wikimedia\Rdbms\DatabaseMssql\$mAffectedRows
$mAffectedRows
Definition: DatabaseMssql.php:43
Wikimedia\Rdbms\DatabaseMssql\buildGroupConcatField
buildGroupConcatField( $delim, $table, $field, $conds='', $join_conds=[])
Build a GROUP_CONCAT or equivalent statement for a query.
Definition: DatabaseMssql.php:1196
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:45
Wikimedia\Rdbms\DatabaseMssql\limitResult
limitResult( $sql, $limit, $offset=false)
Construct a LIMIT query with optional offset This is used for query pages.
Definition: DatabaseMssql.php:866
Wikimedia\Rdbms\DatabaseMssql\resultObject
resultObject( $result)
Definition: DatabaseMssql.php:140
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
Wikimedia\Rdbms\Database\$mConn
resource null $mConn
Database connection.
Definition: Database.php:92
Wikimedia\Rdbms\Database\$mSchema
string $mSchema
Definition: Database.php:110
Wikimedia\Rdbms\DatabaseMssql\realTableName
realTableName( $name, $format='quoted')
call this instead of tableName() in the updater when renaming tables
Definition: DatabaseMssql.php:1287
Wikimedia\Rdbms\DatabaseMssql\freeResult
freeResult( $res)
Free a result object returned by query() or select().
Definition: DatabaseMssql.php:228
Wikimedia\Rdbms\DatabaseMssql\$mBitColumnCache
$mBitColumnCache
Definition: DatabaseMssql.php:48
Wikimedia\Rdbms\DatabaseMssql\affectedRows
affectedRows()
Definition: DatabaseMssql.php:356
Wikimedia\Rdbms\DatabaseMssql\deleteJoin
deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname=__METHOD__)
DELETE where the condition is a join.
Definition: DatabaseMssql.php:462
Wikimedia\Rdbms\DatabaseMssql\__construct
__construct(array $params)
Constructor and database handle and attempt to connect to the DB server.
Definition: DatabaseMssql.php:64
captcha-old.count
count
Definition: captcha-old.py:225
$last
$last
Definition: profileinfo.php:415
Wikimedia\Rdbms\DatabaseMssql\insert
insert( $table, $arrToInsert, $fname=__METHOD__, $options=[])
INSERT wrapper, inserts an array into a table.
Definition: DatabaseMssql.php:575
Wikimedia\Rdbms\DatabaseMssql\implicitOrderby
implicitOrderby()
Returns true if this database does an implicit order by when the column has an index For example: SEL...
Definition: DatabaseMssql.php:56
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links: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! 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:1954
Wikimedia\Rdbms\DatabaseMssql\$mIgnoreDupKeyErrors
$mIgnoreDupKeyErrors
Definition: DatabaseMssql.php:49
Wikimedia\Rdbms\DatabaseMssql\getServerVersion
getServerVersion()
Definition: DatabaseMssql.php:947
Wikimedia\Rdbms\DatabaseMssql\lastErrno
lastErrno()
Definition: DatabaseMssql.php:344
use
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 use
Definition: MIT-LICENSE.txt:10
Wikimedia\Rdbms\DatabaseMssql\buildConcat
buildConcat( $stringList)
Definition: DatabaseMssql.php:1175
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:246
Wikimedia\Rdbms
Definition: ChronologyProtector.php:24
$fname
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
Definition: Setup.php:36
$params
$params
Definition: styleTest.css.php:40
serialize
serialize()
Definition: ApiMessage.php:177
$s
$s
Definition: mergeMessageFileList.php:188
Wikimedia\Rdbms\DatabaseMssql\implicitGroupby
implicitGroupby()
Returns true if this database does an implicit sort when doing GROUP BY.
Definition: DatabaseMssql.php:52
$res
$res
Definition: database.txt:21
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
Wikimedia\Rdbms\ResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: ResultWrapper.php:24
Wikimedia\Rdbms\DatabaseMssql\dropTable
dropTable( $tableName, $fName=__METHOD__)
Delete a table.
Definition: DatabaseMssql.php:1307
$success
$success
Definition: NoLocalSettings.php:44
Wikimedia\Rdbms\DatabaseMssql\populateColumnCaches
populateColumnCaches()
Definition: DatabaseMssql.php:1247
Wikimedia\Rdbms\DatabaseMssql\dataSeek
dataSeek( $res, $row)
Definition: DatabaseMssql.php:311
Wikimedia\Rdbms\DatabaseMssql\nativeInsertSelect
nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname=__METHOD__, $insertOptions=[], $selectOptions=[])
INSERT SELECT wrapper $varMap must be an associative array of the form [ 'dest1' => 'source1',...
Definition: DatabaseMssql.php:723
Wikimedia\Rdbms\DatabaseMssql\makeList
makeList( $a, $mode=LIST_COMMA, $binaryColumns=[])
Makes an encoded list of strings from an array.
Definition: DatabaseMssql.php:809
Wikimedia\Rdbms\DatabaseMssql\getType
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
Definition: DatabaseMssql.php:1167
Wikimedia\Rdbms\DatabaseMssql\isQuotedIdentifier
isQuotedIdentifier( $name)
Definition: DatabaseMssql.php:1103
php
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
LIST_AND
const LIST_AND
Definition: Defines.php:41
Wikimedia\Rdbms\DatabaseMssql\$mInsertId
$mInsertId
Definition: DatabaseMssql.php:41
Wikimedia\Rdbms\DatabaseMssql\$mPort
$mPort
Definition: DatabaseMssql.php:38
Wikimedia\Rdbms\DatabaseMssql\tableExists
tableExists( $table, $fname=__METHOD__)
Definition: DatabaseMssql.php:962
Wikimedia\Rdbms\DatabaseMssql\unionSupportsOrderAndLimit
unionSupportsOrderAndLimit()
Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries within th...
Definition: DatabaseMssql.php:60
Wikimedia\Rdbms\DatabaseMssql\doCommit
doCommit( $fname=__METHOD__)
End a transaction.
Definition: DatabaseMssql.php:1045
Wikimedia\Rdbms\DatabaseMssql\$mPrepareStatements
$mPrepareStatements
Definition: DatabaseMssql.php:46
$blob
$blob
Definition: testCompression.php:63
Wikimedia\Rdbms\DatabaseMssql\closeConnection
closeConnection()
Closes a database connection, if it is open Returns success, true if already closed.
Definition: DatabaseMssql.php:132
tableName
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for tableName() and addQuotes(). You will need both of them. ------------------------------------------------------------------------ Basic query optimisation ------------------------------------------------------------------------ MediaWiki developers who need to write DB queries should have some understanding of databases and the performance issues associated with them. Patches containing unacceptably slow features will not be accepted. Unindexed queries are generally not welcome in MediaWiki
$matches
$matches
Definition: NoLocalSettings.php:24
Wikimedia\Rdbms\DatabaseMssql\fieldInfo
fieldInfo( $table, $field)
mysql_fetch_field() wrapper Returns false if the field doesn't exist
Definition: DatabaseMssql.php:1012
LIST_SET
const LIST_SET
Definition: Defines.php:42
$limit
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers please use GetContentModels hook to make them known to core if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1049
Wikimedia\Rdbms\DatabaseMssql\escapeLikeInternal
escapeLikeInternal( $s, $escapeChar='`')
MS SQL supports more pattern operators than other databases (ex: [,],^)
Definition: DatabaseMssql.php:1113
Wikimedia\Rdbms\MssqlBlob
Definition: MssqlBlob.php:5
MediaWiki
This document describes the state of Postgres support in MediaWiki
Definition: postgres.txt:4
Wikimedia\Rdbms\DatabaseMssql\select
select( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
SELECT wrapper.
Definition: DatabaseMssql.php:378
Wikimedia\Rdbms\DatabaseMssql\$mSubqueryId
$mSubqueryId
Definition: DatabaseMssql.php:44
$vars
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:2179
Wikimedia\Rdbms\DatabaseMssql\fetchRow
fetchRow( $res)
Definition: DatabaseMssql.php:249
Wikimedia\Rdbms\DatabaseMssql\$mUseWindowsAuth
$mUseWindowsAuth
Definition: DatabaseMssql.php:39
Wikimedia\Rdbms\DatabaseMssql\$mLastResult
$mLastResult
Definition: DatabaseMssql.php:42
Wikimedia\Rdbms\DatabaseMssql\prepareStatements
prepareStatements( $value=null)
Called in the installer and updater.
Definition: DatabaseMssql.php:1324
Wikimedia\Rdbms\DatabaseMssql\estimateRowCount
estimateRowCount( $table, $vars=' *', $conds='', $fname=__METHOD__, $options=[])
Estimate rows in dataset Returns estimated count, based on SHOWPLAN_ALL output This is not necessaril...
Definition: DatabaseMssql.php:499
Wikimedia\Rdbms\MssqlField
Definition: MssqlField.php:5
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
Wikimedia\Rdbms\DatabaseMssql\$mIgnoreErrors
$mIgnoreErrors
Definition: DatabaseMssql.php:50
Wikimedia\Rdbms\DBQueryError
Definition: DBQueryError.php:27
Wikimedia\Rdbms\Database\makeUpdateOptions
makeUpdateOptions( $options)
Make UPDATE options for the Database::update function.
Definition: Database.php:1544
LIST_COMMA
const LIST_COMMA
Definition: Defines.php:40
Wikimedia\Rdbms\DatabaseMssql\strencode
strencode( $s)
Definition: DatabaseMssql.php:1064
Wikimedia\Rdbms\DatabaseMssql\numRows
numRows( $res)
Definition: DatabaseMssql.php:257
Wikimedia\Rdbms\DatabaseMssql\doRollback
doRollback( $fname=__METHOD__)
Rollback a transaction.
Definition: DatabaseMssql.php:1055
Wikimedia\Rdbms\DatabaseMssql\selectSQLText
selectSQLText( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
SELECT wrapper.
Definition: DatabaseMssql.php:430
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2122
$value
$value
Definition: styleTest.css.php:45
Wikimedia\Rdbms\DatabaseMssql\$mBinaryColumnCache
$mBinaryColumnCache
Definition: DatabaseMssql.php:47
Wikimedia\Rdbms\DatabaseMssql\lastError
lastError()
Definition: DatabaseMssql.php:318
Wikimedia\Rdbms\DatabaseMssql\fetchObject
fetchObject( $res)
Definition: DatabaseMssql.php:240
Wikimedia\Rdbms\DatabaseMssql\addQuotes
addQuotes( $s)
Definition: DatabaseMssql.php:1074
Wikimedia\Rdbms\DatabaseMssql\doBegin
doBegin( $fname=__METHOD__)
Begin a transaction, committing any previously open transaction.
Definition: DatabaseMssql.php:1036
Wikimedia\Rdbms\DatabaseMssql\insertId
insertId()
This must be called after nextSequenceVal.
Definition: DatabaseMssql.php:302
Wikimedia\Rdbms\DatabaseMssql\textFieldSize
textFieldSize( $table, $field)
Definition: DatabaseMssql.php:842
Wikimedia\Rdbms\DatabaseMssql\getBinaryColumns
getBinaryColumns( $table)
Returns an associative array for fields that are of type varbinary, binary, or image $table can be ei...
Definition: DatabaseMssql.php:1217
$ret
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:1956
Wikimedia\Rdbms\DatabaseMssql\formatError
formatError( $err)
Definition: DatabaseMssql.php:336
Wikimedia\Rdbms\DatabaseMssql\selectDB
selectDB( $db)
Definition: DatabaseMssql.php:1124
Wikimedia\Rdbms\DatabaseMssql\open
open( $server, $user, $password, $dbName)
Usually aborts on failure.
Definition: DatabaseMssql.php:80
Wikimedia\Rdbms\Database\query
query( $sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition: Database.php:850
Wikimedia\Rdbms\DatabaseMssql\makeSelectOptions
makeSelectOptions( $options)
Definition: DatabaseMssql.php:1139
Wikimedia\Rdbms\DBUnexpectedError
Definition: DBUnexpectedError.php:27
Wikimedia\Rdbms\MssqlResultWrapper
Definition: MssqlResultWrapper.php:7
Wikimedia\Rdbms\DatabaseMssql\doQuery
doQuery( $sql)
Definition: DatabaseMssql.php:158
Wikimedia\Rdbms\Database\close
close()
Closes a database connection.
Definition: Database.php:726
Wikimedia\Rdbms\DatabaseMssql\fieldName
fieldName( $res, $n)
Definition: DatabaseMssql.php:290
Wikimedia\Rdbms\DatabaseMssql\getBitColumns
getBitColumns( $table)
Definition: DatabaseMssql.php:1234
Wikimedia\Rdbms\Database\makeGroupByWithHaving
makeGroupByWithHaving( $options)
Returns an optional GROUP BY with an optional HAVING.
Definition: Database.php:1227
as
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
Wikimedia\Rdbms\DatabaseMssql
Definition: DatabaseMssql.php:37
Wikimedia\Rdbms\DatabaseMssql\LimitToTopN
LimitToTopN( $sql)
If there is a limit clause, parse it, strip it, and pass the remaining SQL through limitResult() with...
Definition: DatabaseMssql.php:921
Wikimedia\Rdbms\DatabaseMssql\$mScrollableCursor
$mScrollableCursor
Definition: DatabaseMssql.php:45
Wikimedia\Rdbms\DBConnectionError
Definition: DBConnectionError.php:26
$keys
$keys
Definition: testCompression.php:65
Wikimedia\Rdbms\Database\makeOrderBy
makeOrderBy( $options)
Returns an optional ORDER BY.
Definition: Database.php:1253
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
$t
$t
Definition: testCompression.php:67
Wikimedia\Rdbms\DatabaseMssql\tableName
tableName( $name, $format='quoted')
Definition: DatabaseMssql.php:1271
LIST_NAMES
const LIST_NAMES
Definition: Defines.php:43
Wikimedia\Rdbms\DatabaseMssql\getSoftwareLink
getSoftwareLink()
Definition: DatabaseMssql.php:940
$options
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1049
Wikimedia\Rdbms\DatabaseMssql\scrollableCursor
scrollableCursor( $value=null)
Called in the installer and updater.
Definition: DatabaseMssql.php:1339
Wikimedia\Rdbms\DatabaseMssql\numFields
numFields( $res)
Definition: DatabaseMssql.php:277
Wikimedia\Rdbms\DatabaseMssql\addIdentifierQuotes
addIdentifierQuotes( $s)
Definition: DatabaseMssql.php:1094
Wikimedia\Rdbms\DatabaseMssql\fieldExists
fieldExists( $table, $field, $fname=__METHOD__)
Query whether a given column exists in the mediawiki schema.
Definition: DatabaseMssql.php:993
array
the array() calling protocol came about after MediaWiki 1.4rc1.
Wikimedia\Rdbms\Blob
Definition: Blob.php:5