/** * Return the history of this file, line by line. * starts with current version, then old versions. * uses $this->historyLine to check which line to return: * 0 return line for current version * 1 query for old versions, return first one * 2, ... return next old version from above query * @return bool */ public function nextHistoryLine() { # Polymorphic function name to distinguish foreign and local fetches $fname = get_class( $this ) . '::' . __FUNCTION__; $dbr = $this->repo->getSlaveDB(); if ( $this->historyLine == 0 ) {// called for the first time, return line from cur $this->historyRes = $dbr->select( 'image', array( '*', "'' AS oi_archive_name", '0 as oi_deleted', 'img_sha1' ), array( 'img_name' => $this->title->getDBkey() ), $fname ); if ( 0 == $dbr->numRows( $this->historyRes ) ) { $this->historyRes = null; return false; } } elseif ( $this->historyLine == 1 ) { $this->historyRes = $dbr->select( 'oldimage', '*', array( 'oi_name' => $this->title->getDBkey() ), $fname, array( 'ORDER BY' => 'oi_timestamp DESC' ) ); } $this->historyLine ++; return $dbr->fetchObject( $this->historyRes ); }
protected function checkFiles(LocalRepo $repo, array $paths, $verbose) { if (!count($paths)) { return; } $dbr = $repo->getSlaveDB(); $curNames = []; $oldNames = []; $imgIN = []; $oiWheres = []; foreach ($paths as $path) { $name = basename($path); if (preg_match('#^archive/#', $path)) { if ($verbose) { $this->output("Checking old file {$name}\n"); } $oldNames[] = $name; list(, $base) = explode('!', $name, 2); // <TS_MW>!<img_name> $oiWheres[] = $dbr->makeList(['oi_name' => $base, 'oi_archive_name' => $name], LIST_AND); } else { if ($verbose) { $this->output("Checking current file {$name}\n"); } $curNames[] = $name; $imgIN[] = $name; } } $res = $dbr->query($dbr->unionQueries([$dbr->selectSQLText('image', ['name' => 'img_name', 'old' => 0], $imgIN ? ['img_name' => $imgIN] : '1=0'), $dbr->selectSQLText('oldimage', ['name' => 'oi_archive_name', 'old' => 1], $oiWheres ? $dbr->makeList($oiWheres, LIST_OR) : '1=0')], true), __METHOD__); $curNamesFound = []; $oldNamesFound = []; foreach ($res as $row) { if ($row->old) { $oldNamesFound[] = $row->name; } else { $curNamesFound[] = $row->name; } } foreach (array_diff($curNames, $curNamesFound) as $name) { $file = $repo->newFile($name); // Print name and public URL to ease recovery if ($file) { $this->output($name . "\n" . $file->getCanonicalUrl() . "\n\n"); } else { $this->error("Cannot get URL for bad file title '{$name}'"); } } foreach (array_diff($oldNames, $oldNamesFound) as $name) { list(, $base) = explode('!', $name, 2); // <TS_MW>!<img_name> $file = $repo->newFromArchiveName(Title::makeTitle(NS_FILE, $base), $name); // Print name and public URL to ease recovery $this->output($name . "\n" . $file->getCanonicalUrl() . "\n\n"); } }
/** * Create a OldLocalFile from a SHA-1 key * Do not call this except from inside a repo class. * * @param string $sha1 Base-36 SHA-1 * @param LocalRepo $repo * @param string|bool $timestamp MW_timestamp (optional) * * @return bool|OldLocalFile */ static function newFromKey($sha1, $repo, $timestamp = false) { $dbr = $repo->getSlaveDB(); $conds = array('oi_sha1' => $sha1); if ($timestamp) { $conds['oi_timestamp'] = $dbr->timestamp($timestamp); } $row = $dbr->selectRow('oldimage', self::selectFields(), $conds, __METHOD__); if ($row) { return self::newFromRow($row, $repo); } else { return false; } }
/** * Helper function: do the actual database query to fetch file metadata. * * @param string $key * @param int $readFromDB Constant (default: DB_REPLICA) * @return bool */ protected function fetchFileMetadata($key, $readFromDB = DB_REPLICA) { // populate $fileMetadata[$key] $dbr = null; if ($readFromDB === DB_MASTER) { // sometimes reading from the master is necessary, if there's replication lag. $dbr = $this->repo->getMasterDB(); } else { $dbr = $this->repo->getSlaveDB(); } $row = $dbr->selectRow('uploadstash', '*', ['us_key' => $key], __METHOD__); if (!is_object($row)) { // key wasn't present in the database. this will happen sometimes. return false; } $this->fileMetadata[$key] = (array) $row; $this->fileMetadata[$key]['us_props'] = $dbr->decodeBlob($row->us_props); return true; }
protected function checkFiles(LocalRepo $repo, array $names, $verbose) { if (!count($names)) { return; } $dbr = $repo->getSlaveDB(); $imgIN = array(); $oiWheres = array(); foreach ($names as $name) { if (strpos($name, '!') !== false) { if ($verbose) { $this->output("Checking old file {$name}\n"); } list(, $base) = explode('!', $name); // <TS_MW>!<img_name> $oiWheres[] = $dbr->makeList(array('oi_name' => $base, 'oi_archive_name' => $name), LIST_AND); } else { if ($verbose) { $this->output("Checking current file {$name}\n"); } $imgIN[] = $name; } } $res = $dbr->query($dbr->unionQueries(array($dbr->selectSQLText('image', array('name' => 'img_name'), array('img_name' => $imgIN)), $dbr->selectSQLText('oldimage', array('name' => 'oi_archive_name'), $dbr->makeList($oiWheres, LIST_OR))), true), __METHOD__); $namesFound = array(); foreach ($res as $row) { $namesFound[] = $row->name; } $namesOrphans = array_diff($names, $namesFound); foreach ($namesOrphans as $name) { // Print name and public URL to ease recovery if (strpos($name, '!') !== false) { list(, $base) = explode('!', $name); // <TS_MW>!<img_name> $file = $repo->newFromArchiveName(Title::makeTitle(NS_FILE, $base), $name); } else { $file = $repo->newFile($name); } $this->output($name . "\n" . $file->getUrl() . "\n\n"); } }
protected function purgeFromArchiveTable(LocalRepo $repo, LocalFile $file) { $dbr = $repo->getSlaveDB(); $res = $dbr->select('filearchive', array('fa_archive_name'), array('fa_name' => $file->getName()), __METHOD__); foreach ($res as $row) { if ($row->fa_archive_name === null) { // Was not an old version (current version names checked already) continue; } $ofile = $repo->newFromArchiveName($file->getTitle(), $row->fa_archive_name); // If there is an orphaned storage file still there...delete it if (!$file->exists() && $repo->fileExists($ofile->getPath())) { $dpath = $this->getDeletedPath($repo, $ofile); if ($repo->fileExists($dpath)) { // Sanity check to avoid data loss $repo->getBackend()->delete(array('src' => $ofile->getPath())); $this->output("Deleted orphan file: {$ofile->getPath()}.\n"); } else { $this->error("File was not deleted: {$ofile->getPath()}.\n"); } } $file->purgeOldThumbnails($row->fa_archive_name); } }