fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2011-11-16 05:25 am

[dw-free] move cgi-bin/lj*.pl files into proper modules (in cgi-bin/LJ)

[commit: http://hg.dwscoalition.org/dw-free/rev/b06a6502feca]

http://bugs.dwscoalition.org/show_bug.cgi?id=1726

Move ljrelation.pl to LJ/User.pm

Patch by [personal profile] kareila.

Files modified:
  • cgi-bin/LJ/User.pm
  • cgi-bin/ljlib.pl
  • cgi-bin/ljrelation.pl
--------------------------------------------------------------------------------
diff -r 628592b2c07e -r b06a6502feca cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Fri Nov 11 21:25:34 2011 +0800
+++ b/cgi-bin/LJ/User.pm	Wed Nov 16 13:27:24 2011 +0800
@@ -7247,6 +7247,7 @@
 ###  16. Entry-Related Functions
 ###  19. OpenID and Identity Functions
 ###  21. Password Functions
+###  23. Relationship Functions
 ###  24. Styles and S2-Related Functions
 
 ########################################################################
@@ -8588,6 +8589,551 @@
 
 
 ########################################################################
+###  23. Relationship Functions
+
+=head2 Relationship Functions (formerly ljrelation.pl)
+=cut
+
+# <LJFUNC>
+# name: LJ::get_reluser_id
+# des: for [dbtable[reluser2]], numbers 1 - 31999 are reserved for
+#      livejournal stuff, whereas numbers 32000-65535 are used for local sites.
+# info: If you wish to add your own hooks to this, you should define a
+#       hook "get_reluser_id" in ljlib-local.pl. No reluser2 [special[reluserdefs]]
+#        types can be a single character, those are reserved for
+#        the [dbtable[reluser]] table, so we don't have namespace problems.
+# args: type
+# des-type: the name of the type you're trying to access, e.g. "hide_comm_assoc"
+# returns: id of type, 0 means it's not a reluser2 type
+# </LJFUNC>
+sub get_reluser_id {
+    my $type = shift;
+    return 0 if length $type == 1; # must be more than a single character
+    my $val =
+        {
+            'hide_comm_assoc' => 1,
+        }->{$type}+0;
+    return $val if $val;
+    return 0 unless $type =~ /^local-/;
+    return LJ::Hooks::run_hook('get_reluser_id', $type)+0;
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_user
+# des: Load user relationship information. Loads all relationships of type 'type' in
+#      which user 'userid' participates on the left side (is the source of the
+#      relationship).
+# args: db?, userid, type
+# des-userid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_user {
+    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
+    my ($userid, $type) = @_;
+    return undef unless $type and $userid;
+    my $u = LJ::want_user($userid);
+    $userid = LJ::want_userid($userid);
+    my $typeid = LJ::get_reluser_id($type)+0;
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_reader($u);
+        return $db->selectcol_arrayref("SELECT targetid FROM reluser2 WHERE userid=? AND type=?",
+                                       undef, $userid, $typeid);
+    } else {
+        # non-clustered reluser global table
+        $db ||= LJ::get_db_reader();
+        return $db->selectcol_arrayref("SELECT targetid FROM reluser WHERE userid=? AND type=?",
+                                       undef, $userid, $type);
+    }
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_user_cache
+# des: Loads user relationship information of the type 'type' where user
+#      'targetid' participates on the left side (is the source of the relationship)
+#      trying memcache first.  The results from this sub should be
+#      <strong>treated as inaccurate and out of date</strong>.
+# args: userid, type
+# des-userid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_user_cache {
+    my ($userid, $type) = @_;
+    return undef unless $type && $userid;
+
+    my $u = LJ::want_user($userid);
+    return undef unless $u;
+    $userid = $u->{'userid'};
+
+    my $key = [ $userid, "reluser:$userid:$type" ];
+    my $res = LJ::MemCache::get($key);
+
+    return $res if $res;
+
+    $res = LJ::load_rel_user($userid, $type);
+
+    my $exp = time() + 60*30; # 30 min
+    LJ::MemCache::set($key, $res, $exp);
+
+    return $res;
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_target
+# des: Load user relationship information. Loads all relationships of type 'type' in
+#      which user 'targetid' participates on the right side (is the target of the
+#      relationship).
+# args: db?, targetid, type
+# des-targetid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_target {
+    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
+    my ($targetid, $type) = @_;
+    return undef unless $type and $targetid;
+    my $u = LJ::want_user($targetid);
+    $targetid = LJ::want_userid($targetid);
+    my $typeid = LJ::get_reluser_id($type)+0;
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_reader($u);
+        return $db->selectcol_arrayref("SELECT userid FROM reluser2 WHERE targetid=? AND type=?",
+                                       undef, $targetid, $typeid);
+    } else {
+        # non-clustered reluser global table
+        $db ||= LJ::get_db_reader();
+        return $db->selectcol_arrayref("SELECT userid FROM reluser WHERE targetid=? AND type=?",
+                                       undef, $targetid, $type);
+    }
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_target_cache
+# des: Loads user relationship information of the type 'type' where user
+#      'targetid' participates on the right side (is the target of the relationship)
+#      trying memcache first.  The results from this sub should be
+#      <strong>treated as inaccurate and out of date</strong>.
+# args: targetid, type
+# des-userid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_target_cache {
+    my ($userid, $type) = @_;
+    return undef unless $type && $userid;
+
+    my $u = LJ::want_user($userid);
+    return undef unless $u;
+    $userid = $u->{'userid'};
+
+    my $key = [ $userid, "reluser_rev:$userid:$type" ];
+    my $res = LJ::MemCache::get($key);
+
+    return $res if $res;
+
+    $res = LJ::load_rel_target($userid, $type);
+
+    my $exp = time() + 60*30; # 30 min
+    LJ::MemCache::set($key, $res, $exp);
+
+    return $res;
+}
+
+# <LJFUNC>
+# name: LJ::_get_rel_memcache
+# des: Helper function: returns memcached value for a given (userid, targetid, type) triple, if valid.
+# args: userid, targetid, type
+# des-userid: source userid, nonzero
+# des-targetid: target userid, nonzero
+# des-type: type (reluser) or typeid (rel2) of the relationship
+# returns: undef on failure, 0 or 1 depending on edge existence
+# </LJFUNC>
+sub _get_rel_memcache {
+    return undef unless @LJ::MEMCACHE_SERVERS;
+    return undef unless LJ::is_enabled('memcache_reluser');
+
+    my ($userid, $targetid, $type) = @_;
+    return undef unless $userid && $targetid && defined $type;
+
+    # memcache keys
+    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
+    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
+    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
+
+    # do a get_multi since $relkey and $modukey are both hashed on $userid
+    my $memc = LJ::MemCache::get_multi($relkey, $modukey);
+    return undef unless $memc && ref $memc eq 'HASH';
+
+    # [{0|1}, modtime]
+    my $rel = $memc->{$relkey->[1]};
+    return undef unless $rel && ref $rel eq 'ARRAY';
+
+    # check rel modtime for $userid
+    my $relmodu = $memc->{$modukey->[1]};
+    return undef if ! $relmodu || $relmodu > $rel->[1];
+
+    # check rel modtime for $targetid
+    my $relmodt = LJ::MemCache::get($modtkey);
+    return undef if ! $relmodt || $relmodt > $rel->[1];
+
+    # return memcache value if it's up-to-date
+    return $rel->[0] ? 1 : 0;
+}
+
+# <LJFUNC>
+# name: LJ::_set_rel_memcache
+# des: Helper function: sets memcache values for a given (userid, targetid, type) triple
+# args: userid, targetid, type
+# des-userid: source userid, nonzero
+# des-targetid: target userid, nonzero
+# des-type: type (reluser) or typeid (rel2) of the relationship
+# returns: 1 on success, undef on failure
+# </LJFUNC>
+sub _set_rel_memcache {
+    return 1 unless @LJ::MEMCACHE_SERVERS;
+
+    my ($userid, $targetid, $type, $val) = @_;
+    return undef unless $userid && $targetid && defined $type;
+    $val = $val ? 1 : 0;
+
+    # memcache keys
+    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
+    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
+    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
+
+    my $now = time();
+    my $exp = $now + 3600*6; # 6 hour
+    LJ::MemCache::set($relkey, [$val, $now], $exp);
+    LJ::MemCache::set($modukey, $now, $exp);
+    LJ::MemCache::set($modtkey, $now, $exp);
+
+    # Also, delete these keys, since the contents have changed.
+    LJ::MemCache::delete([$userid, "reluser:$userid:$type"]);
+    LJ::MemCache::delete([$targetid, "reluser_rev:$targetid:$type"]);
+
+    return 1;
+}
+
+# <LJFUNC>
+# name: LJ::check_rel
+# des: Checks whether two users are in a specified relationship to each other.
+# args: userid, targetid, type
+# des-userid: source userid, nonzero; may also be a user hash.
+# des-targetid: target userid, nonzero; may also be a user hash.
+# des-type: type of the relationship
+# returns: 1 if the relationship exists, 0 otherwise
+# </LJFUNC>
+sub check_rel {
+    my ($userid, $targetid, $type) = @_;
+    return undef unless $type && $userid && $targetid;
+
+    my $u = LJ::want_user($userid);
+    $userid = LJ::want_userid($userid);
+    $targetid = LJ::want_userid($targetid);
+
+    my $typeid = LJ::get_reluser_id($type)+0;
+    my $eff_type = $typeid || $type;
+
+    my $key = "$userid-$targetid-$eff_type";
+    return $LJ::REQ_CACHE_REL{$key} if defined $LJ::REQ_CACHE_REL{$key};
+
+    # did we get something from memcache?
+    my $memval = LJ::_get_rel_memcache($userid, $targetid, $eff_type);
+    return $memval if defined $memval;
+
+    # are we working on reluser or reluser2?
+    my ( $db, $table );
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_reader($u);
+        $table = "reluser2";
+    } else {
+        # non-clustered reluser table
+        $db = LJ::get_db_reader();
+        $table = "reluser";
+    }
+
+    # get data from db, force result to be {0|1}
+    my $dbval = $db->selectrow_array("SELECT COUNT(*) FROM $table ".
+                                     "WHERE userid=? AND targetid=? AND type=? ",
+                                     undef, $userid, $targetid, $eff_type)
+        ? 1 : 0;
+
+    # set in memcache
+    LJ::_set_rel_memcache($userid, $targetid, $eff_type, $dbval);
+
+    # return and set request cache
+    return $LJ::REQ_CACHE_REL{$key} = $dbval;
+}
+
+# <LJFUNC>
+# name: LJ::set_rel
+# des: Sets relationship information for two users.
+# args: dbs?, userid, targetid, type
+# des-dbs: Deprecated; optional, a master/slave set of database handles.
+# des-userid: source userid, or a user hash
+# des-targetid: target userid, or a user hash
+# des-type: type of the relationship
+# returns: 1 if set succeeded, otherwise undef
+# </LJFUNC>
+sub set_rel {
+    my ($userid, $targetid, $type) = @_;
+    return undef unless $type and $userid and $targetid;
+
+    my $u = LJ::want_user($userid);
+    $userid = LJ::want_userid($userid);
+    $targetid = LJ::want_userid($targetid);
+
+    my $typeid = LJ::get_reluser_id($type)+0;
+    my $eff_type = $typeid || $type;
+
+    # working on reluser or reluser2?
+    my ($db, $table);
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_master($u);
+        $table = "reluser2";
+    } else {
+        # non-clustered reluser global table
+        $db = LJ::get_db_writer();
+        $table = "reluser";
+    }
+    return undef unless $db;
+
+    # set in database
+    $db->do("REPLACE INTO $table (userid, targetid, type) VALUES (?, ?, ?)",
+            undef, $userid, $targetid, $eff_type);
+    return undef if $db->err;
+
+    # set in memcache
+    LJ::_set_rel_memcache($userid, $targetid, $eff_type, 1);
+
+    return 1;
+}
+
+# <LJFUNC>
+# name: LJ::set_rel_multi
+# des: Sets relationship edges for lists of user tuples.
+# args: edges
+# des-edges: array of arrayrefs of edges to set: [userid, targetid, type].
+#            Where:
+#            userid: source userid, or a user hash;
+#            targetid: target userid, or a user hash;
+#            type: type of the relationship.
+# returns: 1 if all sets succeeded, otherwise undef
+# </LJFUNC>
+sub set_rel_multi {
+    return _mod_rel_multi({ mode => 'set', edges => \@_ });
+}
+
+# <LJFUNC>
+# name: LJ::clear_rel_multi
+# des: Clear relationship edges for lists of user tuples.
+# args: edges
+# des-edges: array of arrayrefs of edges to clear: [userid, targetid, type].
+#            Where:
+#            userid: source userid, or a user hash;
+#            targetid: target userid, or a user hash;
+#            type: type of the relationship.
+# returns: 1 if all clears succeeded, otherwise undef
+# </LJFUNC>
+sub clear_rel_multi {
+    return _mod_rel_multi({ mode => 'clear', edges => \@_ });
+}
+
+# <LJFUNC>
+# name: LJ::_mod_rel_multi
+# des: Sets/Clears relationship edges for lists of user tuples.
+# args: keys, edges
+# des-keys: keys: mode  => {clear|set}.
+# des-edges: edges =>  array of arrayrefs of edges to set: [userid, targetid, type]
+#            Where:
+#            userid: source userid, or a user hash;
+#            targetid: target userid, or a user hash;
+#            type: type of the relationship.
+# returns: 1 if all updates succeeded, otherwise undef
+# </LJFUNC>
+sub _mod_rel_multi {
+    my $opts = shift;
+    return undef unless @{$opts->{edges}};
+
+    my $mode = $opts->{mode} eq 'clear' ? 'clear' : 'set';
+    my $memval = $mode eq 'set' ? 1 : 0;
+
+    my @reluser  = (); # [userid, targetid, type]
+    my @reluser2 = ();
+    foreach my $edge (@{$opts->{edges}}) {
+        my ($userid, $targetid, $type) = @$edge;
+        $userid = LJ::want_userid($userid);
+        $targetid = LJ::want_userid($targetid);
+        next unless $type && $userid && $targetid;
+
+        my $typeid = LJ::get_reluser_id($type)+0;
+        my $eff_type = $typeid || $type;
+
+        # working on reluser or reluser2?
+        push @{$typeid ? \@reluser2 : \@reluser}, [$userid, $targetid, $eff_type];
+    }
+
+    # now group reluser2 edges by clusterid
+    my %reluser2 = (); # cid => [userid, targetid, type]
+    my $users = LJ::load_userids(map { $_->[0] } @reluser2);
+    foreach (@reluser2) {
+        my $cid = $users->{$_->[0]}->{clusterid} or next;
+        push @{$reluser2{$cid}}, $_;
+    }
+    @reluser2 = ();
+
+    # try to get all required cluster masters before we start doing database updates
+    my %cache_dbcm = ();
+    foreach my $cid (keys %reluser2) {
+        next unless @{$reluser2{$cid}};
+
+        # return undef immediately if we won't be able to do all the updates
+        $cache_dbcm{$cid} = LJ::get_cluster_master($cid)
+            or return undef;
+    }
+
+    # if any error occurs with a cluster, we'll skip over that cluster and continue
+    # trying to process others since we've likely already done some amount of db
+    # updates already, but we'll return undef to signify that everything did not
+    # go smoothly
+    my $ret = 1;
+
+    # do clustered reluser2 updates
+    foreach my $cid (keys %cache_dbcm) {
+        # array of arrayrefs: [userid, targetid, type]
+        my @edges = @{$reluser2{$cid}};
+
+        # set in database, then in memcache.  keep the two atomic per clusterid
+        my $dbcm = $cache_dbcm{$cid};
+
+        my @vals = map { @$_ } @edges;
+
+        if ($mode eq 'set') {
+            my $bind = join(",", map { "(?,?,?)" } @edges);
+            $dbcm->do("REPLACE INTO reluser2 (userid, targetid, type) VALUES $bind",
+                      undef, @vals);
+        }
+
+        if ($mode eq 'clear') {
+            my $where = join(" OR ", map { "(userid=? AND targetid=? AND type=?)" } @edges);
+            $dbcm->do("DELETE FROM reluser2 WHERE $where", undef, @vals);
+        }
+
+        # don't update memcache if db update failed for this cluster
+        if ($dbcm->err) {
+            $ret = undef;
+            next;
+        }
+
+        # updates to this cluster succeeded, set memcache
+        LJ::_set_rel_memcache(@$_, $memval) foreach @edges;
+    }
+
+    # do global reluser updates
+    if (@reluser) {
+
+        # nothing to do after this block but return, so we can
+        # immediately return undef from here if there's a problem
+        my $dbh = LJ::get_db_writer()
+            or return undef;
+
+        my @vals = map { @$_ } @reluser;
+
+        if ($mode eq 'set') {
+            my $bind = join(",", map { "(?,?,?)" } @reluser);
+            $dbh->do("REPLACE INTO reluser (userid, targetid, type) VALUES $bind",
+                     undef, @vals);
+        }
+
+        if ($mode eq 'clear') {
+            my $where = join(" OR ", map { "userid=? AND targetid=? AND type=?" } @reluser);
+            $dbh->do("DELETE FROM reluser WHERE $where", undef, @vals);
+        }
+
+        # don't update memcache if db update failed for this cluster
+        return undef if $dbh->err;
+
+        # $_ = [userid, targetid, type] for each iteration
+        LJ::_set_rel_memcache(@$_, $memval) foreach @reluser;
+    }
+
+    return $ret;
+}
+
+
+# <LJFUNC>
+# name: LJ::clear_rel
+# des: Deletes a relationship between two users or all relationships of a particular type
+#      for one user, on either side of the relationship.
+# info: One of userid,targetid -- bit not both -- may be '*'. In that case,
+#       if, say, userid is '*', then all relationship edges with target equal to
+#       targetid and of the specified type are deleted.
+#       If both userid and targetid are numbers, just one edge is deleted.
+# args: dbs?, userid, targetid, type
+# des-dbs: Deprecated; optional, a master/slave set of database handles.
+# des-userid: source userid, or a user hash, or '*'
+# des-targetid: target userid, or a user hash, or '*'
+# des-type: type of the relationship
+# returns: 1 if clear succeeded, otherwise undef
+# </LJFUNC>
+sub clear_rel {
+    my ($userid, $targetid, $type) = @_;
+    return undef if $userid eq '*' and $targetid eq '*';
+
+    my $u;
+    $u = LJ::want_user($userid) unless $userid eq '*';
+    $userid = LJ::want_userid($userid) unless $userid eq '*';
+    $targetid = LJ::want_userid($targetid) unless $targetid eq '*';
+    return undef unless $type && $userid && $targetid;
+
+    my $typeid = LJ::get_reluser_id($type)+0;
+
+    if ($typeid) {
+        # clustered reluser2 table
+        return undef unless $u->writer;
+
+        $u->do("DELETE FROM reluser2 WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
+               ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$typeid");
+
+        return undef if $u->err;
+    } else {
+        # non-clustered global reluser table
+        my $dbh = LJ::get_db_writer()
+            or return undef;
+
+        my $qtype = $dbh->quote($type);
+        $dbh->do("DELETE FROM reluser WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
+                 ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$qtype");
+
+        return undef if $dbh->err;
+    }
+
+    # if one of userid or targetid are '*', then we need to note the modtime
+    # of the reluser edge from the specified id (the one that's not '*')
+    # so that subsequent gets on rel:userid:targetid:type will know to ignore
+    # what they got from memcache
+    my $eff_type = $typeid || $type;
+    if ($userid eq '*') {
+        LJ::MemCache::set([$targetid, "relmodt:$targetid:$eff_type"], time());
+    } elsif ($targetid eq '*') {
+        LJ::MemCache::set([$userid, "relmodu:$userid:$eff_type"], time());
+
+    # if neither userid nor targetid are '*', then just call _set_rel_memcache
+    # to update the rel:userid:targetid:type memcache key as well as the
+    # userid and targetid modtime keys
+    } else {
+        LJ::_set_rel_memcache($userid, $targetid, $eff_type, 0);
+    }
+
+    return 1;
+}
+
+########################################################################
 ###  24. Styles and S2-Related Functions
 
 =head2 Styles and S2-Related Functions (LJ)
diff -r 628592b2c07e -r b06a6502feca cgi-bin/ljlib.pl
--- a/cgi-bin/ljlib.pl	Fri Nov 11 21:25:34 2011 +0800
+++ b/cgi-bin/ljlib.pl	Wed Nov 16 13:27:24 2011 +0800
@@ -107,7 +107,6 @@
 require "ljtimeutil.pl";
 use LJ::Capabilities;
 use DW::Mood;
-require "ljrelation.pl";
 use LJ::Global::Img;  # defines LJ::Img
 
 require "$LJ::HOME/cgi-bin/ljlib-local.pl"
diff -r 628592b2c07e -r b06a6502feca cgi-bin/ljrelation.pl
--- a/cgi-bin/ljrelation.pl	Fri Nov 11 21:25:34 2011 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,561 +0,0 @@
-# This code was forked from the LiveJournal project owned and operated
-# by Live Journal, Inc. The code has been modified and expanded by
-# Dreamwidth Studios, LLC. These files were originally licensed under
-# the terms of the license supplied by Live Journal, Inc, which can
-# currently be found at:
-#
-# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
-#
-# In accordance with the original license, this code and all its
-# modifications are provided under the GNU General Public License.
-# A copy of that license can be found in the LICENSE file included as
-# part of this distribution.
-
-package LJ;
-use strict;
-
-# <LJFUNC>
-# name: LJ::get_reluser_id
-# des: for [dbtable[reluser2]], numbers 1 - 31999 are reserved for
-#      livejournal stuff, whereas numbers 32000-65535 are used for local sites.
-# info: If you wish to add your own hooks to this, you should define a
-#       hook "get_reluser_id" in ljlib-local.pl. No reluser2 [special[reluserdefs]]
-#        types can be a single character, those are reserved for
-#        the [dbtable[reluser]] table, so we don't have namespace problems.
-# args: type
-# des-type: the name of the type you're trying to access, e.g. "hide_comm_assoc"
-# returns: id of type, 0 means it's not a reluser2 type
-# </LJFUNC>
-sub get_reluser_id {
-    my $type = shift;
-    return 0 if length $type == 1; # must be more than a single character
-    my $val =
-        {
-            'hide_comm_assoc' => 1,
-        }->{$type}+0;
-    return $val if $val;
-    return 0 unless $type =~ /^local-/;
-    return LJ::Hooks::run_hook('get_reluser_id', $type)+0;
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_user
-# des: Load user relationship information. Loads all relationships of type 'type' in
-#      which user 'userid' participates on the left side (is the source of the
-#      relationship).
-# args: db?, userid, type
-# des-userid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_user
-{
-    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
-    my ($userid, $type) = @_;
-    return undef unless $type and $userid;
-    my $u = LJ::want_user($userid);
-    $userid = LJ::want_userid($userid);
-    my $typeid = LJ::get_reluser_id($type)+0;
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_reader($u);
-        return $db->selectcol_arrayref("SELECT targetid FROM reluser2 WHERE userid=? AND type=?",
-                                       undef, $userid, $typeid);
-    } else {
-        # non-clustered reluser global table
-        $db ||= LJ::get_db_reader();
-        return $db->selectcol_arrayref("SELECT targetid FROM reluser WHERE userid=? AND type=?",
-                                       undef, $userid, $type);
-    }
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_user_cache
-# des: Loads user relationship information of the type 'type' where user
-#      'targetid' participates on the left side (is the source of the relationship)
-#      trying memcache first.  The results from this sub should be
-#      <strong>treated as inaccurate and out of date</strong>.
-# args: userid, type
-# des-userid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_user_cache
-{
-    my ($userid, $type) = @_;
-    return undef unless $type && $userid;
-
-    my $u = LJ::want_user($userid);
-    return undef unless $u;
-    $userid = $u->{'userid'};
-
-    my $key = [ $userid, "reluser:$userid:$type" ];
-    my $res = LJ::MemCache::get($key);
-
-    return $res if $res;
-
-    $res = LJ::load_rel_user($userid, $type);
-
-    my $exp = time() + 60*30; # 30 min
-    LJ::MemCache::set($key, $res, $exp);
-
-    return $res;
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_target
-# des: Load user relationship information. Loads all relationships of type 'type' in
-#      which user 'targetid' participates on the right side (is the target of the
-#      relationship).
-# args: db?, targetid, type
-# des-targetid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_target
-{
-    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
-    my ($targetid, $type) = @_;
-    return undef unless $type and $targetid;
-    my $u = LJ::want_user($targetid);
-    $targetid = LJ::want_userid($targetid);
-    my $typeid = LJ::get_reluser_id($type)+0;
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_reader($u);
-        return $db->selectcol_arrayref("SELECT userid FROM reluser2 WHERE targetid=? AND type=?",
-                                       undef, $targetid, $typeid);
-    } else {
-        # non-clustered reluser global table
-        $db ||= LJ::get_db_reader();
-        return $db->selectcol_arrayref("SELECT userid FROM reluser WHERE targetid=? AND type=?",
-                                       undef, $targetid, $type);
-    }
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_target_cache
-# des: Loads user relationship information of the type 'type' where user
-#      'targetid' participates on the right side (is the target of the relationship)
-#      trying memcache first.  The results from this sub should be
-#      <strong>treated as inaccurate and out of date</strong>.
-# args: targetid, type
-# des-userid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_target_cache
-{
-    my ($userid, $type) = @_;
-    return undef unless $type && $userid;
-
-    my $u = LJ::want_user($userid);
-    return undef unless $u;
-    $userid = $u->{'userid'};
-
-    my $key = [ $userid, "reluser_rev:$userid:$type" ];
-    my $res = LJ::MemCache::get($key);
-
-    return $res if $res;
-
-    $res = LJ::load_rel_target($userid, $type);
-
-    my $exp = time() + 60*30; # 30 min
-    LJ::MemCache::set($key, $res, $exp);
-
-    return $res;
-}
-
-# <LJFUNC>
-# name: LJ::_get_rel_memcache
-# des: Helper function: returns memcached value for a given (userid, targetid, type) triple, if valid.
-# args: userid, targetid, type
-# des-userid: source userid, nonzero
-# des-targetid: target userid, nonzero
-# des-type: type (reluser) or typeid (rel2) of the relationship
-# returns: undef on failure, 0 or 1 depending on edge existence
-# </LJFUNC>
-sub _get_rel_memcache {
-    return undef unless @LJ::MEMCACHE_SERVERS;
-    return undef unless LJ::is_enabled('memcache_reluser');
-
-    my ($userid, $targetid, $type) = @_;
-    return undef unless $userid && $targetid && defined $type;
-
-    # memcache keys
-    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
-    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
-    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
-
-    # do a get_multi since $relkey and $modukey are both hashed on $userid
-    my $memc = LJ::MemCache::get_multi($relkey, $modukey);
-    return undef unless $memc && ref $memc eq 'HASH';
-
-    # [{0|1}, modtime]
-    my $rel = $memc->{$relkey->[1]};
-    return undef unless $rel && ref $rel eq 'ARRAY';
-
-    # check rel modtime for $userid
-    my $relmodu = $memc->{$modukey->[1]};
-    return undef if ! $relmodu || $relmodu > $rel->[1];
-
-    # check rel modtime for $targetid
-    my $relmodt = LJ::MemCache::get($modtkey);
-    return undef if ! $relmodt || $relmodt > $rel->[1];
-
-    # return memcache value if it's up-to-date
-    return $rel->[0] ? 1 : 0;
-}
-
-# <LJFUNC>
-# name: LJ::_set_rel_memcache
-# des: Helper function: sets memcache values for a given (userid, targetid, type) triple
-# args: userid, targetid, type
-# des-userid: source userid, nonzero
-# des-targetid: target userid, nonzero
-# des-type: type (reluser) or typeid (rel2) of the relationship
-# returns: 1 on success, undef on failure
-# </LJFUNC>
-sub _set_rel_memcache {
-    return 1 unless @LJ::MEMCACHE_SERVERS;
-
-    my ($userid, $targetid, $type, $val) = @_;
-    return undef unless $userid && $targetid && defined $type;
-    $val = $val ? 1 : 0;
-
-    # memcache keys
-    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
-    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
-    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
-
-    my $now = time();
-    my $exp = $now + 3600*6; # 6 hour
-    LJ::MemCache::set($relkey, [$val, $now], $exp);
-    LJ::MemCache::set($modukey, $now, $exp);
-    LJ::MemCache::set($modtkey, $now, $exp);
-
-    # Also, delete these keys, since the contents have changed.
-    LJ::MemCache::delete([$userid, "reluser:$userid:$type"]);
-    LJ::MemCache::delete([$targetid, "reluser_rev:$targetid:$type"]);
-
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::check_rel
-# des: Checks whether two users are in a specified relationship to each other.
-# args: userid, targetid, type
-# des-userid: source userid, nonzero; may also be a user hash.
-# des-targetid: target userid, nonzero; may also be a user hash.
-# des-type: type of the relationship
-# returns: 1 if the relationship exists, 0 otherwise
-# </LJFUNC>
-sub check_rel {
-    my ($userid, $targetid, $type) = @_;
-    return undef unless $type && $userid && $targetid;
-
-    my $u = LJ::want_user($userid);
-    $userid = LJ::want_userid($userid);
-    $targetid = LJ::want_userid($targetid);
-
-    my $typeid = LJ::get_reluser_id($type)+0;
-    my $eff_type = $typeid || $type;
-
-    my $key = "$userid-$targetid-$eff_type";
-    return $LJ::REQ_CACHE_REL{$key} if defined $LJ::REQ_CACHE_REL{$key};
-
-    # did we get something from memcache?
-    my $memval = LJ::_get_rel_memcache($userid, $targetid, $eff_type);
-    return $memval if defined $memval;
-
-    # are we working on reluser or reluser2?
-    my ( $db, $table );
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_reader($u);
-        $table = "reluser2";
-    } else {
-        # non-clustered reluser table
-        $db = LJ::get_db_reader();
-        $table = "reluser";
-    }
-
-    # get data from db, force result to be {0|1}
-    my $dbval = $db->selectrow_array("SELECT COUNT(*) FROM $table ".
-                                     "WHERE userid=? AND targetid=? AND type=? ",
-                                     undef, $userid, $targetid, $eff_type)
-        ? 1 : 0;
-
-    # set in memcache
-    LJ::_set_rel_memcache($userid, $targetid, $eff_type, $dbval);
-
-    # return and set request cache
-    return $LJ::REQ_CACHE_REL{$key} = $dbval;
-}
-
-# <LJFUNC>
-# name: LJ::set_rel
-# des: Sets relationship information for two users.
-# args: dbs?, userid, targetid, type
-# des-dbs: Deprecated; optional, a master/slave set of database handles.
-# des-userid: source userid, or a user hash
-# des-targetid: target userid, or a user hash
-# des-type: type of the relationship
-# returns: 1 if set succeeded, otherwise undef
-# </LJFUNC>
-sub set_rel {
-    my ($userid, $targetid, $type) = @_;
-    return undef unless $type and $userid and $targetid;
-
-    my $u = LJ::want_user($userid);
-    $userid = LJ::want_userid($userid);
-    $targetid = LJ::want_userid($targetid);
-
-    my $typeid = LJ::get_reluser_id($type)+0;
-    my $eff_type = $typeid || $type;
-
-    # working on reluser or reluser2?
-    my ($db, $table);
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_master($u);
-        $table = "reluser2";
-    } else {
-        # non-clustered reluser global table
-        $db = LJ::get_db_writer();
-        $table = "reluser";
-    }
-    return undef unless $db;
-
-    # set in database
-    $db->do("REPLACE INTO $table (userid, targetid, type) VALUES (?, ?, ?)",
-            undef, $userid, $targetid, $eff_type);
-    return undef if $db->err;
-
-    # set in memcache
-    LJ::_set_rel_memcache($userid, $targetid, $eff_type, 1);
-
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::set_rel_multi
-# des: Sets relationship edges for lists of user tuples.
-# args: edges
-# des-edges: array of arrayrefs of edges to set: [userid, targetid, type].
-#            Where:
-#            userid: source userid, or a user hash;
-#            targetid: target userid, or a user hash;
-#            type: type of the relationship.
-# returns: 1 if all sets succeeded, otherwise undef
-# </LJFUNC>
-sub set_rel_multi {
-    return _mod_rel_multi({ mode => 'set', edges => \@_ });
-}
-
-# <LJFUNC>
-# name: LJ::clear_rel_multi
-# des: Clear relationship edges for lists of user tuples.
-# args: edges
-# des-edges: array of arrayrefs of edges to clear: [userid, targetid, type].
-#            Where:
-#            userid: source userid, or a user hash;
-#            targetid: target userid, or a user hash;
-#            type: type of the relationship.
-# returns: 1 if all clears succeeded, otherwise undef
-# </LJFUNC>
-sub clear_rel_multi {
-    return _mod_rel_multi({ mode => 'clear', edges => \@_ });
-}
-
-# <LJFUNC>
-# name: LJ::_mod_rel_multi
-# des: Sets/Clears relationship edges for lists of user tuples.
-# args: keys, edges
-# des-keys: keys: mode  => {clear|set}.
-# des-edges: edges =>  array of arrayrefs of edges to set: [userid, targetid, type]
-#            Where:
-#            userid: source userid, or a user hash;
-#            targetid: target userid, or a user hash;
-#            type: type of the relationship.
-# returns: 1 if all updates succeeded, otherwise undef
-# </LJFUNC>
-sub _mod_rel_multi
-{
-    my $opts = shift;
-    return undef unless @{$opts->{edges}};
-
-    my $mode = $opts->{mode} eq 'clear' ? 'clear' : 'set';
-    my $memval = $mode eq 'set' ? 1 : 0;
-
-    my @reluser  = (); # [userid, targetid, type]
-    my @reluser2 = ();
-    foreach my $edge (@{$opts->{edges}}) {
-        my ($userid, $targetid, $type) = @$edge;
-        $userid = LJ::want_userid($userid);
-        $targetid = LJ::want_userid($targetid);
-        next unless $type && $userid && $targetid;
-
-        my $typeid = LJ::get_reluser_id($type)+0;
-        my $eff_type = $typeid || $type;
-
-        # working on reluser or reluser2?
-        push @{$typeid ? \@reluser2 : \@reluser}, [$userid, $targetid, $eff_type];
-    }
-
-    # now group reluser2 edges by clusterid
-    my %reluser2 = (); # cid => [userid, targetid, type]
-    my $users = LJ::load_userids(map { $_->[0] } @reluser2);
-    foreach (@reluser2) {
-        my $cid = $users->{$_->[0]}->{clusterid} or next;
-        push @{$reluser2{$cid}}, $_;
-    }
-    @reluser2 = ();
-
-    # try to get all required cluster masters before we start doing database updates
-    my %cache_dbcm = ();
-    foreach my $cid (keys %reluser2) {
-        next unless @{$reluser2{$cid}};
-
-        # return undef immediately if we won't be able to do all the updates
-        $cache_dbcm{$cid} = LJ::get_cluster_master($cid)
-            or return undef;
-    }
-
-    # if any error occurs with a cluster, we'll skip over that cluster and continue
-    # trying to process others since we've likely already done some amount of db
-    # updates already, but we'll return undef to signify that everything did not
-    # go smoothly
-    my $ret = 1;
-
-    # do clustered reluser2 updates
-    foreach my $cid (keys %cache_dbcm) {
-        # array of arrayrefs: [userid, targetid, type]
-        my @edges = @{$reluser2{$cid}};
-
-        # set in database, then in memcache.  keep the two atomic per clusterid
-        my $dbcm = $cache_dbcm{$cid};
-
-        my @vals = map { @$_ } @edges;
-
-        if ($mode eq 'set') {
-            my $bind = join(",", map { "(?,?,?)" } @edges);
-            $dbcm->do("REPLACE INTO reluser2 (userid, targetid, type) VALUES $bind",
-                      undef, @vals);
-        }
-
-        if ($mode eq 'clear') {
-            my $where = join(" OR ", map { "(userid=? AND targetid=? AND type=?)" } @edges);
-            $dbcm->do("DELETE FROM reluser2 WHERE $where", undef, @vals);
-        }
-
-        # don't update memcache if db update failed for this cluster
-        if ($dbcm->err) {
-            $ret = undef;
-            next;
-        }
-
-        # updates to this cluster succeeded, set memcache
-        LJ::_set_rel_memcache(@$_, $memval) foreach @edges;
-    }
-
-    # do global reluser updates
-    if (@reluser) {
-
-        # nothing to do after this block but return, so we can
-        # immediately return undef from here if there's a problem
-        my $dbh = LJ::get_db_writer()
-            or return undef;
-
-        my @vals = map { @$_ } @reluser;
-
-        if ($mode eq 'set') {
-            my $bind = join(",", map { "(?,?,?)" } @reluser);
-            $dbh->do("REPLACE INTO reluser (userid, targetid, type) VALUES $bind",
-                     undef, @vals);
-        }
-
-        if ($mode eq 'clear') {
-            my $where = join(" OR ", map { "userid=? AND targetid=? AND type=?" } @reluser);
-            $dbh->do("DELETE FROM reluser WHERE $where", undef, @vals);
-        }
-
-        # don't update memcache if db update failed for this cluster
-        return undef if $dbh->err;
-
-        # $_ = [userid, targetid, type] for each iteration
-        LJ::_set_rel_memcache(@$_, $memval) foreach @reluser;
-    }
-
-    return $ret;
-}
-
-
-# <LJFUNC>
-# name: LJ::clear_rel
-# des: Deletes a relationship between two users or all relationships of a particular type
-#      for one user, on either side of the relationship.
-# info: One of userid,targetid -- bit not both -- may be '*'. In that case,
-#       if, say, userid is '*', then all relationship edges with target equal to
-#       targetid and of the specified type are deleted.
-#       If both userid and targetid are numbers, just one edge is deleted.
-# args: dbs?, userid, targetid, type
-# des-dbs: Deprecated; optional, a master/slave set of database handles.
-# des-userid: source userid, or a user hash, or '*'
-# des-targetid: target userid, or a user hash, or '*'
-# des-type: type of the relationship
-# returns: 1 if clear succeeded, otherwise undef
-# </LJFUNC>
-sub clear_rel {
-    my ($userid, $targetid, $type) = @_;
-    return undef if $userid eq '*' and $targetid eq '*';
-
-    my $u;
-    $u = LJ::want_user($userid) unless $userid eq '*';
-    $userid = LJ::want_userid($userid) unless $userid eq '*';
-    $targetid = LJ::want_userid($targetid) unless $targetid eq '*';
-    return undef unless $type && $userid && $targetid;
-
-    my $typeid = LJ::get_reluser_id($type)+0;
-
-    if ($typeid) {
-        # clustered reluser2 table
-        return undef unless $u->writer;
-
-        $u->do("DELETE FROM reluser2 WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
-               ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$typeid");
-
-        return undef if $u->err;
-    } else {
-        # non-clustered global reluser table
-        my $dbh = LJ::get_db_writer()
-            or return undef;
-
-        my $qtype = $dbh->quote($type);
-        $dbh->do("DELETE FROM reluser WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
-                 ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$qtype");
-
-        return undef if $dbh->err;
-    }
-
-    # if one of userid or targetid are '*', then we need to note the modtime
-    # of the reluser edge from the specified id (the one that's not '*')
-    # so that subsequent gets on rel:userid:targetid:type will know to ignore
-    # what they got from memcache
-    my $eff_type = $typeid || $type;
-    if ($userid eq '*') {
-        LJ::MemCache::set([$targetid, "relmodt:$targetid:$eff_type"], time());
-    } elsif ($targetid eq '*') {
-        LJ::MemCache::set([$userid, "relmodu:$userid:$eff_type"], time());
-
-    # if neither userid nor targetid are '*', then just call _set_rel_memcache
-    # to update the rel:userid:targetid:type memcache key as well as the
-    # userid and targetid modtime keys
-    } else {
-        LJ::_set_rel_memcache($userid, $targetid, $eff_type, 0);
-    }
-
-    return 1;
-}
-
-1;
--------------------------------------------------------------------------------

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org