1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 19:40:55 +01:00

Fix "Blocked" task queries with multiple subtasks, and update language

Summary:
Ref T8126. See that task for discussion. This change:

  - Updates language to be more consistent ("Parents", "Subtasks") since I moved us away from the often-confusing "Block" language in T4788.
  - Fixes bugs with finding the wrong set of tasks if tasks have a mixture of open and closed subtasks or parents.

Test Plan:
  - Created four tasks: no subtasks, one closed subtask, one open subtask, mixture of open and closed subtasks.
  - Created four more tasks: no parents, one closed parent, one open parent, mixture of open and closed parents.
  - Searched for all this stuff, got the proper results:

{F1740683}

{F1740684}

{F1740685}

{F1740686}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T8126

Differential Revision: https://secure.phabricator.com/D16340
This commit is contained in:
epriestley 2016-07-28 09:18:47 -07:00
parent 2e41c85cc9
commit c715b42f36
2 changed files with 100 additions and 98 deletions

View file

@ -20,6 +20,8 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $subpriorityMin;
private $subpriorityMax;
private $bridgedObjectPHIDs;
private $hasOpenParents;
private $hasOpenSubtasks;
private $fullTextSearch = '';
@ -51,8 +53,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $needSubscriberPHIDs;
private $needProjectPHIDs;
private $blockingTasks;
private $blockedTasks;
public function withAuthors(array $authors) {
$this->authorPHIDs = $authors;
@ -151,34 +151,16 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
return $this;
}
/**
* True returns tasks that are blocking other tasks only.
* False returns tasks that are not blocking other tasks only.
* Null returns tasks regardless of blocking status.
*/
public function withBlockingTasks($mode) {
$this->blockingTasks = $mode;
public function withOpenSubtasks($value) {
$this->hasOpenSubtasks = $value;
return $this;
}
public function shouldJoinBlockingTasks() {
return $this->blockingTasks !== null;
}
/**
* True returns tasks that are blocked by other tasks only.
* False returns tasks that are not blocked by other tasks only.
* Null returns tasks regardless of blocked by status.
*/
public function withBlockedTasks($mode) {
$this->blockedTasks = $mode;
public function withOpenParents($value) {
$this->hasOpenParents = $value;
return $this;
}
public function shouldJoinBlockedTasks() {
return $this->blockedTasks !== null;
}
public function withDateCreatedBefore($date_created_before) {
$this->dateCreatedBefore = $date_created_before;
return $this;
@ -335,7 +317,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
$where = parent::buildWhereClauseParts($conn);
$where[] = $this->buildStatusWhereClause($conn);
$where[] = $this->buildDependenciesWhereClause($conn);
$where[] = $this->buildOwnerWhereClause($conn);
$where[] = $this->buildFullTextWhereClause($conn);
@ -526,71 +507,65 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
$fulltext_results);
}
private function buildDependenciesWhereClause(
AphrontDatabaseConnection $conn) {
if (!$this->shouldJoinBlockedTasks() &&
!$this->shouldJoinBlockingTasks()) {
return null;
}
$parts = array();
if ($this->blockingTasks === true) {
$parts[] = qsprintf(
$conn,
'blocking.dst IS NOT NULL AND blockingtask.status IN (%Ls)',
ManiphestTaskStatus::getOpenStatusConstants());
} else if ($this->blockingTasks === false) {
$parts[] = qsprintf(
$conn,
'blocking.dst IS NULL OR blockingtask.status NOT IN (%Ls)',
ManiphestTaskStatus::getOpenStatusConstants());
}
if ($this->blockedTasks === true) {
$parts[] = qsprintf(
$conn,
'blocked.dst IS NOT NULL AND blockedtask.status IN (%Ls)',
ManiphestTaskStatus::getOpenStatusConstants());
} else if ($this->blockedTasks === false) {
$parts[] = qsprintf(
$conn,
'blocked.dst IS NULL OR blockedtask.status NOT IN (%Ls)',
ManiphestTaskStatus::getOpenStatusConstants());
}
return '('.implode(') OR (', $parts).')';
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) {
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$open_statuses = ManiphestTaskStatus::getOpenStatusConstants();
$edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
$task_table = $this->newResultObject()->getTableName();
$joins = array();
if ($this->hasOpenParents !== null) {
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
if ($this->hasOpenParents) {
$join_type = 'JOIN';
} else {
$join_type = 'LEFT JOIN';
}
if ($this->shouldJoinBlockingTasks()) {
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T blocking ON blocking.src = task.phid '.
'AND blocking.type = %d '.
'LEFT JOIN %T blockingtask ON blocking.dst = blockingtask.phid',
$conn,
'%Q %T e_parent
ON e_parent.src = task.phid
AND e_parent.type = %d
%Q %T parent
ON e_parent.dst = parent.phid
AND parent.status IN (%Ls)',
$join_type,
$edge_table,
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST,
id(new ManiphestTask())->getTableName());
$parent_type,
$join_type,
$task_table,
$open_statuses);
}
if ($this->shouldJoinBlockedTasks()) {
if ($this->hasOpenSubtasks !== null) {
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
if ($this->hasOpenSubtasks) {
$join_type = 'JOIN';
} else {
$join_type = 'LEFT JOIN';
}
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T blocked ON blocked.src = task.phid '.
'AND blocked.type = %d '.
'LEFT JOIN %T blockedtask ON blocked.dst = blockedtask.phid',
$conn,
'%Q %T e_subtask
ON e_subtask.src = task.phid
AND e_subtask.type = %d
%Q %T subtask
ON e_subtask.dst = subtask.phid
AND subtask.status IN (%Ls)',
$join_type,
$edge_table,
ManiphestTaskDependsOnTaskEdgeType::EDGECONST,
id(new ManiphestTask())->getTableName());
$subtask_type,
$join_type,
$task_table,
$open_statuses);
}
if ($this->subscriberPHIDs !== null) {
$joins[] = qsprintf(
$conn_r,
$conn,
'JOIN %T e_ccs ON e_ccs.src = task.phid '.
'AND e_ccs.type = %s '.
'AND e_ccs.dst in (%Ls)',
@ -604,7 +579,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
$ignore_group_phids = $this->getIgnoreGroupedProjectPHIDs();
if ($ignore_group_phids) {
$joins[] = qsprintf(
$conn_r,
$conn,
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
AND projectGroup.type = %d
AND projectGroup.dst NOT IN (%Ls)',
@ -613,29 +588,30 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
$ignore_group_phids);
} else {
$joins[] = qsprintf(
$conn_r,
$conn,
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
AND projectGroup.type = %d',
$edge_table,
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
}
$joins[] = qsprintf(
$conn_r,
$conn,
'LEFT JOIN %T projectGroupName
ON projectGroup.dst = projectGroupName.indexedObjectPHID',
id(new ManiphestNameIndex())->getTableName());
break;
}
$joins[] = parent::buildJoinClauseParts($conn_r);
$joins[] = parent::buildJoinClauseParts($conn);
return $joins;
}
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
$joined_multiple_rows = $this->shouldJoinBlockingTasks() ||
$this->shouldJoinBlockedTasks() ||
($this->shouldGroupQueryResultRows());
$joined_multiple_rows =
($this->hasOpenParents !== null) ||
($this->hasOpenSubtasks !== null) ||
$this->shouldGroupQueryResultRows();
$joined_project_name = ($this->groupBy == self::GROUP_PROJECT);
@ -652,6 +628,30 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
}
}
protected function buildHavingClauseParts(AphrontDatabaseConnection $conn) {
$having = parent::buildHavingClauseParts($conn);
if ($this->hasOpenParents !== null) {
if (!$this->hasOpenParents) {
$having[] = qsprintf(
$conn,
'COUNT(parent.phid) = 0');
}
}
if ($this->hasOpenSubtasks !== null) {
if (!$this->hasOpenSubtasks) {
$having[] = qsprintf(
$conn,
'COUNT(subtask.phid) = 0');
}
}
return $having;
}
/**
* Return project PHIDs which we should ignore when grouping tasks by
* project. For example, if a user issues a query like:

View file

@ -77,19 +77,21 @@ final class ManiphestTaskSearchEngine
->setLabel(pht('Contains Words'))
->setKey('fulltext'),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Blocking'))
->setKey('blocking')
->setLabel(pht('Open Parents'))
->setKey('hasParents')
->setAliases(array('blocking'))
->setOptions(
pht('(Show All)'),
pht('Show Only Tasks Blocking Other Tasks'),
pht('Hide Tasks Blocking Other Tasks')),
pht('Show Only Tasks With Open Parents'),
pht('Show Only Tasks Without Open Parents')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Blocked'))
->setKey('blocked')
->setLabel(pht('Open Subtasks'))
->setKey('hasSubtasks')
->setAliases(array('blocked'))
->setOptions(
pht('(Show All)'),
pht('Show Only Task Blocked By Other Tasks'),
pht('Hide Tasks Blocked By Other Tasks')),
pht('Show Only Tasks With Open Subtasks'),
pht('Show Only Tasks Without Open Subtasks')),
id(new PhabricatorSearchSelectField())
->setLabel(pht('Group By'))
->setKey('group')
@ -121,8 +123,8 @@ final class ManiphestTaskSearchEngine
'statuses',
'priorities',
'fulltext',
'blocking',
'blocked',
'hasParents',
'hasSubtasks',
'group',
'order',
'ids',
@ -182,12 +184,12 @@ final class ManiphestTaskSearchEngine
$query->withDateModifiedBefore($map['modifiedEnd']);
}
if ($map['blocking'] !== null) {
$query->withBlockingTasks($map['blocking']);
if ($map['hasParents'] !== null) {
$query->withOpenParents($map['hasParents']);
}
if ($map['blocked'] !== null) {
$query->withBlockedTasks($map['blocked']);
if ($map['hasSubtasks'] !== null) {
$query->withOpenSubtasks($map['hasSubtasks']);
}
if (strlen($map['fulltext'])) {