* make additional queries to pull source data while the
* main query is still running.
*
- * @param $db Database
+ * @param $db DatabaseBase
* @param $history Mixed: one of WikiExporter::FULL, WikiExporter::CURRENT,
* WikiExporter::RANGE or WikiExporter::STABLE,
* or an associative array:
* the most recent version.
*/
public function allPages() {
- return $this->dumpFrom( '' );
+ $this->dumpFrom( '' );
}
/**
if ( $end ) {
$condition .= ' AND page_id < ' . intval( $end );
}
- return $this->dumpFrom( $condition );
+ $this->dumpFrom( $condition );
}
/**
if ( $end ) {
$condition .= ' AND rev_id < ' . intval( $end );
}
- return $this->dumpFrom( $condition );
+ $this->dumpFrom( $condition );
}
/**
* @param $title Title
*/
public function pageByTitle( $title ) {
- return $this->dumpFrom(
+ $this->dumpFrom(
'page_namespace=' . $title->getNamespace() .
' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) );
}
if ( is_null( $title ) ) {
throw new MWException( "Can't export invalid title" );
} else {
- return $this->pageByTitle( $title );
+ $this->pageByTitle( $title );
}
}
}
public function allLogs() {
- return $this->dumpFrom( '' );
+ $this->dumpFrom( '' );
}
public function logsByRange( $start, $end ) {
if ( $end ) {
$condition .= ' AND log_id < ' . intval( $end );
}
- return $this->dumpFrom( $condition );
+ $this->dumpFrom( $condition );
}
# Generates the distinct list of authors of an article
wfProfileIn( __METHOD__ );
# For logging dumps...
if ( $this->history & self::LOGS ) {
- if ( $this->buffer == WikiExporter::STREAM ) {
- $prev = $this->db->bufferResults( false );
- }
$where = array( 'user_id = log_user' );
# Hide private logs
$hideLogs = LogEventsList::getExcludeClause( $this->db );
if ( $cond ) $where[] = $cond;
# Get logging table name for logging.* clause
$logging = $this->db->tableName( 'logging' );
- $result = $this->db->select( array( 'logging', 'user' ),
- array( "{$logging}.*", 'user_name' ), // grab the user name
- $where,
- __METHOD__,
- array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) )
- );
- $wrapper = $this->db->resultObject( $result );
- $this->outputLogStream( $wrapper );
+
if ( $this->buffer == WikiExporter::STREAM ) {
- $this->db->bufferResults( $prev );
+ $prev = $this->db->bufferResults( false );
+ }
+ $wrapper = null; // Assuring $wrapper is not undefined, if exception occurs early
+ try {
+ $result = $this->db->select( array( 'logging', 'user' ),
+ array( "{$logging}.*", 'user_name' ), // grab the user name
+ $where,
+ __METHOD__,
+ array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) )
+ );
+ $wrapper = $this->db->resultObject( $result );
+ $this->outputLogStream( $wrapper );
+ if ( $this->buffer == WikiExporter::STREAM ) {
+ $this->db->bufferResults( $prev );
+ }
+ } catch ( Exception $e ) {
+ // Throwing the exception does not reliably free the resultset, and
+ // would also leave the connection in unbuffered mode.
+
+ // Freeing result
+ try {
+ if ( $wrapper ) {
+ $wrapper->free();
+ }
+ } catch ( Exception $e2 ) {
+ // Already in panic mode -> ignoring $e2 as $e has
+ // higher priority
+ }
+
+ // Putting database back in previous buffer mode
+ try {
+ if ( $this->buffer == WikiExporter::STREAM ) {
+ $this->db->bufferResults( $prev );
+ }
+ } catch ( Exception $e2 ) {
+ // Already in panic mode -> ignoring $e2 as $e has
+ // higher priority
+ }
+
+ // Inform caller about problem
+ throw $e;
}
# For page dumps...
} else {
$prev = $this->db->bufferResults( false );
}
- wfRunHooks( 'ModifyExportQuery',
+ $wrapper = null; // Assuring $wrapper is not undefined, if exception occurs early
+ try {
+ wfRunHooks( 'ModifyExportQuery',
array( $this->db, &$tables, &$cond, &$opts, &$join ) );
- # Do the query!
- $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join );
- $wrapper = $this->db->resultObject( $result );
- # Output dump results
- $this->outputPageStream( $wrapper );
- if ( $this->list_authors ) {
+ # Do the query!
+ $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join );
+ $wrapper = $this->db->resultObject( $result );
+ # Output dump results
$this->outputPageStream( $wrapper );
- }
- if ( $this->buffer == WikiExporter::STREAM ) {
- $this->db->bufferResults( $prev );
+ if ( $this->buffer == WikiExporter::STREAM ) {
+ $this->db->bufferResults( $prev );
+ }
+ } catch ( Exception $e ) {
+ // Throwing the exception does not reliably free the resultset, and
+ // would also leave the connection in unbuffered mode.
+
+ // Freeing result
+ try {
+ if ( $wrapper ) {
+ $wrapper->free();
+ }
+ } catch ( Exception $e2 ) {
+ // Already in panic mode -> ignoring $e2 as $e has
+ // higher priority
+ }
+
+ // Putting database back in previous buffer mode
+ try {
+ if ( $this->buffer == WikiExporter::STREAM ) {
+ $this->db->bufferResults( $prev );
+ }
+ } catch ( Exception $e2 ) {
+ // Already in panic mode -> ignoring $e2 as $e has
+ // higher priority
+ }
+
+ // Inform caller about problem
+ throw $e;
}
}
wfProfileOut( __METHOD__ );
* The result set should be sorted/grouped by page to avoid duplicate
* page records in the output.
*
- * The result set will be freed once complete. Should be safe for
+ * Should be safe for
* streaming (non-buffered) queries, as long as it was made on a
* separate database connection not managed by LoadBalancer; some
* blob storage types will make queries to pull source data.
function openPage( $row ) {
$out = " <page>\n";
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $out .= ' ' . Xml::elementClean( 'title', array(), $title->getPrefixedText() ) . "\n";
+ $out .= ' ' . Xml::elementClean( 'title', array(), self::canonicalTitle( $title ) ) . "\n";
$out .= ' ' . Xml::element( 'ns', array(), strval( $row->page_namespace) ) . "\n";
$out .= ' ' . Xml::element( 'id', array(), strval( $row->page_id ) ) . "\n";
if ( $row->page_is_redirect ) {
$page = WikiPage::factory( $title );
$redirect = $page->getRedirectTarget();
if ( $redirect instanceOf Title && $redirect->isValidRedirectTarget() ) {
- $out .= ' ' . Xml::element( 'redirect', array( 'title' => $redirect->getPrefixedText() ) ) . "\n";
+ $out .= ' ' . Xml::element( 'redirect', array( 'title' => self::canonicalTitle( $redirect ) ) ) . "\n";
}
}
+
if ( $row->page_restrictions != '' ) {
$out .= ' ' . Xml::element( 'restrictions', array(),
strval( $row->page_restrictions ) ) . "\n";
* Closes a <page> section on the output stream.
*
* @access private
+ * @return string
*/
function closePage() {
return " </page>\n";
// Raw text from the database may have invalid chars
$text = strval( Revision::getRevisionText( $row ) );
$out .= " " . Xml::elementClean( 'text',
- array( 'xml:space' => 'preserve', 'bytes' => $row->rev_len ),
+ array( 'xml:space' => 'preserve', 'bytes' => intval( $row->rev_len ) ),
strval( $text ) ) . "\n";
} else {
// Stub output
$out .= " " . Xml::element( 'text',
- array( 'id' => $row->rev_text_id, 'bytes' => $row->rev_len ),
+ array( 'id' => $row->rev_text_id, 'bytes' => intval( $row->rev_len ) ),
"" ) . "\n";
}
+ if ( $row->rev_sha1 && !( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
+ $out .= " " . Xml::element('sha1', null, strval( $row->rev_sha1 ) ) . "\n";
+ } else {
+ $out .= " <sha1/>\n";
+ }
+
wfRunHooks( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
$out .= " </revision>\n";
$out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
} else {
$title = Title::makeTitle( $row->log_namespace, $row->log_title );
- $out .= " " . Xml::elementClean( 'logtitle', null, $title->getPrefixedText() ) . "\n";
+ $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
$out .= " " . Xml::elementClean( 'params',
array( 'xml:space' => 'preserve' ),
strval( $row->log_params ) ) . "\n";
/**
* Warning! This data is potentially inconsistent. :(
+ * @return string
*/
function writeUploads( $row, $dumpContents = false ) {
if ( $row->page_namespace == NS_IMAGE ) {
" </upload>\n";
}
+ /**
+ * Return prefixed text form of title, but using the content language's
+ * canonical namespace. This skips any special-casing such as gendered
+ * user namespaces -- which while useful, are not yet listed in the
+ * XML <siteinfo> data so are unsafe in export.
+ *
+ * @param Title $title
+ * @return string
+ * @since 1.18
+ */
+ public static function canonicalTitle( Title $title ) {
+ if ( $title->getInterwiki() ) {
+ return $title->getPrefixedText();
+ }
+
+ global $wgContLang;
+ $prefix = str_replace( '_', ' ', $wgContLang->getNsText( $title->getNamespace() ) );
+
+ if ( $prefix !== '' ) {
+ $prefix .= ':';
+ }
+
+ return $prefix . $title->getText();
+ }
}
/**
* Returns the name of the file or files which are
* being written to, if there are any.
+ * @return null
*/
function getFilenames() {
return NULL;
* @ingroup Dump
*/
class DumpFileOutput extends DumpOutput {
- protected $handle, $filename;
+ protected $handle = false, $filename;
function __construct( $file ) {
$this->handle = fopen( $file, "wt" );
$this->filename = $file;
}
+ function writeCloseStream( $string ) {
+ parent::writeCloseStream( $string );
+ if ( $this->handle ) {
+ fclose( $this->handle );
+ $this->handle = false;
+ }
+ }
+
function write( $string ) {
fputs( $this->handle, $string );
}
function closeAndRename( $newname, $open = false ) {
$newname = $this->checkRenameArgCount( $newname );
if ( $newname ) {
- fclose( $this->handle );
+ if ( $this->handle ) {
+ fclose( $this->handle );
+ $this->handle = false;
+ }
$this->renameOrException( $newname );
if ( $open ) {
$this->handle = fopen( $this->filename, "wt" );
*/
class DumpPipeOutput extends DumpFileOutput {
protected $command, $filename;
+ protected $procOpenResource = false;
function __construct( $command, $file = null ) {
if ( !is_null( $file ) ) {
$this->filename = $file;
}
+ function writeCloseStream( $string ) {
+ parent::writeCloseStream( $string );
+ if ( $this->procOpenResource ) {
+ proc_close( $this->procOpenResource );
+ $this->procOpenResource = false;
+ }
+ }
+
function startCommand( $command ) {
$spec = array(
0 => array( "pipe", "r" ),
function closeAndRename( $newname, $open = false ) {
$newname = $this->checkRenameArgCount( $newname );
if ( $newname ) {
- fclose( $this->handle );
- proc_close( $this->procOpenResource );
+ if ( $this->handle ) {
+ fclose( $this->handle );
+ $this->handle = false;
+ }
+ if ( $this->procOpenResource ) {
+ proc_close( $this->procOpenResource );
+ $this->procOpenResource = false;
+ }
$this->renameOrException( $newname );
if ( $open ) {
$command = $this->command;
* @ingroup Dump
*/
class Dump7ZipOutput extends DumpPipeOutput {
- protected $filename;
-
function __construct( $file ) {
$command = $this->setup7zCommand( $file );
parent::__construct( $command );
return( $command );
}
- function closeRenameAndReopen( $newname ) {
- $this->closeAndRename( $newname, true );
- }
-
function closeAndRename( $newname, $open = false ) {
$newname = $this->checkRenameArgCount( $newname );
if ( $newname ) {
proc_close( $this->procOpenResource );
$this->renameOrException( $newname );
if ( $open ) {
- $command = $this->setup7zCommand( $file );
+ $command = $this->setup7zCommand( $this->filename );
$this->startCommand( $command );
}
}