mourningdove/cgi-bin/DW/User/DVersion/Migrate8To9.pm

296 lines
9.9 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
#!/usr/bin/perl
#
# DW::User::DVersion::Migrate8To9 - Handling dversion 8 to 9 migration
#
# Authors:
# Andrea Nall <anall@andreanall.com>
#
# Copyright (c) 2010 by Dreamwidth Studios, LLC.
#
# This program is free software; you may redistribute it and/or modify it under
# the same terms as Perl itself. For a copy of the license, please reference
# 'perldoc perlartistic' or 'perldoc perlgpl'.
package DW::User::DVersion::Migrate8To9;
use strict;
use warnings;
require 'ljlib.pl';
use LJ::User;
use Time::HiRes qw( usleep );
sub do_upgrade {
my $readonly_bit;
foreach ( keys %LJ::CAP ) {
if ( $LJ::CAP{$_}->{'_name'} eq "_moveinprogress"
&& $LJ::CAP{$_}->{'readonly'} == 1 )
{
$readonly_bit = $_;
last;
}
}
unless ( defined $readonly_bit ) {
die
"Won't move user without %LJ::CAP capability class named '_moveinprogress' with readonly => 1\n";
}
my $logpropid = LJ::get_prop( log => 'picture_keyword' )->{id};
my $talkpropid = LJ::get_prop( talk => 'picture_keyword' )->{id};
my $logpropid_map = LJ::get_prop( log => 'picture_mapid' )->{id};
my $talkpropid_map = LJ::get_prop( talk => 'picture_mapid' )->{id};
my $BLOCK_INSERT = 25;
my ($u) = @_;
return 0 if $u->readonly;
return 1 if $u->dversion >= 9;
# we really cannot have the user doing things during this process
$u->modify_caps( [$readonly_bit], [] );
# wait a quarter of a second, give any request the user might be doing a chance to stop
# as the user changing things could lead to slight data loss re. userpic selection
# on entries and comments
usleep(250000);
# do this in an eval so, in case something dies, we don't leave the user locked
my $rv = 0;
eval {
# Unfortunately, we need to iterate over all clusters to get a list
# of used keywords so we can give proper ids to everything,
# even removed keywords
my %keywords;
my %to_update;
if ( $u->is_individual ) {
foreach my $cluster_id (@LJ::CLUSTERS) {
my $dbcm_o = LJ::get_cluster_master($cluster_id);
my $entries = $dbcm_o->selectall_arrayref(
q{
SELECT log2.journalid AS journalid,
log2.jitemid AS jitemid,
logprop2.value AS value
FROM logprop2
INNER JOIN log2
ON ( logprop2.journalid = log2.journalid
AND logprop2.jitemid = log2.jitemid )
WHERE posterid = ?
AND propid=?
}, undef, $u->id, $logpropid
);
die $dbcm_o->errstr if $dbcm_o->err;
my $comments = $dbcm_o->selectall_arrayref(
q{
SELECT talkprop2.journalid AS journalid,
talkprop2.jtalkid AS jtalkid,
talkprop2.value AS value
FROM talkprop2
INNER JOIN talk2
ON ( talkprop2.journalid = talk2.journalid
AND talkprop2.jtalkid = talk2.jtalkid )
WHERE posterid = ?
AND tpropid=?
}, undef, $u->id, $talkpropid
);
die $dbcm_o->errstr if $dbcm_o->err;
$to_update{$cluster_id} = {
entries => $entries,
comments => $comments,
};
$keywords{ $_->[2] }->{count}++ foreach ( @$entries, @$comments );
}
}
my $origmap = $u->selectall_hashref(
q{
SELECT kwid, picid FROM userpicmap2 WHERE userid=?
}, "kwid", undef, $u->id
);
die $u->errstr if $u->err;
my $picmap = $u->selectall_hashref(
q{
SELECT picid, state FROM userpic2 WHERE userid=?
}, "picid", undef, $u->id
);
die $u->errstr if $u->err;
my %outrows;
my %kwid_map;
foreach my $k ( keys %keywords ) {
if ( $k =~ m/^pic#(\d+)$/ ) {
my $picid = $1;
next if !exists $picmap->{$picid} || $picmap->{$picid}->{state} eq 'X';
$keywords{$k}->{kwid} = undef;
$keywords{$k}->{picid} = $picid;
$outrows{$picid}->{0}++;
}
else {
my $kwid = $u->get_keyword_id( $k, 1 );
$kwid_map{$kwid} = $k;
my $picid = $origmap->{$kwid}->{picid};
$keywords{$k}->{kwid} = $kwid;
$keywords{$k}->{picid} = $picid;
$outrows{ $picid || 0 }->{$kwid}++;
}
}
foreach my $r ( values %$origmap ) {
$outrows{ $r->{picid} }->{ $r->{kwid} }++ if $r->{picid} && $r->{kwid};
}
{
my ( @bind, @vals );
# flush rows to destination table
my $flush = sub {
return unless @bind;
# insert data
my $bind = join( ",", @bind );
$u->do( "REPLACE INTO userpicmap3 (userid,mapid,kwid,picid) VALUES $bind",
undef, @vals );
die $u->errstr if $u->err;
# reset values
@bind = ();
@vals = ();
};
foreach my $picid ( sort { $a <=> $b } keys %outrows ) {
foreach my $kwid ( sort { $a <=> $b } keys %{ $outrows{$picid} } ) {
next if $kwid == 0 && $picid == 0;
push @bind, "(?,?,?,?)";
my $mapid = LJ::alloc_user_counter( $u, 'Y' );
my $keyword = $kwid == 0 ? "pic#$picid" : $kwid_map{$kwid};
# if $keyword is undef, this isn't used on any entries, so we don't care about the mapid
# however, if $kwid is undef, this is a pic#xxx keyword, and had to have existed on an entry
$keywords{$keyword}->{mapid} = $mapid if defined $keyword;
push @vals, ( $u->id, $mapid, $kwid || undef, $picid || undef );
$flush->() if @bind > $BLOCK_INSERT;
}
}
$flush->();
}
if ( $u->is_individual ) {
foreach my $cluster_id (@LJ::CLUSTERS) {
next unless $to_update{$cluster_id};
my $data = $to_update{$cluster_id};
my $dbcm_o = LJ::get_cluster_master($cluster_id);
{
my ( @bind, @vals );
# flush rows to destination table
my $flush = sub {
return unless @bind;
# insert data
my $bind = join( ",", @bind );
$dbcm_o->do(
"REPLACE INTO logprop2 (journalid,jitemid,propid,value) VALUES $bind",
undef, @vals );
die $u->errstr if $u->err;
# reset values
@bind = ();
@vals = ();
};
foreach my $entry ( @{ $data->{entries} } ) {
next unless $keywords{ $entry->[2] }->{mapid};
push @bind, "(?,?,?,?)";
push @vals,
(
$entry->[0], $entry->[1], $logpropid_map,
$keywords{ $entry->[2] }->{mapid}
);
$flush->() if @bind > $BLOCK_INSERT;
}
$flush->();
foreach my $entry ( @{ $data->{entries} } ) {
LJ::MemCache::delete(
[ $entry->[0], "logprop:" . $entry->[0] . ":" . $entry->[1] ] );
}
}
{
my ( @bind, @vals );
# flush rows to destination table
my $flush = sub {
return unless @bind;
# insert data
my $bind = join( ",", @bind );
$dbcm_o->do(
"REPLACE INTO talkprop2 (journalid,jtalkid,tpropid,value) VALUES $bind",
undef, @vals
);
die $u->errstr if $u->err;
# reset values
@bind = ();
@vals = ();
};
foreach my $comment ( @{ $data->{comments} } ) {
next unless $keywords{ $comment->[2] }->{mapid};
push @bind, "(?,?,?,?)";
push @vals,
(
$comment->[0], $comment->[1], $talkpropid_map,
$keywords{ $comment->[2] }->{mapid}
);
$flush->() if @bind > $BLOCK_INSERT;
}
$flush->();
foreach my $comment ( @{ $data->{comments} } ) {
LJ::MemCache::delete(
[ $comment->[0], "talkprop:" . $comment->[0] . ":" . $comment->[1] ] );
}
}
}
}
$rv = 1;
};
my $err = $@;
# okay, we're done, the user can do things again
$u->modify_caps( [], [$readonly_bit] );
die $err if $err;
return $rv;
}
sub upgrade_to_dversion_9 {
# If user has been purged, go ahead and update version
# Otherwise move their data
my $ok = $_[0]->is_expunged ? 1 : do_upgrade(@_);
$_[0]->update_self( { 'dversion' => 9 } ) if $ok;
LJ::Userpic->delete_cache( $_[0] );
return $ok;
}
*LJ::User::upgrade_to_dversion_9 = \&upgrade_to_dversion_9;