Merge "Update Microsoft SQL Server schema"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 2 May 2016 21:34:50 +0000 (21:34 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 2 May 2016 21:34:50 +0000 (21:34 +0000)
1  2 
includes/db/DatabaseMssql.php

@@@ -38,6 -38,7 +38,7 @@@ class DatabaseMssql extends Database 
        protected $mBinaryColumnCache = null;
        protected $mBitColumnCache = null;
        protected $mIgnoreDupKeyErrors = false;
+       protected $mIgnoreErrors = [];
  
        protected $mPort;
  
                        $success = (bool)$stmt;
                }
  
+               // make a copy so that anything we add below does not get reflected in future queries
+               $ignoreErrors = $this->mIgnoreErrors;
                if ( $this->mIgnoreDupKeyErrors ) {
-                       // ignore duplicate key errors, but nothing else
+                       // ignore duplicate key errors
                        // this emulates INSERT IGNORE in MySQL
-                       if ( $success === false ) {
-                               $errors = sqlsrv_errors( SQLSRV_ERR_ERRORS );
-                               $success = true;
-                               foreach ( $errors as $err ) {
-                                       if ( $err['SQLSTATE'] == '23000' && $err['code'] == '2601' ) {
-                                               continue; // duplicate key error caused by unique index
-                                       } elseif ( $err['SQLSTATE'] == '23000' && $err['code'] == '2627' ) {
-                                               continue; // duplicate key error caused by primary key
-                                       } elseif ( $err['SQLSTATE'] == '01000' && $err['code'] == '3621' ) {
-                                               continue; // generic "the statement has been terminated" error
-                                       }
+                       $ignoreErrors[] = '2601'; // duplicate key error caused by unique index
+                       $ignoreErrors[] = '2627'; // duplicate key error caused by primary key
+                       $ignoreErrors[] = '3621'; // generic "the statement has been terminated" error
+               }
  
-                                       $success = false; // getting here means we got an error we weren't expecting
-                                       break;
-                               }
+               if ( $success === false ) {
+                       $errors = sqlsrv_errors();
+                       $success = true;
  
-                               if ( $success ) {
-                                       $this->mAffectedRows = 0;
-                                       return $stmt;
+                       foreach ( $errors as $err ) {
+                               if ( !in_array( $err['code'], $ignoreErrors ) ) {
+                                       $success = false;
+                                       break;
                                }
                        }
-               }
  
-               if ( $success === false ) {
-                       return false;
+                       if ( $success === false ) {
+                               return false;
+                       }
                }
                // remember number of rows affected
                $this->mAffectedRows = sqlsrv_rows_affected( $stmt );
                        $res = $res->result;
                }
  
-               return sqlsrv_num_rows( $res );
+               $ret = sqlsrv_num_rows( $res );
+               if ( $ret === false ) {
+                       // we cannot get an amount of rows from this cursor type
+                       // has_rows returns bool true/false if the result has rows
+                       $ret = (int)sqlsrv_has_rows( $res );
+               }
+               return $ret;
        }
  
        /**
                # This does not return the same info as MYSQL would, but that's OK
                # because MediaWiki never uses the returned value except to check for
                # the existance of indexes.
-               $sql = "sp_helpindex '" . $table . "'";
+               $sql = "sp_helpindex '" . $this->tableName( $table ) . "'";
                $res = $this->query( $sql, $fname );
                if ( !$res ) {
                        return null;
                }
                                $row = $ret->fetchObject();
                                if ( is_object( $row ) ) {
                                        $this->mInsertId = $row->$identity;
+                                       // it seems that mAffectedRows is -1 sometimes when OUTPUT INSERTED.identity is used
+                                       // if we got an identity back, we know for sure a row was affected, so adjust that here
+                                       if ( $this->mAffectedRows == -1 ) {
+                                               $this->mAffectedRows = 1;
+                                       }
                                }
                        }
                }
                return strlen( $name ) && $name[0] == '[' && substr( $name, -1, 1 ) == ']';
        }
  
 +      /**
 +       * MS SQL supports more pattern operators than other databases (ex: [,],^)
 +       *
 +       * @param string $s
 +       * @return string
 +       */
 +      protected function escapeLikeInternal( $s ) {
 +              return addcslashes( $s, '\%_[]^' );
 +      }
 +
 +      /**
 +       * MS SQL requires specifying the escape character used in a LIKE query
 +       * or using Square brackets to surround characters that are to be escaped
 +       * http://msdn.microsoft.com/en-us/library/ms179859.aspx
 +       * Here we take the Specify-Escape-Character approach since it's less
 +       * invasive, renders a query that is closer to other DB's and better at
 +       * handling square bracket escaping
 +       *
 +       * @return string Fully built LIKE statement
 +       */
 +      public function buildLike() {
 +              $params = func_get_args();
 +              if ( count( $params ) > 0 && is_array( $params[0] ) ) {
 +                      $params = $params[0];
 +              }
 +
 +              return parent::buildLike( $params ) . " ESCAPE '\' ";
 +      }
 +
        /**
         * @param string $db
         * @return bool
                return $table;
        }
  
+       /**
+        * Delete a table
+        * @param string $tableName
+        * @param string $fName
+        * @return bool|ResultWrapper
+        * @since 1.18
+        */
+       public function dropTable( $tableName, $fName = __METHOD__ ) {
+               if ( !$this->tableExists( $tableName, $fName ) ) {
+                       return false;
+               }
+               // parent function incorrectly appends CASCADE, which we don't want
+               $sql = "DROP TABLE " . $this->tableName( $tableName );
+               return $this->query( $sql, $fName );
+       }
        /**
         * Called in the installer and updater.
         * Probably doesn't need to be called anywhere else in the codebase.
        public function scrollableCursor( $value = null ) {
                return wfSetVar( $this->mScrollableCursor, $value );
        }
+       /**
+        * Called in the installer and updater.
+        * Probably doesn't need to be called anywhere else in the codebase.
+        * @param array|null $value
+        * @return array|null
+        */
+       public function ignoreErrors( array $value = null ) {
+               return wfSetVar( $this->mIgnoreErrors, $value );
+       }
  } // end DatabaseMssql class
  
  /**