mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-24 06:20:56 +01:00
Modernize more paging/order queries
Summary: Ref T7803. Removes some getReversePaging(). This also fixes `null` column handling, by adding an explicit `'null'` key with possible values "head" (put NULL before other values) or "tail" (put NULL after other values). Maniphest has some glitchiness in paging through NULLs right now, but I believe it's all pre-existing and will be resolved when it fully converts. Diffusion is fully converted and pages through NULL correctly. Test Plan: - Failed to identify any reason for ChangesetQuery to reverse paging. - Paged thorugh Diffusion. - Paged through Maniphest. - Maniphest has some issues when paging inside a NULL section, but these issues are preexisting and will be resolved later in this change sequence. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T7803 Differential Revision: https://secure.phabricator.com/D12371
This commit is contained in:
parent
9c7c13ffc8
commit
4114560844
5 changed files with 230 additions and 213 deletions
|
@ -150,8 +150,4 @@ final class DifferentialChangesetQuery
|
||||||
return 'PhabricatorDifferentialApplication';
|
return 'PhabricatorDifferentialApplication';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getReversePaging() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -969,13 +969,11 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
|
protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
|
||||||
$default = parent::buildPagingClause($conn_r);
|
|
||||||
|
|
||||||
$before_id = $this->getBeforeID();
|
$before_id = $this->getBeforeID();
|
||||||
$after_id = $this->getAfterID();
|
$after_id = $this->getAfterID();
|
||||||
|
|
||||||
if (!$before_id && !$after_id) {
|
if (!$before_id && !$after_id) {
|
||||||
return $default;
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$cursor_id = nonempty($before_id, $after_id);
|
$cursor_id = nonempty($before_id, $after_id);
|
||||||
|
@ -1004,28 +1002,24 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case self::GROUP_OWNER:
|
case self::GROUP_OWNER:
|
||||||
$columns[] = array(
|
$value = null;
|
||||||
'table' => 'task',
|
|
||||||
'column' => 'ownerOrdering',
|
|
||||||
'value' => strlen($group_id),
|
|
||||||
'type' => 'null',
|
|
||||||
);
|
|
||||||
if ($group_id) {
|
if ($group_id) {
|
||||||
$paging_users = id(new PhabricatorPeopleQuery())
|
$paging_users = id(new PhabricatorPeopleQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->withPHIDs(array($group_id))
|
->withPHIDs(array($group_id))
|
||||||
->execute();
|
->execute();
|
||||||
if (!$paging_users) {
|
if ($paging_users) {
|
||||||
return null;
|
$value = head($paging_users)->getUsername();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$columns[] = array(
|
$columns[] = array(
|
||||||
'table' => 'task',
|
'table' => 'task',
|
||||||
'column' => 'ownerOrdering',
|
'column' => 'ownerOrdering',
|
||||||
'value' => head($paging_users)->getUsername(),
|
'value' => $value,
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
|
'null' => 'head',
|
||||||
'reverse' => true,
|
'reverse' => true,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case self::GROUP_STATUS:
|
case self::GROUP_STATUS:
|
||||||
$columns[] = array(
|
$columns[] = array(
|
||||||
|
@ -1036,28 +1030,25 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case self::GROUP_PROJECT:
|
case self::GROUP_PROJECT:
|
||||||
$columns[] = array(
|
$value = null;
|
||||||
'table' => 'projectGroupName',
|
|
||||||
'column' => 'indexedObjectName',
|
|
||||||
'value' => strlen($group_id),
|
|
||||||
'type' => 'null',
|
|
||||||
);
|
|
||||||
if ($group_id) {
|
if ($group_id) {
|
||||||
$paging_projects = id(new PhabricatorProjectQuery())
|
$paging_projects = id(new PhabricatorProjectQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->withPHIDs(array($group_id))
|
->withPHIDs(array($group_id))
|
||||||
->execute();
|
->execute();
|
||||||
if (!$paging_projects) {
|
if ($paging_projects) {
|
||||||
return null;
|
$value = head($paging_projects)->getName();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$columns[] = array(
|
$columns[] = array(
|
||||||
'table' => 'projectGroupName',
|
'table' => 'projectGroupName',
|
||||||
'column' => 'indexedObjectName',
|
'column' => 'indexedObjectName',
|
||||||
'value' => head($paging_projects)->getName(),
|
'value' => $value,
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
|
'null' => 'head',
|
||||||
'reverse' => true,
|
'reverse' => true,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown group query '{$this->groupBy}'!");
|
throw new Exception("Unknown group query '{$this->groupBy}'!");
|
||||||
|
|
|
@ -56,10 +56,10 @@ final class PhrequentUserTimeQuery
|
||||||
$this->setOrderVector(array('start', 'id'));
|
$this->setOrderVector(array('start', 'id'));
|
||||||
break;
|
break;
|
||||||
case self::ORDER_ENDED_ASC:
|
case self::ORDER_ENDED_ASC:
|
||||||
$this->setOrderVector(array('-ongoing', '-end', '-id'));
|
$this->setOrderVector(array('-end', '-id'));
|
||||||
break;
|
break;
|
||||||
case self::ORDER_ENDED_DESC:
|
case self::ORDER_ENDED_DESC:
|
||||||
$this->setOrderVector(array('ongoing', 'end', 'id'));
|
$this->setOrderVector(array('end', 'id'));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception(pht('Unknown order "%s".', $order));
|
throw new Exception(pht('Unknown order "%s".', $order));
|
||||||
|
@ -125,13 +125,10 @@ final class PhrequentUserTimeQuery
|
||||||
'column' => 'dateStarted',
|
'column' => 'dateStarted',
|
||||||
'type' => 'int',
|
'type' => 'int',
|
||||||
),
|
),
|
||||||
'ongoing' => array(
|
|
||||||
'column' => 'dateEnded',
|
|
||||||
'type' => 'null',
|
|
||||||
),
|
|
||||||
'end' => array(
|
'end' => array(
|
||||||
'column' => 'dateEnded',
|
'column' => 'dateEnded',
|
||||||
'type' => 'int',
|
'type' => 'int',
|
||||||
|
'null' => 'head',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -141,7 +138,6 @@ final class PhrequentUserTimeQuery
|
||||||
return array(
|
return array(
|
||||||
'id' => $usertime->getID(),
|
'id' => $usertime->getID(),
|
||||||
'start' => $usertime->getDateStarted(),
|
'start' => $usertime->getDateStarted(),
|
||||||
'ongoing' => $usertime->getDateEnded(),
|
|
||||||
'end' => $usertime->getDateEnded(),
|
'end' => $usertime->getDateEnded(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ final class PhabricatorRepositoryQuery
|
||||||
const ORDER_CALLSIGN = 'order-callsign';
|
const ORDER_CALLSIGN = 'order-callsign';
|
||||||
const ORDER_NAME = 'order-name';
|
const ORDER_NAME = 'order-name';
|
||||||
const ORDER_SIZE = 'order-size';
|
const ORDER_SIZE = 'order-size';
|
||||||
private $order = self::ORDER_CREATED;
|
|
||||||
|
|
||||||
const HOSTED_PHABRICATOR = 'hosted-phab';
|
const HOSTED_PHABRICATOR = 'hosted-phab';
|
||||||
const HOSTED_REMOTE = 'hosted-remote';
|
const HOSTED_REMOTE = 'hosted-remote';
|
||||||
|
@ -126,7 +125,25 @@ final class PhabricatorRepositoryQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setOrder($order) {
|
public function setOrder($order) {
|
||||||
$this->order = $order;
|
switch ($order) {
|
||||||
|
case self::ORDER_CREATED:
|
||||||
|
$this->setOrderVector(array('id'));
|
||||||
|
break;
|
||||||
|
case self::ORDER_COMMITTED:
|
||||||
|
$this->setOrderVector(array('committed', 'id'));
|
||||||
|
break;
|
||||||
|
case self::ORDER_CALLSIGN:
|
||||||
|
$this->setOrderVector(array('callsign'));
|
||||||
|
break;
|
||||||
|
case self::ORDER_NAME:
|
||||||
|
$this->setOrderVector(array('name', 'id'));
|
||||||
|
break;
|
||||||
|
case self::ORDER_SIZE:
|
||||||
|
$this->setOrderVector(array('size', 'id'));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception(pht('Unknown order "%s".', $order));
|
||||||
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +169,7 @@ final class PhabricatorRepositoryQuery
|
||||||
$table->getTableName(),
|
$table->getTableName(),
|
||||||
$this->buildJoinsClause($conn_r),
|
$this->buildJoinsClause($conn_r),
|
||||||
$this->buildWhereClause($conn_r),
|
$this->buildWhereClause($conn_r),
|
||||||
$this->buildCustomOrderClause($conn_r),
|
$this->buildOrderClause($conn_r),
|
||||||
$this->buildLimitClause($conn_r));
|
$this->buildLimitClause($conn_r));
|
||||||
|
|
||||||
$repositories = $table->loadAllFromArray($data);
|
$repositories = $table->loadAllFromArray($data);
|
||||||
|
@ -295,162 +312,97 @@ final class PhabricatorRepositoryQuery
|
||||||
return $repositories;
|
return $repositories;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildCustomOrderClause(AphrontDatabaseConnection $conn) {
|
public function getPrimaryTableAlias() {
|
||||||
$parts = array();
|
return 'r';
|
||||||
|
}
|
||||||
|
|
||||||
$order = $this->order;
|
public function getOrderableColumns() {
|
||||||
switch ($order) {
|
return parent::getOrderableColumns() + array(
|
||||||
case self::ORDER_CREATED:
|
'committed' => array(
|
||||||
break;
|
|
||||||
case self::ORDER_COMMITTED:
|
|
||||||
$parts[] = array(
|
|
||||||
'table' => 's',
|
'table' => 's',
|
||||||
'column' => 'epoch',
|
'column' => 'epoch',
|
||||||
);
|
'type' => 'int',
|
||||||
break;
|
'null' => 'tail',
|
||||||
case self::ORDER_CALLSIGN:
|
),
|
||||||
$parts[] = array(
|
'callsign' => array(
|
||||||
'table' => 'r',
|
'table' => 'r',
|
||||||
'column' => 'callsign',
|
'column' => 'callsign',
|
||||||
|
'type' => 'string',
|
||||||
|
'unique' => true,
|
||||||
'reverse' => true,
|
'reverse' => true,
|
||||||
);
|
),
|
||||||
break;
|
'name' => array(
|
||||||
case self::ORDER_NAME:
|
|
||||||
$parts[] = array(
|
|
||||||
'table' => 'r',
|
'table' => 'r',
|
||||||
'column' => 'name',
|
'column' => 'name',
|
||||||
|
'type' => 'string',
|
||||||
'reverse' => true,
|
'reverse' => true,
|
||||||
);
|
),
|
||||||
break;
|
'size' => array(
|
||||||
case self::ORDER_SIZE:
|
|
||||||
$parts[] = array(
|
|
||||||
'table' => 's',
|
'table' => 's',
|
||||||
'column' => 'size',
|
'column' => 'size',
|
||||||
|
'type' => 'int',
|
||||||
|
'null' => 'tail',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Unknown order '{$order}!'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$parts[] = array(
|
protected function willExecuteCursorQuery(
|
||||||
'table' => 'r',
|
PhabricatorCursorPagedPolicyAwareQuery $query) {
|
||||||
'column' => 'id',
|
$vector = $this->getOrderVector();
|
||||||
);
|
|
||||||
|
|
||||||
return $this->formatOrderClause($conn, $parts);
|
if ($vector->containsKey('committed')) {
|
||||||
}
|
|
||||||
|
|
||||||
protected function loadCursorObject($id) {
|
|
||||||
$query = id(new PhabricatorRepositoryQuery())
|
|
||||||
->setViewer($this->getPagingViewer())
|
|
||||||
->withIDs(array((int)$id));
|
|
||||||
|
|
||||||
if ($this->order == self::ORDER_COMMITTED) {
|
|
||||||
$query->needMostRecentCommits(true);
|
$query->needMostRecentCommits(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->order == self::ORDER_SIZE) {
|
if ($vector->containsKey('size')) {
|
||||||
$query->needCommitCounts(true);
|
$query->needCommitCounts(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $query->execute();
|
|
||||||
return head($results);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
|
protected function getPagingValueMap($cursor, array $keys) {
|
||||||
$default = parent::buildPagingClause($conn_r);
|
$repository = $this->loadCursorObject($cursor);
|
||||||
|
|
||||||
$before_id = $this->getBeforeID();
|
$map = array(
|
||||||
$after_id = $this->getAfterID();
|
'id' => $repository->getID(),
|
||||||
|
'callsign' => $repository->getCallsign(),
|
||||||
|
'name' => $repository->getName(),
|
||||||
|
);
|
||||||
|
|
||||||
if (!$before_id && !$after_id) {
|
foreach ($keys as $key) {
|
||||||
return $default;
|
switch ($key) {
|
||||||
}
|
case 'committed':
|
||||||
|
$commit = $repository->getMostRecentCommit();
|
||||||
$order = $this->order;
|
if ($commit) {
|
||||||
if ($order == self::ORDER_CREATED) {
|
$map[$key] = $commit->getEpoch();
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($before_id) {
|
|
||||||
$cursor = $this->loadCursorObject($before_id);
|
|
||||||
} else {
|
} else {
|
||||||
$cursor = $this->loadCursorObject($after_id);
|
$map[$key] = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'size':
|
||||||
|
$count = $repository->getCommitCount();
|
||||||
|
if ($count) {
|
||||||
|
$map[$key] = $count;
|
||||||
|
} else {
|
||||||
|
$map[$key] = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$cursor) {
|
return $map;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id_column = array(
|
|
||||||
'table' => 'r',
|
|
||||||
'column' => 'id',
|
|
||||||
'type' => 'int',
|
|
||||||
'value' => $cursor->getID(),
|
|
||||||
);
|
|
||||||
|
|
||||||
$columns = array();
|
|
||||||
switch ($order) {
|
|
||||||
case self::ORDER_COMMITTED:
|
|
||||||
$commit = $cursor->getMostRecentCommit();
|
|
||||||
if (!$commit) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$columns[] = array(
|
|
||||||
'table' => 's',
|
|
||||||
'column' => 'epoch',
|
|
||||||
'type' => 'int',
|
|
||||||
'value' => $commit->getEpoch(),
|
|
||||||
);
|
|
||||||
$columns[] = $id_column;
|
|
||||||
break;
|
|
||||||
case self::ORDER_CALLSIGN:
|
|
||||||
$columns[] = array(
|
|
||||||
'table' => 'r',
|
|
||||||
'column' => 'callsign',
|
|
||||||
'type' => 'string',
|
|
||||||
'value' => $cursor->getCallsign(),
|
|
||||||
'reverse' => true,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case self::ORDER_NAME:
|
|
||||||
$columns[] = array(
|
|
||||||
'table' => 'r',
|
|
||||||
'column' => 'name',
|
|
||||||
'type' => 'string',
|
|
||||||
'value' => $cursor->getName(),
|
|
||||||
'reverse' => true,
|
|
||||||
);
|
|
||||||
$columns[] = $id_column;
|
|
||||||
break;
|
|
||||||
case self::ORDER_SIZE:
|
|
||||||
$columns[] = array(
|
|
||||||
'table' => 's',
|
|
||||||
'column' => 'size',
|
|
||||||
'type' => 'int',
|
|
||||||
'value' => $cursor->getCommitCount(),
|
|
||||||
);
|
|
||||||
$columns[] = $id_column;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Unknown order '{$order}'!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->buildPagingClauseFromMultipleColumns(
|
|
||||||
$conn_r,
|
|
||||||
$columns,
|
|
||||||
array(
|
|
||||||
'reversed' => ($this->getReversePaging() xor (bool)($before_id)),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
|
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
|
||||||
$joins = array();
|
$joins = array();
|
||||||
|
|
||||||
$join_summary_table = $this->needCommitCounts ||
|
$join_summary_table = $this->needCommitCounts ||
|
||||||
$this->needMostRecentCommits ||
|
$this->needMostRecentCommits;
|
||||||
($this->order == self::ORDER_COMMITTED) ||
|
|
||||||
($this->order == self::ORDER_SIZE);
|
$vector = $this->getOrderVector();
|
||||||
|
if ($vector->containsKey('committed') ||
|
||||||
|
$vector->containsKey('size')) {
|
||||||
|
$join_summary_table = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($join_summary_table) {
|
if ($join_summary_table) {
|
||||||
$joins[] = qsprintf(
|
$joins[] = qsprintf(
|
||||||
|
@ -554,6 +506,8 @@ final class PhabricatorRepositoryQuery
|
||||||
return $this->formatWhereClause($where);
|
return $this->formatWhereClause($where);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function getQueryApplicationClass() {
|
public function getQueryApplicationClass() {
|
||||||
return 'PhabricatorDiffusionApplication';
|
return 'PhabricatorDiffusionApplication';
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,6 +320,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'reverse' => 'optional bool',
|
'reverse' => 'optional bool',
|
||||||
'unique' => 'optional bool',
|
'unique' => 'optional bool',
|
||||||
|
'null' => 'optional string|null',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,10 +337,20 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
$last_key = last_key($columns);
|
$last_key = last_key($columns);
|
||||||
foreach ($columns as $key => $column) {
|
foreach ($columns as $key => $column) {
|
||||||
$type = $column['type'];
|
$type = $column['type'];
|
||||||
|
|
||||||
|
$null = idx($column, 'null');
|
||||||
|
if ($column['value'] === null) {
|
||||||
|
if ($null) {
|
||||||
|
$value = null;
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Column "%s" has null value, but does not specify a null '.
|
||||||
|
'behavior.',
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'null':
|
|
||||||
$value = qsprintf($conn, '%d', ($column['value'] ? 0 : 1));
|
|
||||||
break;
|
|
||||||
case 'int':
|
case 'int':
|
||||||
$value = qsprintf($conn, '%d', $column['value']);
|
$value = qsprintf($conn, '%d', $column['value']);
|
||||||
break;
|
break;
|
||||||
|
@ -350,7 +361,12 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
$value = qsprintf($conn, '%s', $column['value']);
|
$value = qsprintf($conn, '%s', $column['value']);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown column type '{$type}'!");
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Column "%s" has unknown column type "%s".',
|
||||||
|
$column['column'],
|
||||||
|
$type));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$is_column_reversed = idx($column, 'reverse', false);
|
$is_column_reversed = idx($column, 'reverse', false);
|
||||||
|
@ -366,24 +382,68 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
$field = qsprintf($conn, '%T', $column_name);
|
$field = qsprintf($conn, '%T', $column_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type == 'null') {
|
$parts = array();
|
||||||
$field = qsprintf($conn, '(%Q IS NULL)', $field);
|
if ($null) {
|
||||||
|
$can_page_if_null = ($null === 'head');
|
||||||
|
$can_page_if_nonnull = ($null === 'tail');
|
||||||
|
|
||||||
|
if ($reverse) {
|
||||||
|
$can_page_if_null = !$can_page_if_null;
|
||||||
|
$can_page_if_nonnull = !$can_page_if_nonnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
$clause[] = qsprintf(
|
$subclause = null;
|
||||||
|
if ($can_page_if_null && $value === null) {
|
||||||
|
$parts[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'(%Q IS NOT NULL)',
|
||||||
|
$field);
|
||||||
|
} else if ($can_page_if_nonnull && $value !== null) {
|
||||||
|
$parts[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'(%Q IS NULL)',
|
||||||
|
$field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value !== null) {
|
||||||
|
$parts[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'%Q %Q %Q',
|
'%Q %Q %Q',
|
||||||
$field,
|
$field,
|
||||||
$reverse ? '>' : '<',
|
$reverse ? '>' : '<',
|
||||||
$value);
|
$value);
|
||||||
$clauses[] = '('.implode(') AND (', $clause).')';
|
}
|
||||||
|
|
||||||
|
if ($parts) {
|
||||||
|
if (count($parts) > 1) {
|
||||||
|
$clause[] = '('.implode(') OR (', $parts).')';
|
||||||
|
} else {
|
||||||
|
$clause[] = head($parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($clause) {
|
||||||
|
if (count($clause) > 1) {
|
||||||
|
$clauses[] = '('.implode(') AND (', $clause).')';
|
||||||
|
} else {
|
||||||
|
$clauses[] = head($clause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === null) {
|
||||||
|
$accumulated[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'%Q IS NULL',
|
||||||
|
$field);
|
||||||
|
} else {
|
||||||
$accumulated[] = qsprintf(
|
$accumulated[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'%Q = %Q',
|
'%Q = %Q',
|
||||||
$field,
|
$field,
|
||||||
$value);
|
$value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return '('.implode(') OR (', $clauses).')';
|
return '('.implode(') OR (', $clauses).')';
|
||||||
}
|
}
|
||||||
|
@ -576,8 +636,28 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
$field = qsprintf($conn, '%T', $column);
|
$field = qsprintf($conn, '%T', $column);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idx($part, 'type') === 'null') {
|
$null = idx($part, 'null');
|
||||||
$field = qsprintf($conn, '(%Q IS NULL)', $field);
|
if ($null) {
|
||||||
|
switch ($null) {
|
||||||
|
case 'head':
|
||||||
|
$null_field = qsprintf($conn, '(%Q IS NULL)', $field);
|
||||||
|
break;
|
||||||
|
case 'tail':
|
||||||
|
$null_field = qsprintf($conn, '(%Q IS NOT NULL)', $field);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'NULL value "%s" is invalid. Valid values are "head" and '.
|
||||||
|
'"tail".',
|
||||||
|
$null));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descending) {
|
||||||
|
$sql[] = qsprintf($conn, '%Q DESC', $null_field);
|
||||||
|
} else {
|
||||||
|
$sql[] = qsprintf($conn, '%Q ASC', $null_field);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($descending) {
|
if ($descending) {
|
||||||
|
|
Loading…
Reference in a new issue