PostgreSQL: Make l10n_cache.lc_value binary
authorJeff Janes <jeff.janes@gmail.com>
Mon, 5 May 2014 18:20:52 +0000 (11:20 -0700)
committerJeff Janes <jeff.janes@gmail.com>
Mon, 5 May 2014 18:20:52 +0000 (11:20 -0700)
Change-Id I427c6de5a0a29b43cff755db0eb8a750db620173 increases the
probability that a null byte will attempt to be stored in the
lc_value column.  PostgreSQL does not allow that byte in a text
column, so convert the column to bytea.

If the column already contains corrupted data, the upgrade routine
might fail.  To prevent this, delete the contents of the table before
changing the type.

Bug: 62098
Change-Id: Ie8368bde398b2ae4d3cfc9ee7bf35874bd2ded68

includes/cache/LocalisationCache.php
includes/installer/PostgresUpdater.php
maintenance/postgres/tables.sql

index 3bbf1bb..1153fd2 100644 (file)
@@ -1171,7 +1171,7 @@ class LCStoreDB implements LCStore {
                $row = $db->selectRow( 'l10n_cache', array( 'lc_value' ),
                        array( 'lc_lang' => $code, 'lc_key' => $key ), __METHOD__ );
                if ( $row ) {
-                       return unserialize( $row->lc_value );
+                       return unserialize( $db->decodeBlob( $row->lc_value ) );
                } else {
                        return null;
                }
@@ -1233,7 +1233,7 @@ class LCStoreDB implements LCStore {
                $this->batch[] = array(
                        'lc_lang' => $this->currentLang,
                        'lc_key' => $key,
-                       'lc_value' => serialize( $value ) );
+                       'lc_value' => $this->dbw->encodeBlob( serialize( $value ) ) );
 
                if ( count( $this->batch ) >= 100 ) {
                        $this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ );
index 8f6aac7..e7aab8a 100644 (file)
@@ -406,6 +406,7 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addPgField', 'recentchanges', 'rc_source', "TEXT NOT NULL DEFAULT ''" ),
                        array( 'addPgField', 'page', 'page_links_updated', "TIMESTAMPTZ NULL" ),
                        array( 'addPgField', 'mwuser', 'user_password_expires', 'TIMESTAMPTZ NULL' ),
+                       array( 'changeFieldPurgeTable', 'l10n_cache', 'lc_value', 'bytea', "replace(lc_value,'\','\\\\')::bytea" ),
 
                        // 1.24
                        array( 'addPgField', 'page_props', 'pp_sortkey', 'float NULL' ),
@@ -677,6 +678,35 @@ END;
                }
        }
 
+       protected function changeFieldPurgeTable( $table, $field, $newtype, $default ) {
+               ## For a cache table, empty it if the field needs to be changed, because the old contents
+               ## may be corrupted.  If the column is already the desired type, refrain from purging.
+               $fi = $this->db->fieldInfo( $table, $field );
+               if ( is_null( $fi ) ) {
+                       $this->output( "...ERROR: expected column $table.$field to exist\n" );
+                       exit( 1 );
+               }
+
+               if ( $fi->type() === $newtype ) {
+                       $this->output( "...column '$table.$field' is already of type '$newtype'\n" );
+               } else {
+                       $this->output( "Purging data from cache table '$table'\n" );
+                       $this->db->query("DELETE from $table" );
+                       $this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
+                       $sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
+                       if ( strlen( $default ) ) {
+                               $res = array();
+                               if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
+                                       $sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
+                                       $this->db->query( $sqldef );
+                                       $default = preg_replace( '/\s*DEFAULT .+/', '', $default );
+                               }
+                               $sql .= " USING $default";
+                       }
+                       $this->db->query( $sql );
+               }
+       }
+
        protected function setDefault( $table, $field, $default ) {
 
                $info = $this->db->fieldInfo( $table, $field );
index 6a2c41d..abbfd3a 100644 (file)
@@ -678,7 +678,7 @@ CREATE INDEX user_properties_property ON user_properties (up_property);
 CREATE TABLE l10n_cache (
   lc_lang   TEXT  NOT NULL,
   lc_key    TEXT  NOT NULL,
-  lc_value  TEXT  NOT NULL
+  lc_value  BYTEA NOT NULL
 );
 CREATE INDEX l10n_cache_lc_lang_key ON l10n_cache (lc_lang, lc_key);