3 final class PhabricatorProjectsMembershipIndexEngineExtension
4 extends PhabricatorIndexEngineExtension
{
6 const EXTENSIONKEY
= 'project.members';
8 public function getExtensionName() {
9 return pht('Project Members');
12 public function shouldIndexObject($object) {
13 if (!($object instanceof PhabricatorProject
)) {
20 public function indexObject(
21 PhabricatorIndexEngine
$engine,
24 $this->rematerialize($object);
27 public function rematerialize(PhabricatorProject
$project) {
28 $materialize = $project->getAncestorProjects();
29 array_unshift($materialize, $project);
31 foreach ($materialize as $project) {
32 $this->materializeProject($project);
36 private function materializeProject(PhabricatorProject
$project) {
37 $material_type = PhabricatorProjectMaterializedMemberEdgeType
::EDGECONST
;
38 $member_type = PhabricatorProjectProjectHasMemberEdgeType
::EDGECONST
;
40 $project_phid = $project->getPHID();
42 if ($project->isMilestone()) {
43 $source_phids = array($project->getParentProjectPHID());
44 $has_subprojects = false;
46 $descendants = id(new PhabricatorProjectQuery())
47 ->setViewer($this->getViewer())
48 ->withAncestorProjectPHIDs(array($project->getPHID()))
49 ->withIsMilestone(false)
50 ->withHasSubprojects(false)
52 $descendant_phids = mpull($descendants, 'getPHID');
54 if ($descendant_phids) {
55 $source_phids = $descendant_phids;
56 $has_subprojects = true;
58 $source_phids = array($project->getPHID());
59 $has_subprojects = false;
63 $conn_w = $project->establishConnection('w');
65 $any_milestone = queryfx_one(
68 WHERE parentProjectPHID = %s AND milestoneNumber IS NOT NULL
70 $project->getTableName(),
72 $has_milestones = (bool)$any_milestone;
74 $project->openTransaction();
76 // Copy current member edges to create new materialized edges.
78 // See T13596. Avoid executing this as an "INSERT ... SELECT" to reduce
79 // the required level of table locking. Since we're decomposing it into
80 // "SELECT" + "INSERT" anyway, we can also compute exactly which rows
81 // need to be modified.
83 $have_rows = queryfx_all(
86 WHERE src = %s AND type = %d',
87 PhabricatorEdgeConfig
::TABLE_NAME_EDGE
,
91 $want_rows = queryfx_all(
93 'SELECT dst, dateCreated, seq FROM %T
94 WHERE src IN (%Ls) AND type = %d',
95 PhabricatorEdgeConfig
::TABLE_NAME_EDGE
,
99 $have_phids = ipull($have_rows, 'dst', 'dst');
100 $want_phids = ipull($want_rows, null, 'dst');
102 $rem_phids = array_diff_key($have_phids, $want_phids);
103 $rem_phids = array_keys($rem_phids);
105 $add_phids = array_diff_key($want_phids, $have_phids);
106 $add_phids = array_keys($add_phids);
109 foreach ($rem_phids as $rem_phid) {
110 $rem_sql[] = qsprintf(
117 foreach ($add_phids as $add_phid) {
118 $add_row = $want_phids[$add_phid];
119 $add_sql[] = qsprintf(
121 '(%s, %d, %s, %d, %d)',
125 $add_row['dateCreated'],
129 // Remove materialized members who are no longer project members.
132 foreach (PhabricatorLiskDAO
::chunkSQL($rem_sql) as $sql_chunk) {
136 WHERE src = %s AND type = %s AND dst IN (%LQ)',
137 PhabricatorEdgeConfig
::TABLE_NAME_EDGE
,
144 // Add project members who are not yet materialized members.
147 foreach (PhabricatorLiskDAO
::chunkSQL($add_sql) as $sql_chunk) {
150 'INSERT IGNORE INTO %T (src, type, dst, dateCreated, seq)
152 PhabricatorEdgeConfig
::TABLE_NAME_EDGE
,
157 // Update the hasSubprojects flag.
160 'UPDATE %T SET hasSubprojects = %d WHERE id = %d',
161 $project->getTableName(),
162 (int)$has_subprojects,
165 // Update the hasMilestones flag.
168 'UPDATE %T SET hasMilestones = %d WHERE id = %d',
169 $project->getTableName(),
170 (int)$has_milestones,
173 $project->saveTransaction();