Merge "Always decode Blob objects from Database::addQuotes"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 25 Feb 2015 21:17:06 +0000 (21:17 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 25 Feb 2015 21:17:06 +0000 (21:17 +0000)
1  2 
autoload.php
includes/db/Database.php
includes/db/DatabaseMssql.php
includes/db/DatabasePostgres.php

diff --combined autoload.php
@@@ -4,7 -4,7 +4,7 @@@
  global $wgAutoloadLocalClasses;
  
  $wgAutoloadLocalClasses = array(
 -      'APCBagOStuff' => __DIR__ . '/includes/objectcache/APCBagOStuff.php',
 +      'APCBagOStuff' => __DIR__ . '/includes/libs/objectcache/APCBagOStuff.php',
        'AbstractContent' => __DIR__ . '/includes/content/AbstractContent.php',
        'Action' => __DIR__ . '/includes/actions/Action.php',
        'ActiveUsersPager' => __DIR__ . '/includes/specials/SpecialActiveusers.php',
@@@ -18,7 -18,6 +18,7 @@@
        'AnsiTermColorer' => __DIR__ . '/maintenance/term/MWTerm.php',
        'ApiBase' => __DIR__ . '/includes/api/ApiBase.php',
        'ApiBlock' => __DIR__ . '/includes/api/ApiBlock.php',
 +      'ApiCheckToken' => __DIR__ . '/includes/api/ApiCheckToken.php',
        'ApiClearHasMsg' => __DIR__ . '/includes/api/ApiClearHasMsg.php',
        'ApiComparePages' => __DIR__ . '/includes/api/ApiComparePages.php',
        'ApiCreateAccount' => __DIR__ . '/includes/api/ApiCreateAccount.php',
@@@ -52,7 -51,6 +52,7 @@@
        'ApiLogin' => __DIR__ . '/includes/api/ApiLogin.php',
        'ApiLogout' => __DIR__ . '/includes/api/ApiLogout.php',
        'ApiMain' => __DIR__ . '/includes/api/ApiMain.php',
 +      'ApiManageTags' => __DIR__ . '/includes/api/ApiManageTags.php',
        'ApiModuleManager' => __DIR__ . '/includes/api/ApiModuleManager.php',
        'ApiMove' => __DIR__ . '/includes/api/ApiMove.php',
        'ApiOpenSearch' => __DIR__ . '/includes/api/ApiOpenSearch.php',
        'BackupDumper' => __DIR__ . '/maintenance/backup.inc',
        'BackupReader' => __DIR__ . '/maintenance/importDump.php',
        'BadTitleError' => __DIR__ . '/includes/exception/BadTitleError.php',
 -      'BagOStuff' => __DIR__ . '/includes/objectcache/BagOStuff.php',
 +      'BagOStuff' => __DIR__ . '/includes/libs/objectcache/BagOStuff.php',
        'BaseDump' => __DIR__ . '/maintenance/backupPrefetch.inc',
        'BaseTemplate' => __DIR__ . '/includes/skins/BaseTemplate.php',
        'BatchedQueryRunner' => __DIR__ . '/maintenance/runBatchedQuery.php',
        'Blob' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'Block' => __DIR__ . '/includes/Block.php',
        'BlockListPager' => __DIR__ . '/includes/specials/SpecialBlockList.php',
 +      'BlockLogFormatter' => __DIR__ . '/includes/logging/BlockLogFormatter.php',
        'BloomCache' => __DIR__ . '/includes/cache/bloom/BloomCache.php',
        'BloomCacheRedis' => __DIR__ . '/includes/cache/bloom/BloomCacheRedis.php',
        'BloomFilterTitleHasLogs' => __DIR__ . '/includes/cache/bloom/BloomFilters.php',
        'DumpPipeOutput' => __DIR__ . '/includes/Export.php',
        'DumpRenderer' => __DIR__ . '/maintenance/renderDump.php',
        'DumpRev' => __DIR__ . '/maintenance/storage/dumpRev.php',
 -      'DumpSisterSites' => __DIR__ . '/maintenance/dumpSisterSites.php',
        'DuplicateJob' => __DIR__ . '/includes/jobqueue/jobs/DuplicateJob.php',
        'EditAction' => __DIR__ . '/includes/actions/EditAction.php',
        'EditCLI' => __DIR__ . '/maintenance/edit.php',
        'EmailInvalidation' => __DIR__ . '/includes/specials/SpecialConfirmemail.php',
        'EmailNotification' => __DIR__ . '/includes/mail/EmailNotification.php',
        'EmaillingJob' => __DIR__ . '/includes/jobqueue/jobs/EmaillingJob.php',
 -      'EmptyBagOStuff' => __DIR__ . '/includes/objectcache/EmptyBagOStuff.php',
 +      'EmptyBagOStuff' => __DIR__ . '/includes/libs/objectcache/EmptyBagOStuff.php',
        'EmptyBloomCache' => __DIR__ . '/includes/cache/bloom/BloomCache.php',
        'EncryptedPassword' => __DIR__ . '/includes/password/EncryptedPassword.php',
        'EnhancedChangesList' => __DIR__ . '/includes/changes/EnhancedChangesList.php',
        'ExifBitmapHandler' => __DIR__ . '/includes/media/ExifBitmap.php',
        'ExplodeIterator' => __DIR__ . '/includes/libs/ExplodeIterator.php',
        'ExportProgressFilter' => __DIR__ . '/maintenance/backup.inc',
 +      'ExportSites' => __DIR__ . '/maintenance/exportSites.php',
        'ExtensionLanguages' => __DIR__ . '/maintenance/language/languages.inc',
        'ExtensionProcessor' => __DIR__ . '/includes/registration/ExtensionProcessor.php',
        'ExtensionRegistry' => __DIR__ . '/includes/registration/ExtensionRegistry.php',
        'HTMLTextAreaField' => __DIR__ . '/includes/htmlform/HTMLTextAreaField.php',
        'HTMLTextField' => __DIR__ . '/includes/htmlform/HTMLTextField.php',
        'HWLDFWordAccumulator' => __DIR__ . '/includes/diff/DairikiDiff.php',
 -      'HashBagOStuff' => __DIR__ . '/includes/objectcache/HashBagOStuff.php',
 +      'HashBagOStuff' => __DIR__ . '/includes/libs/objectcache/HashBagOStuff.php',
        'HashConfig' => __DIR__ . '/includes/config/HashConfig.php',
        'HashRing' => __DIR__ . '/includes/libs/HashRing.php',
        'HashtableReplacer' => __DIR__ . '/includes/libs/replacers/HashtableReplacer.php',
        'ImageQueryPage' => __DIR__ . '/includes/specialpage/ImageQueryPage.php',
        'ImportReporter' => __DIR__ . '/includes/specials/SpecialImport.php',
        'ImportSiteScripts' => __DIR__ . '/maintenance/importSiteScripts.php',
 +      'ImportSites' => __DIR__ . '/maintenance/importSites.php',
 +      'ImportSource' => __DIR__ . '/includes/Import.php',
        'ImportStreamSource' => __DIR__ . '/includes/Import.php',
        'ImportStringSource' => __DIR__ . '/includes/Import.php',
        'ImportTitleFactory' => __DIR__ . '/includes/title/ImportTitleFactory.php',
        'MWLoggerMonologProcessor' => __DIR__ . '/includes/debug/logger/monolog/Processor.php',
        'MWLoggerMonologSamplingHandler' => __DIR__ . '/includes/debug/logger/monolog/SamplingHandler.php',
        'MWLoggerMonologSpi' => __DIR__ . '/includes/debug/logger/monolog/Spi.php',
 +      'MWLoggerMonologSyslogHandler' => __DIR__ . '/includes/debug/logger/monolog/SyslogHandler.php',
        'MWLoggerNullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php',
        'MWLoggerSpi' => __DIR__ . '/includes/debug/logger/Spi.php',
        'MWMemcached' => __DIR__ . '/includes/objectcache/MemcachedClient.php',
        'MediaHandler' => __DIR__ . '/includes/media/MediaHandler.php',
        'MediaStatisticsPage' => __DIR__ . '/includes/specials/SpecialMediaStatistics.php',
        'MediaTransformError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
 +      'MediaTransformInvalidParametersException' => __DIR__ . '/includes/media/MediaTransformInvalidParametersException.php',
        'MediaTransformOutput' => __DIR__ . '/includes/media/MediaTransformOutput.php',
        'MediaWiki' => __DIR__ . '/includes/MediaWiki.php',
 -      'MediaWikiBagOStuff' => __DIR__ . '/includes/objectcache/SqlBagOStuff.php',
        'MediaWikiI18N' => __DIR__ . '/includes/skins/MediaWikiI18N.php',
        'MediaWikiPageLinkRenderer' => __DIR__ . '/includes/title/MediaWikiPageLinkRenderer.php',
        'MediaWikiSite' => __DIR__ . '/includes/site/MediaWikiSite.php',
        'MessageBlobStore' => __DIR__ . '/includes/MessageBlobStore.php',
        'MessageCache' => __DIR__ . '/includes/cache/MessageCache.php',
        'MessageContent' => __DIR__ . '/includes/content/MessageContent.php',
 +      'MessageSpecifier' => __DIR__ . '/includes/libs/MessageSpecifier.php',
        'MigrateUserGroup' => __DIR__ . '/maintenance/migrateUserGroup.php',
        'MimeMagic' => __DIR__ . '/includes/MimeMagic.php',
        'MinifyScript' => __DIR__ . '/maintenance/minify.php',
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
        'PopulateRevisionSha1' => __DIR__ . '/maintenance/populateRevisionSha1.php',
        'PostgreSqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/DBLockManager.php',
+       'PostgresBlob' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'PostgresField' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'PostgresInstaller' => __DIR__ . '/includes/installer/PostgresInstaller.php',
        'PostgresTransactionState' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'RebuildSitesCache' => __DIR__ . '/maintenance/rebuildSitesCache.php',
        'RebuildTextIndex' => __DIR__ . '/maintenance/rebuildtextindex.php',
        'RecentChange' => __DIR__ . '/includes/changes/RecentChange.php',
 +      'RecentChangesUpdateJob' => __DIR__ . '/includes/jobqueue/jobs/RecentChangesUpdateJob.php',
        'RecompressTracked' => __DIR__ . '/maintenance/storage/recompressTracked.php',
        'RedirectSpecialArticle' => __DIR__ . '/includes/specialpage/RedirectSpecialPage.php',
        'RedirectSpecialPage' => __DIR__ . '/includes/specialpage/RedirectSpecialPage.php',
        'RefreshImageMetadata' => __DIR__ . '/maintenance/refreshImageMetadata.php',
        'RefreshLinks' => __DIR__ . '/maintenance/refreshLinks.php',
        'RefreshLinksJob' => __DIR__ . '/includes/jobqueue/jobs/RefreshLinksJob.php',
 -      'RefreshLinksJob2' => __DIR__ . '/includes/jobqueue/jobs/RefreshLinksJob2.php',
        'RegexlikeReplacer' => __DIR__ . '/includes/libs/replacers/RegexlikeReplacer.php',
        'RemoveInvalidEmails' => __DIR__ . '/maintenance/removeInvalidEmails.php',
        'RemoveUnusedAccounts' => __DIR__ . '/maintenance/removeUnusedAccounts.php',
        'RenderAction' => __DIR__ . '/includes/actions/RenderAction.php',
        'ReplacementArray' => __DIR__ . '/includes/libs/ReplacementArray.php',
        'Replacer' => __DIR__ . '/includes/libs/replacers/Replacer.php',
 +      'ReplicatedBagOStuff' => __DIR__ . '/includes/objectcache/ReplicatedBagOStuff.php',
        'RepoGroup' => __DIR__ . '/includes/filerepo/RepoGroup.php',
        'RequestContext' => __DIR__ . '/includes/context/RequestContext.php',
        'ResetUserTokens' => __DIR__ . '/maintenance/resetUserTokens.php',
        'Site' => __DIR__ . '/includes/site/Site.php',
        'SiteArray' => __DIR__ . '/includes/site/SiteList.php',
        'SiteConfiguration' => __DIR__ . '/includes/SiteConfiguration.php',
 +      'SiteExporter' => __DIR__ . '/includes/site/SiteExporter.php',
 +      'SiteImporter' => __DIR__ . '/includes/site/SiteImporter.php',
        'SiteList' => __DIR__ . '/includes/site/SiteList.php',
        'SiteListFileCache' => __DIR__ . '/includes/site/SiteListFileCache.php',
        'SiteListFileCacheBuilder' => __DIR__ . '/includes/site/SiteListFileCacheBuilder.php',
        'StatCounter' => __DIR__ . '/includes/StatCounter.php',
        'StatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'Status' => __DIR__ . '/includes/Status.php',
 +      'StatusValue' => __DIR__ . '/includes/libs/StatusValue.php',
        'StorageTypeStats' => __DIR__ . '/maintenance/storage/storageTypeStats.php',
        'StoreFileOp' => __DIR__ . '/includes/filebackend/FileOp.php',
        'StreamFile' => __DIR__ . '/includes/StreamFile.php',
        'TablePager' => __DIR__ . '/includes/pager/TablePager.php',
        'TempFSFile' => __DIR__ . '/includes/filebackend/TempFSFile.php',
        'TempFileRepo' => __DIR__ . '/includes/filerepo/FileRepo.php',
 +      'TemplateParser' => __DIR__ . '/includes/TemplateParser.php',
        'TestFileOpPerformance' => __DIR__ . '/maintenance/fileOpPerfTest.php',
        'TextContent' => __DIR__ . '/includes/content/TextContent.php',
        'TextContentHandler' => __DIR__ . '/includes/content/TextContentHandler.php',
        'WikiStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
 -      'WinCacheBagOStuff' => __DIR__ . '/includes/objectcache/WinCacheBagOStuff.php',
 +      'WinCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/WinCacheBagOStuff.php',
        'WithoutInterwikiPage' => __DIR__ . '/includes/specials/SpecialWithoutinterwiki.php',
        'WordLevelDiff' => __DIR__ . '/includes/diff/DairikiDiff.php',
        'WrapOldPasswords' => __DIR__ . '/maintenance/wrapOldPasswords.php',
        'XCFHandler' => __DIR__ . '/includes/media/XCF.php',
 -      'XCacheBagOStuff' => __DIR__ . '/includes/objectcache/XCacheBagOStuff.php',
 +      'XCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/XCacheBagOStuff.php',
        'XMLRCFeedFormatter' => __DIR__ . '/includes/rcfeed/XMLRCFeedFormatter.php',
        'XMPInfo' => __DIR__ . '/includes/media/XMPInfo.php',
        'XMPReader' => __DIR__ . '/includes/media/XMP.php',
diff --combined includes/db/Database.php
@@@ -46,6 -46,9 +46,6 @@@ abstract class DatabaseBase implements 
        /** Maximum time to wait before retry */
        const DEADLOCK_DELAY_MAX = 1500000;
  
 -      /** How many row changes in a write query trigger a log entry */
 -      const LOG_WRITE_THRESHOLD = 300;
 -
        protected $mLastQuery = '';
        protected $mDoneWrites = false;
        protected $mPHPError = false;
         *
         * @param array $params Parameters passed from DatabaseBase::factory()
         */
 -      function __construct( $params = null ) {
 +      function __construct( array $params ) {
                global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode, $wgDebugDBTransactions;
  
                $this->mTrxAtomicLevels = new SplStack;
  
 -              if ( is_array( $params ) ) { // MW 1.22
 -                      $server = $params['host'];
 -                      $user = $params['user'];
 -                      $password = $params['password'];
 -                      $dbName = $params['dbname'];
 -                      $flags = $params['flags'];
 -                      $tablePrefix = $params['tablePrefix'];
 -                      $schema = $params['schema'];
 -                      $foreign = $params['foreign'];
 -              } else { // legacy calling pattern
 -                      wfDeprecated( __METHOD__ . " method called without parameter array.", "1.23" );
 -                      $args = func_get_args();
 -                      $server = isset( $args[0] ) ? $args[0] : false;
 -                      $user = isset( $args[1] ) ? $args[1] : false;
 -                      $password = isset( $args[2] ) ? $args[2] : false;
 -                      $dbName = isset( $args[3] ) ? $args[3] : false;
 -                      $flags = isset( $args[4] ) ? $args[4] : 0;
 -                      $tablePrefix = isset( $args[5] ) ? $args[5] : 'get from global';
 -                      $schema = 'get from global';
 -                      $foreign = isset( $args[6] ) ? $args[6] : false;
 -              }
 +              $server = $params['host'];
 +              $user = $params['user'];
 +              $password = $params['password'];
 +              $dbName = $params['dbname'];
 +              $flags = $params['flags'];
 +              $tablePrefix = $params['tablePrefix'];
 +              $schema = $params['schema'];
 +              $foreign = $params['foreign'];
  
                $this->mFlags = $flags;
                if ( $this->mFlags & DBO_DEFAULT ) {
                if ( $user ) {
                        $this->open( $server, $user, $password, $dbName );
                }
 +
 +              $isMaster = !is_null( $this->getLBInfo( 'master' ) );
 +              $trxProf = Profiler::instance()->getTransactionProfiler();
 +              $trxProf->recordConnection( $this->mServer, $this->mDBname, $isMaster );
        }
  
        /**
  
                $class = 'Database' . ucfirst( $driver );
                if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
 -                      $params = array(
 -                              'host' => isset( $p['host'] ) ? $p['host'] : false,
 -                              'user' => isset( $p['user'] ) ? $p['user'] : false,
 -                              'password' => isset( $p['password'] ) ? $p['password'] : false,
 -                              'dbname' => isset( $p['dbname'] ) ? $p['dbname'] : false,
 -                              'flags' => isset( $p['flags'] ) ? $p['flags'] : 0,
 -                              'tablePrefix' => isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global',
 -                              'schema' => isset( $p['schema'] ) ? $p['schema'] : $defaultSchemas[$dbType],
 -                              'foreign' => isset( $p['foreign'] ) ? $p['foreign'] : false
 -                      );
 -
 -                      return new $class( $params );
 +                      // Resolve some defaults for b/c
 +                      $p['host'] = isset( $p['host'] ) ? $p['host'] : false;
 +                      $p['user'] = isset( $p['user'] ) ? $p['user'] : false;
 +                      $p['password'] = isset( $p['password'] ) ? $p['password'] : false;
 +                      $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false;
 +                      $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0;
 +                      $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global';
 +                      $p['schema'] = isset( $p['schema'] ) ? $p['schema'] : $defaultSchemas[$dbType];
 +                      $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false;
 +
 +                      return new $class( $p );
                } else {
                        return null;
                }
  
                $isWriteQuery = $this->isWriteQuery( $sql );
                if ( $isWriteQuery ) {
 +                      if ( !$this->mDoneWrites ) {
 +                              wfDebug( __METHOD__ . ': Writes done: ' .
 +                                      DatabaseBase::generalizeSQL( $sql ) . "\n" );
 +                      }
                        # Set a flag indicating that writes have been done
 -                      wfDebug( __METHOD__ . ': Writes done: ' . DatabaseBase::generalizeSQL( $sql ) . "\n" );
                        $this->mDoneWrites = microtime( true );
                }
  
                                $this->mServer, $this->mDBname, $this->mTrxShortId );
                }
  
 -              $queryProf = '';
 -              $totalProf = '';
                $isMaster = !is_null( $this->getLBInfo( 'master' ) );
 +              # generalizeSQL will probably cut down the query to reasonable
 +              # logging size most of the time. The substr is really just a sanity check.
 +              if ( $isMaster ) {
 +                      $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
 +                      $totalProf = 'DatabaseBase::query-master';
 +              } else {
 +                      $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
 +                      $totalProf = 'DatabaseBase::query';
 +              }
 +              # Include query transaction state
 +              $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
  
                $profiler = Profiler::instance();
                if ( !$profiler instanceof ProfilerStub ) {
 -                      # generalizeSQL will probably cut down the query to reasonable
 -                      # logging size most of the time. The substr is really just a sanity check.
 -                      if ( $isMaster ) {
 -                              $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
 -                              $totalProf = 'DatabaseBase::query-master';
 -                      } else {
 -                              $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
 -                              $totalProf = 'DatabaseBase::query';
 -                      }
 -                      # Include query transaction state
 -                      $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
 -
                        $totalProfSection = $profiler->scopedProfileIn( $totalProf );
                        $queryProfSection = $profiler->scopedProfileIn( $queryProf );
                }
                        throw new DBUnexpectedError( $this, "DB connection was already closed." );
                }
  
 -              # Log the query time and feed it into the DB trx profiler
 -              if ( $queryProf != '' ) {
 -                      $queryStartTime = microtime( true );
 -                      $queryProfile = new ScopedCallback(
 -                              function () use ( $queryStartTime, $queryProf, $isMaster ) {
 -                                      $trxProfiler = Profiler::instance()->getTransactionProfiler();
 -                                      $trxProfiler->recordQueryCompletion( $queryProf, $queryStartTime, $isMaster );
 -                              }
 -                      );
 -              }
 -
                # Do the query and handle errors
 +              $startTime = microtime( true );
                $ret = $this->doQuery( $commentedSql );
 +              # Log the query time and feed it into the DB trx profiler
 +              $profiler->getTransactionProfiler()->recordQueryCompletion(
 +                      $queryProf, $startTime, $isWriteQuery, $this->affectedRows() );
  
                MWDebug::queryTime( $queryId );
  
                                        $this->reportQueryError( $lastError, $lastErrno, $sql, $fname, $tempIgnore );
                                } else {
                                        # Should be safe to silently retry (no trx and thus no callbacks)
 +                                      $startTime = microtime( true );
                                        $ret = $this->doQuery( $commentedSql );
 +                                      # Log the query time and feed it into the DB trx profiler
 +                                      $profiler->getTransactionProfiler()->recordQueryCompletion(
 +                                              $queryProf, $startTime, $isWriteQuery, $this->affectedRows() );
                                }
                        } else {
                                wfDebug( "Failed\n" );
                }
  
                if ( false === $ret ) {
 -                      $this->reportQueryError( $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
 -              } else {
 -                      $n = $this->affectedRows();
 -                      if ( $isWriteQuery && $n > self::LOG_WRITE_THRESHOLD && PHP_SAPI !== 'cli' ) {
 -                              wfDebugLog( 'DBPerformance',
 -                                      "Query affected $n rows:\n" .
 -                                      DatabaseBase::generalizeSQL( $sql ) . "\n" . wfBacktrace( true ) );
 -                      }
 +                      $this->reportQueryError(
 +                              $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
                }
  
                $res = $this->resultObject( $ret );
         *
         * @return bool|mixed The value from the field, or false on failure.
         */
 -      public function selectField( $table, $var, $cond = '', $fname = __METHOD__,
 -              $options = array()
 +      public function selectField(
 +              $table, $var, $cond = '', $fname = __METHOD__, $options = array()
        ) {
 +              if ( $var === '*' ) { // sanity
 +                      throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
 +              }
 +
                if ( !is_array( $options ) ) {
                        $options = array( $options );
                }
                $options['LIMIT'] = 1;
  
                $res = $this->select( $table, $var, $cond, $fname, $options );
 -
                if ( $res === false || !$this->numRows( $res ) ) {
                        return false;
                }
                }
        }
  
 +      /**
 +       * A SELECT wrapper which returns a list of single field values from result rows.
 +       *
 +       * Usually throws a DBQueryError on failure. If errors are explicitly
 +       * ignored, returns false on failure.
 +       *
 +       * If no result rows are returned from the query, false is returned.
 +       *
 +       * @param string|array $table Table name. See DatabaseBase::select() for details.
 +       * @param string $var The field name to select. This must be a valid SQL
 +       *   fragment: do not use unvalidated user input.
 +       * @param string|array $cond The condition array. See DatabaseBase::select() for details.
 +       * @param string $fname The function name of the caller.
 +       * @param string|array $options The query options. See DatabaseBase::select() for details.
 +       *
 +       * @return bool|array The values from the field, or false on failure
 +       * @since 1.25
 +       */
 +      public function selectFieldValues(
 +              $table, $var, $cond = '', $fname = __METHOD__, $options = array()
 +      ) {
 +              if ( $var === '*' ) { // sanity
 +                      throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
 +              }
 +
 +              if ( !is_array( $options ) ) {
 +                      $options = array( $options );
 +              }
 +
 +              $res = $this->select( $table, $var, $cond, $fname, $options );
 +              if ( $res === false ) {
 +                      return false;
 +              }
 +
 +              $values = array();
 +              foreach ( $res as $row ) {
 +                      $values[] = $row->$var;
 +              }
 +
 +              return $values;
 +      }
 +
        /**
         * Returns an optional USE INDEX clause to go after the table, and a
         * string to go at the end of the query.
         *     - If the value of such an array element is a scalar (such as a
         *       string), it will be treated as data and thus quoted appropriately.
         *       If it is null, an IS NULL clause will be added.
 -       *     - If the value is an array, an IN(...) clause will be constructed,
 -       *       such that the field name may match any of the elements in the
 -       *       array. The elements of the array will be quoted.
 +       *     - If the value is an array, an IN (...) clause will be constructed
 +       *       from its non-null elements, and an IS NULL clause will be added
 +       *       if null is present, such that the field may match any of the
 +       *       elements in the array. The non-null elements will be quoted.
         *
         * Note that expressions are often DBMS-dependent in their syntax.
         * DBMS-independent wrappers are provided for constructing several types of
  
                if ( $res ) {
                        $row = $this->fetchRow( $res );
 -                      $rows = ( isset( $row['rowcount'] ) ) ? $row['rowcount'] : 0;
 +                      $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
                }
  
                return $rows;
  
                if ( $res ) {
                        $row = $this->fetchRow( $res );
 -                      $rows = ( isset( $row['rowcount'] ) ) ? $row['rowcount'] : 0;
 +                      $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
                }
  
                return $rows;
        /**
         * Adds quotes and backslashes.
         *
-        * @param string $s
+        * @param string|Blob $s
         * @return string
         */
        public function addQuotes( $s ) {
+               if ( $s instanceof Blob ) {
+                       $s = $s->fetch();
+               }
                if ( $s === null ) {
                        return 'NULL';
                } else {
         * in result objects. Pass the object through this function to return the
         * original string.
         *
-        * @param string $b
+        * @param string|Blob $b
         * @return string
         */
        public function decodeBlob( $b ) {
+               if ( $b instanceof Blob ) {
+                       $b = $b->fetch();
+               }
                return $b;
        }
  
@@@ -518,7 -518,7 +518,7 @@@ class DatabaseMssql extends DatabaseBas
                        $row = $this->fetchRow( $res );
  
                        if ( isset( $row['EstimateRows'] ) ) {
 -                              $rows = $row['EstimateRows'];
 +                              $rows = (int)$row['EstimateRows'];
                        }
                }
  
                                'DatabaseBase::makeList called with incorrect parameters' );
                }
  
 -              $first = true;
 -              $list = '';
 +              if ( $mode != LIST_NAMES ) {
 +                      // In MS SQL, values need to be specially encoded when they are
 +                      // inserted into binary fields. Perform this necessary encoding
 +                      // for the specified set of columns.
 +                      foreach ( array_keys( $a ) as $field ) {
 +                              if ( !isset( $binaryColumns[$field] ) ) {
 +                                      continue;
 +                              }
  
 -              foreach ( $a as $field => $value ) {
 -                      if ( $mode != LIST_NAMES && isset( $binaryColumns[$field] ) ) {
 -                              if ( is_array( $value ) ) {
 -                                      foreach ( $value as &$v ) {
 +                              if ( is_array( $a[$field] ) ) {
 +                                      foreach ( $a[$field] as &$v ) {
                                                $v = new MssqlBlob( $v );
                                        }
 +                                      unset( $v );
                                } else {
 -                                      $value = new MssqlBlob( $value );
 -                              }
 -                      }
 -
 -                      if ( !$first ) {
 -                              if ( $mode == LIST_AND ) {
 -                                      $list .= ' AND ';
 -                              } elseif ( $mode == LIST_OR ) {
 -                                      $list .= ' OR ';
 -                              } else {
 -                                      $list .= ',';
 -                              }
 -                      } else {
 -                              $first = false;
 -                      }
 -
 -                      if ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_numeric( $field ) ) {
 -                              $list .= "($value)";
 -                      } elseif ( ( $mode == LIST_SET ) && is_numeric( $field ) ) {
 -                              $list .= "$value";
 -                      } elseif ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_array( $value ) ) {
 -                              if ( count( $value ) == 0 ) {
 -                                      throw new MWException( __METHOD__ . ": empty input for field $field" );
 -                              } elseif ( count( $value ) == 1 ) {
 -                                      // Special-case single values, as IN isn't terribly efficient
 -                                      // Don't necessarily assume the single key is 0; we don't
 -                                      // enforce linear numeric ordering on other arrays here.
 -                                      $value = array_values( $value );
 -                                      $list .= $field . " = " . $this->addQuotes( $value[0] );
 -                              } else {
 -                                      $list .= $field . " IN (" . $this->makeList( $value ) . ") ";
 -                              }
 -                      } elseif ( $value === null ) {
 -                              if ( $mode == LIST_AND || $mode == LIST_OR ) {
 -                                      $list .= "$field IS ";
 -                              } elseif ( $mode == LIST_SET ) {
 -                                      $list .= "$field = ";
 -                              }
 -                              $list .= 'NULL';
 -                      } else {
 -                              if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
 -                                      $list .= "$field = ";
 +                                      $a[$field] = new MssqlBlob( $a[$field] );
                                }
 -                              $list .= $mode == LIST_NAMES ? $value : $this->addQuotes( $value );
                        }
                }
  
 -              return $list;
 +              return parent::makeList( $a, $mode );
        }
  
        /**
        }
  
        /**
-        * @param string $s
+        * @param string|Blob $s
         * @return string
         */
        public function addQuotes( $s ) {
@@@ -712,7 -712,7 +712,7 @@@ class DatabasePostgres extends Database
                        $row = $this->fetchRow( $res );
                        $count = array();
                        if ( preg_match( '/rows=(\d+)/', $row[0], $count ) ) {
 -                              $rows = $count[1];
 +                              $rows = (int)$count[1];
                        }
                }
  
         * @return Blob
         */
        function encodeBlob( $b ) {
-               return new Blob( pg_escape_bytea( $this->mConn, $b ) );
+               return new PostgresBlob( pg_escape_bytea( $b ) );
        }
  
        function decodeBlob( $b ) {
-               if ( $b instanceof Blob ) {
+               if ( $b instanceof PostgresBlob ) {
                        $b = $b->fetch();
+               } elseif ( $b instanceof Blob ) {
+                       return $b->fetch();
                }
  
                return pg_unescape_bytea( $b );
                } elseif ( is_bool( $s ) ) {
                        return intval( $s );
                } elseif ( $s instanceof Blob ) {
-                       return "'" . $s->fetch( $s ) . "'";
+                       if ( $s instanceof PostgresBlob ) {
+                               $s = $s->fetch();
+                       } else {
+                               $s = pg_escape_bytea( $this->mConn, $s->fetch() );
+                       }
+                       return "'$s'";
                }
  
                return "'" . pg_escape_string( $this->mConn, $s ) . "'";
                return wfBaseConvert( substr( sha1( $lockName ), 0, 15 ), 16, 10 );
        }
  } // end DatabasePostgres class
+ class PostgresBlob extends Blob {}