+ $this->mCompressed = false;
+ }
+ }
+
+ /**
+ * @return array
+ */
+ function __sleep() {
+ $this->compress();
+ return array( 'mVersion', 'mCompressed', 'mItems', 'mDefaultHash' );
+ }
+
+ function __wakeup() {
+ $this->uncompress();
+ }
+
+ /**
+ * Helper function for compression jobs
+ * Returns true until the object is "full" and ready to be committed
+ *
+ * @return bool
+ */
+ public function isHappy() {
+ return $this->mSize < $this->mMaxSize
+ && count( $this->mItems ) < $this->mMaxCount;
+ }
+}
+
+
+/**
+ * Pointer object for an item within a CGZ blob stored in the text table.
+ */
+class HistoryBlobStub {
+ /**
+ * One-step cache variable to hold base blobs; operations that
+ * pull multiple revisions may often pull multiple times from
+ * the same blob. By keeping the last-used one open, we avoid
+ * redundant unserialization and decompression overhead.
+ */
+ protected static $blobCache = array();
+
+ var $mOldId, $mHash, $mRef;
+
+ /**
+ * @param $hash Strng: the content hash of the text
+ * @param $oldid Integer: the old_id for the CGZ object
+ */
+ function __construct( $hash = '', $oldid = 0 ) {
+ $this->mHash = $hash;
+ }
+
+ /**
+ * Sets the location (old_id) of the main object to which this object
+ * points
+ */
+ function setLocation( $id ) {
+ $this->mOldId = $id;
+ }
+
+ /**
+ * Sets the location (old_id) of the referring object
+ */
+ function setReferrer( $id ) {
+ $this->mRef = $id;
+ }
+
+ /**
+ * Gets the location of the referring object
+ */
+ function getReferrer() {
+ return $this->mRef;
+ }
+
+ /**
+ * @return string
+ */
+ function getText() {
+ $fname = 'HistoryBlobStub::getText';
+
+ if( isset( self::$blobCache[$this->mOldId] ) ) {
+ $obj = self::$blobCache[$this->mOldId];
+ } else {
+ $dbr = wfGetDB( DB_SLAVE );
+ $row = $dbr->selectRow( 'text', array( 'old_flags', 'old_text' ), array( 'old_id' => $this->mOldId ) );
+ if( !$row ) {
+ return false;
+ }
+ $flags = explode( ',', $row->old_flags );
+ if( in_array( 'external', $flags ) ) {
+ $url=$row->old_text;
+ $parts = explode( '://', $url, 2 );
+ if ( !isset( $parts[1] ) || $parts[1] == '' ) {
+ wfProfileOut( $fname );
+ return false;
+ }
+ $row->old_text = ExternalStore::fetchFromUrl($url);
+
+ }
+ if( !in_array( 'object', $flags ) ) {
+ return false;
+ }
+
+ if( in_array( 'gzip', $flags ) ) {
+ // This shouldn't happen, but a bug in the compress script
+ // may at times gzip-compress a HistoryBlob object row.
+ $obj = unserialize( gzinflate( $row->old_text ) );
+ } else {
+ $obj = unserialize( $row->old_text );
+ }
+
+ if( !is_object( $obj ) ) {
+ // Correct for old double-serialization bug.
+ $obj = unserialize( $obj );
+ }
+
+ // Save this item for reference; if pulling many
+ // items in a row we'll likely use it again.
+ $obj->uncompress();
+ self::$blobCache = array( $this->mOldId => $obj );
+ }
+ return $obj->getItem( $this->mHash );
+ }
+
+ /**
+ * Get the content hash
+ *
+ * @return string
+ */
+ function getHash() {
+ return $this->mHash;
+ }
+}
+
+
+/**
+ * To speed up conversion from 1.4 to 1.5 schema, text rows can refer to the
+ * leftover cur table as the backend. This avoids expensively copying hundreds
+ * of megabytes of data during the conversion downtime.
+ *
+ * Serialized HistoryBlobCurStub objects will be inserted into the text table
+ * on conversion if $wgFastSchemaUpgrades is set to true.
+ */
+class HistoryBlobCurStub {
+ var $mCurId;
+
+ /**
+ * @param $curid Integer: the cur_id pointed to
+ */
+ function __construct( $curid = 0 ) {
+ $this->mCurId = $curid;
+ }
+
+ /**
+ * Sets the location (cur_id) of the main object to which this object
+ * points
+ *
+ * @param $id int
+ */
+ function setLocation( $id ) {
+ $this->mCurId = $id;
+ }
+
+ /**
+ * @return string|false
+ */
+ function getText() {
+ $dbr = wfGetDB( DB_SLAVE );
+ $row = $dbr->selectRow( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mCurId ) );
+ if( !$row ) {
+ return false;