PostgreSQL: Make l10n_cache.lc_value binary

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
This commit is contained in:
Jeff Janes 2014-05-05 11:20:52 -07:00
parent e6dd1aa418
commit 0582e3339d
3 changed files with 33 additions and 3 deletions

View 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__ );

View 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 );

View 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);